0%

大作业2:金融数据分析实验报告

一、问题简述

在此作业中,我们致力于开发一个股票交易模型,它的主要流程为:

  1. 对股票的价格和交易数据进行适当的标记,并打上买卖动作的标签。
  2. 插入具体的特征,用以更好解释标签。
  3. 采用合适的方法训练模型。
  4. 使用机器学习的输出对当前为止的价格数据做出买入、卖出、不做动作的预测。
  5. 最后反馈模型的利润率、最大回撤、夏普指数、交易次数等详细信息。

二、特征选取

1. 布林线

布林线指标(Bollinger Bands),主要利用统计原理,求出股价的移动平均值、标准差及其信赖区间,从而确定股价的波动范围及未来走势,利用波带显示股价的安全高低价位。

图1 训练集股票1中2021年10月至2023年1月布林线指标。绿色实线为股票价格,上下红色虚线为上轨线和下轨线,中间黄色点划线为中轨线。

布林线指标属于路径指标,股价波动在上限和下限的区间之内,这条带状区的宽窄,随着股价波动幅度的大小而变化,股价涨跌幅度加大时,带状区变宽,涨跌幅度狭小盘整时,带状区则变窄。

布林线Python上可由以下代码实现:

1
2
import talib as ta
df['UP'], df['MD'], df['DN'] = ta.BBANDS(df.Close, timeperiod=20, matype=0)

其中的UP、MD、DN分别表示上、中、下轨线。

2. CCI

CCI(Commodity Channel Index)指标又叫顺势指标,是一种专门测量股价或外汇是否已超出常态分布范围的指标。

图2 训练集股票1中2021年10月至2023年1月CCI指标。绿色实线为股票价格,红色实线为CCI指标。

CCI指标属于超买超卖类指标中较为特殊的一种,波动在正无穷大和负无穷大之间,但又不需要以0为中轴线。因此CCI不会出现指标钝化现象,有利于投资者更好地研判行情,特别是那些短期内暴涨暴跌的非常态行情。这对于我们在股票市场上追涨杀跌、短期获得较高收益率由较大的帮助。

布林线Python上可由以下代码实现:

1
2
import talib as ta
df['CCI'] = ta.CCI(df.High, df.Low, df.Close, timeperiod=20)

3. 其他特征:MTM、涨停与复权

股票的动量指标MTM当主要通过前价格和一段时间前的价格,或对比开盘价与收盘价的高低,来判断股票价格走势的强弱和方向。MTM指标可以用来发现股票价格趋势的转折点,也可以用来确认价格趋势的持续性。当MTM指标从上升转为下降时,可能意味着股票价格即将出现下跌趋势;当MTM指标从下降转为上升时,可能意味着股票价格即将出现上涨趋势。

股票涨停是指为了减少股市交易的投机行为,规定每个股票每个交易日的涨跌幅度,达到上涨上限幅度的就叫涨停。中国股市每个交易日涨跌限幅为**10%**。股票看到有涨停势头时尽快买入,增长势头减弱时卖出,可以快速增加手里持有的现金量。

股票复权是指当股价因送股、配股等原因而发生下跌时,股价瞬间变为原来的二分之一或者三分之一等,但该股票实际价值并没有发生变化。我们的测试数据没有直接看出股票是否发生复权的指标,但是复权的发生不会影响股票原有的走势。对于股票复权,Adj Close可以作为间接的判断依据之一。

三、标签插入

1. 布林线

布林线的绘制可以帮助我们判断股票的走势,从而确定买卖情况:

  1. BOLL指标中的上、中、下轨线所形成的价格通道的移动范围是不确定的,在正常情况下,市场价格应始终处于价格通道内运行。如果市场价格脱离价格通道运行,则意味着行情处于极端的状态下;
  2. 当布林线的上、中、下轨线同时向上运行时,表明市场强势特征非常明显,短期内将继续上涨,应坚决持股做多。反之则表明市场的弱势特征非常明显,短期内将继续下跌,投资者应坚决做空;
  3. 当布林线的上轨线向下运行,中轨线和下轨线向上运行时,表明市场处于整理态势之中。如果市场是处于长期上升趋势时,则表明市场是上涨途中的强势整理;如果市场是处于长期下跌趋势时,则表明市场是下跌途中的弱势整理。

同时,对于布林线自身的研判也有一定的标准:

  1. 当股价穿越上轨线(动态上限压力线,静态最上压力线BOLB1)时,为卖点信号;
  2. 当股价穿越下轨线(动态下限支撑线,静态最下支撑线BOLB4)时,为买点信号;
  3. 当股价由下向上穿越中界线(静态从BOLB4穿越BOLB3)时,为加码信号;
  4. 当股价由上向下穿越中界线(静态由BOLB1穿越BOLB2)时,为卖出信号。

基于以上标准我们确定了买卖标志的BOLL定量模型:

1
2
3
4
5
6
df['BOLL_last'] = df['BOLL'].shift(1)
df['Signal_BOLL'] = np.where(df['BOLL'] > 1, -1.0, 0.0)\
+ np.where((df['BOLL'] < -1) & (df['BOLL_last'] > -1), 0.5, 0.0)\
+ np.where((df['BOLL'] < -1) & (df['BOLL_last'] <= -1), 1.0, 0.0)\
+ np.where((df['BOLL_last'] > 0) & (df['BOLL'] < 0), -0.5, 0.0)\
+ np.where((df['BOLL_last'] < 0) & (df['BOLL'] > 0), 0.5, 0.0)

其中的正向信号表示买入,负向信号表示卖出,而值的多少(通常为0.5或者1.0)表示了买入信号的强烈与否。例如1.0的指标表示建议将所有现金梭哈股市;-0.5的指标则是建议卖出一半的股票转为流动资本。后续的信号系统与此一致。

图3 训练集股票1中2021年10月至2023年1月由布林线指标确定的买卖标志(蓝色线)。

2. CCI

CCI指标在+100线(也叫天线)~ -100线(也叫地线)的常态区间里运行时,参考意义不大,可以用布林线等其它技术指标进行研判。(这也意味着我们确定权重时的先后顺序)

但CCI指标穿过两条标准线(+100线和-100线)时,意味着我们需要进行适当的买卖操作了:

  1. 当CCI自常态行情由下往上突破+100天线时为短线追涨抢进时机;
  2. 而当CCI由上往下跌破-100地线时为痛打落水狗的卖空杀跌时机;
  3. CCI由下往上突破-100地线为空头回补短线的买进时机;
  4. CCI从+100天线由上往下跌破天线,可以卖出止赢。

此外,CCI指标还有其他可以作为买卖标准的判断方式,但由于专业性过强、有一定主观判断的因素、缺乏合适的定性标准,因此我们未予采纳。

基于以上标准我们确定了买卖标志的CCI定量模型:

1
2
3
4
5
df['CCI_last'] = df['CCI'].shift(1)
df['Signal_CCI'] = np.where((df['CCI_last'] <= 100) & (df['CCI'] > 100), 1, 0.0)\
+np.where((df['CCI_last'] >= -100) & (df['CCI'] < -100), -1, 0.0)\
+np.where((df['CCI_last'] <= -100) & (df['CCI'] > -100), 0.5, 0.0)\
+np.where((df['CCI_last'] >= 100) & (df['CCI'] < 100), -1, 0.0)

图4 训练集股票1中2021年10月至2023年1月由CCI指标确定的买卖标志(蓝色线)。

3. 其他指标

MTM、涨停、复权等定性指标对于股票买卖策略的计算有着重要作用,但在机器学习的过程中表现欠佳,且该信号往往快速强烈,可以直接算出或用Pyalgotrade计算得出,因此不需要再进行机器学习的标签标记和模拟训练。

四、机器学习

1. 模型构建

对于特征提取和标签插入,尽管我们计算了各项指标,但是我们最终得到的还是取值从-1到+1的“推荐买卖量”。其中全买为+1,全卖为-1。为了加权计算得出买卖股票的量,我们加入了“推荐股票持有量”来进行计算。具体的计算步骤为:

  1. 计算各指标的推荐买卖程度;
  2. 计算各指标推荐您现在持有股票的量;
  3. 根据权重和优先级计算加权平均推荐股票持有量;
  4. 计算推荐您买卖的量(一阶差分)。

而从“推荐买卖量”到“推荐持有量”在交易策略中以函数Sig2sig(self, dataframe, column_name, new_column_name)实现。

在特征提取和标签插入的过程中,我们按照MTM->涨停复权->BOLL->CCI的先后顺序(优先级)来处理股票的各项指标。具体计算方法在模型文件MyModel.ipynb(代码块14)与交易文件MyStrategy.py(函数onBars(self, bars))中都有所体现。

图5 训练集股票1中各指标与最终推荐买卖量的相关系数

2. 模型优化

与样例文件不同,我们的模型本质上属于多分类问题,在测试极度随机树、逻辑回归、K近邻等分类模型之后,我们最终选择了支持向量机(SVM)进行分类和预测。多类分类器采用间接法,二分类器选择one-versus-one(ovo),核函数选择径向基函数(radial basis function)。Python代码如下:

1
2
3
from sklearn import svm
model = svm.SVC(decision_function_shape='ovo', kernel='rbf')
fit = model.fit(X,y)

我们将训练完的模型保存在了文件“mystock.pkl”中。由于模型是多分类且包含较多主客观杂糅的因素,我们模型的accuracy为72%。具体分类评价报告为:

1
2
3
              precision    recall  f1-score   support
accuracy 0.72 3086
weighted avg 0.51 0.72 0.60 3086

此外,对于真实股票交易中可能出现的诸多状况,我们也在具体实现的过程中进行了模拟和优化:

  1. 交易过量问题。在模拟交易的过程中,可能由于自身持有资金的量过大或股价过低,而同时机器学习模型又强烈推荐购入股票(例如连续涨停)。此时的预期交易量可能大于股票的每日交易量,Pyalgotrade会产生以下报错:
    1
    201X-XX-XX 00:00:00 broker.backtesting [DEBUG] Not enough volume to fill stockXX market order [XX] for XXXX share/s
    为了避免以上情况的产生,最佳的策略就是减少交易量。简化起见,我们对此提出了动态交易策略。亦即当手中资金较多时,对推荐的交易量乘一个交易乘数,来控制交易量,保证买入和卖出股票的量都在可控的区间内。同时我们还会将交易量与过去10天的指数平均交易量进行对比,动态改变交易策略,防止出现超量交易或“Invalid quantity”等报错。
  2. 负交易量问题。Pyalgotrade允许股票的做空与现金的贷款,因此在买卖的过程中偶尔会有“负现金”和“负股数”的出现(这种问题也通常是由上述的交易过量导致失控而产生)。但买入股票(enterLong函数)和卖出股票(enterShort函数)均不允许负交易量,因此在发生这种特殊情况时,我们将不按照预测结果,而是直接买入稍多余手中做空股票的数量(或卖出约等于手中贷款的股票)保证二者都在正区间。

尽管精确度并不是极其准确,但在最终交易标志signal_FINAL的正负号的预测上,准确率可以达到100%,因此对于预测股票的买卖操作可以起到较强的指导和推荐作用。同时,其他对于交易模型的优化也保证了交易的可靠性。

五、测试结果

对于真实的股票数据,模型取得了较好的测试结果。对于用作测试集的股票都取得了较好的效果。

表1 各支股票的收益率等指标情况展示
利润率(%) 夏普指数 最大回撤(%) 交易次数
stock11 152.3 0.73 0.1334 232
stock12 131.74 0.63 0.0892 209
stock13 124.96 0.5 0.1674 223
stock14 169.05 0.7 0.1512 227
stock15 172.63 0.6 0.1554 212
stock16 122.93 0.55 0.1547 233
stock17 74.49 0.23 0.1374 229
stock18 166.75 0.78 0.0993 227
stock19 107.23 0.41 0.1651 242
stock20 198.21 1.05 0.0946 266

图6 stock06(未用作训练数据)应用股票交易模型的收益情况

各支股票收益率情况如表1。可以看出,12年间,股票收益普遍位于100%至200%之间,使用年金模型折算年化收益率约为6%~10%,在无组合资产投资方面属于较为先进的水平。

六、总结

基于Pyalgotrade平台和talib库,我们实现了一个股票交易模型,它主要运用布林线、CCI等指标,结合动量、涨停、复权等股票专业知识,进行主观客观综合分析,采用支持向量机进行预测推荐的交易量,并进行投资规划,可以实现约为6%~10%的年化收益率。与中长期持股的股票交易策略不同,本交易模型主要采用了“快买快卖”“买了就卖”的短线交易策略,主要的收益来源是短期持股,因此也有较高的交易次数。