股票量化交易軟件:利用 CatBoost 算法尋找海外市場(chǎng)的季節(jié)性模式
時(shí)間過(guò)濾器函數(shù)
通過(guò)添加過(guò)濾函數(shù),可以很容易地?cái)U(kuò)展開(kāi)發(fā)庫(kù)。
def time_filter(data, count): ????# filter by hour ????hours=[15]????if data.index[count].hour not in hours: ????????return False ????# filter by day of week ????days = [1]????if data.index[count].dayofweek not in days: ????????return False ????return True
函數(shù)檢查其中指定的條件,可以實(shí)現(xiàn)其他附加條件(不僅僅是時(shí)間過(guò)濾器)。但由于本文專(zhuān)門(mén)討論季節(jié)性模式,因此我將只使用時(shí)間過(guò)濾器。如果滿(mǎn)足所有條件,函數(shù)將返回True,并將適當(dāng)?shù)臉颖咎砑拥接?xùn)練集中。例如,在這種特殊情況下,我們指示模型僅在周二15:00進(jìn)行交易?!癶ours”和“days”列表可以包括其他小時(shí)和天。通過(guò)注釋掉所有條件,您可以讓算法無(wú)條件地工作,就像前一篇文章中的工作方式一樣。?
add_labels 函數(shù)現(xiàn)在接收這個(gè)條件作為輸入。在Python中,函數(shù)是一級(jí)對(duì)象,因此可以將它們作為參數(shù)安全地傳遞給其他函數(shù)。
def add_labels(dataset, min, max, filter=time_filter): ????labels = [] ????for i in range(dataset.shape[0]-max): ????????rand = random.randint(min, max) ????????curr_pr = dataset['close'][i] ????????future_pr = dataset['close'][i + rand] ????????if filter(dataset, i): ????????????if future_pr + MARKUP < curr_pr: ????????????????labels.append(1.0) ????????????elif future_pr - MARKUP > curr_pr: ????????????????labels.append(0.0) ????????????else: ????????????????labels.append(2.0) ????????else: ????????????labels.append(2.0) ????dataset = dataset.iloc[:len(labels)].copy() ????dataset['labels'] = labels ????dataset = dataset.dropna() ????dataset = dataset.drop( ????????dataset[dataset.labels == 2].index) ????return dataset
一旦過(guò)濾器被傳遞給函數(shù),它就可以用來(lái)標(biāo)記買(mǎi)入或賣(mài)出交易。過(guò)濾器接收原始數(shù)據(jù)集和當(dāng)前柱的索引。數(shù)據(jù)集中的索引表示為包含時(shí)間的“datetime index”。過(guò)濾器按第i個(gè)數(shù)字在數(shù)據(jù)幀的“datetime index”中搜索小時(shí)和天,如果未找到任何內(nèi)容,則返回 False。如果滿(mǎn)足條件,則交易標(biāo)記為1或0,否則標(biāo)記為2。最后,從訓(xùn)練數(shù)據(jù)集中刪除所有2,因此只留下由過(guò)濾器確定的特定天數(shù)和小時(shí)的示例。
還應(yīng)向自定義測(cè)試儀添加一個(gè)過(guò)濾器,以在特定時(shí)間啟用交易打開(kāi)(或根據(jù)此過(guò)濾器設(shè)置的任何其他條件)。
def tester(dataset, markup=0.0, plot=False, filter=time_filter): ????last_deal = int(2)????last_price = 0.0????report = [0.0] ????for i in range(dataset.shape[0]): ????????pred = dataset['labels'][i] ????????ind = dataset.index[i].hour ????????if last_deal == 2 and filter(dataset, i): ????????????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 = 2????????????report.append(report[-1] - markup + ??????????????????????????(dataset['close'][i] - last_price)) ????????????continue????????if last_deal == 1 and pred < 0.5: ????????????last_deal = 2????????????report.append(report[-1] - markup + ??????????????????????????(last_price - dataset['close'][i])) ????y = np.array(report).reshape(-1, 1) ????X = np.arange(len(report)).reshape(-1, 1) ????lr = LinearRegression() ????lr.fit(X, y) ????l = lr.coef_ ????if l >= 0: ????????l = 1????else: ????????l = -1????if(plot): ????????plt.plot(report) ????????plt.plot(lr.predict(X)) ????????plt.title("Strategy performance") ????????plt.xlabel("the number of trades") ????????plt.ylabel("cumulative profit in pips") ????????plt.show() ????return lr.score(X, y) * l
具體實(shí)現(xiàn)如下:當(dāng)沒(méi)有未平倉(cāng)時(shí)使用數(shù)字2:last_deal=2。測(cè)試開(kāi)始前沒(méi)有未平的倉(cāng)位,因此設(shè)置2。遍歷整個(gè)數(shù)據(jù)集并檢查是否滿(mǎn)足篩選條件。如果條件滿(mǎn)足,就開(kāi)始買(mǎi)賣(mài)交易。過(guò)濾條件不應(yīng)用于交易結(jié)束,因?yàn)樗鼈兛梢栽谝恢艿牧硪粋€(gè)小時(shí)或一天結(jié)束。這些變化足以進(jìn)行進(jìn)一步的正確訓(xùn)練和測(cè)試。?
每個(gè)交易小時(shí)的探索性分析
對(duì)于每個(gè)單獨(dú)的條件(以及幾個(gè)小時(shí)或幾天的組合),手動(dòng)測(cè)試模型不是很方便。為此編寫(xiě)了一個(gè)特殊函數(shù),可以分別快速獲取每個(gè)條件的匯總統(tǒng)計(jì)信息。該函數(shù)可能需要一些時(shí)間才能完成,但它會(huì)輸出模型顯示出更好性能的時(shí)間范圍。
def exploratory_analysis(): ????h = [x for x in range(24)]????result = pd.DataFrame() ????for _h in h: ????????global hours ????????hours = [_h] ????????pr = get_prices(START_DATE, STOP_DATE) ????????pr = add_labels(pr, min=15, max=15, filter=time_filter) ????????gmm = mixture.GaussianMixture( ????????????n_components=n_compnents, covariance_type='full', n_init=1).fit(pr[pr.columns[1:]]) ????????# iterative learning ????????res = [] ????????iterations = 10????????for i in range(iterations): ????????????res.append(brute_force(10000, gmm)) ????????????print('Iteration: ', i, 'R^2: ', res[-1][0], ' hour= ', _h) ???????? ????????r = pd.DataFrame(np.array(res)[:, 0], np.full(iterations,_h)) ????????result = result.append(r) ????plt.scatter(result.index, result, c = result.index) ????plt.show() ????return result
您可以在函數(shù)中設(shè)置要檢查的小時(shí)數(shù)列表。在我的例子中,所有的24小時(shí)都設(shè)置好了。為了實(shí)驗(yàn)的純度,我通過(guò)將“min”和“max”(開(kāi)啟倉(cāng)位的最小和最大水平)設(shè)置為15來(lái)禁用采樣。“iterations”變量負(fù)責(zé)每小時(shí)的再訓(xùn)練周期數(shù)。增加這個(gè)參數(shù)可以得到更可靠的統(tǒng)計(jì)數(shù)據(jù)。操作完成后,函數(shù)將顯示下圖:
X軸以小時(shí)的序號(hào)為特征。Y軸表示每次迭代的R^2分?jǐn)?shù)(使用10次迭代,這意味著每小時(shí)進(jìn)行一次模型再培訓(xùn))。如您所見(jiàn),4小時(shí)、5小時(shí)和6小時(shí)的通行證位于更近的位置,這使您對(duì)找到的模式的質(zhì)量更有信心。選擇原則很簡(jiǎn)單 - 點(diǎn)的位置和密度越高,模型就越好。例如,在9-15點(diǎn)的時(shí)間間隔內(nèi),圖中顯示了大量的點(diǎn)分散,模型的平均質(zhì)量下降到0.6。您可以進(jìn)一步選擇所需的小時(shí)數(shù),重新訓(xùn)練模型并在自定義測(cè)試器中查看其結(jié)果。
測(cè)試選擇的模型
對(duì) GBPUSD 貨幣對(duì)進(jìn)行了探索性分析,參數(shù)如下:
SYMBOL = 'GBPUSD' MARKUP = 0.00010TIMEFRAME = mt5.TIMEFRAME_H1 START_DATE = datetime(2017, 1, 1) TSTART_DATE = datetime(2015, 1, 1) FULL_DATE = datetime(2015, 1, 1) STOP_DATE = datetime(2021, 1, 1)
相同的參數(shù)將用于測(cè)試。為了獲得更大的可信度,您可以更改 FULL_DATE 值以查看模型在早期歷史數(shù)據(jù)中的執(zhí)行情況。
我們可以直觀地分辨出3、4、5和6小時(shí)的一組??梢约僭O(shè)相鄰的時(shí)間有相似的模式,所以模型可以針對(duì)所有的時(shí)間進(jìn)行訓(xùn)練。
hours = [3,4,5,6]# make dataset pr = get_prices(START_DATE, STOP_DATE) pr = add_labels(pr, min=15, max=15, filter=time_filter) tester(pr, MARKUP, plot=True, filter=time_filter) # perform GMM clasterizatin over dataset # gmm = mixture.BayesianGaussianMixture(n_components=n_compnents, covariance_type='full').fit(X)gmm = mixture.GaussianMixture( ????n_components=n_compnents, covariance_type='full', n_init=1).fit(pr[pr.columns[1:]]) # iterative learning res = [] for i in range(10): ????res.append(brute_force(10000, gmm)) ????print('Iteration: ', i, 'R^2: ', res[-1][0]) # test best model res.sort() test_model(res[-1])
其余的代碼不需要額外的解釋?zhuān)驗(yàn)樵谝郧暗奈恼轮幸呀?jīng)詳細(xì)解釋過(guò)。唯一的例外是,你可以使用注釋貝葉斯模型來(lái)代替一個(gè)簡(jiǎn)單的GMM,盡管這只是一個(gè)實(shí)驗(yàn)性的想法。?
交易抽樣后的理想模型如下所示:
經(jīng)過(guò)訓(xùn)練的模型(包括測(cè)試數(shù)據(jù))顯示了以下性能:
單獨(dú)的模型可以進(jìn)行高密度訓(xùn)練。下面是已經(jīng)訓(xùn)練過(guò)的5小時(shí)和20小時(shí)模型的余額圖:
現(xiàn)在,為了進(jìn)行比較,你可以看看那些訓(xùn)練時(shí)數(shù)變化較大的模型。例如查看第9和11小時(shí)。
這里的余額圖顯示的比任何注釋都多,顯然,在訓(xùn)練模式時(shí),要特別注意時(shí)機(jī)。?
每個(gè)交易日的探索性分析
對(duì)于其他時(shí)間間隔,例如一周中的幾天,可以很容易地修改過(guò)濾器。您只需將小時(shí)替換為一周中的一天。
def time_filter(data, count): ????# filter by day of week ????global hours ????if data.index[count].dayofweek not in hours: ????????return False ????return True
在這種情況下,迭代應(yīng)該在0到5之間的范圍內(nèi)執(zhí)行(不包括第5個(gè)序號(hào),也就是星期六)。
def exploratory_analysis(): ????h = [x for x in range(5)]
現(xiàn)在,對(duì) GBPUSD 貨幣對(duì)進(jìn)行探索性分析。交易的頻率,或者說(shuō)它們的范圍,是相同的(15個(gè)柱)。
pr = add_labels(pr, min=15, max=15, filter=time_filter)
訓(xùn)練過(guò)程顯示在控制臺(tái)中,您可以立即查看當(dāng)前期間的R^2分?jǐn)?shù)?!癶our”變量現(xiàn)在不包含小時(shí)數(shù),而是一周中某一天的序號(hào)。
Iteration:??0 R^2:??0.5297625368835237??hour=??0Iteration:??1 R^2:??0.8166096906047893??hour=??0Iteration:??2 R^2:??0.9357674260125702??hour=??0Iteration:??3 R^2:??0.8913802241811986??hour=??0Iteration:??4 R^2:??0.8079720208707672??hour=??0Iteration:??5 R^2:??0.8505663844866759??hour=??0Iteration:??6 R^2:??0.2736870273207084??hour=??0Iteration:??7 R^2:??0.9282442121644887??hour=??0Iteration:??8 R^2:??0.8769775718602929??hour=??0Iteration:??9 R^2:??0.7046666925774866??hour=??0Iteration:??0 R^2:??0.7492883761480897??hour=??1Iteration:??1 R^2:??0.6101962958733655??hour=??1Iteration:??2 R^2:??0.6877652983219245??hour=??1Iteration:??3 R^2:??0.8579669286548137??hour=??1Iteration:??4 R^2:??0.3822441930760343??hour=??1Iteration:??5 R^2:??0.5207801806491617??hour=??1Iteration:??6 R^2:??0.6893157850263495??hour=??1Iteration:??7 R^2:??0.5799059801202937??hour=??1Iteration:??8 R^2:??0.8228326786957887??hour=??1Iteration:??9 R^2:??0.8742262956151615??hour=??1Iteration:??0 R^2:??0.9257707800422799??hour=??2Iteration:??1 R^2:??0.9413981795880517??hour=??2Iteration:??2 R^2:??0.9354221623113591??hour=??2Iteration:??3 R^2:??0.8370429185837882??hour=??2Iteration:??4 R^2:??0.9142875737195697??hour=??2Iteration:??5 R^2:??0.9586871067966855??hour=??2Iteration:??6 R^2:??0.8209392060391961??hour=??2Iteration:??7 R^2:??0.9457287035542066??hour=??2Iteration:??8 R^2:??0.9587372191281025??hour=??2Iteration:??9 R^2:??0.9269140213952402??hour=??2Iteration:??0 R^2:??0.9001009579436263??hour=??3Iteration:??1 R^2:??0.8735623527502183??hour=??3Iteration:??2 R^2:??0.9460714774572146??hour=??3Iteration:??3 R^2:??0.7221720163838841??hour=??3Iteration:??4 R^2:??0.9063579778744433??hour=??3Iteration:??5 R^2:??0.9695391076372475??hour=??3Iteration:??6 R^2:??0.9297881558889788??hour=??3Iteration:??7 R^2:??0.9271590681844957??hour=??3Iteration:??8 R^2:??0.8817985496711311??hour=??3Iteration:??9 R^2:??0.915205007218742?? hour=??3Iteration:??0 R^2:??0.9378516360378022??hour=??4Iteration:??1 R^2:??0.9210968481902528??hour=??4Iteration:??2 R^2:??0.9072205941748894??hour=??4Iteration:??3 R^2:??0.9408826184927528??hour=??4Iteration:??4 R^2:??0.9671981453714584??hour=??4Iteration:??5 R^2:??0.9625144032389237??hour=??4Iteration:??6 R^2:??0.9759244293257822??hour=??4Iteration:??7 R^2:??0.9461473783201281??hour=??4Iteration:??8 R^2:??0.9190627222826241??hour=??4Iteration:??9 R^2:??0.9130350931314233??hour=??4
請(qǐng)注意,所有模型都是使用2017年初以來(lái)的數(shù)據(jù)進(jìn)行培訓(xùn)的,而R^2分?jǐn)?shù)也包括測(cè)試期(從2015年開(kāi)始的額外數(shù)據(jù))。每天高估計(jì)值的一致性提供了更大的信心。讓我們看看最后的結(jié)果。
探索性分析表明,周三和周五是最有利的交易日,尤其是周五。周二是交易最糟糕的一天,因?yàn)樗泻艽蟮恼`差方差和較低的平均值。讓我們訓(xùn)練模型只在星期五交易,看看結(jié)果。
同樣,我們可以在周二獲得一個(gè)交易模型。
固定的交易時(shí)間段并不總是合適的,因此讓我們嘗試擴(kuò)展搜索窗口,并將探索性分析迭代次數(shù)增加到20次。
pr = add_labels(pr, min=5, max=25, filter=time_filter) ????????gmm = mixture.GaussianMixture( ????????????n_components=n_compnents, covariance_type='full', n_init=1).fit(pr[pr.columns[1:]]) ????????# iterative learning ????????res = [] ????????iterations = 20
價(jià)值區(qū)間已經(jīng)變大,而周四和周五是最好的交易日。
現(xiàn)在讓我們?yōu)橹芩挠?xùn)練一個(gè)控制模型,看看結(jié)果。這就是學(xué)習(xí)周期的樣子(對(duì)于那些沒(méi)有讀過(guò)之前文章的人來(lái)說(shuō))。
hours = [3] # make dataset pr = get_prices(START_DATE, STOP_DATE) pr = add_labels(pr, min=5, max=25, filter=time_filter) tester(pr, MARKUP, plot=True, filter=time_filter) # perform GMM clasterizatin over dataset # gmm = mixture.BayesianGaussianMixture(n_components=n_compnents, covariance_type='full').fit(X) gmm = mixture.GaussianMixture( ????n_components=n_compnents, covariance_type='full', n_init=1).fit(pr[pr.columns[1:]]) # iterative learning res = [] for i in range(10): ????res.append(brute_force(10000, gmm)) ????print('Iteration: ', i, 'R^2: ', res[-1][0]) # test best model res.sort() test_model(res[-1])
結(jié)果比固定交易期限的情況稍差。?
顯然,特定時(shí)期的頻率(水平)參數(shù)很重要。接下來(lái),讓我們迭代這些值并檢查它們?nèi)绾斡绊懡Y(jié)果。
交易期間對(duì)模型質(zhì)量影響的評(píng)估
與所選標(biāo)準(zhǔn)(過(guò)濾器)的探索性分析函數(shù)類(lèi)似,我們可以創(chuàng)建一個(gè)輔助函數(shù),該函數(shù)將根據(jù)交易期間評(píng)估模型性能。假設(shè)我們可以在1到50個(gè)柱(或任何其他時(shí)間段)的間隔內(nèi)設(shè)置一個(gè)固定的事務(wù)生存期,那么函數(shù)將如下所示。
def deals_frequency_analyzer(): ????freq = [x for x in range(1, 50)]????result = pd.DataFrame() ????for _h in freq: ????????pr = get_prices(START_DATE, STOP_DATE) ????????pr = add_labels(pr, min=_h, max=_h, filter=time_filter) ????????gmm = mixture.GaussianMixture( ????????????n_components=n_compnents, covariance_type='full', n_init=1).fit(pr[pr.columns[1:]]) ????????# iterative learning ????????res = [] ????????iterations = 5????????for i in range(iterations): ????????????res.append(brute_force(10000, gmm)) ????????????print('Iteration: ', i, 'R^2: ', res[-1][0], ' deal lifetime = ', _h) ???????? ????????r = pd.DataFrame(np.array(res)[:, 0], np.full(iterations,_h)) ????????result = result.append(r) ????plt.scatter(result.index, result, c = result.index) ????plt.xticks(np.arange(0, len(freq)+1, 1)) ????plt.title("Performance by deals lifetime") ????plt.xlabel("deals frequency") ????plt.ylabel("R^2 estimation") ????plt.show() ????return result
“freq”列表包含要迭代的交易時(shí)期的值。我在 GBPUSD 對(duì)的第5個(gè)小時(shí)執(zhí)行了這個(gè)迭代,這是結(jié)果。