如何识别股票价格数据中的转折点

9
这个问题是此问题的延续。
我的目标是找到股票价格数据中的拐点。
到目前为止,我已经尝试使用Dr. Andrew Burnett-Thompson的中心五点法对平滑后的价格集进行微分,如此处所述。
我使用EMA20的tick数据对数据集进行平滑处理。
对于图表上的每个点,我获得了1阶导数(dy/dx)。我为拐点创建了第二个图表。每当dy/dx在[-一些小值]和[+一些小值]之间时,我就向该图表添加一个点。
问题在于: 我没有得到真正的拐点,只是得到了接近的结果。 我得到的点太多或太少——取决于[某个小值]
我尝试了第二种方法,即当dy/dx从负变为正时添加一个点,但由于我使用的是tick数据的EMA(而不是1分钟收盘价的EMA),所以可能会创建太多的点。
第三种方法是将数据集分成n个点的片段,并找到最小点和最大点。这种方法效果很好(不完美),但有滞后。
有没有更好的方法?
我附上了两张输出图片(1阶导数和n个点的最小/最大值)。

1
为什么这被标记为“图算法”? - harold
@harold 我猜他想要一个算法,而且输入数据可以绘制成图形(见上文)。;D 更严肃地说,这显然不是一个图形算法。 - Patrick87
标签已删除,现在你有解决方法的想法了吗?谢谢。 - Yaron
你是否使用了“拐点”的正确定义?在微积分中,这个术语更常用于函数从凸到凹或者从凹到凸的转折点,也就是二阶导数的符号变化。 - Lutz Lehmann
4个回答

4
您可以考虑计算二阶导数,这意味着您需要额外(在一阶导数的基础上)评估 (y_{i-1} + y_{i+1} - 2y_i) / (dx)²。如果这个值超过了某个特定的阈值,那么您就有了一个极大值;如果它低于阈值,则是一个极小值;否则,您可以将其丢弃。这样做应该会排除很多点,这些点您一直在使用找到极值的方法(y' = 0),因为这个条件也适用于鞍点。

1

基于这里的一些想法,另一种方法是:对于系列中的每个点,查看之前和之后的n个点(窗口)。如果当前点的值是窗口中最高的,则将其作为峰值转折点(如果最低,则将其作为低谷)。在系列中排除第一个和最后一个n个点。

使用月度数据进行实验,并得到了以下结果,其中n=6。 enter image description here


1

感谢Patrick87的工作,以下是实现相同功能的Java函数:

假设StockPrices是一个键为日期、值为股票价格(价格、平均值,其中x=5)的映射表

private double getCx(StockPrices stockPrices, LocalDate executionDate, int x, double m) { return Math.abs(getFx(stockPrices, executionDate) - getGx(stockPrices, executionDate)) - m * getHx(stockPrices, executionDate, x); }

private double getGx(StockPrices stockPrices, LocalDate executionDate) {
    return stockPrices.getAvg(executionDate, 5);
}

private double getFx(StockPrices stockPrices, LocalDate executionDate) {
    return stockPrices.getPrice(executionDate);
}

public double getHx(StockPrices stockPrice, LocalDate localDate, int x) {
    //standard deviation
    return Math.sqrt(getVariance(stockPrice, localDate, x));
}

private double getVariance(StockPrices stockPrice, LocalDate localDate, int x) {
    double sum = 0;
    int count = 0;
    for (int i = - (x / 2); i <= (x / 2) ; i++) {
        LocalDate date = localDate.with(BusinessDay.add(localDate, i, stockPrice.getPriceMap(), 2));
        double avg = stockPrice.getAvg(date, 5);
        double price = stockPrice.getPrice(date);
        if (price != 0.0) {
            sum += Math.pow((price - avg), 2);
            count++;
        }
    }
    return sum / count;
}

请问您能提供完整的源代码吗?有一些实现缺失。 - endeffects

1

这里只是一个想法,从不同的角度来看,可能是一个非常糟糕的想法,但由于差异化并没有起作用,类似这样的东西可能会是一个思路。

首先,您需要确定最小的有意义的X轴间隔。在您的图中,如果您将其设置得太小,您将从凸起处得到错误的正面结果。这在概念上类似于平滑数据的想法。将此间隔称为dx。

接下来,使用大小为dx的滑动窗口,生成与您的曲线相对应的移动平均曲线。有很多不同的方法可以考虑这样做(去除统计异常值,或者在窗口中使用更多或更少的点)。将此曲线称为g(x),您的原始曲线为f(x)。此外,制作一条曲线h(x),该曲线给出了您用于计算g(x)的滑动窗口中数据变异性的某些度量(如果您从区间中使用了几个点,则标准偏差应该可以正常工作)。

现在,开始计算形式为 c_m(x) = |f(x) - g(x)| - m * h(x) 的曲线。您可以从 m = 1 开始。对于任何使 c_m(x) 为正数的点都是局部极小值/极大值的候选点。根据您得到的结果,您可以开始增加或减少 m。您可以通过类似于二分搜索的方式来实现:如果您想要更多的点,请将 m = (min + m) / 2,如果您想要更少的点,请将 m = (max + m) / 2(相应地调整 min 和 max)。
因此,这里有一个我建议的示例。假设我们有以下系列:
f(x) = [  1,   2,   4,   3,   2,   3,   6,   7,   8,   7, 
          5,   4,   3,   2,   2,   3,   2,   3,   5,   8,   9]

我们选择dx = 5。我们通过取x周围点的简单平均值来构造g(x):

g(x) = [2.3, 2.5, 2.4, 2.8, 3.6, 4.2, 5.2, 6.2, 6.6, 6.2, 
        5.4, 4.2, 3.2, 2.8, 2.4, 2.4, 3.0, 4.2, 5.4, 6.3, 7.3]

h(x) = [1.2, 1.1, 1.0, 0.7, 1.4, 2.4, 2.3, 1.7, 1.0, 1.5,
        1.9, 1.7, 1.2, 0.7, 0.5, 0.6, 1.1, 2.1, 2.7, 2.4, 1.7]

当 m = 1 时,我们得到:

c(x) = [0.1, xxx, 0.6, xxx, 0.2, xxx, xxx, xxx, 0.4, xxx,
        xxx, xxx, xxx, 0.1, xxx, 0.0, xxx, xxx, xxx, xxx, 0.0]

实际上,这似乎相当有效。欢迎分享想法。请注意,这可能是平均值定理的等价物。


你能详细说明一下你是如何从f(x)到达g(x)的吗?f(x)和g(x)具有相同数量的数据点,所以我不认为这可能只是一个简单的移动平均? - Ivan
啊,我明白了,这是一个移动平均。 - Ivan
@Patrick87 最后一个从5到8的尖峰,9有一个c(x) = 0.0,意味着最后一个极小值是平的吗? - Rock
@patrick87,我可以问你几个关于建议的问题吗?在这些评论中进行对话很困难。 - dchhetri
@Ivan 移动平均和移动均值有什么不同?我还在努力弄清楚为什么g(0) = 2.3。我原以为它应该是(1+2+4+3+2)/5 = 2.4。 - dchhetri
@dchhetri 我不知道移动平均和移动均值之间有任何区别。我可能计算了其中一个数据点。 - Patrick87

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