I'm writing a small technical analysis library that consists of items that are not availabile in TA-lib. I've started with an example I found on cTrader and matched it against the code found in the TradingView version.
Here's the Pine Script code from TradingView:
len = input(9, minval=1, title="Length")
high_ = highest(hl2, len)
low_ = lowest(hl2, len)
round_(val) => val > .99 ? .999 : val < -.99 ? -.999 : val
value = 0.0
value := round_(.66 * ((hl2 - low_) / max(high_ - low_, .001) - .5) + .67 * nz(value[1]))
fish1 = 0.0
fish1 := .5 * log((1 + value) / max(1 - value, .001)) + .5 * nz(fish1[1])
fish2 = fish1[1]
Here's my attempt to implement the indicator:
public class FisherTransform : IndicatorBase
{
public int Length = 9;
public decimal[] Fish { get; set; }
public decimal[] Trigger { get; set; }
decimal _maxHigh;
decimal _minLow;
private decimal _value1;
private decimal _lastValue1;
public FisherTransform(IEnumerable<Candle> candles, int length)
: base(candles)
{
Length = length;
RequiredCount = Length;
_lastValue1 = 1;
}
protected override void Initialize()
{
Fish = new decimal[Series.Length];
Trigger = new decimal[Series.Length];
}
public override void Compute(int startIndex = 0, int? endIndex = null)
{
if (endIndex == null)
endIndex = Series.Length;
for (int index = 0; index < endIndex; index++)
{
if (index == 1)
{
Fish[index - 1] = 1;
}
_minLow = Series.Average.Lowest(Length, index);
_maxHigh = Series.Average.Highest(Length, index);
_value1 = Maths.Normalize(0.66m * ((Maths.Divide(Series.Average[index] - _minLow, Math.Max(_maxHigh - _minLow, 0.001m)) - 0.5m) + 0.67m * _lastValue1));
_lastValue1 = _value1;
Fish[index] = 0.5m * Maths.Log(Maths.Divide(1 + _value1, Math.Max(1 - _value1, .001m))) + 0.5m * Fish[index - 1];
Trigger[index] = Fish[index - 1];
}
}
}
IndicatorBase class and CandleSeries class
Math Helpers
The problem
The output values appear to be within the expected range however my Fisher Transform cross-overs do not match up with what I am seeing on TradingView's version of the indicator.
Question
How do I properly implement the Fisher Transform indicator in C#? I'd like this to match TradingView's Fisher Transform output.
What I Know
I've check my data against other indicators that I have personally written and indicators from TA-Lib and those indicators pass my unit tests. I've also checked my data against the TradingView data candle by candle and found that my data matches as expected. So I don't suspect my data is the issue.
CSV Data - NFLX 5 min agg
Pictured below is the above-shown Fisher Transform code applied to a TradingView chart. My goal is to match this output as close as possible.
Fisher Cyan Trigger Magenta
Expected Outputs:
Crossover completed at 15:30 ET
Approx Fisher Value is 2.86
Approx Trigger Value is 1.79
Crossover completed at 10:45 ET
Approx Fisher Value is -3.67
Approx Trigger Value is -3.10
My Actual Outputs:
Crossover completed at 15:30 ET
My Fisher Value is 1.64
My Trigger Value is 1.99
Crossover completed at 10:45 ET
My Fisher Value is -1.63
My Trigger Value is -2.00
To make your life easier I'm including a small console application complete with passing and failing unit tests. All unit tests are conducted against the same data set. The passing unit tests are from a tested working Simple Moving Average indicator. The failing unit tests are against the Fisher Transform indicator in question.
Project Files (updated 5/14)
Help get my FisherTransform tests to pass and I'll award the bounty.
Just comment if you need any additional resources or information.
Alternative Answers that I'll consider
Submit your own working FisherTransform in C#
Explain why my FisherTransform is actually working as expected
This indicator never repaints and if you want a reliable indicators, search all indicators coded by Igorad. Goodluck! I have tested about 12 of the fisher based indicators and found most to repaint but 3 do not.
The Inverse Fisher Transform (IFISH) was authored by John Ehlers. The IFISH applies some math functions and constants to a weighted moving average (wma) of the relative strength index (rsi) of the closing price to calculate its oscillator position. The user may change the input (close) and period lengths.
The code has two errors.
1) wrong extra brackets. The correct line is:
_value1 = Maths.Normalize(0.66m * (Maths.Divide(Series.Average[index] - _minLow, Math.Max(_maxHigh - _minLow, 0.001m)) - 0.5m) + 0.67m * _lastValue1);
2) Min and max functions must be:
public static decimal Highest(this decimal[] series, int length, int index)
{
var maxVal = series[index]; // <----- HERE WAS AN ERROR!
var lookback = Math.Max(index - length, 0);
for (int i = index; i-- > lookback;)
maxVal = Math.Max(series[i], maxVal);
return maxVal;
}
public static decimal Lowest(this decimal[] series, int length, int index)
{
var minVal = series[index]; // <----- HERE WAS AN ERROR!
var lookback = Math.Max(index - length, 0);
for (int i = index; i-- > lookback;)
{
//if (series[i] != 0) // <----- HERE WAS AN ERROR!
minVal = Math.Min(series[i], minVal);
}
return minVal;
}
3) confusing test params. Please recheck your unittest values. AFTER THE UPDATE TESTS STILL NOT FIXED. For an example, the first FisherTransforms_ValuesAreReasonablyClose_First()
has mixed values
var fish = result.Fish.Last(); //is equal to -3.1113144510775780365063063706
var trig = result.Trigger.Last(); //is equal to -3.6057793808025449204415435710
// TradingView Values for NFLX 5m chart at 10:45 ET
var fisherValue = -3.67m;
var triggerValue = -3.10m;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With