量化交易軟件:梯度提升CatBoost在交易系統(tǒng)開發(fā)中的應(yīng)用
介紹
赫茲量化梯度提升是一種強(qiáng)大的機(jī)器學(xué)習(xí)算法。該方法產(chǎn)生了一個(gè)弱模型的集合(例如,決策樹),其中(與bagging相反)模型是按順序構(gòu)建的,而不是獨(dú)立地(并行地)構(gòu)建的。這意味著下一棵樹從上一棵樹的錯(cuò)誤中學(xué)習(xí),然后重復(fù)這個(gè)過程,增加了弱模型的數(shù)量。這就建立了一個(gè)強(qiáng)大的模型,可以使用異構(gòu)數(shù)據(jù)進(jìn)行泛化。在這個(gè)實(shí)驗(yàn)中,我使用了Yandex開發(fā)的CatBoost庫,它與 XGBoost和 LightGBM 一起是最流行的庫之一。
本文的目的是演示如何創(chuàng)建一個(gè)基于機(jī)器學(xué)習(xí)的模型。創(chuàng)建過程包括以下步驟:

編輯切換為居中
接收和預(yù)處理數(shù)據(jù)
使用準(zhǔn)備好的數(shù)據(jù)訓(xùn)練模型
在自定義策略測試器中測試模型
將模型移植到赫茲量化
Python 語言和 赫茲量化 庫用于準(zhǔn)備數(shù)據(jù)和訓(xùn)練模型。
準(zhǔn)備數(shù)據(jù)
導(dǎo)入所需的 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)
這些參數(shù)的作用如下:
look_back — 分析歷史的深度
ma_period ?— 用于計(jì)算價(jià)格增量的移動(dòng)平均周期數(shù)
symbol — 應(yīng)當(dāng)在 赫茲量化終端中載入的交易品種報(bào)價(jià)
markup ?— 用于自定義測試器的點(diǎn)差大小
timeframe ?— 應(yīng)當(dāng)載入數(shù)據(jù)的時(shí)間框架
start, stop ?— 數(shù)據(jù)范圍
赫茲量化編寫一個(gè)函數(shù),直接接收原始數(shù)據(jù)并創(chuàng)建一個(gè)包含訓(xùn)練所需列的數(shù)據(jù)幀:
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()
函數(shù)接收指定時(shí)間段的收盤價(jià)并計(jì)算移動(dòng)平均值,然后計(jì)算增量(價(jià)格和移動(dòng)平均值之間的差)。在最后一步中,它通過 look_back 來計(jì)算額外的列,其中的行向后移動(dòng)到歷史中,這意味著向模型中添加額外的(滯后的)特性。
例如,對于 look_back=10,數(shù)據(jù)幀中將包含10個(gè)額外的列,其價(jià)格增量為:
>>> 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]
黃色高亮顯示表示每列都有相同的數(shù)據(jù)集,但有一個(gè)偏移量。因此,每一行都是一個(gè)單獨(dú)的訓(xùn)練實(shí)例。
創(chuàng)建訓(xùn)練標(biāo)簽(隨機(jī)抽樣)
訓(xùn)練實(shí)例是特征及其相應(yīng)標(biāo)簽的集合。模型必須輸出一定的信息,模型必須學(xué)會(huì)預(yù)測這些信息。赫茲量化考慮二元分類,其中模型將預(yù)測將訓(xùn)練示例確定為類0或1的概率。0和1可用于交易方向:買入或賣出。換句話說,模型必須學(xué)會(huì)預(yù)測給定環(huán)境參數(shù)(一組特征)的交易方向。
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 函數(shù)隨機(jī)(在最小、最大范圍內(nèi))設(shè)置每筆交易的持續(xù)時(shí)間(以柱形為單位)。通過更改最大和最小持續(xù)時(shí)間,您可以更改交易采樣頻率。因此,如果當(dāng)前價(jià)格大于下一個(gè)“rand”柱向前的價(jià)格,這就是賣出標(biāo)簽(1)。在相反的情況下,標(biāo)簽是0。讓我們看看應(yīng)用上述函數(shù)后數(shù)據(jù)集的外觀:
>>> 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
添加了“l(fā)abels”列,其中分別包含買入和賣出的類別號(hào)(0或1)?,F(xiàn)在,每個(gè)訓(xùn)練示例或功能集(這里是10個(gè))都有自己的標(biāo)簽,它指示在什么條件下應(yīng)該買入,在什么條件下應(yīng)該賣出(即它屬于哪個(gè)類)。模型必須能夠記住和泛化這些例子-這個(gè)能力將在后面討論。
開發(fā)自定義測試器
因?yàn)楹掌澚炕趧?chuàng)建一個(gè)交易系統(tǒng),所以最好有一個(gè)策略測試器來進(jìn)行及時(shí)的模型測試。下面是此類測試器的示例:
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 函數(shù)接受一個(gè)數(shù)據(jù)集和一個(gè)“標(biāo)記”(可選)并檢查整個(gè)數(shù)據(jù)集,類似于在 赫茲量化測試器中的操作。在每一個(gè)新柱都會(huì)檢查一個(gè)信號(hào)(標(biāo)簽),當(dāng)標(biāo)簽改變時(shí),交易就會(huì)反轉(zhuǎn)。因此,賣出信號(hào)作為結(jié)束買入頭寸和打開賣出頭寸的信號(hào)?,F(xiàn)在,讓我們測試上述數(shù)據(jù)集:
pr = get_prices(look_back=LOOK_BACK) pr = add_labels(pr, 10, 25) rep = tester(pr, MARKUP) plt.plot(rep) plt.show()

編輯切換為居中
不計(jì)入點(diǎn)差測試原始數(shù)據(jù)集

編輯切換為居中
以70個(gè)五位小數(shù)點(diǎn)差測試原始數(shù)據(jù)集
這是一種理想化的圖像(這就是我們希望模型工作的方式)。由于標(biāo)簽是隨機(jī)抽樣的,這取決于一系列參數(shù),這些參數(shù)決定了交易的最短和最長壽命,因此曲線總是不同的。盡管如此,它們都會(huì)表現(xiàn)出一個(gè)很好的點(diǎn)增長(沿Y軸)和不同的交易數(shù)量(沿X軸)。