量化交易软件:梯度提升CatBoost在交易系统开发中的应用
介绍
赫兹量化梯度提升是一种强大的机器学习算法。该方法产生了一个弱模型的集合(例如,决策树),其中(与bagging相反)模型是按顺序构建的,而不是独立地(并行地)构建的。这意味着下一棵树从上一棵树的错误中学习,然后重复这个过程,增加了弱模型的数量。这就建立了一个强大的模型,可以使用异构数据进行泛化。在这个实验中,我使用了Yandex开发的CatBoost库,它与 XGBoost和 LightGBM 一起是最流行的库之一。
本文的目的是演示如何创建一个基于机器学习的模型。创建过程包括以下步骤:

编辑切换为居中
接收和预处理数据
使用准备好的数据训练模型
在自定义策略测试器中测试模型
将模型移植到赫兹量化
Python 语言和 赫兹量化 库用于准备数据和训练模型。
准备数据
导入所需的 Python 模块:
import MetaTrader5 as mt5 import pandas as pd import numpy as np from datetime import datetime import random import matplotlib.pyplot as plt from catboost import CatBoostClassifier from sklearn.model_selection import train_test_split mt5.initialize() # check for gpu devices is availible from catboost.utils import get_gpu_device_count print('%i GPU devices' % get_gpu_device_count())
然后初始化所有全局变量:
LOOK_BACK = 250 MA_PERIOD = 15 SYMBOL = 'EURUSD' MARKUP = 0.0001 TIMEFRAME = mt5.TIMEFRAME_H1 START = datetime(2020, 5, 1) STOP = datetime(2021, 1, 1)
这些参数的作用如下:
look_back — 分析历史的深度
ma_period — 用于计算价格增量的移动平均周期数
symbol — 应当在 赫兹量化终端中载入的交易品种报价
markup — 用于自定义测试器的点差大小
timeframe — 应当载入数据的时间框架
start, stop — 数据范围
赫兹量化编写一个函数,直接接收原始数据并创建一个包含训练所需列的数据帧:
def get_prices(look_back = 15): prices = pd.DataFrame(mt5.copy_rates_range(SYMBOL, TIMEFRAME, START, STOP), columns=['time', 'close']).set_index('time') # set df index as datetime prices.index = pd.to_datetime(prices.index, unit='s') prices = prices.dropna() ratesM = prices.rolling(MA_PERIOD).mean() ratesD = prices - ratesM for i in range(look_back): prices[str(i)] = ratesD.shift(i) return prices.dropna()
函数接收指定时间段的收盘价并计算移动平均值,然后计算增量(价格和移动平均值之间的差)。在最后一步中,它通过 look_back 来计算额外的列,其中的行向后移动到历史中,这意味着向模型中添加额外的(滞后的)特性。
例如,对于 look_back=10,数据帧中将包含10个额外的列,其价格增量为:
>>> pr = get_prices(look_back=LOOK_BACK) >>> pr close 0 1 2 3 4 5 6 7 8 9 time 2020-05-01 16:00:00 1.09750 0.001405 0.002169 0.001600 0.002595 0.002794 0.002442 0.001477 0.001190 0.000566 0.000285 2020-05-01 17:00:00 1.10074 0.004227 0.001405 0.002169 0.001600 0.002595 0.002794 0.002442 0.001477 0.001190 0.000566 2020-05-01 18:00:00 1.09976 0.002900 0.004227 0.001405 0.002169 0.001600 0.002595 0.002794 0.002442 0.001477 0.001190 2020-05-01 19:00:00 1.09874 0.001577 0.002900 0.004227 0.001405 0.002169 0.001600 0.002595 0.002794 0.002442 0.001477 2020-05-01 20:00:00 1.09817 0.000759 0.001577 0.002900 0.004227 0.001405 0.002169 0.001600 0.002595 0.002794 0.002442 ... ... ... ... ... ... ... ... ... ... ... ... 2020-11-02 23:00:00 1.16404 0.000400 0.000105 -0.000581 -0.001212 -0.000999 -0.000547 -0.000344 -0.000773 -0.000326 0.000501 2020-11-03 00:00:00 1.16392 0.000217 0.000400 0.000105 -0.000581 -0.001212 -0.000999 -0.000547 -0.000344 -0.000773 -0.000326 2020-11-03 01:00:00 1.16402 0.000270 0.000217 0.000400 0.000105 -0.000581 -0.001212 -0.000999 -0.000547 -0.000344 -0.000773 2020-11-03 02:00:00 1.16423 0.000465 0.000270 0.000217 0.000400 0.000105 -0.000581 -0.001212 -0.000999 -0.000547 -0.000344 2020-11-03 03:00:00 1.16464 0.000885 0.000465 0.000270 0.000217 0.000400 0.000105 -0.000581 -0.001212 -0.000999 -0.000547 [3155 rows x 11 columns]
黄色高亮显示表示每列都有相同的数据集,但有一个偏移量。因此,每一行都是一个单独的训练实例。
创建训练标签(随机抽样)
训练实例是特征及其相应标签的集合。模型必须输出一定的信息,模型必须学会预测这些信息。赫兹量化考虑二元分类,其中模型将预测将训练示例确定为类0或1的概率。0和1可用于交易方向:买入或卖出。换句话说,模型必须学会预测给定环境参数(一组特征)的交易方向。
def add_labels(dataset, min, max): labels = [] for i in range(dataset.shape[0]-max): rand = random.randint(min, max) if dataset['close'][i] >= (dataset['close'][i + rand]): labels.append(1.0) elif dataset['close'][i] <= (dataset['close'][i + rand]): labels.append(0.0) else: labels.append(0.0) dataset = dataset.iloc[:len(labels)].copy() dataset['labels'] = labels dataset = dataset.dropna() 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 labels time 2020-05-01 16:00:00 1.09750 0.001405 0.002169 0.001600 0.002595 0.002794 0.002442 0.001477 0.001190 0.000566 0.000285 1.0 2020-05-01 17:00:00 1.10074 0.004227 0.001405 0.002169 0.001600 0.002595 0.002794 0.002442 0.001477 0.001190 0.000566 1.0 2020-05-01 18:00:00 1.09976 0.002900 0.004227 0.001405 0.002169 0.001600 0.002595 0.002794 0.002442 0.001477 0.001190 1.0 2020-05-01 19:00:00 1.09874 0.001577 0.002900 0.004227 0.001405 0.002169 0.001600 0.002595 0.002794 0.002442 0.001477 1.0 2020-05-01 20:00:00 1.09817 0.000759 0.001577 0.002900 0.004227 0.001405 0.002169 0.001600 0.002595 0.002794 0.002442 1.0 ... ... ... ... ... ... ... ... ... ... ... ... ... 2020-10-29 20:00:00 1.16700 -0.003651 -0.005429 -0.005767 -0.006750 -0.004699 -0.004328 -0.003475 -0.003769 -0.002719 -0.002075 1.0 2020-10-29 21:00:00 1.16743 -0.002699 -0.003651 -0.005429 -0.005767 -0.006750 -0.004699 -0.004328 -0.003475 -0.003769 -0.002719 0.0 2020-10-29 22:00:00 1.16731 -0.002276 -0.002699 -0.003651 -0.005429 -0.005767 -0.006750 -0.004699 -0.004328 -0.003475 -0.003769 0.0 2020-10-29 23:00:00 1.16740 -0.001648 -0.002276 -0.002699 -0.003651 -0.005429 -0.005767 -0.006750 -0.004699 -0.004328 -0.003475 0.0 2020-10-30 00:00:00 1.16695 -0.001655 -0.001648 -0.002276 -0.002699 -0.003651 -0.005429 -0.005767 -0.006750 -0.004699 -0.004328 1.0
添加了“labels”列,其中分别包含买入和卖出的类别号(0或1)。现在,每个训练示例或功能集(这里是10个)都有自己的标签,它指示在什么条件下应该买入,在什么条件下应该卖出(即它属于哪个类)。模型必须能够记住和泛化这些例子-这个能力将在后面讨论。
开发自定义测试器
因为赫兹量化正在创建一个交易系统,所以最好有一个策略测试器来进行及时的模型测试。下面是此类测试器的示例:
def tester(dataset, markup = 0.0): last_deal = int(2) last_price = 0.0 report = [0.0] for i in range(dataset.shape[0]): pred = dataset['labels'][i] if last_deal == 2: last_price = dataset['close'][i] last_deal = 0 if pred <=0.5 else 1 continue if last_deal == 0 and pred > 0.5: last_deal = 1 report.append(report[-1] - markup + (dataset['close'][i] - last_price)) last_price = dataset['close'][i] continue if last_deal == 1 and pred <=0.5: last_deal = 0 report.append(report[-1] - markup + (last_price - dataset['close'][i])) last_price = dataset['close'][i] return report
tester 函数接受一个数据集和一个“标记”(可选)并检查整个数据集,类似于在 赫兹量化测试器中的操作。在每一个新柱都会检查一个信号(标签),当标签改变时,交易就会反转。因此,卖出信号作为结束买入头寸和打开卖出头寸的信号。现在,让我们测试上述数据集:
pr = get_prices(look_back=LOOK_BACK) pr = add_labels(pr, 10, 25) rep = tester(pr, MARKUP) plt.plot(rep) plt.show()

编辑切换为居中
不计入点差测试原始数据集

编辑切换为居中
以70个五位小数点差测试原始数据集
这是一种理想化的图像(这就是我们希望模型工作的方式)。由于标签是随机抽样的,这取决于一系列参数,这些参数决定了交易的最短和最长寿命,因此曲线总是不同的。尽管如此,它们都会表现出一个很好的点增长(沿Y轴)和不同的交易数量(沿X轴)。