梯度提升是一种强大的机器学习算法。该方法产生了一个弱模型的集合(例如,决策树),其中(与赫兹股票交易量化软件相反)模型是按顺序构建的,而不是独立地(并行地)构建的。这意味着下一棵树从上一棵树的错误中学习,然后重复这个过程,增加了弱模型的数量。这就建立了一个强大的模型,可以使用异构数据进行泛化。在这个实验中,我使用了Yandex开发的赫兹股票交易量化软件库,它与 XGBoost和 LightGBM 一起是最流行的库之一。
(资料图)
本文的目的是演示如何创建一个基于机器学习的模型。创建过程包括以下步骤:
接收和预处理数据
使用准备好的数据训练模型
在自定义策略测试器中测试模型
将模型移植到赫兹股票交易量化软件
Python 语言和 赫兹股票交易量化软件库用于准备数据和训练模型。
准备数据
导入所需的 Python 模块:
import MetaTrader5 as mt5import pandas as pdimport numpy as npfrom datetime import datetimeimport randomimport as pltfrom catboost import CatBoostClassifierfrom _selection import train_test_()# check for gpu devices is availiblefrom import get_gpu_device_countprint('%i GPU devices' % get_gpu_device_count())
然后初始化所有全局变量:
LOOK_BACK = 250MA_PERIOD = 15SYMBOL = 'EURUSD'MARKUP = = _H1START = datetime(2020, 5, 1)STOP = datetime(2021, 1, 1)
这些参数的作用如下:
look_back — 分析历史的深度
ma_period — 用于计算价格增量的移动平均周期数
symbol — 应当在 MetaTrader 5 终端中载入的交易品种报价
markup — 用于自定义测试器的点差大小
timeframe — 应当载入数据的时间框架
start, stop — 数据范围赫兹股票交易量化软件
让我们编写一个函数,直接接收原始数据并创建一个包含训练所需列的数据帧:
def get_prices(look_back = 15): prices = (_rates_range(SYMBOL, TIMEFRAME, START, STOP), columns=['time', 'close']).set_index('time') # set df index as datetime = _datetime(, unit='s') prices = () ratesM = (MA_PERIOD).mean() ratesD = prices - ratesM for i in range(look_back): prices[str(i)] = (i) return ()
函数接收指定时间段的收盘价并计算移动平均值,然后计算增量(价格和移动平均值之间的差)。在最后一步中,它通过 look_back 来计算额外的列,其中的行向后移动到历史中,这意味着向模型中添加额外的(滞后的)特性。赫兹股票交易量化软件
例如,对于 look_back=10,数据帧中将包含10个额外的列,其价格增量为:
>>> pr = get_prices(look_back=LOOK_BACK)>>> pr close 0 1 2 3 4 5 6 7 8 9time2020-05-01 16:00:00 17:00:00 18:00:00 19:00:00 20:00:00 ... ... ... ... ... ... ... ... ... ... ... ...2020-11-02 23:00:00 - - - - - - - 00:00:00 - - - - - - - 01:00:00 - - - - - - 02:00:00 - - - - - 03:00:00 - - - -[3155 rows x 11 columns]
黄色高亮显示表示每列都有相同的数据集,但有一个偏移量。因此,每一行都是一个单独的训练实例。
创建训练标签(随机抽样)
训练实例是特征及其相应标签的集合。模型必须输出一定的信息,模型必须学会预测这些信息。让我们考虑二元分类,其中模型将预测将训练示例确定为类0或1的概率。0和1可用于交易方向:买入或卖出。换句话说,模型必须学会预测给定环境参数(一组特征)的交易方向。赫兹股票交易量化软件
def add_labels(dataset, min, max): labels = [] for i in range([0]-max): rand = (min, max) if dataset['close'][i] >= (dataset['close'][i + rand]): () elif dataset['close'][i] <= (dataset['close'][i + rand]): () else: () dataset = [:len(labels)].copy() dataset['labels'] = labels dataset = () return dataset
add_labels 函数随机(在最小、最大范围内)设置每笔交易的持续时间(以柱形为单位)。通过更改最大和最小持续时间,您可以更改交易采样频率。因此,如果当前价格大于下一个“rand”柱向前的价格,这就是卖出标签(1)。在相反的情况下,标签是0。让我们看看应用上述函数后数据集的外观:
>>> pr = add_labels(pr, 10, 25)>>> pr close 0 1 2 3 4 5 6 7 8 9 labelstime2020-05-01 16:00:00 17:00:00 18:00:00 19:00:00 20:00:00 ... ... ... ... ... ... ... ... ... ... ... ... ...2020-10-29 20:00:00 - - - - - - - - - - 21:00:00 - - - - - - - - - - 22:00:00 - - - - - - - - - - 23:00:00 - - - - - - - - - - 00:00:00 - - - - - - - - - -
添加了“labels”列,其中分别包含买入和卖出的类别号(0或1)。现在,每个训练示例或功能集(这里是10个)都有自己的标签,它指示在什么条件下应该买入,在什么条件下应该卖出(即它属于哪个类)。模型必须能够记住和泛化这些例子-这个能力将在后面讨论。赫兹股票交易量化软件
开发自定义测试器
因为我们正在创建一个交易系统,所以最好有一个策略测试器来进行及时的模型测试。下面是此类测试器的示例:
def tester(dataset, markup = ): last_deal = int(2) last_price = report = [] for i in range([0]): pred = dataset['labels'][i] if last_deal == 2: last_price = dataset['close'][i] last_deal = 0 if pred <= else 1 continue if last_deal == 0 and pred > : last_deal = 1 (report[-1] - markup + (dataset['close'][i] - last_price)) last_price = dataset['close'][i] continue if last_deal == 1 and pred <=: last_deal = 0 (report[-1] - markup + (last_price - dataset['close'][i])) last_price = dataset['close'][i] return report
tester 函数接受一个数据集和一个“标记”(可选)并检查整个数据集,类似于在 MetaTrader 5 测试器中的操作。在每一个新柱都会检查一个信号(标签),当标签改变时,交易就会反转。因此,卖出信号作为结束买入头寸和打开卖出头寸的信号。现在,让我们测试上述数据集:赫兹股票交易量化软件
pr = get_prices(look_back=LOOK_BACK)pr = add_labels(pr, 10, 25)rep = tester(pr, MARKUP)(rep)()
编辑切换为居中
不计入点差测试原始数据集
编辑切换为居中
以70个五位小数点差测试原始数据集
这是一种理想化的图像(这就是我们希望模型工作的方式)。由于标签是随机抽样的,这取决于一系列参数,这些参数决定了交易的最短和最长寿命,因此曲线总是不同的。尽管如此,它们都会表现出一个很好的点增长(沿Y轴)和不同的交易数量(沿X轴)。赫兹股票交易量化软件
训练 CatBoost 模型
现在,让我们直接开始训练模型。首先,让我们将数据集分成两个样本:训练和验证。这用于减少模型过拟合。当模型继续在训练子样本上训练,试图最小化分类误差时,同样的误差也在验证子样本上测量。如果这些误差的差别很大,则该模型被称为过拟合。相反,接近值表示模型的训练是正确的。赫兹股票交易量化软件
#splitting on train and validation subsetsX = pr[[1:-1]]y = pr[[-1]]train_X, test_X, train_y, test_y = train_test_split(X, y, train_size = , test_size = , shuffle=True)
在随机混合训练示例之后,让我们将数据分成两个长度相等的数据集。接下来,创建并训练模型:
#learning with train and validation subsetsmodel = CatBoostClassifier(iterations=1000, depth=6, learning_rate=, custom_loss=['Accuracy'], eval_metric='Accuracy', verbose=True, use_best_model=True, task_type='CPU')(train_X, train_y, eval_set = (test_X, test_y), early_stopping_rounds=50, plot=False)
该模型采用了许多参数,但并非所有参数都显示在本例中。如果您想微调模型,可以参考文档,这通常不是必需的。CatBoost 在开箱即用的情况下工作得很好,只需最少的调整。
以下是模型参数的简要说明:
iterations — 模型中树的最大数目。模型在每次迭代后都会增加弱模型(树)的数量,因此请确保设置足够大的值。根据我的实践,对于这个特定的例子,1000次迭代通常已经足够了。赫兹股票交易量化软件
depth — 每棵树的深度。深度越小,模型越粗糙-输出的交易越少。深度在6到10之间似乎是最佳的。
learning_rate — 梯度步长值;这与神经网络中使用的原理相同。合理的参数范围为~。值越低,模型训练的时间就越长。但在这种情况下,它可以找到更好的结果。赫兹股票交易量化软件
custom_loss, eval_metric — 用于评估模型的度量。分类的经典标准是“准确度”
use_best_model — 在每一步中,模型都会评估“准确性”,这可能会随着时间的推移而改变。此标志允许以最小的误差保存模型,否则最后一次迭代得到的模型将被保存。赫兹股票交易量化软件
task_type — 允许在GPU上训练模型(默认情况下使用CPU)。这只适用于非常大的数据;在其他情况下,在GPU内核上执行训练的速度比在处理器上执行训练的速度慢。
early_stopping_rounds — 该模型有一个内置的过拟合检测器,其工作原理简单。如果度量在指定的迭代次数内停止减少/增加(对于“精确度”,它停止增加),则训练停止。
训练开始后,控制台将显示每个迭代中模型的当前状态:
170: learn: test: best: (165) total: remaining: : learn: test: best: (165) total: remaining: : learn: test: best: (165) total: remaining: : learn: test: best: (165) total: remaining: : learn: test: best: (165) total: remaining: : learn: test: best: (165) total: remaining: : learn: test: best: (165) total: remaining: 21s177: learn: test: best: (165) total: remaining: 21s178: learn: test: best: (165) total: remaining: : learn: test: best: (165) total: remaining: : learn: test: best: (165) total: remaining: by overfitting detector (15 iterations wait)bestTest = = 165
在上面的例子中,过拟合检测器在第180次迭代时触发并停止训练。此外,控制台还显示训练子样本(learn)和验证子样本(test)的统计信息,以及总的模型训练时间(仅20秒)。在输出时,训练子样本的准确度最好为(与理想结果相对应),验证子样本的准确度为,虽然更差,但仍高于(被认为是随机的)。最佳迭代是165 - 模型已经保存了。现在,我们可以在测试器中测试:
#test the learned modelp = _proba(X)p2 = [x[0]< for x in p]pr2 = [:len(p2)].copy()pr2['labels'] = p2rep = tester(pr2, MARKUP)(rep)()
X - 是包含特征但没有标签的源数据集。为了得到标签,有必要从训练模型中获得标签,并预测分配到0类或1类的“p”概率。由于该模型生成两个类的概率,而我们只需要0或1,因此“p2”变量只接收第一维(0)中的概率。此外,原始数据集中的标签将替换为模型预测的标签。以下是测试器中的结果:赫兹股票交易量化软件
编辑切换为居中
抽样交易后的理想结果
编辑切换为居中
在模型输出时得到的结果
如您所见,模型学习得很好,这意味着它记住了训练示例,并且在验证集上显示了比随机结果更好的结果。让我们进入最后一个阶段:导出模型并创建一个交易机器人。
将模型移植到 赫兹股票交易量化软件
赫兹股票交易量化软件允许直接从 Python 程序进行交易,因此不需要移植模型。但是,我想检查我的自定义测试器,并将其与标准策略测试器进行比较。此外,编译好的机器人的可用性在许多情况下都很方便,包括在VPS上的使用(在这种情况下,您不必安装Python)。因此,我编写了一个辅助函数,它将准备好的模型保存到 MQH 文件中。函数如下:
def export_model_to_MQL_code(model): _model('', format="cpp", export_parameters=None, pool=None) code = 'double catboost_model' + '(const double &features[]) { \n' code += ' ' with open('', 'r') as file: data = () code += data[("unsigned int TreeDepth"):("double Scale = 1;")] code +='\n\n' code+= 'return ' + 'ApplyCatboostModel(features, TreeDepth, TreeSplits , BorderCounts, Borders, LeafValues); } \n\n' code += 'double ApplyCatboostModel(const double &features[],uint &TreeDepth_[],uint &TreeSplits_[],uint &BorderCounts_[],float &Borders_[],double &LeafValues_[]) {\n\ uint FloatFeatureCount=ArrayRange(BorderCounts_,0);\n\ uint BinaryFeatureCount=ArrayRange(Borders_,0);\n\ uint TreeCount=ArrayRange(TreeDepth_,0);\n\ bool binaryFeatures[];\n\ ArrayResize(binaryFeatures,BinaryFeatureCount);\n\ uint binFeatureIndex=0;\n\ for(uint i=0; i<FloatFeatureCount; i++) {\n\ for(uint j=0; j<BorderCounts_[i]; j++) {\n\ binaryFeatures[binFeatureIndex]=features[i]>Borders_[binFeatureIndex];\n\ binFeatureIndex++;\n\ }\n\ }\n\ double result=;\n\ uint treeSplitsPtr=0;\n\ uint leafValuesForCurrentTreePtr=0;\n\ for(uint treeId=0; treeId<TreeCount; treeId++) {\n\ uint currentTreeDepth=TreeDepth_[treeId];\n\ uint index=0;\n\ for(uint depth=0; depth<currentTreeDepth; depth++) {\n\ index|=(binaryFeatures[TreeSplits_[treeSplitsPtr+depth]]<<depth);\n\ }\n\ result+=LeafValues_[leafValuesForCurrentTreePtr+index];\n\ treeSplitsPtr+=currentTreeDepth;\n\ leafValuesForCurrentTreePtr+=(1<<currentTreeDepth);\n\ }\n\ return /(+MathPow(M_E,-result));\n\ }' file = open('C:/Users/dmitrievsky/AppData/Roaming/MetaQuotes/Terminal/D0E8209F77C8CF37AD8BF550E51FF075/MQL5/Include/' + 'cat_model' + '.mqh', "w") (code) () print('The file ' + 'cat_model' + '.mqh ' + 'has been written to disc')
函数代码看起来既奇怪又笨拙,经过训练的模型对象被输入到函数中,然后以C++格式保存对象:
_model('', format="cpp", export_parameters=None, pool=None)
然后创建一个字符串,并使用标准 Python 函数将 C++ 代码解析为MQL5:
code = 'double catboost_model' + '(const double &features[]) { \n' code += ' ' with open('', 'r') as file: data = () code += data[("unsigned int TreeDepth"):("double Scale = 1;")] code +='\n\n' code+= 'return ' + 'ApplyCatboostModel(features, TreeDepth, TreeSplits , BorderCounts, Borders, LeafValues); } \n\n'
在上述操作之后,将插入此库中的“ApplyCatboostModel”函数。它根据保存的模型和传递的特征向量,返回(0;1)范围内的计算结果。
之后,我们需要指定赫兹股票交易量化软件 终端的 \\Include 文件夹的路径,模型将保存到该文件夹中。因此,在设置所有参数后,只需单击一下即可对模型进行训练,并立即保存为MQH文件,这非常方便。这个选项也很好,因为这是用 Python 教授模型的常见和流行的实践。
在 MetaTrader 5 中编写一个 EA 交易
在训练和保存 CatBoost 模型之后,我们需要编写一个简单的 EA 进行测试:
#include <>#include <Trade\>#include <cat_>sinput int look_back = 50;sinput int MA_period = 15;sinput int OrderMagic = 666; //Orders magicsinput double MaximumRisk=; //Maximum risksinput double CustomLot=0; //Custom lotinput int stoploss = 500;static datetime last_time=0;#define Ask SymbolInfoDouble(_Symbol, SYMBOL_ASK)#define Bid SymbolInfoDouble(_Symbol, SYMBOL_BID)int hnd;
现在,连接保存的 cat_ 和由fxsaber提供的赫兹股票交易量化软件
look_back 和 MA_period 参数的设置必须与在 Python 程序中训练时指定的完全一致,否则将引发错误。
此外,在每一个柱上,我们检查模型的信号,其中输入增量向量(价格和移动平均值之间的差异):
if(!isNewBar()) return; double ma[]; double pr[]; double ret[]; ArrayResize(ret, look_back); CopyBuffer(hnd, 0, 1, look_back, ma); CopyClose(NULL,PERIOD_CURRENT,1,look_back,pr); for(int i=0; i<look_back; i++) ret[i] = pr[i] - ma[i]; ArraySetAsSeries(ret, true); double sig = catboost_model(ret);