如何正确计算Fisher变换指标

14
我正在编写一个小型的技术分析库,其中包含TA-lib中没有的项目。我从cTrader找到了一个示例,并将其与TradingView版本中的代码进行了匹配。
以下是TradingView中的Pine Script代码:
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]

以下是我尝试实现该指示器的方法:

    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类和CandleSeries类

数学助手

问题

输出值似乎在预期范围内,但我的Fisher变换交叉点与TradingView版本的指标不匹配。

问题

如何在C#中正确实现Fisher Transform指标?我希望这能够与TradingView的Fisher Transform输出匹配。

我知道的

我已经检查了自己编写的其他指标以及TA-Lib的指标,并通过了单元测试。我还逐个蜡烛图检查了我的数据与TradingView数据是否匹配,并发现我的数据与预期相符。因此,我不认为我的数据有问题。

具体细节

CSV数据-NFLX 5分钟agg

下图显示了应用于TradingView图表的上述Fisher变换代码。我的目标是尽可能接近匹配此输出。

enter image description here

Fisher Cyan Trigger Magenta

预期输出:

于东部时间15:30完成交叉

  • 大致 Fisher 值为2.86

  • 大致 Trigger 值为1.79

于东部时间10:45完成交叉

  • 大致 Fisher 值为-3.67

  • 大致 Trigger 值为-3.10

实际输出:

于东部时间15:30完成交叉

  • 我的 Fisher 值为1.64

  • 我的 Trigger 值为1.99

于东部时间10:45完成交叉

  • 我的 Fisher 值为-1.63

  • 我的 Trigger 值为-2.00

Bounty

为了让您的生活更加轻松,我提供了一个小型控制台应用程序,其中包括通过和失败的单元测试。所有单元测试都针对相同的数据集进行。通过的单元测试来自经过测试的“简单移动平均”指标。失败的单元测试是针对所讨论的“Fisher变换”指标进行的。

项目文件 (更新于5/14)

帮助我通过FisherTransform测试,我会奖励赏金。

enter image description here

如果需要额外的资源或信息,请在评论中提出。

我会考虑的替代答案

  • 提交您自己在C#中工作的FisherTransform

  • 解释为什么我的FisherTransform实际上是按预期工作的


1
那么我们如何测试自己是否正确呢?我们如何知道我们是否按照您的规格使其正常工作? - TheGeneral
1
我认为你需要更加深入地思考这个问题,TradingViews的实现可能存在错误,而且我们没有测试数据可以匹配,只有你测试并告诉我们这个方法不起作用的结果。我的意思是,这个问题可能会一直持续下去。 - TheGeneral
1
我同意@TheGeneral的观点,我发现不止一次TradingView公式没有意义。他们的商业模式主要依赖于用户提供的、可能未经验证的公式。 - Dave Skender
@DaveSkender 没问题,我现在可以访问你的 Fisher Transform ;) - Dan Beaulieu
1
为了比较,可以随时查看我在开源.NET库中实现的Ehlers Fisher Transform。我在GitHub存储库中包含手动计算以供比较。 - Dave Skender
显示剩余3条评论
1个回答

6
代码有两个错误。
1)有多余的括号。正确的代码应该是:
``` ```
_value1 = Maths.Normalize(0.66m * (Maths.Divide(Series.Average[index] - _minLow, Math.Max(_maxHigh - _minLow, 0.001m)) - 0.5m) + 0.67m * _lastValue1);

2) 最小值和最大值函数必须满足以下要求:

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) 测试参数混乱,请重新检查您的单元测试值。更新后测试仍未修复。例如,第一个FisherTransforms_ValuesAreReasonablyClose_First()有混合值。

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;

我马上尝试一下。单元测试值是从TradingView指标中派生出来的,如OP中所圈出的图像所示。FisherTransform信号是基于交叉点创建的。我的目标是使这些交叉点与我在TradingView上看到的交叉点相当接近,因为我们正在使用与TradingView相同的计算方法。 - Dan Beaulieu
1
我不相信这些测试,因为在一行代码中存在Fish和Trigger之间的比较,而在另一个地方,它们明显被放错了位置。 - Andrey Chistyakov
我知道了。那我一定是犯了个错误。我会检查我的代码并重新上传项目的。谢谢。 - Dan Beaulieu
1
int startIndex 必须是1,而不是0。 Lowest() 和 Highest() 函数必须通过 value[index] 正确初始化(不能为0或9999)。也许还有其他问题,但我卡在测试中了。 - Andrey Chistyakov
1
Unittests 中的另一个错误(我没有测试)是时区问题。CSV 文件使用了 +5 时区(我想可能是你的)。但是 Unittest 代码中提到了本地时区 (new DateTime(2019, 05, 10, 10, 45, 00))。我的时区不同于你的。 - Andrey Chistyakov
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接