效能提升30倍丨基於 DolphinDB 的 mytt 指標庫實現

語言: CN / TW / HK

MyTT 是一個簡單易用的 Python 庫,它將通達信、同花順、文華麥語言等指標公式最簡化移植到了 Python 中,實現的常見指標包括 MACD、RSI、BOLL、ATR、KDJ、CCI、PSY 等。MyTT 全部基於 numpy 和 pandas 的函式進行封裝。

為了方便使用者在 DolphinDB 中計算這些技術指標,我們使用 DolphinDB 指令碼實現了 MyTT 中包含的指標函式,並封裝在 DolphinDB mytt module (mytt.dos)中。 相比於 Python 中的 MyTT 庫,DolphinDB mytt module 中的計算函式不僅在批處理中效能有大幅提升,而且支援 DolphinDB 的流式增量計算引擎,可以直接用於實時流計算場景。

因為 DolphinDB mytt module 是基於 DolphinDB V1.30.18 和 DolphinDB V2.00.6 開發的,所以建議使用者使用 DolphinDB V1.30.18 和 DolphinDB V2.00.6 及以上版本執行 mytt 指標庫中的函式。


1. 函式及引數的命名與用法規範

  • Python MyTT 庫中所有函式名大寫,所有引數名大寫,為適應使用者的使用習慣,DolphinDB mytt module 中的函式名、引數、引數預設值均與 MyTT 保持一致。
  • 為得到有意義的計算結果,mytt 中函式的引數表示時間跨度的引數均要求至少是 2。
  • 由於 LAST 函式與 DolphinDB 中內建關鍵字衝突,mytt 中將此函式命名為 LAST_。

2. 環境配置

把附件的 mytt.dos 放在節點的 [home]/modules 目錄下,[home] 目錄由系統配置引數 home 決定,可以通過 getHomeDir() 函式檢視。初次使用模組檔案時,[home]目錄沒有 modules 目錄,手動建立 modules 目錄,然後把 mytt.dos 模組檔案放入該目錄即可。

DolphinDB 模組使用的教程文件:DolphinDB 教程:模組

3. 使用範例

3.1 指令碼中直接使用指標函式

對一個向量直接使用 mytt 模組中的 EMA 函式(指數平滑法)進行計算:

// 如果未設定自動載入 mytt module,新會話需要手動載入一次 mytt module
use mytt

close = 7.2 6.97 7.08 6.74 6.49 5.9 6.26 5.9 5.35 5.63
x = EMA(close, 5)

3.2 在 SQL 語句中分組使用

使用者經常需要在資料表中對多組資料在每組內進行計算。在以下例子中,先構造了一個包含 2 個股票的資料表:

use mytt

close = 7.2 6.97 7.08 6.74 6.49 5.9 6.26 5.9 5.35 5.63 3.81 3.935 4.04 3.74 3.7 3.33 3.64 3.31 2.69 2.72
date = (2020.03.02 + 0..4 join 7..11).take(20)
symbol = take(`F,10) join take(`GPRO,10)
t = table(symbol, date, close)

對其中每隻股票使用 mytt 模組中的 EMA 函式進行計算:

update t set EMA = EMA(close, 5) context by symbol

3.3 返回多個列的結果

某些函式會返回多個列的結果,例如函式 BIAS(乘離率指標)。

直接使用的例子:

use mytt

close = 7.2 6.97 7.08 6.74 6.49 5.9 6.26 5.9 5.35 5.63
bias1, bias2, bias3 = BIAS(close, L1 = 2, L2 = 4, L3 = 6)

在 SQL 語句中使用的例子:

use mytt

close = 7.2 6.97 7.08 6.74 6.49 5.9 6.26 5.9 5.35 5.63 3.81 3.935 4.04 3.74 3.7 3.33 3.64 3.31 2.69 2.72
date = (2020.03.02 + 0..4 join 7..11).take(20)
symbol = take(`F,10) join take(`GPRO,10)
t = table(symbol, date, close)
select *, BIAS(close, L1 = 2, L2 = 4, L3 = 6) as `bias1`bias2`bias3 from t context by symbol

symbol date       close  bias1    bias2    bias3
------ ---------- ----- -------- -------- --------
F      2020.03.02 7.2
F      2020.03.03 6.97 -1.623
F      2020.03.04 7.08  0.783
F      2020.03.05 6.74 -2.46     -3.68
F      2020.03.06 6.49 -1.89     -4.839
F      2020.03.09 5.9  -4.762    -9.958   -12.333
F      2020.03.10 6.26  2.961    -1.378   -4.767
F      2020.03.11 5.9  -2.961    -3.87    -7.74
F      2020.03.12 5.35 -4.889    -8.586   -12.391
F      2020.03.13 5.63  2.55     -2.679   -4.925
GPRO   2020.03.02 3.81
GPRO   2020.03.03 3.935 1.614
GPRO   2020.03.04 4.04  1.317
GPRO   2020.03.05 3.74 -3.856    -3.639
GPRO   2020.03.06 3.7  -0.538    -3.99
GPRO   2020.03.09 3.33 -5.263    -10.061 -11.417
GPRO   2020.03.10 3.64  4.448     1.041  -2.435
GPRO   2020.03.11 3.31 -4.748    -5.293  -8.732
GPRO   2020.03.12 2.69 -10.333   -17.039 -20.921
GPRO   2020.03.13 2.72  0.555    -11.974 -15.833

4. 函式計算效能

本節將以 AVEDEV 函式為例做直接使用的效能對比,同時使用真實股票日頻資料對所有函式進行分組使用效能對比。

4.1 直接使用效能對比

在 DolphinDB 中:

use mytt

close = 7.2 6.97 7.08 6.74 6.49 5.9 6.26 5.9 5.35 5.63
close = take(close, 100000)
timer x = mytt::AVEDEV(close, 100)

對一個長度為 100000 的向量直接使用 mytt 模組中的 AVEDEV 函式,耗時為 25ms。

與之對應的 Python 程式碼如下:

import numpy as np
from MyTT import *
import time

close = np.array([7.2,6.97,7.08,6.74,6.49,5.9,6.26,5.9,5.35,5.63])
close = np.tile(close,10000)
start_time = time.time()
x = AVEDEV(close, 100)
print("--- %s seconds ---" % (time.time() - start_time))

Python MyTT 庫中的 AVEDEV 函式耗時為 25000ms,是 DolphinDB mytt module 中的 AVEDEV 函式的 1000 倍。測試資料量越大,效能差異越顯著。

4.2 分組使用效能對比

  • 測試資料為上海證券交易所 2020 年,全年 2919 個證券(篩選交易日大於 120)日頻交易資料,總記錄數為 686,104 條。
  • 計算邏輯為按照股票程式碼進行分組計算各指標。
  • 為了測試函式計算效能,DolphinDB 和 Python 測試程式碼都是單執行緒執行。
  • DolphinDB 測試程式碼
  • Python 測試程式碼
  • 測試資料
  • Python MyTT 庫

測試結果如下表所示:

序號 函式 Python(ms) DolphinDB(ms) 執行時間比
1 RD 296 16 18
2 RET 243 13 18
3 ABS 229 15 15
4 LN 253 25 10
5 POW 311 30 10
6 SQRT 248 19 13
7 MAX 390 34 11
8 MIN 373 29 12
9 IF 282 21 13
10 REF 740 17 43
11 DIFF 662 22 30
12 STD 1,263 24 98
13 SUM 1,297 22 58
14 CONST 258 22 11
15 HHV 1,207 30 40
16 LLV 1,218 31 39
17 HHVBARS 2,952 41 72
18 LLVBARS 2,878 38 75
19 MA 1,220 24 50
20 EMA 1,171 26 45
21 SMA 1,199 28 42
22 WMA 4,322 20 216
23 DMA 1,123 27 41
24 AVEDEV 176,652 32 5,520
25 SLOPE 53,703 29 1,851
26 FORCAST 60,321 38 1,587
27 LAST 4,132 38 108
28 COUNT 1,249 20 62
29 EVERY 1,267 28 45
30 EXIST 1,490 22 67
31 BARSLAST 559 18 31
32 BARSLASTCOUNT 607 17 35
33 CROSS 2,088 80 26
34 LONGCROSS 6,019 94 64
35 VALUEWHEN 968 27 35
36 BETWEEN 489 42 11
37 MACD 3,060 86 35
38 KDJ 4,705 144 32
39 RSI 2,539 103 24
40 WR 5,632 166 33
41 BIAS 5,318 135 39
42 BOLL 3,067 90 34
43 PSY 2,596 82 31
44 CCI 163,681 76 2,153
45 ATR 2,281 101 22
46 BBI 3,667 66 55
47 DMI 6,181 250 24
48 TAQ 2,292 64 35
49 KTN 3,170 164 19
50 TRIX 4,329 97 44
51 VR 2,732 117 23
52 EMV 4,437 132 33
53 DPO 2,455 59 41
54 BRAR 4,909 156 31
55 DFMA 2,890 52 55
56 MTM 1,659 43 38
57 MASS 4,602 99 46
58 ROC 2,000 63 31
59 EXPMA 1,900 49 38
60 OBV 1,790 94 19
61 MFI 3,488 158 22
62 ASI 4,173 316 13

從測試結果分析可知:

  • DolphinDB mytt module 中的函式計算效能遠遠超過 Python MyTT 庫,最大的效能差距達到 5520 倍,普遍效能差距在 30 倍左右。

**Python pandas 測試核心程式碼 **

data.groupby("symbol").apply(lambda x: RSI(np.array(x.close), N = 24))

DolphinDB 測試核心程式碼

RSI = select symbol, tradedate, mytt::RSI(close, N=24) as `RSI from data context by symbol

5. 正確性驗證

基於 4.2 分組使用效能對比中的測試資料和程式碼,驗證 DolphinDB mytt module 中函式的計算結果是否和 Python MyTT 庫一致。

5.1 浮點數精度問題

結果有差異的函式

  • CROSS, LONGCROSS

原因

  • 浮點數精度問題
  • 對於 CROSS 和 LONGCROSS 函式,在浮點數比較上,DolphinDB mytt module 中的處理比 Python MyTT 庫更加嚴謹。DolphinDB mytt module 中首先會對浮點數 round 保留小數點後 6 位,然後再進行大小判斷,而 MyTT 中並沒有類似處理,因此對於相同大小的浮點數,Python 的判別可能會出錯,如下圖所示:

5.2 NULL 值的處理

結果有差異的函式

  • SUM, DMI, EMV, MASS, MFI, ASI

原因

  • 若輸入向量開始包含空值,則從第一個非空位置開始計算。DolphinDB mytt module 與 Python MyTT 庫的計算規則一致。
  • 對一個滾動 / 累積視窗長度為 k 的函式,每組最初的 (k-1) 個位置的結果均為空。DolphinDB mytt module 與 Python MyTT 庫的計算規則一致。
  • 對一個滾動 / 累積視窗長度為 k 的函式,若一組中第一個非空值之後再有空值,Python MyTT 庫會對包含 nan 的視窗計算結果都處理為 nan。DolphinDB mytt module 會對視窗內非 NULL 的元素按計算規則計算,得到一個非 NULL 的計算結果。

DolphinDB 程式碼與結果:

close = [99.9, NULL, 84.69, 31.38, 60.9, 83.3, 97.26, 98.67]
mytt::SUM(close, 5);

[,,,,276.87, 260.27, 357.53, 371.51]

Python 程式碼與結果:

close = np.array([99.9, np.nan, 84.69, 31.38, 60.9, 83.3, 97.26, 98.67])
MyTT.SUM(close,5)

array([nan, nan, nan, nan, nan,  nan, 357.53, 371.51])

以滑動視窗求和為例,close 向量的第 2 個元素為空值,DolphinDB mytt module 在計算第 5 個元素(60.9)時,回看過去 5 個視窗內的資料 [99.9, NULL, 84.69, 31.38, 60.9],對非 NULL 的元素求和,所以結果向量的第 5 個元素為 276.87。

Python MyTT 庫會對包含 nan 的視窗計算結果都處理為 nan,所以結果向量的前 6 個元素都為 nan。

除上述因為浮點數精度問題和 NULL 值的處理問題導致計算結果存在差異外,其餘函式計算結果的百分比誤差均小於 1e-10。

6. 實時流計算案例

在 DolphinDB V1.30.3 中釋出的響應式狀態引擎(Reactive State Engine)是許多金融場景流批統一計算中的重要構件,DolphinDB mytt module 在開發時就對其做了適配,使得 mytt 模組中的大部分函式可以在響應式狀態引擎中實現增量計算。

  • 當前無法在響應式狀態引擎中使用的指標函式:RET, CONST, BARSLAST, BARSLASTCOUNT,已經規劃開發。
  • 所有 mytt 中的 技術指標函式 均支援增量計算。

示例程式碼如下:

def cleanEnvironment(){
	try{unsubscribeTable(tableName="snapshotStream",actionName="aggr1min") } catch(ex){ print(ex) }
	try{dropStreamEngine("myttReactiveStateEngine") } catch(ex){ print(ex) }
	try{dropStreamEngine("aggr1min") } catch(ex){ print(ex) }
	try{dropStreamTable(`snapshotStream) } catch(ex){ print(ex) }
	try{dropStreamTable(`outputTable) } catch(ex){ print(ex) }
	undef all
}
cleanEnvironment()
go

//load modules
use mytt

//define stream table
name = `tradetime`SecurityID`high`low`open`close`vol
type = `TIMESTAMP`SYMBOL`DOUBLE`DOUBLE`DOUBLE`DOUBLE`INT
share streamTable(100:0, name, type) as snapshotStream
name = `SecurityID`tradetime`K`D`J`DIF`DEA`MACD`UPPER`MID`LOWER`ROC`MAROC
type = `SYMBOL`TIMESTAMP`DOUBLE`DOUBLE`DOUBLE`DOUBLE`DOUBLE`DOUBLE`DOUBLE`DOUBLE`DOUBLE`DOUBLE`DOUBLE
share streamTable(1000000:0, name, type) as outputTable

//register stream computing engine
reactiveStateMetrics=<[
    tradetime,
    mytt::KDJ(close, high, low, N=9, M1=3, M2=3) as `K`D`J,
    mytt::MACD(close, SHORT_=12, LONG_=26, M=9) as `DIF`DEA`MACD,
    mytt::KTN(close, high, low, N=20, M=10) as `UPPER`MID`LOWER,
    mytt::ROC(close, N=12, M=6) as `ROC`MAROC
]>

createReactiveStateEngine("myttReactiveStateEngine", metrics=reactiveStateMetrics, dummyTable=snapshotStream, outputTable=outputTable, keyColumn=`SecurityID, keepOrder=true)

createTimeSeriesEngine(name="aggr1min", windowSize=60000, step=60000, metrics=<[first(open),max(high),min(low),last(close),sum(vol)]>, dummyTable=snapshotStream, outputTable=getStreamEngine("myttReactiveStateEngine"), timeColumn=`tradetime, useWindowStartTime=true, keyColumn=`SecurityID)

subscribeTable(tableName="snapshotStream", actionName="aggr1min", offset=-1, handler=getStreamEngine("aggr1min"), msgAsTable=true, batchSize=2000, throttle=1, hash=0, reconnect=true)

7. DolphinDB mytt 指標列表

7.1 核心工具函式

函式 語法 解釋
RD RN(N, D = 3) 四捨五入取 3 位小數
RET RET(S, N = 1) 返回序列 倒數第 N 個值,預設返回最後一個
ABS ABS(S) 返回序列或數值 S 的絕對值
LN LN(S) 求序列 S 底是 e 的自然對數
POW POW(S, N) 求序列 S 的 N 次方
SQRT SQRT(S) 求序列 S 的平方根
MAX MAX(S1, S2) 配對比較兩個序列,給出比較以後大的序列
MIN MIN(S1, S2) 配對比較兩個序列,給出比較以後小的序列
IF IF(S, A, B) 序列布爾判斷,if S == True return A else B
REF REF(S, N = 1) 對序列整體下移動 N 個單位,返回平移後的序列,會產生 NAN
DIFF DIFF(S, N = 1) 序列 S 的前一個值減後一個值,序列頭部會產生 NAN
STD STD(S, N) 求序列 S 的滾動 N 日標準差,返回滾動標準差序列
SUM SUM(S, N) 對序列 S 求滾動 N 日總和
CONST COUNT(S) 返回序列 S 最後一個值組成常量序列
HHV HHV(S, N) 求序列 S 的滾動 N 日最大值,返回滾動最大值序列
LLV LLV(S, N) 求序列 S 的滾動 N 日最小值,返回滾動最小值序列
HHVBARS HHVBARS(S, N) 求序列 S 的滾動 N 期內最高值到當前的天數, 返回距離天數序列
LLVBARS LLVBARS(S, N) 求序列 S 的滾動 N 期內最低值到當前的天數, 返回距離天數序列
MA MA(S, N) 求序列 S 的 N 日簡單移動平均值,返回移動平均序列
EMA EMA(S, N) 求序列 S 的指數移動平均,為了精度,S>4*N,EMA 至少需要 120 週期 alpha = 2/(span+1)
SMA SMA(S, N, M = 1) 中國式的 SMA,至少需要 120 週期才精確 (雪球 180 週期),alpha = 1/(1+N)
WMA WMA(S, N) 求 S 序列 S 的 N 日加權移動平均,Yn = (1*X1+2*X2+3*X3+...+n*Xn)/(1+2+3+...+n)
DMA DMA(S, A) 求 S 的動態移動平均,A 作平滑因子,必須 0 < A < 1
AVEDEV AVEDEV(S, N) 求序列 S 的滾動平均絕對偏差
SLOPE SLOPE(S, N) 求序列 S 的滾動 N 週期內的線性迴歸模型的斜率
FORCAST FORCAST(S, N) 求序列 S 的滾動 N 週期內的線性迴歸模型的預測值
LAST_ LAST_(S, A, B) BOOL 型判斷,從前 A 日到前 B 日一直滿足 BOOL 條件,要求 A > B & A > 0 & B >= 0

7.2 應用層函式 (通過核心工具函式實現)

函式 語法 解釋
COUNT COUNT(S, N) 序列 S 是 BOOL 型,求最滾動 N 天內滿足 BOOL 為 True 的天數
EVERY EVERY(S, N) 序列 S 是 BOOL 型,求最滾動 N 天內 全部 滿足 BOOL 為 True 的天數
EXIST EXIST(S, N) 序列 S 是 BOOL 型,判斷最滾動 N 天內 是否存在 滿足 BOOL 為 True
BARSLAST BARSLAST(S) 序列 S 是 BOOL 型,統計上一次條件成立到當前的週期
BARSLASTCOUNT BARSLASTCOUNT(S) 序列 S 是 BOOL 型,統計連續滿足條件的週期數
BARSSINCEN BARSSINCEN(S, N) 序列 S 是 BOOL 型,統計滾動週期 N 內第一次滿足條件到當前的週期數
CROSS CROSS(S1, S2) 判斷兩個序列是否交叉的函式,判斷向上金叉穿越 CROSS(MA(C,5),MA(C,10)) ,判斷向下死叉穿越 CROSS(MA(C,10),MA(C,5))
LONGCROSS LONGCROSS(S1, S2, N) 判斷兩個序列是否在個持一定週期後再交叉的函式,判斷兩個序列是否再個持 N 週期後再交叉,N = 1 時等同於 CROSS(S1, S2)
VALUEWHEN VALUEWHEN(S, X) 解決當 S 條件成立時, 取 X 的當前值, 否則取 S 的上個成立時對應的 X 值
BETWEEN BETWEEN(S, A, B) 判斷 S 序列是否介於 A 和 B 之間的函式,當 S 處於 A 和 B 之間時為真,包括 A<S<B 或 A>S>B

7.3 技術指標函式 (全部通過核心工具和應用函式實現)

函式 語法 解釋
MACD MACD(CLOSE, SHORT = 12, LONG = 26, M = 9) 平滑異同平均線
KDJ KDJ(CLOSE, HIGH, LOW, N = 9, M1 = 3, M2 = 3) KDJ 指標
RSI RSI(CLOSE, N = 24) RSI 指標, 和通達信小數點 2 位相同
WR WR(CLOSE, HIGH, LOW, N = 10, N1 = 6) W&R 威廉指標
BIAS BIAS(CLOSE, L1 = 6, L2 = 12, L3 = 24) BIAS 乖離率
BOLL BOLL(CLOSE, N = 20, P = 2) BOLL 指標,布林帶
PSY PSY(CLOSE, N = 12, M = 6) PSY 指標,心理線
CCI CCI(CLOSE, HIGH, LOW, N = 14) CCI 指標,順勢線
ATR ATR(CLOSE, HIGH, LOW, N = 20) 真實波動 N 日平均值
BBI BBI(CLOSE, M1 = 3, M2 = 6, M3 = 12, M4 = 20) BBI 多空指標
DMI DMI(CLOSE, HIGH, LOW, M1 = 14, M2 = 6) DMI 動向指標
TAQ TAQ(HIGH, LOW, N) 唐安奇通道 (海龜) 交易指標
KTN KTN(CLOSE, HIGH, LOW, N = 20, M = 10) 肯特納交易通道
TRIX TRIX(CLOSE, M1 = 12, M2 = 20) 三重指數平滑平均線
VR VR(CLOSE, VOL, M1 = 26) VR 容量比率
EMV EMV(HIGH, LOW, VOL, N = 14, M = 9) EMV 簡易波動指標
DPO DPO(CLOSE, M1 = 20, M2 = 10, M3 = 6) 區間震盪線
BRAR BRAR(OPEN, CLOSE, HIGH, LOW, M1 = 26) BRAR-ARBR 情緒指標
DFMA DFMA(CLOSE, N1 = 10, N2 = 50, M = 10) DFMA 平行線差指標
MTM MTM(CLOSE, N = 12, M = 6) MTM 動量指標
MASS MASS(HIGH, LOW, N1 = 9, N2 = 25, M = 6) 梅斯線
ROC ROC(CLOSE, N = 12, M = 6) 變動率指標
EXPMA EXPMA(CLOSE, N1 = 12, N2 = 50) 指數平均數指標
OBV OBV(CLOSE, VOL) 能量潮指標
MFI MFI(CLOSE, HIGH, LOW, VOL, N = 14) MFI 資金流量
ASI ASI(OPEN, CLOSE, HIGH, LOW, M1 = 26, M2 = 10) 振動升降指標

8. 路線圖(Road Map)

  • 優化當前無法在響應式狀態引擎中使用的指標函式,包括 RET, CONST, BARSLAST, BARSLASTCOUNT,預計在 DolphinDB V1.30.19 和 DolphinDB V2.00.7 支援上述函式的增量計算和在響應式狀態引擎中的使用。
  • 長期保持對 Python MyTT 包的同步更新。

附件

計算效能測試環境

  • CPU 型別:Intel(R) Core(TM) i7-7700 CPU @ 3.60GHz 3.60 GHz
  • 邏輯 CPU 總數:8
  • 記憶體:32GB
  • OS:Windows 10