梯度提升是一种强大的机器学习算法。该方法产生了一个弱模型的集合(例如,决策树),其中(与赫兹股票交易量化软件相反)模型是按顺序构建的,而不是独立地(并行地)构建的。这意味着下一棵树从上一棵树的错误中学习,然后重复这个过程,增加了弱模型的数量。这就建立了一个强大的模型,可以使用异构数据进行泛化。在这个实验中,我使用了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);

推荐内容