之前在研究用机器学习库Sci-kit做计算指标(特征值)和金融产品趋势(分类)关系学习的时候,对于如何判断趋势,是直接使用当前之后5根k线close值做线性回归,如果拟合的P值可信的直线斜率向上则是上涨,斜率向下则是下跌。具体代码可以见之前我之前blog。
在vnpy有个网友讨论,为什么用这样方法判断趋势的时候;我做了些搜索,才发现判断一组时序队列的趋势并不是一个简单的事情,虽然人眼看很直接,但是数学分析并不简单,尤其考虑置信范围和变点存在。
-
所谓置信范围一两句说不清,可以看看这个问答,我的理解95%置信区间就是在这个一个时序队列里面95%都是符合上涨趋势,但是存在5%可能不符合:
-
突变点:变点理论是统计学中的一个经典分支,其基本定义是在一个序列或过程中,当某个统计特性(分布类型、分布参数)在某时间点受系统性因素而非偶然性因素影响发生变化,我们就称该时间点为变点。变点识别即利用统计量或统计方法将该变点位置估计出来。这里主要是用数学方法,过滤出偶尔出现的异常点,和真正改变规律的变点。
其实趋势检测和突变点检测是很数据挖掘很经典的话题,尤其在气象,水文,医药验证等,使用方法很多,从简单的均值,方差,到概率线性回归等。这里主要讲 Mann-Kendall检验法 。
其他的,网上搜到的好多:
1。均值突变的检测方法有:(1)低通滤波法;(2)t-检验法;(3)Cramer法;(4)滑动t-检验法;(5)Yamaoto法;(6)M-K法;(7)最小二乘法;(8)连续滑动t-检验法;(9)Pttitt法;(10)Lepage法;(11)局部比较法。等等。在均值突变分析中,除了突变点、突变个数的估计外,一般还要分析跃变度等。
2。方差突变的检测方法有:(1)F-检验法;(2)滑动F-检验法;(3)连续滑动F-检验法。等等。
3。线性回归突变(也称趋势突变)的检测方法有:(1)最小二乘法;(2)局部比较法;滑动参数法等等。
4。概率突变的检测方法有:(1)极大似然法;(2)累计次数法;(3)Bayes法;等等。
曼-肯德尔法又称Mann—Kenddall 检验法,是一种气候诊断与预测技术,应用Mann-Kendall检验法可以判断气候序列中是否存在气候突变,如果存在,可确定出突变发生的时间。Mann-Kendall检验法也经常用于气候变化影响下的降水、干旱频次趋势检测。Mann—Kenddall的检验方法是非参数方法。非参数检验方法亦称无分布检验,其优点是不需要样本遵从一定的分布,也不受少数异常值的干扰,更适用于类型变量和顺序变量,计算也比较简便。
具体算法推导不讲了,主要有两种用法,一个是计算这个时序队列的趋势;还有一个是算这个时序队列是否有变点,如果有,是队列中几个点。
代码之前自己写了一些,后来在这个链接发现一个非常全的,修改引用了。他还有其他几个突变点算法的python程序化,可以学习。
一,趋势校验,算法如下
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
32
33
34
35
36
37
38
39
40
41
|
def Mann_Kenddall_Trend_desc(inputdata):
# 计算总趋势秩次和
inputdata = np.array(inputdata)
n = inputdata.shape[ 0 ]
sum_sgn = 0
for i in np.arange(n):
if i < = (n - 1 ):
for j in np.arange(i + 1 ,n):
if inputdata[j] > inputdata[i]:
sum_sgn = sum_sgn + 1
elif inputdata[j] < inputdata[i]:
sum_sgn = sum_sgn - 1
else :
sum_sgn = sum_sgn
# 计算Z统计值
if n < = 10 :
Z_value = sum_sgn / (n * (n - 1 ) / 2 )
else :
if sum_sgn > 0 :
Z_value = (sum_sgn - 1 ) / np.sqrt(n * (n - 1 ) * ( 2 * n + 5 ) / 18 )
elif sum_sgn = = 0 :
Z_value = 0
else :
Z_value = (sum_sgn + 1 ) / np.sqrt(n * (n - 1 ) * ( 2 * n + 5 ) / 18 )
# 趋势描述
# 99% ——> +—2.576
# 95% ——> +—1.96
# 90% ——> +—1.645
if np. abs (Z_value) > 1.96 and np. abs (Z_value) < = 2.576 :
if Z_value > 0 :
result_desc = u "95% up"
else :
result_desc = u "95% down"
elif np. abs (Z_value) > 2.576 :
if Z_value > 0 :
result_desc = u "99% up"
else :
result_desc = u "99% down"
else :
result_desc = u "not trendency"
return result_desc
|
二,突变点校验
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
def Kendall_change_point_detection(inputdata):
inputdata = np.array(inputdata)
n = inputdata.shape[ 0 ]
# 正序列计算---------------------------------
# 定义累计量序列Sk,初始值=0
Sk = [ 0 ]
# 定义统计量UFk,初始值 =0
UFk = [ 0 ]
# 定义Sk序列元素s,初始值 =0
s = 0
Exp_value = [ 0 ]
Var_value = [ 0 ]
# i从1开始,因为根据统计量UFk公式,i=0时,Sk(0)、E(0)、Var(0)均为0
# 此时UFk无意义,因此公式中,令UFk(0)=0
for i in range ( 1 ,n):
for j in range (i):
if inputdata[i] > inputdata[j]:
s = s + 1
else :
s = s + 0
Sk.append(s)
Exp_value.append((i + 1 ) * (i + 2 ) / 4 ) # Sk[i]的均值
Var_value.append((i + 1 ) * i * ( 2 * (i + 1 ) + 5 ) / 72 ) # Sk[i]的方差
UFk.append((Sk[i] - Exp_value[i]) / np.sqrt(Var_value[i]))
# ------------------------------正序列计算
# 逆序列计算---------------------------------
# 定义逆序累计量序列Sk2,长度与inputdata一致,初始值=0
Sk2 = [ 0 ]
# 定义逆序统计量UBk,长度与inputdata一致,初始值=0
UBk = [ 0 ]
UBk2 = [ 0 ]
# s归0
s2 = 0
Exp_value2 = [ 0 ]
Var_value2 = [ 0 ]
# 按时间序列逆转样本y
inputdataT = list ( reversed (inputdata))
# i从2开始,因为根据统计量UBk公式,i=1时,Sk2(1)、E(1)、Var(1)均为0
# 此时UBk无意义,因此公式中,令UBk(1)=0
for i in range ( 1 ,n):
for j in range (i):
if inputdataT[i] > inputdataT[j]:
s2 = s2 + 1
else :
s2 = s2 + 0
Sk2.append(s2)
Exp_value2.append((i + 1 ) * (i + 2 ) / 4 ) # Sk[i]的均值
Var_value2.append((i + 1 ) * i * ( 2 * (i + 1 ) + 5 ) / 72 ) # Sk[i]的方差
UBk.append((Sk2[i] - Exp_value2[i]) / np.sqrt(Var_value2[i]))
UBk2.append( - UBk[i])
# 由于对逆序序列的累计量Sk2的构建中,依然用的是累加法,即后者大于前者时s加1,
# 则s的大小表征了一种上升的趋势的大小,而序列逆序以后,应当表现出与原序列相反
# 的趋势表现,因此,用累加法统计Sk2序列,统计量公式(S(i)-E(i))/sqrt(Var(i))
#也不应改变,但统计量UBk应取相反数以表征正确的逆序序列的趋势
# UBk(i)=0-(Sk2(i)-E)/sqrt(Var)
# ------------------------------逆序列计算
# 此时上一步的到UBk表现的是逆序列在逆序时间上的趋势统计量
# 与UFk做图寻找突变点时,2条曲线应具有同样的时间轴,因此
# 再按时间序列逆转结果统计量UBk,得到时间正序的UBkT,
UBkT = list ( reversed (UBk2))
diff = np.array(UFk) - np.array(UBkT)
K = list ()
# 找出交叉点
for k in range ( 1 ,n):
if diff[k - 1 ] * diff[k]< 0 and (UFk[k] > 1.96 or UFk[k] < - 1.96 ) :
K.append(k)
# 做突变检测图时,使用UFk和UBkT
return K
|
最后,我尝试着用M-K做了期货策略;长周期来看回测效果还不错,但是太长了,往往就超过一个品种期间,存在跨期的风险。感觉不是和做期货。
对于股票的大蓝筹和etf更加适合,用于长线的投资。
策略思路说下:
-
读取历史数据,载入close数据;分析过去一组数据中是否存在变异点
-
如果有变异点,分析从变异点到最近一个close的趋势,是否满足95%置信区间
-
如果满足,结合一些指标,比如布林线,唐安琦等买入或者卖出
-
持有后,继续监控趋势,如果低于90%置信区间,平仓。
-