之前在研究用机器学习库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 | defMann_Kenddall_Trend_desc(inputdata):    # 计算总趋势秩次和    inputdata =np.array(inputdata)    n =inputdata.shape[0]    sum_sgn =0    fori innp.arange(n):        ifi <=(n -1):            forj innp.arange(i+1,n):                ifinputdata[j] > inputdata[i]:                    sum_sgn =sum_sgn+1                elifinputdata[j] < inputdata[i]:                    sum_sgn =sum_sgn-1                else:                    sum_sgn =sum_sgn    # 计算Z统计值    ifn <=10:        Z_value =sum_sgn/(n*(n-1)/2)    else:        ifsum_sgn > 0:            Z_value =(sum_sgn-1)/np.sqrt(n*(n-1)*(2*n+5)/18)        elifsum_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    ifnp.abs(Z_value) > 1.96andnp.abs(Z_value) <=2.576:        ifZ_value > 0:            result_desc =u"95% up"        else:            result_desc =u"95% down"    elifnp.abs(Z_value) > 2.576:        ifZ_value > 0:            result_desc =u"99% up"        else:            result_desc =u"99% down"    else:        result_desc     =u"not trendency"    returnresult_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 | defKendall_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    fori inrange(1,n):        forj inrange(i):            ifinputdata[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    fori inrange(1,n):        forj inrange(i):            ifinputdataT[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()    # 找出交叉点    fork inrange(1,n):        ifdiff[k-1]*diff[k]<0and(UFk[k] > 1.96orUFk[k] < -1.96) :            K.append(k)    # 做突变检测图时,使用UFk和UBkT    returnK | 
最后,我尝试着用M-K做了期货策略;长周期来看回测效果还不错,但是太长了,往往就超过一个品种期间,存在跨期的风险。感觉不是和做期货。
对于股票的大蓝筹和etf更加适合,用于长线的投资。
策略思路说下:
读取历史数据,载入close数据;分析过去一组数据中是否存在变异点
如果有变异点,分析从变异点到最近一个close的趋势,是否满足95%置信区间
如果满足,结合一些指标,比如布林线,唐安琦等买入或者卖出
持有后,继续监控趋势,如果低于90%置信区间,平仓。
Mann-Kendall算法用于金融品种长周期趋势判断和变点检测,以及策略思路
原文:https://www.cnblogs.com/chenguopa/p/15240080.html