技术分析(Technical Analysis)指标库

TA-Lib 是一个 Python 库,封装了用 C 语言实现的金融交易技术分析的诸多常用指标。为了方便用户在 DolphinDB 中计算这些技术指标,我们使用 DolphinDB 脚本实现了 TA-Lib 中包含的指标函数,并封装在 DolphinDB ta module 中。因为 DolphinDB ta module 是基于 DolphinDB V1.10.3 开发的,所以使用 ta 模块要求 DolphinDB V1.10.3 或以上版本。

为了更好地支持 ta module 中的函数在 DolphinDB 的流式增量计算引擎中使用,我们基于 DolphinDB V1.30.20 和 DolphinDB V2.00.8 对 ta module 进行了修订。

匹配不同 DolphinDB 版本的 ta module:

注意: 如果 DolpinDB 版本高于 V1.30.20 或 V2.00.8,选择支持 V1.30.20, V2.00.8 版本的 ta.dos。

1. 函数及参数的命名与用法规范

  • 与 TA-Lib 中所有函数名大写以及所有参数名小写的规范不同,ta 模块中,函数名及参数名均采用驼峰式命名法。

    例如,TA-Lib 中 DEMA 函数的语法为 DEMA(close, timeperiod=30)。在 ta 模块中相应的函数为 dema(close, timePeriod)

  • TA-Lib 中某些函数有可选参数。ta 模块中,如果应用于流计算中,所有参数皆为必选。

  • 为得到有意义的结果,ta 模块中函数的参数 timePeriod 要求至少是2。

2. 使用范例

2.1. 脚本中直接使用指标函数

对一个向量直接使用 ta 模块中的 wma 函数进行计算:

use ta
close = 7.2 6.97 7.08 6.74 6.49 5.9 6.26 5.9 5.35 5.63
x = wma(close, 5);

2.2. 在 SQL 语句中分组使用

用户经常需要在数据表中对多组数据在每组内进行计算。在以下例子中,我们构造了一个包含2个股票的数据表:

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)

对其中每只股票使用 ta 模块中的 wma 函数进行计算:

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

2.3. 返回多个列的结果

某些函数会返回多个列的结果,例如函数 bBands

直接使用的例子:

close = 7.2 6.97 7.08 6.74 6.49 5.9 6.26 5.9 5.35 5.63
low, mid, high = bBands(close, 5, 2, 2, 2);

在 SQL 语句中使用的例子:

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 *, bBands(close, 5, 2, 2, 2) as `high`mid`low from t context by symbol

symbol date       close high     mid      low
------ ---------- ----- -------- -------- --------
F      2020.03.02 7.2
F      2020.03.03 6.97
F      2020.03.04 7.08
F      2020.03.05 6.74
F      2020.03.06 6.49  7.292691 6.786    6.279309
F      2020.03.09 5.9   7.294248 6.454    5.613752
F      2020.03.10 6.26  7.134406 6.328667 5.522927
F      2020.03.11 5.9   6.789441 6.130667 5.471892
F      2020.03.12 5.35  6.601667 5.828    5.054333
F      2020.03.13 5.63  6.319728 5.711333 5.102939
GPRO   2020.03.02 3.81
GPRO   2020.03.03 3.935
GPRO   2020.03.04 4.04
GPRO   2020.03.05 3.74
GPRO   2020.03.06 3.7   4.069365 3.817333 3.565302
GPRO   2020.03.09 3.33  4.133371 3.645667 3.157962
GPRO   2020.03.10 3.64  4.062941 3.609333 3.155726
GPRO   2020.03.11 3.31  3.854172 3.482667 3.111162
GPRO   2020.03.12 2.69  3.915172 3.198    2.480828
GPRO   2020.03.13 2.72  3.738386 2.993333 2.24828

3. 函数计算性能

本节将以 EMA 函数为例做直接使用的性能对比,同时使用真实股票日频数据对所有函数进行分组使用性能对比。

3.1. 直接使用性能对比

在 DolphinDB 中:

use ta

close = 7.2 6.97 7.08 6.74 6.49 5.9 6.26 5.9 5.35 5.63
close = take(close, 10000000)
timer x = ta::ema(close, 30)

对一个长度为10000000的向量直接使用 ta 模块中的 ema 函数,耗时为42ms。

与之对应的 Python 代码如下:

import numpy as np
import talib
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,10000000)
start_time = time.time()
x = talib.EMA(close, 30)
print("--- %s seconds ---" % (time.time() - start_time))

Python TA-Lib 库中的 EMA 函数耗时为418ms,是 DolphinDB ta module 中的 ema 函数的10倍左右。

3.2. 分组使用性能对比

  • 测试数据为上海证券交易所2020年,全年2919个证券(筛选交易日大于120)日频交易数据,总记录数为686,104条。下载 测试数据 ,解压后放置于[home]下。
  • 计算逻辑为按照股票代码进行分组计算各指标。
  • 为了测试函数计算性能,DolphinDB 和 Python 测试代码都是单线程运行。

测试结果如下表所示:

序号函数Python(ms)DolphinDB(ms)运行时间比
1VAR2941717
2STDDEV2552112
3BETA390429
4SMA2861617
5EMA2621419
6WMA2671716
7DEMA2561913
8TEMA262309
9TRIMA2712610
10KAMA2731914
11T3265319
12MA2441913
13BBANDS298545
14RSI278515
15STOCHF516836
16STOCH477935
17STOCHRSI2631332
18TRIX243327
19CORREL327359
20LINEARREG_SLOPE2292111
21LINEARREG_INTERCEPT2382111
22LINEARREG_ANGLE265328
23LINEARREG288565
24TSF254555
25BOP5054012
26CCI444756
27TRANGE4542518
28PLUS_DM345546
29PLUS_DI4131174
30MINUS_DM331586
31MINUS_DI428785
32DX4171184
33ADX4191104
34ADXR4151184
35CMO238534
36MACD283595
37MACDEXT296724
38MACDFIX285565
39MIDPRICE365488
40MIDPOINT256446
41MOM2741716
42ROC2492311
43ROCP2462111
44ROCR2481714
45ROCR1002432012
46PPO256396
47MAVP4011273
48APO259338
49AROON361626
50AROONOSC371596
51ULTOSC4762022
52WILLR435657
53AD5024212
54OBV329477
55AVGPRICE5034511
56MEDPRICE3342017
57TYPPRICE4432716
58WCLPRICE4112815
59ATR4223114
60NATR4193711
61MFI5101035

从测试结果分析可知:

  • DolphinDB ta module 中的函数计算性能都超过了 Python TA-Lib 库,最大的性能差距达到18倍,普遍性能差距在9倍左右。

Python pandas测试核心代码

data.groupby("symbol").apply(lambda x: talib.EMA(np.array(x.close), 30))

DolphinDB测试核心代码

select ta::ema(close, timePeriod=30) as `EMA from data context by symbol

4. 正确性验证

基于分组使用性能对比中的测试数据和代码,验证 DolphinDB ta module 中函数的计算结果是否和 Python TA-Lib 库一致。

4.1. NULL 值的处理

若 TA-Lib 的输入向量开始包含空值,则从第一个非空位置开始计算。ta 模块采用了相同的策略。

对一个滚动/累积窗口长度为 k 的函数,每组最初的 (k-1) 个位置的结果均为空。这一点 ta 模块与 TA-Lib 模块的结果一致。但若一组中第一个非空值之后再有空值,该组此空值位置以及所有以后位置在 TA-Lib 函数中的结果有可能均为空值。对 ta 模块函数,除非窗口中非空值数据的数量不足以计算指标(例如计算方差时只有一个非空值),否则在这些位置均会产生非空的结果。

DolphinDB 代码与结果:

close = [99.9, NULL, 84.69, 31.38, 60.9, 83.3, 97.26, 98.67]
ta::var(close, 5, 1);

[,,,,670.417819,467.420569,539.753584,644.748976]

Python 代码与结果:

close = np.array([99.9, np.nan, 84.69, 31.38, 60.9, 83.3, 97.26, 98.67])
talib.VAR(close, 5, 1)

array([nan, nan, nan, nan, nan, nan, nan, nan])

上面的总体方差计算中,因为 close 的第二个值为空值,ta 模块和 TA-Lib 的输出不同,TA-Lib 输出全部为空值。如果替换空值为非空值 81.11,ta 模块和 TA-Lib 得到相同的结果。在第一个元素 99.9 之前加一个空值,两者的结果仍然相同。

简而言之,当输入参数中空值,若存在的话,只集中在开始的位置时,ta 模块和 TA-Lib 的输出结果才会完全一致。

4.2. 中间值近似问题

结果有差异的函数:

  • BETA, CORREL

原因:

BETA, CORREL指标函数的公式为:





在 TA-Lib 中,会对两个分母值用 TA_IS_ZERO 进行判断,如果为真,结果值为0.0,以beta为例,Python TA-Lib部分代码如下:



其中,TA_IS_ZERO 的定义为:



而在 DolphinDB 的 ta 模块中,我们直接使用内置函数 mbetamcorr 来计算:

//beta
@state
def beta(high, low, timePeriod=5){
	return talib(mbeta, low.ratios() - 1, high.ratios() - 1, timePeriod)
}

//correl
@state
def correl(high, low, timePeriod=30){
	high_, low_ = talibNull(high, low)
	return talib(mcorr, high, low, timePeriod)
}

这里没有与Python TA-Lib 相同的TA_IS_ZERO判断,所以会导致某些结果会与Python TA-Lib计算结果不同。但是使用内置函数 mbetamcorr 来计算不光能提高计算效率,同时也能以极简的代码实现指标的计算。

4.3. 浮点数精度优化

结果有差异的函数:

  • CCI, STOCHRSI, MFI

原因:

Python TA-Lib 中,CCI, MFI 的浮点数精度问题与4.2中所提及的问题相似,会涉及到一些对中间值的判断,如:

/* CCI */
if( (tempReal != 0.0) && (tempReal2 != 0.0) )
...
else

/* MFI */
if( tempValue2 < 0 )
...
else if( tempValue2 > 0 )
...
else

对计算中间值判断是否大于零、小于零、等于或不等于零很有可能会让我们陷入到机器误差造成的麻烦中,举一个例子,在 Python中 运行:

(2.86 + 2.7 + 2.73) - (2.81 + 2.7 + 2.78)

1.7763568394002505e-15

上述计算结果应该是为0,但是由于浮点数的存储机制,最终计算结果就变成了一个很小的正数,这样便会导致判断语句走向错误的分支,使得最终计算结果与正确的结果相差甚远。很明显这并不是 TA-Lib 作者的本意,是浮点数精度问题导致的误差。

另外,STOCHRSI 也是由于浮点数的细微精度问题造成最终结果与预想的结果相差很大的一个指标。具体来说,STOCHRSI 可以简单的理解为 STOCH(RSI(close, period), period, fastkPeriod, fastdPeriod, fastdMatype),以第一个结果 slowk 为例:



在 Python TA-Lib 中,直接调用了 RSI,并没有对其做任何处理,这样就会让极小的机器误差对最终结果造成极大的影响。这里举一个例子:



这里计算了一组RSI并打印出了最后5个值,可以看到,close中最后5个值都是2.14,那么RSI最后5个值应该是相等的,但是实际上计算的结果值会有10E-14数量级的误差。也就是说,Python TA-Lib在计算RSI的时候会有一个很小的误差,如果我们只需得到RSI的计算结果,那么这个很小的误差完全是可以忽略的。但如果将RSI的计算结果不加任何处理地作为STOCH的输入的话,这个机器误差就会对最终的结果造成非常大的影响。继续往下,我们用上述例子继续计算slowk,窗口内最大值为37.95876021268321,最小值为37.95876021268319,当前值为37.958760212683195,最终计算得到slowk为 33.33,与真实值 0 相差非常之大!综上所述,STOCHRSI 同样也是 Python TA-Lib 计算不准确的地方。

针对以上两种浮点数精度造成的问题,DolphinDB 的 ta 模块采取了舍弃部分精度的方法来避免计算中间值的微小机器误差造成的最终结果的巨大误差,从而可以计算出更精确的结果,具体代码如下:

//CCI
@state
def cci(high, low, close, timePeriod=14){
	high_, low_, close_ = talibNull(high, low, close)
	tp= (high_ + low_ + close_) / 3.0
	tmp = tp - talib(mavg, tp, timePeriod)
	return iif(abs(tmp) > 1E-12, tmp \ (0.015 * talib(mmad, tp, timePeriod)), 0.0)
}

//MFI
@state
def mfi(high, low, close, volume, timePeriod=14){
	tp = round((high + low + close) / 3.0, 8)
	deltasTp = deltas(tp)
	pos = iif(nullCompare(>, deltasTp, 0), tp, 0)
	neg = iif(nullCompare(<, deltasTp, 0), tp, 0)
	return talib(msum, pos * volume, timePeriod) * 100 / (talib(msum, pos * volume , timePeriod) + talib(msum, neg * volume , timePeriod))
}

//STOCHRSI
@state
def stochRsi(close, timePeriod=14, fastkPeriod=5, fastdPeriod=3, fastdMatype=0) {
	rsidx = round(rsi(close, timePeriod), 8)
	high_, low_, close_ = talibNull(rsidx, rsidx, rsidx)
	lowestLow = talib(mmin, low_, fastkPeriod)
	fastk = (close_ - lowestLow) \ (talib(mmax, high_, fastkPeriod) - lowestLow) * 100
	fastd = ma(fastk, fastdPeriod, fastdMatype)
	fastk_, fastd_ = talibNull(fastk, fastd)
	return fastk_, fastd_
}

5. 实时流计算案例

DolphinDB V1.30.3 版本发布的响应式状态引擎(createReactiveStateEngine)是许多金融场景流批统一计算中的重要构件,DolphinDB ta module 对其做了适配,使得 ta 模块中的大部分函数可以在响应式状态引擎中实现增量计算。

  • 当前无法在响应式状态引擎中使用的指标函数:mavp

示例代码如下:

//clean environment
def cleanEnvironment(){
	try{ unsubscribeTable(tableName="inputTable",actionName="calculateTA") } catch(ex){ print(ex) }
	try{ dropStreamEngine("taReactiveSateEngine") } catch(ex){ print(ex) }
	try{ dropStreamTable(`inputTable) } catch(ex){ print(ex) }
	try{ dropStreamTable(`outputTable) } catch(ex){ print(ex) }
	undef all
}
cleanEnvironment()
go

//load module
use ta

//load data
schema = table(`tradedate`symbol`high`low`open`close`volume`bs`periods as name, `DATE`SYMBOL`DOUBLE`DOUBLE`DOUBLE`DOUBLE`DOUBLE`BOOL`INT as type)
data=loadText("/data/DolphinDB/200.8/server/testData.csv" ,schema=schema)

//define stream table
share streamTable(1:0, `tradedate`symbol`high`low`open`close`volume`bs`periods, `DATE`SYMBOL`DOUBLE`DOUBLE`DOUBLE`DOUBLE`DOUBLE`BOOL`INT) as inputTable
share streamTable(1:0, `symbol`tradedate`EMA`RSI`ROC`WILLR, `SYMBOL`DATE`DOUBLE`DOUBLE`DOUBLE`DOUBLE) as outputTable

//register stream computing engine
reactiveStateMetrics=<[
    tradedate,
    ta::ema(close, timePeriod=30) as `EMA, 
    ta::rsi(close, timePeriod=14) as `RSI, 
    ta::roc(close, timePeriod=10) as `ROC, 
    ta::willr(high, low, close, timePeriod=14) as `WILLR
]>
createReactiveStateEngine(name="taReactiveSateEngine", metrics=reactiveStateMetrics, dummyTable=inputTable, outputTable=outputTable, keyColumn=`symbol, keepOrder=true)
subscribeTable(tableName="inputTable", actionName="calculateTA", offset=-1, handler=getStreamEngine("taReactiveSateEngine"), msgAsTable=true, reconnect=true)

//replay data
submitJob("replay", "replay",  replay{data, inputTable, `tradedate, `tradedate, 1000, true})

6. DolphinDB ta 指标列表

6.1. Overlap Studies

函数语法解释
bBandsbBands(close, timePeriod, nbDevUp, nbDevDn, maType)Bollinger Bands
demadema(close, timePeriod)Double Exponential Moving Average
emaema(close, timePeriod)Exponential Moving Average
kamakama(close, timePeriod)Kaufman Adaptive Moving Average
mama(close, timePeriod, maType)Moving average
mavpmavp(inReal, periods, minPeriod, maxPeriod, maType)Moving average with variable period
midPointmidPoint(close, timePeriod)MidPoint over period
midPricemidPrice(low, high, timePeriod)Midpoint Price over period
smasma(close, timePeriod)Simple Moving Average
t3t3(close, timePeriod, vfactor)Triple Exponential Moving Average (T3)
tematema(close, timePeriod)Triple Exponential Moving Average
trimatrima(close, timePeriod)Triangular Moving Average
wmawma(close, timePeriod)Weighted Moving Average

6.2. Momentum Indicators

函数语法解释
adxadx(high, low, close, timePeriod)Average Directional Movement Index
adxradxr(high, low, close, timePeriod)Average Directional Movement Index Rating
apoapo(close,fastPeriod,slowPeriod,maType)Absolute Price Oscillator
aroonaroon(high,low,timePeriod)Aroon
aroonOscaroonOsc(high, low, timePeriod)Aroon Oscillator
bopbop(open, high, low, close)Balance Of Power
ccicci(high, low, close, timePeriod)Commodity Channel Index
cmocmo(close, timePeriod)Chande Momentum Oscillator
dxdx(high, low, close, timePeriod)Directional Movement Index
macdmacd(close, fastPeriod, slowPeriod, signalPeriod)Moving Average Convergence/Divergence
macdExtmacdExt(close, fastPeriod, fastMaType, slowPeriod, slowMaType, signalPeriod, signalMaType)MACD with controllable MA type
macdFixmacdFix(close, signalPeriod)Moving Average Convergence/Divergence Fix 12/26
mfimfi(high, low, close, volume, timePeriod)Money Flow Index
minus_diminus_di(high, low, close, timePeriod)Minus Directional Indicator
minus_dmminus_dm(high, low, timePeriod)Minus Directional Movement
mommom(close, timePeriod)Momentum
plus_diplus_di(high, low, close, timePeriod)Plus Directional Indicator
plus_dmplus_dm(high, low, timePeriod)Plus Directional Movement
ppoppo(close, fastPeriod, slowPeriod, maType)Percentage Price Oscillator
rocroc(close, timePeriod)Rate of change : ((price/prevPrice)-1)*100
rocprocp(close, timePeriod)Rate of change Percentage: (price-prevPrice)/prevPrice
rocrrocr(close, timePeriod)Rate of change ratio: (price/prevPrice)
rocr100rocr100(close, timeperiod)Rate of change ratio 100 scale: (price/prevPrice)*100
rsirsi(close, timePeriod)Relative Strength Index
stochstoch(high, low, close, fastkPeriod, slowkPeriod, slowkMatype, slowdPeriod, slowdMatype)Stochastic
stochfstochf(high, low, close, fastkPeriod, fastdPeriod, fastdMatype)Stochastic Fast
stochRsistochRsi(real, timePeriod, fastkPeriod, fastdPeriod, fastdMatype)Stochastic Relative Strength Index
trixtrix(close, timePeriod)1-day Rate-Of-Change (ROC) of a Triple Smooth EMA
ultOscultOsc(high, low, close, timePeriod1, timePeriod2, timePeriod3)Ultimate Oscillator
willrwillr(high, low, close, timePeriod)Williams' %R

6.3. Volume Indicators

函数语法解释
adad(high, low, close, volume)Chaikin A/D Line
obvobv(close, volume)On Balance Volume

6.4. Volatility Indicators

函数语法解释
atratr(high, low, close, timePeriod)Average True Range
natrnatr(high, low, close, timePeriod)Normalized Average True Range
trangetrange(high, low, close)True Range

6.5. Price Transform

函数语法解释
avgPriceavgPrice(open, high, low, close)Average Price
medPricemedPrice(high, low)Median Price
typPricetypPrice(high, low, close)Typical Price
wclPricewclPrice(high, low, close)Weighted Close Price

6.6. Statistic Functions

函数语法解释
betabeta(high, low, timePeriod)Beta
correlcorrel(high, low, timePeriod)Pearson's Correlation Coefficient (r)
linearreglinearreg(close, timePeriod)Linear Regression
linearreg_anglelinearreg_angle(close, timePeriod)Linear Regression Angle
linearreg_interceptlinearreg_intercept(close, timePeriod)Linear Regression Intercept
linearreg_slopelinearreg_slope(close, timePeriod)Linear Regression Slope
stdDevstdDev(close, timePeriod, nbdev)Standard Deviation
tsftsf(close, timePeriod)Time Series Forecast
varvar(close, timePeriod, nbdev)Variance

6.7. 其它说明

  • 对 TA-Lib 中的 Math Transform 与 Math Operators 类函数,可使用相应的 DolphinDB 内置函数代替。例如,TA-Lib 中的 SQRT, LN, SUM 函数,可分别使用DolphinDB中的 sqrt, log, msum 函数代替。
  • 下列 TA-Lib 函数尚未在 ta 模块中实现:所有 Pattern Recognition 与 Cycle Indicators 类函数,以及 HT_TRENDLINE(Hilbert Transform - Instantaneous Trendline), ADOSC(Chaikin A/D Oscillator), MAMA(MESA Adaptive Moving Average), SAR(Parabolic SAR), SAREXT(Parabolic SAR - Extended) 函数。

7. 路线图(Roadmap)

  • 尚未实现的 TA-Lib 函数将在未来版本中实现。
  • 完善现有的 TA-Lib 函数:T3, PLUS_DM, PLUS_DI, MINUS_DM, MINUS_DI, DX, ADX, ADXR, AROON, AROONOSC