Mann-Kendall演算法用於金融品種長週期趨勢判斷和變點檢測,以及策略思路

張國平發表於2019-07-24

之前在研究用機器學習庫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程式化,可以學習。

https://blog.csdn.net/u012111465/article/details/82016757


一,趨勢校驗,演算法如下

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

二,突變點校驗

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更加適合,用於長線的投資。

策略思路說下:

  1. 讀取歷史資料,載入close資料;分析過去一組資料中是否存在變異點

  2. 如果有變異點,分析從變異點到最近一個close的趨勢,是否滿足95%置信區間

  3. 如果滿足,結合一些指標,比如布林線,唐安琦等買入或者賣出

  4. 持有後,繼續監控趨勢,如果低於90%置信區間,平倉。



來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/22259926/viewspace-2651575/,如需轉載,請註明出處,否則將追究法律責任。

相關文章