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)

性能提升30倍丨基于 DolphinDB 的 mytt 指标库实现_DolphinDB

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)

性能提升30倍丨基于 DolphinDB 的 mytt 指标库实现_流计算_02

对其中每只股票使用 mytt 模块中的 ​​EMA​​ 函数进行计算:

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

性能提升30倍丨基于 DolphinDB 的 mytt 指标库实现_DolphinDB_03

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)

性能提升30倍丨基于 DolphinDB 的 mytt 指标库实现_流计算_04

在 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

性能提升30倍丨基于 DolphinDB 的 mytt 指标库实现_流计算_05

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)

性能提升30倍丨基于 DolphinDB 的 mytt 指标库实现_流计算_06

对一个长度为 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))

性能提升30倍丨基于 DolphinDB 的 mytt 指标库实现_python_07

Python MyTT 库中的 ​​AVEDEV​​​ 函数耗时为 25000ms,是 DolphinDB mytt module 中的 ​​AVEDEV​​ 函数的 1000 倍。测试数据量越大,性能差异越显著。

4.2 分组使用性能对比

测试结果如下表所示:

序号

函数

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))

性能提升30倍丨基于 DolphinDB 的 mytt 指标库实现_流计算_08

DolphinDB 测试核心代码

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

性能提升30倍丨基于 DolphinDB 的 mytt 指标库实现_流计算_09

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 的判别可能会出错,如下图所示:

性能提升30倍丨基于 DolphinDB 的 mytt 指标库实现_数据分析_10

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]

性能提升30倍丨基于 DolphinDB 的 mytt 指标库实现_数据分析_11

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])

性能提升30倍丨基于 DolphinDB 的 mytt 指标库实现_DolphinDB_12

以滑动窗口求和为例,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)

性能提升30倍丨基于 DolphinDB 的 mytt 指标库实现_python_13

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