经过一段时间相关工作的生产实践,对于如何运用公式过滤特定形态、特定指标、特定数据的股票产生了一些心得,遂记录如下。
需要提前说明的是,股票的技术分析只能起到辅助的作用。实际的投资决策还需要综合公司经营情况,市场变化等等多方面的因素,不能完全依赖技术分析。
一 计算对象
需要明确的是在量化公式的计算中,计算的对象并非一个确定值,公式中的值通常来说是一个时间相关的序列。其形式可以理解为一个形如[val1,val2,val3,……,valn]
的数组。
以常见指标MACD的源码(默认参数值)为例:
1 | DIF:EMA(CLOSE,12)-EMA(CLOSE,26); |
- 其中参数
CLOSE
指的是股票的收盘价一个时间序列。序列时间与交易日相关,旧的时间在前,新的时间在后。 - 其中公式
EMA(CLOSE,12)
指的是时间序列CLOSE
在12日的指数移动平均值,公式的计算基于入参序列的数值,其结果也是和时间有关序列,具体的公式计算与后文阐述。 - 公式中的数字常量分为两类,一是作为基础公式的入参,如
EMA(DIF,9)
,其理解为单个数字参与运算。另一类是作为不变的序列参与运算,如(DIF-DEA)*2
中的数字常量2。 - 在
DIF:EMA(CLOSE,12)-EMA(CLOSE,26)
部分,EMA(CLOSE,12)
的结果是一个时间序列,EMA(CLOSE,26)
的结果也是一个时间序列。两序列相减,即两序列中相同时间的两个值相减形成的一个新序列。序列与序列的其他基础运算同理。 - 一个基础公式的入参接受时间序列,自然也可以接受结果为时间序列的子表达式。即表达式可以嵌套。
- 完整解释上述公式为
- 输出DIF:收盘价的12日指数移动平均-收盘价的26日指数移动平均
- 输出DEA:DIF的MID日指数移动平均
- 输出平滑异同平均线:(DIF-DEA)*2
二 常见基础函数
1 平均类函数
用于评价某一指标在一段时间内的平均表现,常见用来表示股价在一段时间的内的平均趋势。
1.1 MA(X,r) 简单移动平均
计算时间序列X在最近r个时间周期的简单移动平均值
其中序列X
的实际值为[X1,X2,X3,……,Xn]
则MA(X,r)
的结果序列Y的每一项的通项公式为
$$
Y_{m} = \frac{(x_{m} + x_{m+1} + \dots + x_{m+r-1})}{r}
$$
以序列X[31,32,33,34,35]
的MA(X,3)为例,其结果值为
$$
\left [ \frac{31+32+33}{3} ,\frac{32+33+34}{3},\frac{33+34+35}{3}\right ]
$$
也即
$$
[32,33,34]
$$
可以看到,取值如同一个宽度为r
移动的窗口在序列中取值计算平均值,所以称之为序列X
的简单移动平均。
这一计算可以有效反应在一段时间内序列X的整体趋势,当r值越大,该结果序列表现的越为平滑,受到序列X短时间内变化的影响越小。
也即r取值越大,越反应序列整体的变化情况。
1.2 EMA(X,r) 指数移动平均
在上一基础可以看到,简单移动平均对于序列中的新老数据敏感度是一视同仁的。当处于对近期数据更加敏感应用场景时,简单移动平均变得不再适用。指数移动平均可以解决这一问题。
对于序列X[X1,X2,X3,……,Xn]
,EMA(X,r)的迭代计算公式为
$$
Y_{m} = \frac{2X_{m} + Y_{m-1}*(r-1)}{r+1}
$$
可以看到,当前序列值Ym
的值依赖于序列中上一个Ym
的计算结果
以下的公式推导仅作为后续结论的补充说明,可以直接阅读结论
将公式变形为式1-1
$$
Y_{m} = \frac{2X_{m} }{r+1} + \frac{r-1}{r+1}*Y_{m-1}
$$
迭代为式1-2
$$
Y_{m-1} = \frac{2X_{m-1} }{r+1} + \frac{r-1}{r+1}*Y_{m-2}
$$
将式1-2
带入式1-1
并化简
$$
Y_{m} = \frac{2}{r+1}X_{m} + \frac{2(r-1)}{(r+1)^2}X_{m-1} + (\frac{r-1}{r+1})^{2} Y_{m-2}
$$
继续重复迭代Ym-2
并代入得到下式子
$$
Y_{m} = \frac{2}{r+1}X_{m} + \frac{2(r-1)}{(r+1)^2}X_{m-1} + \frac{2(r-1)^2}{(r+1)^3}X_{m-2} +\frac{(r-1)^3}{(r+1)^3}Y_{m-3}
$$
重复迭代与代入,可以得到式1-3
$$
Y_{m} = \frac{2}{r+1}X_{m} + \frac{2(r-1)}{(r+1)^2}X_{m-1} + \frac{2(r-1)^2}{(r+1)^3}X_{m-2} + \frac{2(r-1)^3}{(r+1)^4}X_{m-3} + \dots
$$
观察得到Xm
的系数N为式1-4
$$
N = \frac{2}{r-1} * \left ( \frac{r-1}{r+1} \right ) ^ n
$$
显而易见当n逐渐增大时该式的值将以指数叠加的速度逐渐缩小。当n趋向正无穷,N收敛于0。
即对于结果序列Y的当前值,其结果由对应时间的X序列值往前的无限个序列值决定。但随着迭代次数逐渐增加,时间越是久远的数据对于当前值的影响则以指数级的速度减少。这也是EMA(X,r)
被称为指数移动平均的原因。
由上述推导可以得到以下结论:
EMA(X,r)
的计算结果是一个对于近期数据更加敏感的序列- 其迭代特点使得其在计算当前值时,往期数据对于当前值的影响以指数级的速度逐渐减少
- 在实际计算时可以使用
式1-3
,当系数缩小至超出计算所需精度时可以将其后的项直接舍弃,避免大量的迭代计算 - 当r越大,系数收敛于0的速度越慢,计算涉及到满足精度的X的项越多
1.3 SMA(X,r,s) 移动平均
在EMA部分,介绍了一个近期数据敏感的基础公式。进一步思考,EMA本质是一个以指数函数对入参序列X的一个加权平均运算。这一指数函数的收敛速度是否适用于我们的需求?公式对于近期数据的敏感程度是否可自定义?指数函数的收敛速度是否可以自定义?
基础公式SMA(X,r,s) 移动平均
可以做到以上需求
对于序列X[X1,X2,X3,……,Xn]
,SMA(X,r,s)的迭代计算公式为
$$
Y_{m} = \frac{ s \cdot X + Y_{m-1} \cdot (r-s) }{r}
$$
可以看到,该迭代公式与EMA
具有相似的结构,类似的推导过程不再赘述。
经过迭代带入后可以得到关于Xm
的系数公式
$$
N = \left ( 1 - \frac{s}{r} \right ) ^ {n-1}
$$
观察系数公式,可以了解到常数参数s
和r
在求加权平均时是如何影响SMA的具体表现
- s和r的大小应当满足
1-s/r
的值大于0小于1,否则SMA的值没有实际意义 - s越大,则系数收敛到0越快,SMA的序列对于近期数据越敏感,SMA越灵敏
- r越大,则系数收敛到0越慢,计算涉及到满足精度的X的项越多,SMA对于近期数据越不敏感,SMA的序列越平滑。
在实际使用中将SMA(X,r,s)`称为X的r日移动平均值,s为权重。
2 行情序列
作为编写量化选股公式中的基础,行情序列代表的是某一支股票的某个指标在一定时间周期内的具体时间序列。
例如,某股票的收盘价价格就可以用变量CLOSE或者简写为C表示。而MA(C,5)
则表示某股票收盘价的5日均线。
常用的行情序列有:
- HIGH 最高价 简写为 H
- LOW 最低价 简写为L
- CLOSE 收盘价 简写为 C
- VOL 成交量(手)简写为 V
- OPEN 开盘价 简写为 O
- ADVANCE 上涨家数
- DECLINE 下跌家数
3 引用函数
在编写公式中,常常存在指定某一时间周期的具体值的需求,如前一天的收盘价,5天内的最高价等。这时就需要用到引用函数
常用的引用函数如下:
COUNT(X,N)
统计N个周期内条件X的满足次数,例如COUNT(CLOSE>OPEN,5)
则表示5日内收盘价大于开盘价的天数。HHV(X,N)
求 N 周期内 X 最高值,如HHV(CLOSE,5)
则表示收盘价的5日最大值。LLV(X,N)
求 N 周期内 X 最低值,如LLV(CLOSE,5)
则表示收盘价的5日最小值。REF(X,A)
,引用A周期前的X值, 如REF(CLOSE,1)
则表示前一日的收盘价。
4 逻辑函数
有一类函数的结果为1和0的时间序列,其结果表示的是入参否满足函数函数要求的逻辑。其中结果1表示为真,结果0表示为假。
常见的逻辑函数有:
CROSS(A,B)
上穿,表示当A从下方向上穿过B时返回1,否则返回0,例如CROSS(MA(C,10),MA(C,30))
则表示收盘价的10日均线上穿收盘价的30日均线。UPNDAY(X,M)
连涨,表示是否连涨M个周期,M为常量,例如UPNDAY(CLOSE,3)
表示收盘价是否连续三天上涨。DOWNNDAY(CLOSE,M)
连跌,表示是否连跌M个周期,M为常量,例如UPNDAY(CLOSE,3)
表示收盘价是否连续三天下跌。
其他未提到的公式请参考通达信金融终端在线说明书 第三章-公式平台章节,也可直接在说明书 文档中搜索。在此不再赘述
三 选股公式的编写思路
选股公式的首要目的,就是在大量的备选股票中筛选出符合要求的股票。经过经验总结出的股票指标特征,通过对股票指标的描述、过滤、比较,在给定的股票范围内选出符合经验的股票便是编写选股思路的一个基本思路。
1 走势特征与双线
双线思路是选股公式中最为常见的一个思路。其本质原理是两个随股价变化而灵敏度不同的指标在股价走势发生变化时会相互交叉。双线原理在表示走势特征时有着广泛的应用。
以MA买入选股公式为例(默认参数值):
1 | CROSS(MA(CLOSE,5),MA(CLOSE,20)); |
这是一个非常基础的选股公式。其由两部分组成,MA(CLOSE,5)
是收盘价的5日均线,MA(CLOSE,20)
是收盘价的20日均线。由于前者当前周期在平均值计算的中占比更大。所以5日均线是一个相较20日均线而言更加灵敏的指标。
当收盘价由下降趋势转为上升趋势时,5日均线的上涨趋势将比20日均线的上涨趋势更为明显。更为直观的表现就是,5日线从下方上穿20日线。当股价上升趋势转为下降趋势时,5日线则从上方下穿20日线。
下面再来分析一下常见指标MACD的源码(默认参数值):
1 | DIF:EMA(CLOSE,12)-EMA(CLOSE,26); |
这其中DIF
指标由收盘价的12日指数移动平均减去收盘价的26日指数移动平均得出。试想一个极端情况,有一支股票从上市以来一直维持着同一收盘价,由EMA的计算过程可以显而易见的得到,这一支股票的DIF
值将会一直为零。当这支股票收盘价上涨时,由于EMA(CLOSE,12)
是一个对于近期数据比EMA(CLOSE,26)
更为灵敏的指标,此时前者的值将比后者的值大,也即DIF表现为正数。同理当支股票收盘价下跌时,DIF表现为负数。当近期的收盘价持续性的上涨时,DIF则会表现出上升的趋势。当近期的收盘价持续性的下跌时,DIF则也会表现出下降的趋势。
到这里可以看出仅仅是DIF
指标本身就足以一定程度反应出股价的上涨或是下跌。但如果需要一个确定买卖信号点,则需要一个确切的“标准”来确定。在MACD
中通常以DEA
指标作为这一标准。
DEA
是DIF
的9日指数平均值。其本身反应了DIF
指标在近期的总体趋势。当DIF
从下方上穿DEA
时,则表示DIF
的当前值超过了在其近期的总体表现,也即DIF
转入了上升趋势。而DIF
转入上升趋势恰恰反应出了股价在近期的持续性上涨。当DIF
从下穿DEA
时也是同理,这里不再赘述。
由上述分析不难看出,双线选股思路的目的便是要筛选出股价从下跌转为上涨或是由上涨转为下跌的形态特征。在更为复杂的选股公式中,双线的编写不再局限于固定的参数。双线的灵敏度往往会根据股价不同的震荡幅度,或者是根据大盘的表现不同,存在着动态的变化。如在大盘整体表现不佳时,同时降低双线的灵敏度,以求更加长线的收益。往往这部分编写更加需要具体的实践和经验。
2 交易量
对于交易量指标的过滤,基于股价变动时常常伴随着的交易量增多的经验。通过针对对交易量的过滤,将市场情绪较好的股票筛选出来。
以条件选股公式持续放量为例(默认参数值):
1 | FINANCE(42)>50 && EVERY(VOL>=REF(VOL,1),5); |
其中FINANCE(42)
表示上市天数。VOL
表示当前交易量,REF(VOL,1)
表示前一天的交易量,EVERY(X,5)
则表示连续是否连续5个周期都满足条件表达式X。EVERY(VOL>=REF(VOL,1),5)
则表示连续5个周期都满足当前交易量大于上一周期的交易量。
综合来看,这一选股公式便是筛选上市的天数>50 并且 最近5日交易量持续上升的股票。
3 K线特征
由于K线直观表现出股价趋势,针对K线特征的直接筛选也是选股公式的一大思路
以选股公式早晨之星之心为例
1 | STAR: |
REF(CLOSE,2)/REF(OPEN,2)<0.95
表示前日收盘价比前日开盘价小于0.95,也即前日为阴线REF(OPEN,1)<REF(CLOSE,2)
表示昨日开盘小于前日开盘,即昨日开盘跳空低开ABS(REF(OPEN,1)-REF(CLOSE,1))/REF(CLOSE,1)<0.03
则是对昨日十字星线的描述REF(OPEN,1)
为昨日开盘价,REF(CLOSE,1)
为昨日收盘价。ABS(REF(OPEN,1)-REF(CLOSE,1))/REF(CLOSE,1)<0.03
也即昨日开盘价与收盘价的差与收盘价的比值小于0.03。
CLOSE>REF(CLOSE,2)
则表示当前收盘价大于前天的收盘价。
借助REF等函数以及各种操作符,对于K线的具体特征进行直接的筛选。
四 实战
经过上文的介绍,接下来面向假设的选股需求,实际编写一个选股公式。
假设有需求如下,注意接下来的公式并未验证实际选股效果,仅用于展示编写选股公式的思路。
- 股价由下降趋势转为上升趋势时为买入信号
- 股价由上升趋势转为下降趋势时为卖出信号
- 买入信号和卖出信号需要有交易量的增长
- 当股价波动平缓时为减少交易频率
- 若当日股价跳开且交易量涨幅超过50%则无视需求4
从上文的分析需求1、需求2适用于使用双线原理来产生买入与卖出信号、这里选择使用两条均线。根据需求4,两条均线的参数由股票的震荡幅度决定。
先表示出股票的震荡幅度:
1 | SHOCK:=STD(CLOSE,30)>0.4; |
STD
是估算标准差函数,用于描述参数序列偏离平均值的程度。
当收盘价30日方差大于0.4时视为股票近日震荡幅度较大,结果赋值给变量SHOCK
,当大于0.4时SHOCK
为1,反之SHOCK
为0
接下来根据变量SHOCK
编写双线
1 | MA_FAST:=MA(C,IF(SHOCK,5,10)); |
可以看到当SHOCK
为1时,双线分别为收盘价的5日均线和20日均线,当SHOCK
为0时,双线分别为收盘价的10日均线和30日均线。
此时在双线交叉的位置做出买卖信号
1 | IS_BUY:=CROSS(MA_FAST,MA_SLOW); |
为满足需求3,规定放量信号如下:
1 | VOL_INC:=(VOL-REF(VOL,1))/REF(VOL,1)>0.2; |
即当前交易量比前日上升超过20%时视为出现放量,修改买卖信号IS_BUY
和IS_SELL
如下:
1 | IS_BUY:=CROSS(MA_FAST,MA_SLOW) AND VOL_INC; |
接下来处理需求5,首先描述出股价跳开且交易量涨幅超过50%如下:
1 | JUMP:=(REF(CLOSE,1)/OPEN<0.95 OR REF(CLOSE,1)/OPEN>1.05) AND (VOL-REF(VOL,1))/REF(VOL,1)>0.5; |
这一公式操作符AND
和OR
复合组成,其中AND
的左边部分(REF(CLOSE,1)/OPEN<0.95 OR REF(CLOSE,1)/OPEN>1.05)
表示昨日收盘价比近日开盘价的比值小于0.95或者大于1.05,也即跳开。
操作符AND
右边部分则表示交易量的涨幅相比昨人交易量上涨超过50。
此时修改变量SHOCK
如下:
1 | SHOCK:=STD(CLOSE,30)>0.4 OR JUMP; |
此时当满足需求5的条件时SHOCK
恒定为1,双线均为更加敏感的参数,也即满足了需求5条件满足时不要减少交易频率的特殊要求。
最后为了便于观察,修改公式使得在叠加K线图的时候标志出明显个买卖信号。
下面整体看一遍公式:
1 | JUMP:=(REF(CLOSE,1)/OPEN<0.95 OR REF(CLOSE,1)/OPEN>1.05) AND (VOL-REF(VOL,1))/REF(VOL,1)>0.5; |
最后再次说明一下,上面的公式仅仅是为了演示如何将抽象选股需求转化为结构化的选股公式。实际的实践效果则依赖于后续的多次回归测试验证,以及针对公式中具体常量参数的反复调整。
五 总结
总的来说,条件选股公式无外乎对于某只股票的各种指标进行过滤,将待选股票中的满足要求的股票筛选出来。如何确定待选股票,更加依赖分析师自己对于市场风向的把握。期望仅仅以技术描述筛选出的股票能战无不胜显然是不现实的。学习如何编写选股公式更多的是在自动化选股中将抽象的经验总结转化为具体的、可描述、可量化的语言。