作者:chen_h
微信号 & QQ:862251340
微信公众号:coderpai
决策树是一种用于分类和回归问题的机器监督学习方法,比如 CART。请记住,分类问题会尝试将未知元素分类为类或类别,输出始终是分类变量(比如,是/否,上/下,红/蓝/黄等)。回归问题会尝试预测某个数字,例如第二天的股票回报。 它不能与用于研究变量之间关系的线性回归相混淆。虽然分类和回归问题有不同的目标,但树具有相同的结构:
-
Root节点位于顶部,没有传入路径。
-
内部节点或测试节点位于中间,可以位于不同的级别或子空间,并具有传入和传出路径。
-
叶节点或决策节点位于底部,具有传入路径但没有传出路径,在这里我们可以找到预期的输出。
感谢 Python 的 Sklearn 库,可以自动为我们创建树,以我们假设认为负责我们正在寻找的输出的预测变量作为起点。在决策树的这篇介绍中,我们将在Python中创建一个分类决策树,以预测我们要分析的金融工具是否会在第二天上升或下降。我们还将创建一个回归决策树,以便在第二天对索引的具体回报进行预测。
构建决策树
构建分类决策树或回归决策树在我们组织输入数据和预测变量的方式上非常相似,然后,通过调用相应的函数,将根据一些自动为我们创建分类决策树或回归决策树。 我们必须指定的标准。
构建决策树的主要步骤是:
- 检索金融工具的市场数据。
- 引入预测变量(即技术指标,情绪指标,广度指标等)
- 设置 Target 变量或所需的输出。
- 在训练和测试数据之间拆分数据。
- 生成决策树训练模型。
- 测试和分析模型。
如果我们查看前四个步骤,它们是数据处理的常见操作。如果您是决策树的新手,那么预测变量和目标变量对您来说可能听起来很奇怪。但是,它们只不过是数据框中包含某种类型指标的其他列。这些指标或预测变量用于预测目标变量,即金融工具将为分类模型上升或下降,或回归模型的未来价格水平。同样,拆分数据是任何回测过程中的强制性任务(ML或不是ML),其想法是使用一组数据来训练模型,而另一组数据(未在训练中使用)来测试模型。
步骤5和6具体涉及决策树的ML算法。正如我们将看到的,Python中的实现非常简单。然而,理解参数化和结果分析是很重要的。这篇文章非常实用,为了深入了解基础数学,我们建议阅读文章底部的参考文献。
获取数据
任何算法的原材料都是数据。 在我们的例子中,它们将是金融工具的时间序列,例如指数,股票等,它通常包含开盘价,最高价,最低价,收盘价和成交量等细节。 此信息以特定频率记录,例如分钟,小时,天或周,并形成时间序列。
有多个数据源可以下载数据,免费和付费。 免费日常数据的最常见来源是Quandl,Yahoo或Google或我们信任的任何其他数据源。
在这里,我们将使用我们将通过Quandl检索的Emini S&P 500的20年日常数据。
import quandl
df = quandl.get("CHRIS/CME_ES2")
df.head()
df.tail()
df.shape
程序输出为:
(5519, 8)
我们现在拥有超过21年的Emini S&P500数据。 我们将使用结算价作为收盘价参考。
创建预测变量
预测变量是我们认为与市场行为相关的数据。 这些数据可以是非常多样化的,例如技术指标,市场数据,情绪数据,广度数据,基础数据,政府数据等,这将有助于我们对市场的未来行为做出预测。
在这里,我们将测试趋势跟踪和范围交易的经典指标,这些是
- EMA
- ATR
- ADX
- RSI
- MACD
因此,决策树算法应该帮助我们选择指标的最佳组合及其参数,以最大化作为目标的预期输出。
我们将通过计算我们将用作预测器的指标来准备数据,为此,我们将使用Ta-lib库:
import talib as ta
df['EMA10'] = ta.EMA(df['Settle'].values, timeperiod=10)
df['EMA30'] = ta.EMA(df['Settle'].values, timeperiod=30)
df['ATR'] = ta.ATR(df['High'].values, df['Low'].values, df['Settle'].values, timeperiod=14)
df['ADX'] = ta.ADX(df['High'].values, df['Low'].values, df['Settle'].values, timeperiod=14)
df['RSI'] = ta.RSI(df['Settle'].values, timeperiod=14)
macd, macdsignal, macdhist = ta.MACD(df['Settle'].values, fastperiod=12, slowperiod=26, signalperiod=9)
df['MACD'] = macd
df['MACDsignal'] = macdsignal
df.tail()
程序输出为:
Open | High | Low | Last | Change | Settle | Volume | Previous Day Open Interest | EMA10 | EMA30 | ATR | ADX | RSI | MACD | MACDsignal | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Date | |||||||||||||||
2019-05-07 | 2918.00 | 2935.75 | 2867.75 | 2896.00 | 42.25 | 2895.75 | 7550.0 | 45430.0 | 2928.329647 | 2900.964954 | 31.144673 | 27.147168 | 47.308682 | 19.324878 | 25.667999 |
2019-05-08 | 2895.50 | 2903.75 | 2874.00 | 2889.75 | 3.50 | 2892.25 | 8461.0 | 46272.0 | 2921.769711 | 2900.402699 | 31.045054 | 26.519254 | 46.471741 | 15.101163 | 23.554632 |
2019-05-09 | 2887.50 | 2890.00 | 2841.50 | 2877.50 | 14.50 | 2877.75 | 6713.0 | 46292.0 | 2913.766127 | 2898.941234 | 32.452550 | 26.947072 | 43.072088 | 10.463196 | 20.936345 |
2019-05-10 | 2876.25 | 2897.75 | 2831.00 | 2892.50 | 14.00 | 2891.75 | 7956.0 | 48964.0 | 2909.763195 | 2898.477284 | 34.902368 | 27.618541 | 47.096261 | 7.827032 | 18.314482 |
2019-05-13 | 2864.75 | 2873.50 | 2806.25 | 2812.50 | 80.25 | 2811.50 | 16409.0 | 51277.0 | 2891.897160 | 2892.865846 | 38.516484 | 28.815698 | 32.788431 | -0.729245 | 14.505737 |
我们已经计算了指标,但有必要强调的是,我们已经用标准参数计算了这些指标,并且这些指标必须优化,因为决策树与预先计算的指标一起工作。
另一方面,EMA 和 MACD 并不像它们那样,因为信号来自与平均值相关的价格,或来自相对于另一个的平均值。 让我们计算将作为平均值和MACD的预测值的列。
import numpy as np
df['ClgtEMA10'] = np.where(df['Settle'] > df['EMA10'], 1, -1)
df['EMA10gtEMA30'] = np.where(df['EMA10'] > df['EMA30'], 1, -1)
df['MACDSIGgtMACD'] = np.where(df['MACDsignal'] > df['MACD'], 1, -1)
df.tail()
程序输出为:
Open | High | Low | Last | Change | Settle | Volume | Previous Day Open Interest | EMA10 | EMA30 | ATR | ADX | RSI | MACD | MACDsignal | ClgtEMA10 | EMA10gtEMA30 | MACDSIGgtMACD | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Date | ||||||||||||||||||
2019-05-07 | 2918.00 | 2935.75 | 2867.75 | 2896.00 | 42.25 | 2895.75 | 7550.0 | 45430.0 | 2928.329647 | 2900.964954 | 31.144673 | 27.147168 | 47.308682 | 19.324878 | 25.667999 | -1 | 1 | 1 |
2019-05-08 | 2895.50 | 2903.75 | 2874.00 | 2889.75 | 3.50 | 2892.25 | 8461.0 | 46272.0 | 2921.769711 | 2900.402699 | 31.045054 | 26.519254 | 46.471741 | 15.101163 | 23.554632 | -1 | 1 | 1 |
2019-05-09 | 2887.50 | 2890.00 | 2841.50 | 2877.50 | 14.50 | 2877.75 | 6713.0 | 46292.0 | 2913.766127 | 2898.941234 | 32.452550 | 26.947072 | 43.072088 | 10.463196 | 20.936345 | -1 | 1 | 1 |
2019-05-10 | 2876.25 | 2897.75 | 2831.00 | 2892.50 | 14.00 | 2891.75 | 7956.0 | 48964.0 | 2909.763195 | 2898.477284 | 34.902368 | 27.618541 | 47.096261 | 7.827032 | 18.314482 | -1 | 1 | 1 |
2019-05-13 | 2864.75 | 2873.50 | 2806.25 | 2812.50 | 80.25 | 2811.50 | 16409.0 | 51277.0 | 2891.897160 | 2892.865846 | 38.516484 | 28.815698 | 32.788431 | -0.729245 | 14.505737 | -1 | -1 | 1 |
我们现在拥有的是可能的交易规则,我们将在决策树中引入这些规则,以帮助我们确定这些指标的最佳组合,以最大化结果。
- EMA,当价格高于平均水平且最快平均值高于最慢平均值时,我们感兴趣。
- ATR(14),我们对触发信号的阈值感兴趣。
- ADX(14),我们对触发信号的阈值感兴趣。
- RSI(14),我们对触发信号的阈值感兴趣。
- MACD,我们对MACD信号何时高于MACD感兴趣。
在该示例中,分类决策树和回归决策树的预测变量将是相同的,尽管目标变量是不同的,因为对于分类算法,输出将是分类的,并且对于回归算法,输出将是连续的。
创建目标变量
正如我们已经说过的,分类和回归决策树有不同的目标。 虽然分类决策树试图通过提供分类变量来表征未来,即市场上升或下降,但是回归决策树试图预测未来价值,即未来市场价格。
我们将在这里为这两类问题创建目标变量,尽管每个问题都会使用自己的目标。
df['Return'] = df['Settle'].pct_change(1).shift(-1)
df['target_cls'] = np.where(df.Return > 0, 1, 0)
df['target_rgs'] = df['Return']
df.tail()
程序输出为:
Open | High | Low | Last | Change | Settle | Volume | Previous Day Open Interest | EMA10 | EMA30 | … | ADX | RSI | MACD | MACDsignal | ClgtEMA10 | EMA10gtEMA30 | MACDSIGgtMACD | Return | target_cls | target_rgs | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Date | |||||||||||||||||||||
2019-05-07 | 2918.00 | 2935.75 | 2867.75 | 2896.00 | 42.25 | 2895.75 | 7550.0 | 45430.0 | 2928.329647 | 2900.964954 | … | 27.147168 | 47.308682 | 19.324878 | 25.667999 | -1 | 1 | 1 | -0.001209 | 0 | -0.001209 |
2019-05-08 | 2895.50 | 2903.75 | 2874.00 | 2889.75 | 3.50 | 2892.25 | 8461.0 | 46272.0 | 2921.769711 | 2900.402699 | … | 26.519254 | 46.471741 | 15.101163 | 23.554632 | -1 | 1 | 1 | -0.005013 | 0 | -0.005013 |
2019-05-09 | 2887.50 | 2890.00 | 2841.50 | 2877.50 | 14.50 | 2877.75 | 6713.0 | 46292.0 | 2913.766127 | 2898.941234 | … | 26.947072 | 43.072088 | 10.463196 | 20.936345 | -1 | 1 | 1 | 0.004865 | 1 | 0.004865 |
2019-05-10 | 2876.25 | 2897.75 | 2831.00 | 2892.50 | 14.00 | 2891.75 | 7956.0 | 48964.0 | 2909.763195 | 2898.477284 | … | 27.618541 | 47.096261 | 7.827032 | 18.314482 | -1 | 1 | 1 | -0.027751 | 0 | -0.027751 |
2019-05-13 | 2864.75 | 2873.50 | 2806.25 | 2812.50 | 80.25 | 2811.50 | 16409.0 | 51277.0 | 2891.897160 | 2892.865846 | … | 28.815698 | 32.788431 | -0.729245 | 14.505737 | -1 | -1 | 1 | NaN | 0 | NaN |
回归算法的目标变量(target_rgs)使用滞后返回,这是因为我们希望算法根据当前可用的信息来了解第二天发生的事情。
分类算法的目标变量(target_cls)也使用滞后返回,但由于输出是分类的,我们必须对其进行转换。如果回报为正,我们分配1,如果是负数,我们分配0。
获取决策树的数据集
我们准备好了所有数据!我们下载了市场数据,应用了一些技术指标作为预测变量,并为每种类型的问题定义了目标变量,分类决策树的分类变量和回归决策树的连续变量。
我们将进行一个小操作来清理数据并准备每个算法将使用的数据集。我们必须清理丢弃NA数据的数据,这一步对于计算干净的树是至关重要的。
接下来,我们将创建预测变量的数据集,也就是说,我们计算的指标,这个数据集对于我们要创建的两个决策树,分类决策树和回归决策树。
predictors_list = ['ATR', 'ADX','RSI', 'ClgtEMA10', 'EMA10gtEMA30', 'MACDSIGgtMACD']
X = df[predictors_list]
X.tail()
程序输出为:
ATR | ADX | RSI | ClgtEMA10 | EMA10gtEMA30 | MACDSIGgtMACD | |
---|---|---|---|---|---|---|
Date | ||||||
2019-05-07 | 31.144673 | 27.147168 | 47.308682 | -1 | 1 | 1 |
2019-05-08 | 31.045054 | 26.519254 | 46.471741 | -1 | 1 | 1 |
2019-05-09 | 32.452550 | 26.947072 | 43.072088 | -1 | 1 | 1 |
2019-05-10 | 34.902368 | 27.618541 | 47.096261 | -1 | 1 | 1 |
2019-05-13 | 38.516484 | 28.815698 | 32.788431 | -1 | -1 | 1 |
然后,我们为分类决策树选择目标数据集:
y_cls = df.target_cls
y_cls.tail()
程序输出为:
Date
2019-05-07 0
2019-05-08 0
2019-05-09 1
2019-05-10 0
2019-05-13 0
Name: target_cls, dtype: int64
最后,我们选择回归决策树的目标数据集:
y_rgs = df.target_rgs
y_rgs.tail()
输出结果为:
Date
2019-05-07 -0.001209
2019-05-08 -0.005013
2019-05-09 0.004865
2019-05-10 -0.027751
2019-05-13 NaN
Name: target_rgs, dtype: float64
将数据拆分为训练和测试数据集
完成数据集准备的最后一步是将它们分成训练和测试数据集。 这对于使模型与一组数据(通常为70%或80%和其余数据)拟合来测试模型的良好性是必要的。 如果我们不这样做,我们就会冒着过度拟合模型的风险。 为了评估模型的准确性,我们希望在安装模型后测试具有未知数据的模型。
我们将创建数据集,其中70%的数据来自预测变量和目标变量数据集,其余30%来测试模型。
对于分类决策树,我们将使用 sklearn model_selection 库中的 train_test_split 函数来分割数据集。 由于输出是分类的,因此训练和测试数据集成比例是很重要的 train_test_split 函数将预测变量和目标数据集以及一些输入参数作为输入:
-
test_size:测试数据集的大小,在这种情况下,测试数据的30%,因此,训练的70%。
-
random_state:由于采样是随机的,因此该参数允许我们在每次执行中重现相同的随机性。
-
分层:为了确保训练和测试样本数据成比例,我们将参数设置为yes。 这意味着,例如,如果有更多天的正面回报率,那么训练和测试样本将保持相同的比例。
from sklearn.model_selection import train_test_split
y=y_cls
X_cls_train, X_cls_test, y_cls_train, y_cls_test = train_test_split(X, y, test_size=0.3, random_state=432, stratify=y)
print (X_cls_train.shape, y_cls_train.shape)
print (X_cls_test.shape, y_cls_test.shape)
程序输出为:
(3863, 6) (3863,)
(1656, 6) (1656,)
我们在这里:
- 训练预测变量数据集:X_cls_train
- 训练目标变量数据集:y_cls_train
- 测试预测变量数据集:X_cls_test
- 测试目标变量数据集:y_cls_test
对于回归决策树,我们只是以指定的比率拆分数据,因为输出是连续的,我们不担心训练和测试数据集中输出的比例。
train_length = int(len(df)*0.70)
X_rgs_train = X[:train_length]
X_rgs_test = X[train_length:]
y_rgs_train = y_rgs[:train_length]
y_rgs_test = y_rgs[train_length:]
print (X_rgs_train.shape, y_rgs_train.shape)
print (X_rgs_test.shape, y_rgs_test.shape)
再次,我们在这里:
- 训练预测变量数据集:X_rgs_train
- 训练目标变量数据集:y_rgs_train
- 测试预测变量数据集:X_rgs_test
- 测试目标变量数据集:y_rgs_test
到目前为止我们已经完成了:
- 下载市场数据。
- 计算我们将用作预测变量的指标。
- 定义目标变量。
- 将数据拆分为训练集和测试集。
在获得目标变量和分割数据集的过程中略有不同,到目前为止所采取的步骤是相同的。
分类决策树
现在让我们使用sklearn.tree库中的DecisionTreeClassifier函数创建分类决策树。
虽然DecisionTreeClassifier函数有许多参数,我邀请您了解并试验(help(DecisionTreeClassifier)),但在这里我们将看到创建分类决策树的基础知识。
基本上是指算法必须用于构建树的参数,因为它遵循构建树的递归方法,我们必须设置一些限制来创建它。
-
criterion:对于分类决策树,我们可以选择基尼或熵和信息增益,这些标准是指用于评估学习机算法性能的损失函数,并且最常用于分类算法,尽管它超出了范围。 这篇文章,基本上是我们调整模型的准确性,也是建立树的算法,停止评估根据损失函数没有得到改进的分支。
-
max_depth:树将具有的最大级别数。
-
min_samples_leaf:此参数是可优化的,表示我们希望在叶子中拥有的最小样本数。
from sklearn.tree import DecisionTreeClassifier
clf = DecisionTreeClassifier(criterion='gini', max_depth=3, min_samples_leaf=6)
clf
程序输出为:
DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=3,
max_features=None, max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=6, min_samples_split=2,
min_weight_fraction_leaf=0.0, presort=False, random_state=None,
splitter='best')
现在我们将使用训练数据集训练模型,我们拟合模型并且算法已经完全训练。
clf = clf.fit(X_cls_train, y_cls_train)
clf
现在我们需要使用模型对未知数据进行预测,为此我们将使用保留用于测试的30%的数据,最后评估模型的性能。 但首先,让我们以图形方式查看ML算法为我们自动创建的分类决策树。
做出预测
现在让我们使用保留用于测试的数据集进行预测,这是让我们知道算法在训练中是否可靠且未知数据的部分。
y_cls_pred = clf.predict(X_cls_test)