0%

蜡烛图形态识别

Intro

刚入职的时候,还以为实习的内容是机器学习,后来才发现工作内容主要是做曲线形态识别。其实我对此非常开心,因为已经慢慢地对机器学习有了一点厌倦。而做更加基础的工作更加能令我感到安心。

如果形态识别模型的效果良好,以后能够被用在机器自动标注上,可以省去人工标注的时间成本,给各种技术形态打上标注。这样就可以得到海量的技术形态数据,为日后结合机器学习算法、使用技术形态相关的交易策略铺路。

什么是技术形态

大概可以理解为某物的价格走势(呈现形式为蜡烛图)出现了某个特定的形态。比如:圆弧底、双底、上升三角形等。

圆弧底
双底
上升三角形

技术形态有用吗

纯粹使用技术形态作为买入或者卖出的信号是无效的。例如,圆弧底在技术形态分析中被认为是上涨的信号。但根据历史数据回测,圆弧底的5日胜率也只是在 50% 上下。

技术形态作为辅助信号,结合其它信息可能会有奇效。

如何识别技术形态

技术形态在业界没有被严格地定义,学术界似乎也没有对此作太多研究,各种长度和幅度的定义也各说纷纭。(可能是因为效果感人?)

所以,在没有严格定义的情况下,技术形态识别变成了一个视觉问题——到底长得像不像。

由于数据是离散的,蜡烛图通常也不平滑,所以严格来讲,“曲线”在蜡烛图形态识别中并不存在。观察某段蜡烛图像不像某种形态,主要是看“趋势”像不像。再以圆弧底为例子,总体来看,它的“一阶导数”单调递增,也就是“二阶导数”为正,但如果出现了一些不影响总体趋势的“毛刺”,我们也认为它是圆弧底。

毛刺

这给识别带来了问题:只用连续曲线的特点作为识别的话,条件太过严格,以致召回率非常低;如果只用规则而不使用曲线的特征,又难以把握总体的趋势,准确率非常低。

所以,总体的思路便是:首先用简单的规则过滤,再用算法对历史数据作匹配,最后使用复杂的规则过滤。

算法

DTW (Dynamic Time Warping)

介绍

DTW 示例

DTW 算法在以前被运用在语音识别中。不同的人在说同一个词或者句子的时候,声调、发音长度不一定相同,但他们发出的声音在波形上相似。DTW 算法就是被用来解决这个问题的,它可以识别被伸缩的相似信号。

上图展示了 DTW 算法的简单示例。对于测试序列的每个点,都在模板序列中找到了与其“趋势”最相近的一个点。

原理

DTW 算法与一票 String Similarity 的算法很像,比如 Edit Distance, Affine Gap, Smith Waterman 等等。这些算法通常都将两段序列 $S_1$, $S_2$ 以矩阵的形式展开,寻找从起始点 $(S_1[0], S_2[0])$ 开始,到两个序列的终点 $(S_1[m], S_2[n])$ 的最短路径。

而寻找最短路径的方法,是对于矩阵中的每个点,都寻找距离最短的相邻点作为回溯方向。这样一来,得到的总距离与实际最短距离已经比较接近,甚至相同。

如此在局部取最优的方法是贪心算法,找出的结果往往是陷入局部最优的产物,而不能找到全局最优。但为了保证计算速度,局部最优在多数时候也是有效的。

Smith Waterman
与 String Similarity 算法的区别

String Similarity 检测的字符串,每个元素之间的距离只有 匹配不匹配(插入或缺失) 两种情况;而 DTW 算法检测的序列多数是数字信号,每个元素之间的距离可以是任意值。

算法步骤
  1. 先把模板序列和测试序列每个元素之间的距离计算出来,构成一个 $M \times N$ 矩阵
  2. 初始化左下角 $$g(1,1) = k_2 \cdot d(1,1)$$
  3. 初始化底边 $$g(1,j) = g(1,j-1) + k_3 \cdot d(1,j)$$
  4. 初始化左边 $$g(i,1) = g(i-1,1) + k_1 \cdot d(i,1)$$
  5. 计算内部 DTW 值
    $$ g(i,j) = min( g(i-1,j) + k_1 \cdot d(i,j), g(i-1,j-1) + k_2 \cdot d(i,j), g(i,j-1) + k_3 \cdot d(i,j) ) $$

其中 $k_1, k_2, k_3$ 可以针对不同场景作优化
6. 得到终点的距离后进行回溯

DTW 得出的最短路径

图中较大的数字是距离,较小的数字是 DTW 值,取 $k_1 = 1, k_2 = 2, k_3 = 1$。

DTW 的 Python 实现

此处取 $k_1=k_2=k_3=1$。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from numpy import zeros, inf, ndim
from scipy.spatial.distance import cdist


def fastdtw(x, y, warp=1):

assert len(x)
assert len(y)

if ndim(x) == 1:
x = x.reshape(-1, 1)

if ndim(y) == 1:
y = y.reshape(-1, 1)

r, c = len(x), len(y)
d0 = zeros((r + 1, c + 1))
d0[0, 1:] = inf
d0[1:, 0] = inf
d1 = d0[1:, 1:]
d0[1:, 1:] = cdist(x, y)
for i in range(r):
for j in range(c):
min_list = [d0[i, j]]
for k in range(1, warp + 1):
min_list += [d0[min(i + k, r - 1), j], d0[i, min(j + k, c - 1)]]
d1[i, j] += min(min_list)

acc = d1

return acc

如何在形态识别中使用 DTW

1
2
3
4
5
6
7
8
9
10
# 遍历股票
for stock in stock_list:
# 遍历右端点
for endpoint in dates:
# 遍历窗口长度
for length in length_range:
if dtw(series) < threshold:
pattern_list.append(series)
# 已经识别出来了,跳过这一段
endpoint += max(length_range)

建立规则系统

DTW 算法识别出来的测试序列在趋势上与模板吻合,但是由于蜡烛图形态识别对形状非常敏感,而 DTW 算法不能做到这一点。比如一些不对称的底部或是尖锐的底部也会被识别出来,这不符合我们的要求。

False Positive

对此,我们有另外的方法——建立复杂的规则系统。以下用圆弧底作为例子。

步骤(替换DTW)

  1. 遍历方法同上
  2. 每日柱体的较大值组成序列 $S_1$,每日柱体的较小值组成序列 $S_2$
  3. 对于 $S_1$ 和 $S_2$,都要满足以下条件:
    1. 序列与拟合的二次曲线距离小于 $threshold_1$
    2. 序列每点的残差小于 $threshold_2$
    3. 二次曲线 $\alpha$ 在某区间内(开口向上且不能太大/太小)
    4. 两端要相对水平(标准化后两端高度差值不能大于 $threshold_3$)
    5. 底部波动不能太大(可以用差分值来控制)

效果

规则系统识别的圆弧底