FisherRVI
Download scriptThe Fisher Stochastic Relative Vigor Index (Fisher RVI) is a sophisticated technical analysis tool designed to help traders identify market momentum and potential turning points. Based on specialized cybernetic analysis principles, this indicator combines several different types of price movement data to create a smoother, more reliable signal than standard oscillators.
How the Logic Works
The script processes market data through a multi-stage calculation to filter out "noise" and highlight true trends:
1. Movement Calculation (Vigor) Instead of looking at simple price changes, the tool first calculates two specific types of movement:
- Body Dynamics: It looks at the difference between the opening and closing prices over a four-period window. This helps determine the "strength" of the candle bodies.
- Range Dynamics: It measures the distance between the high and low prices over that same period. This represents the total volatility or "reach" of the market.
2. The Relative Vigor Index (RVI) The script then creates a ratio by dividing the Body Dynamics by the Range Dynamics. By comparing how much price actually moved (the body) against the total space available (the high/low range), it determines whether the current trend has real substance or is merely volatile fluctuation.
3. Normalization and Smoothing Once the RVI is established, the script performs two more refinements:
- Relative Positioning: It looks at the highest and lowest values achieved over a specific look-back period (defined by the user). It then scales the current value to sit between 0 and 1, effectively "squashing" extreme outliers to keep the data readable.
- Weighted Averaging: To prevent the indicator from jumping erratically, it applies a weighted average to these normalized values, giving more importance to recent price action while maintaining the context of the preceding bars.
4. The Fisher Transformation The final step applies a mathematical "Fisher Transformation." This is a specialized formula that converts the smoothed data into a logarithmic format. The goal of this transformation is to make the indicator less sensitive to sudden, insignificant price spikes while making it highly responsive to significant trend shifts.
Visual Output and Usage
On your trading chart, the script generates two distinct lines:
- The Main Line (Fisher RVI): This represents the primary momentum. When it moves sharply upward or downward, it signals a strengthening trend.
- The Trigger Line: This acts as a reference point (delayed by one period). Traders typically look for "crossovers"—where the main line crosses over the trigger line—as these are often interpreted as entry or exit signals for a trade.
//+------------------------------------------------------------------+
//| FisherRVI.mq4 |
//| |
//| Fisher Stochastic Relative Vigor Index Indicator |
//| |
//| Algorithm taken from book |
//| "Cybernetics Analysis for Stock and Futures" |
//| by John F. Ehlers |
//| |
//| contact@mqlsoft.com |
//| http://www.mqlsoft.com/ |
//+------------------------------------------------------------------+
#property copyright "Coded by Witold Wozniak"
#property link "www.mqlsoft.com"
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_color1 Red
#property indicator_color2 Blue
#property indicator_level1 0
double FisherRVI[];
double Trigger[];
double RVI[];
double Value1[];
double Value2[];
double Value3[];
double Value4[];
extern int Length = 8;
int buffers = 0;
int drawBegin = 0;
int init() {
drawBegin = Length;
initBuffer(FisherRVI, "Fisher RVI", DRAW_LINE);
initBuffer(Trigger, "Trigger", DRAW_LINE);
initBuffer(RVI);
initBuffer(Value1);
initBuffer(Value2);
initBuffer(Value3);
initBuffer(Value4);
IndicatorBuffers(buffers);
IndicatorShortName("Fisher RVI [" + Length + "]");
return (0);
}
int start() {
if (Bars <= drawBegin) return (0);
int countedBars = IndicatorCounted();
if (countedBars < 0) return (-1);
if (countedBars > 0) countedBars--;
int s, limit = Bars - countedBars - 1;
for (s = limit; s >= 0; s--) {
Value1[s] = ((Close[s] - Open[s]) +
2.0 * (Close[s + 1] - Open[s + 1]) +
2.0 * (Close[s + 2] - Open[s + 2]) +
(Close[s + 3] - Open[s + 3])) / 6.0;
Value2[s] = ((High[s] - Low[s]) +
2.0 * (High[s + 1] - Low[s + 1]) +
2.0 * (High[s + 2] - Low[s + 2]) +
(High[s + 3] - Low[s + 3])) / 6.0;
double Num = 0.0;
double Denom = 0.0;
for (int count = 0; count < Length; count++) {
Num += Value1[s + count];
Denom += Value2[s + count];
}
if (Denom != 0.0) {
RVI[s] = Num / Denom;
} else {
RVI[s] = 0.0;
}
double hh = RVI[s], ll = RVI[s];
for (int i = 0; i < Length; i++) {
double tmp = RVI[s + i];
hh = MathMax(hh, tmp);
ll = MathMin(ll, tmp);
}
Value3[s] = 0.0;
if (hh != ll) {
Value3[s] = (RVI[s] - ll) / (hh - ll);
}
Value4[s] = (4.0 * Value3[s] + 3.0 * Value3[s + 1] + 2.0 * Value3[s + 2] + Value3[s + 3]) / 10.0;
FisherRVI[s] = 0.5 * MathLog((1.0 + 1.98 * (Value4[s] - 0.5)) / (1.0 - 1.98 * (Value4[s] - 0.5)));
Trigger[s] = FisherRVI[s + 1];
}
return (0);
}
void initBuffer(double array[], string label = "", int type = DRAW_NONE, int arrow = 0, int style = EMPTY, int width = EMPTY, color clr = CLR_NONE) {
SetIndexBuffer(buffers, array);
SetIndexLabel(buffers, label);
SetIndexEmptyValue(buffers, EMPTY_VALUE);
SetIndexDrawBegin(buffers, drawBegin);
SetIndexShift(buffers, 0);
SetIndexStyle(buffers, type, style, width);
SetIndexArrow(buffers, arrow);
buffers++;
}
Comments