随机森林AI选股策略开发文档


(iQuant) #1

本文档对随机森林AI选股策略开发进行介绍,帮助大家迅速了解平台具体使用,快速上手开发策略。

理论介绍

决策树

日常生活中,我们对于事物的认知都是基于特征的判断与分类,譬如通过胎生与否可判断哺乳动物,根据肚脐尖圆来挑选螃蟹公母。决策树就是采用这样的思想,基于多个特征进行分类决策。在树的每个节点处,根据特征的表现通过某种规则分裂出下一层的叶子节点, 终端的叶子节点即为最终的分类结果。

图1是股票特征数据和涨跌状态的一个表格,一共有两个特征:市值和板块,我们可以将该表数据转换成一棵决策树,见图2。


$$图1:股票数据$$

image
$$图2:决策树模型$$

从图2可以看出,我们可以将样本进行分类构建一个决策树模型,对未来的数据进行预测。

随机森林

三个臭皮匠,顶个诸葛亮。随机森林模型由决策树模型演变而来,单棵决策树的预测能力有限,但是将多个弱分类器组合成一个强分类器,其预测能力可能会得到很大提升,随机森林模型正是这样的一种集成方法,不仅可以用来解决分类问题还能解决预测问题。

随机森林模型的思想如图3所示,我们由原始数据集生成 N 个 Bootstrap 数据集,对于每个 Bootstrap 数据集分别训练一个弱分类器,最终用投票、取平均值等方法组合成强分类器。

image
$$图3:随机森林的集成思想$$

随机森林的优点

决策树有一个明显的缺点是容易受到训练集中极端数据影响出现过拟合,而我 们通过随机森林模型构建则可以达到降低过拟合几率的效果。在随机森林中,虽然每棵树只利用 m 个因子特征进行划分,单独来看分类效果并不出色但是组合在一起后反而更加稳定。不妨这样理解,每一棵决策树就是一个精通于某一个窄领域(从 M 个因子中选取 m 个让每棵树学习)的专家,随机森林则包含很多个精通不同领域的专家,对一个新的问题 (新数据集),可以用不同的角度去看待它,最终投票得到结果。


AI选股策略流程

机器学习和深度学习在量化策略中的应用通常如图4流程所示:


$$图4:AI流程$$

本文所要介绍的随机森林AI选股策略也基本符合上面的流程,见图5:


$$图5:随机森林AI选股策略流程$$

模型调参

模型一般都会具有多个超参数,这些超参数将直接决定模型训练的好坏。随机森林的超参数如图6所示:


$$图6:随机森林参数$$

树的个数

我们将随机森林中最大的弱学习器(决策树)的个数称为树的个数(简记为 n)。一般来说如果树的个数太小,则无法发挥Bagging集成算法的优势而容易产生欠拟合;树的个数过大,会增大计算量,并且树的个数到一定的数量后,再增大树的个数获得的模型提升会很小,所以我们要寻找一个适中的数值。在实际调参的过程中,我们常常将树的个数和学习效率一起考虑。初始设置树的个数为10,这里可以进行手动调整。

最多考虑特征个数

特征个数(max_features)即每个决策树在进行分类时需要考虑的最大特征数,对于决策树的生成时间和具体分类情况有影响作用。当输入样本的总特征个数不多,比如小于 50, 则可以不对最大个数进行限制(即全部选入)。 从调参经验来看,如果特征数量小于30个,那么可以采取默认值,当特征个数非常大时,进行该参数的调整才有意义。

最大深度
默认为30,如果不输入的话,决策树在建立子树的时候不会限制子树的深度。一般来说,数据少或者特征少的时候可以不管这个值。如果模型样本量多,特征也多的情况下,推荐限制这个最大深度,具体的取值取决于数据的分布。常用的可以取值10-100之间。

叶节点最小样本数

若某叶子节点样本数目小于叶节点最小样本数(简记为 l),则会和兄弟节点一起被剪枝。 该参数限制了叶子节点最少的样本数,对决策树的构建形态有较大影响,当训练集样本数 量较多时,增加该值可以对决策树进行有效的剪枝。

我们可以选择多个进程(并行度)进行并行计算,最大为4。随机森林不仅可以用来做分类,还能预测。本文标注数据被离散化到20个类别,因此这里算法类型我们选择“分类”。


策略开发

策略基本设置

股票池:全A股(非创业板)
训练集:2010.1.1-2015.1.1
测试集:2015.1.2至现在
策略思想:随机森林属于分类器,最终在每天可以产生对全部个股未来几天变动预测值(即各决策树分类结果的投票平均值),因此可以看作一个因子合成模型, 该因子越大的股票表明未来上涨的可能性也越大。我们可以买入排序在前的股票,形成一个多头组合,进行回测查看模型预测实际交易效果。
换仓规则:非定期调仓,每日滚动进行仓位调整——买入部分股票,卖出部分股票
资金分配:每天调仓的资金为总资金的20%,随机森林预测结果作为因子排序靠前的股票分配的资金流较大

衍生特征抽取

机器学习算法的关键在于特征工程,直接决定模型预测的效果,市场上多因子策略不断寻找新的alpha因子的本质也是特征工程。借助表达式引擎的功能,我们可以通过简单的运算符和表达式快速构建衍生因子,而不需要进行大量的代码编写。


$$图7:输入特征列表$$
如:图7所示,我们看到输入特征列表一共六个因子,其中第二个因子是成交额因子,其余五个因子都是通过表达式引擎构建的衍生因子,其各自的含义如下:

shift(close, 5)                                 # 前5日收盘价/当日收盘价
std(close, 20)                                 # 20日收盘价的标准差
mean(close, 10)                             # 10日收盘价的均值
shift(close, 3)                                 # 前3日收盘价/收盘价
correlation(close, volume, 20)       # 近20日收盘价和成交量的相关系数

可以看出,在基础因子上通过表达式引擎可以快速衍生构建因子。

灵活数据标注

大多数的AI算法都是监督式学习算法,也就是说训练集数据是通过特征和标注构成的,举个简单例子,线性回归算法既包含自变量x也包含因变量y,自变量x就是抽取的特征,因变量y就是标注的数据,数据标注的重要性并不亚于特征工程的重要性,而且将直接决定交易持仓时间。


$$图8:自动数据标注$$

图8表示的是数据标注的详细内容。每行代码表示对标注数据进行一个操作,第一行的内容(黑色矩形框)表示按什么数据进行标注,shift(close, -5)/shift(open, -1)-1表示按未来5日收益收益率进行标注;第二行代码表示极值处理,将99%以上和1%以下的数据做截取处理;第三行代码 all_wbins(label, 20)是将标注离散化,转化成20个分类,这里就是0到19这20个数值,标注越大,表明未来五日收益率越高;最后一行代码是过滤掉一字涨停的股票样本,这里默认是过滤掉,也可以不过滤。

上下文交互分析

通过连线、搭积木这样的拖拽方式就能够快速开发策略,每一个矩形框是一个模块,每个模块有输入和输出,模块与模块通过线条连接,以实现数据的流动。

image

$$图9:连接数据模块$$
这里m6模块是一个连接数据的模块,目前是v3版本(不同的版本有细微差异,建议用最新版本),m6模块有两个输入,一个是标注数据(m2模块),一个是抽取特征(m5模块),我们新建一个cell(单元),就可以通过模块名把数据结构打印出来,见图10。


$$图10:打印连接数据模块的输出数据$$

上下文交互分析最大的好处就是能够查看各个模块的输入数据和输出数据,知道每个模块的数据结构。

缺失值处理

每一个数据操作都可以成为一个模块,模块本身就是对输入数据进行处理加工得到输出数据。因为缺失值处理经常遇到,因此将其封装成常用的一个模块,每次使用直接拖进来即可。

数据过滤

数据过滤也很常用,就是通过一些过滤条件将部分数据进行过滤掉。
image
$$图11:数据过滤模块$$

比如图11,过滤表达式是“amount > 2000000”的含义就是将成交额这个特征小于200万的样本去除,只在数据集中保留成交额在200万以上的。

自定义模块

自定义模块要着重介绍下,该功能很强大。虽然平台左侧封装好了不少模块和算法,直接可以使用,但是数据分析和策略开发是一项极具挑战和极具创造性的事情,在解决具体业务问题时,会有各种各样的数据操作需求,平台无法事先枚举所有的使用场景,因此提供自定义模块来满足该需求。

image
$$图12:自定义模块$$
图12中自定义模块的作用就是将创业板的数据去除(3开头的证券代码就是创业板)。自定义模块需要一定的代码编写能力,但不需要很高,因为这里只是数据处理相关的操作。

修改交易逻辑

随机森林算法在预测集上进行预测得到的预测结果只能用来选股,虽然我们可以用常规的模型评价指标进行评估,比如准确率、召回率,但是对于量化策略而言,最直接的方式就是直接通过策略回测,看看在这个期间通过模型预测的结果进行交易最终获得怎样的资金收益曲线。

一个完整的交易系统包括进场、出场、资金管理、风险控制,模型选股只决定了何时进场,涉及到其他几个部分的交易逻辑也会直接决定策略回测效果。这里我们以止盈止损为例介绍如何添加交易逻辑。

在平台上“Trade”模块是回测引擎,右键打开属性窗口,可以看到三个重要的函数——主函数、数据准备函数、初始化函数。

$$图13:Trade模块$$

回测引擎采取的是事件驱动机制,主函数是最重要的函数,策略思想和交易逻辑都放在这部分实现。数据准备函数是为了将策略需要用到的外部数据计算出来以便策略使用。初始化函数是为了对账户初始状态和策略参数进行初始配置。

止盈止损

这里我们以止盈止损来介绍如何修改交易逻辑。止盈的条件是:盈利超过3元,止损的条件是:向下跌破持仓以来最高价的10%。具体代码如下,我们将其添加到主函数里面,就可以实现止盈止损功能,如果是百分比止盈或者跟踪止盈,简单修改下代码即可。

    #----------------------------止赢模块START------------------------
    positions = {e.symbol: p.cost_basis  for e, p in context.portfolio.positions.items()}
    # 新建当日止赢股票列表是为了handle_data 策略逻辑部分不再对该股票进行判断
    current_stopwin_stock = [] 
    if len(positions) > 0:
        for i in positions.keys():
            stock_cost = positions[i] 
            stock_market_price = data.current(context.symbol(i), 'price') 
            # 赚3元就止赢
            if stock_market_price - stock_cost  >= 3:   
                context.order_target_percent(context.symbol(i),0)     
                current_stopwin_stock.append(i)
                print('日期:',date,'股票:',i,'出现止盈状况')
    #---------------------------止赢模块END---------------------------
     
 
    #------------------------------------------止损模块START--------------------------------------------
    today = data.current_dt  
    equities = {e.symbol: p for e, p in context.portfolio.positions.items() if p.amount>0}
    
    # 新建当日止损股票列表是为了handle_data 策略逻辑部分不再对该股票进行判断
    current_stoploss_stock = [] 
    if len(equities) > 0:
        for i in equities.keys():
            stock_market_price = data.current(context.symbol(i), 'price')  # 最新市场价格
            last_sale_date = equities[i].last_sale_date   # 上次交易日期
            delta_days = today - last_sale_date  
            hold_days = delta_days.days # 持仓天数
            # 建仓以来的最高价
            highest_price_since_buy = data.history(context.symbol(i), 'high', hold_days, '1d').max()
            # 确定止损位置
            stoploss_line = highest_price_since_buy - highest_price_since_buy * 0.1
            record('止损位置', stoploss_line)
            # 如果价格下穿止损位置
            if stock_market_price < stoploss_line:
                context.order_target_percent(context.symbol(i), 0)     
                current_stoploss_stock.append(i)
                print('日期:', today, '股票:', i, '出现止损状况')
    #-------------------------------------------止损模块END---------------------------------------------