量化交易軟件:以經(jīng)濟方式計算指標的原則
簡介
在人類的實踐活動的一個或多個領(lǐng)域中保存資源的想法或許是人類發(fā)展與進步的過程中最重要和最緊迫的主題。在這一點上,用 MQL5 語言編程也不例外。當然,如果任務(wù)的范圍僅僅局限于可視化交易,則編程的很多缺陷仍然能夠處于未被發(fā)現(xiàn)的狀況。
但是與自動交易有關(guān)的所有一切,開始都需要以最大的經(jīng)濟性編寫代碼,否則交易機器人的測試和優(yōu)化過程可能延長到幾乎不可能等待它們完成的時間。在此類情形中創(chuàng)建某些有價值的東西的想法似乎很不現(xiàn)實。
因此,在著手實施交易策略之前,最好更好地熟悉對 EA 交易程序的優(yōu)化和測試時間有影響的編程細節(jié)。因為大部分的 EA 交易程序在它們的代碼中包含對用戶指標的調(diào)用,因此我認為我們應(yīng)該從它們開始。
一般而言,在構(gòu)建指標時并沒有很多要必須記住的相關(guān)要點,因此按順序簡單地回顧一下它們是最符合邏輯的。

編輯切換為居中
于經(jīng)典指標中在尚未計算的新指標柱的每一次價格變動時重新計算指標
RSI、ADX、ATR、CCI 等經(jīng)典指標的本質(zhì)是在已收盤的指標柱上,這些指標的計算只能進行一次,之后只能在新出現(xiàn)的指標柱上進行計算。唯一例外是當前尚未收盤的指標柱,在其上每一次價格變動時都會進行計算,直到該指標柱收盤為止。
找出在尚未計算的指標柱上計算指標是否合理的最簡單的方式是在策略測試程序中將此類(經(jīng)過優(yōu)化的)指標的運行結(jié)果與在所有指標柱上計算的所有時間的指標(未優(yōu)化)進行比較。
這很簡單。使用空函數(shù) OnInit () 和 OnTick () 創(chuàng)建了一個 EA 交易程序。您要做的只是在 EA 交易程序中調(diào)用經(jīng)過優(yōu)化的或未經(jīng)優(yōu)化的指標的所需版本,并在兩種情形下贊美此類 EA 交易程序在測試程序中的運行結(jié)果。我將采用在本人的 "User Indicators in MQL5 for Beginners"(面向初學(xué)者的 MQL5 用戶指標)一文中所寫的指標 SMA.mq5 作為一個例子,在該指標中,我會替換一行代碼。 ?
? if (prev_calculated == 0) // 如果是第一次計算,重新計算所有已存在的柱 ? ?first = MAPeriod - 1 + begin; ? else first = prev_calculated - 1; // 在之后的計算中, 只計算新出現(xiàn)的柱
替換為
? first = MAPeriod - ?1 ?+ Begin; ?/ / 有訂單時重新計算全部柱
結(jié)果,我得到一個未經(jīng)優(yōu)化的編程代碼版本 (SMA !!!!!!. mq5),該版本與原來的不同,將在每一次價格變動時重新計算所有的值。嚴格地講,在兩種情況下,EA 交易程序的代碼在實際上都是相同的,因此我僅提供其中一個 (SMA_Test.mq5)
//+------------------------------------------------------------------+ //| ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? SMA_Test.mq5 | //| ? ? ? ? ? ? ? ? ? ? ? ?Copyright 2010, MetaQuotes Software Corp. | //| ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2010, MetaQuotes Software Corp." #property link ? ? ?"https://www.mql5.com" #property version ? "1.00" int Handle; //+------------------------------------------------------------------+ //| Expert initialization function ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | //+------------------------------------------------------------------+ int OnInit() ?{ //----+ ? //----+ 取得指標句柄 ? Handle = iCustom(Symbol(), 0, "SMA"); ? if (Handle == INVALID_HANDLE) ? ? Print(" 無法獲得SMA指標句柄"); //----+ ? return(0); ?} //+------------------------------------------------------------------+ //| Expert deinitialization function ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | //+------------------------------------------------------------------+ void OnDeinit(const int reason) ?{ //----+ ? ?//--- 釋放指標句柄 ? ?IndicatorRelease(Handle); //----+ ? ?} //+------------------------------------------------------------------+ //| Expert tick function ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | //+------------------------------------------------------------------+ void OnTick() ?{ //----+ ? double SMA[1]; ? //----+ 使用指標句柄把指標 ? ? ? ? ? ? ? ? ? // 緩沖區(qū)的值復(fù)制到特別準備好的靜態(tài)數(shù)組中 ? CopyBuffer(Handle, 0, 0, 1, SMA); //----+ ?} //+------------------------------------------------------------------+
現(xiàn)在我們可以開始測試了。應(yīng)該注意,在本文的所有測試中,赫茲量化將使用與實際情況非常接近的指標柱改變的模擬模式 -"Every tick"(每一價格變動)!

編輯切換為居中
以下是在測試程序中運行經(jīng)過優(yōu)化的指標的結(jié)果:

編輯切換為居中
紅色表示測試所用的時間。無法說這太長了!但是對于指標 SMA !!!!!!. mq5 的測試完成,赫茲量化不得不等待很長的時間!

編輯切換為居中
基本上,在此情形中的測試處理時間與上一個測試所用的時間相差 500 多倍。盡管選擇了足夠短的測試周期。但是在測試 EA 交易程序期間,赫茲量化能夠造成如此之大的計算成本,我們最好忘記它們的參數(shù)的優(yōu)化!
因此,這是最有力的證據(jù),證明以經(jīng)濟的方式編寫代碼對于編程領(lǐng)域內(nèi)的專業(yè)人士而言不僅僅是一項娛樂,也是編寫您自己的代碼的非常有針對性的方法。
在互聯(lián)網(wǎng)上有一個專門加速個人計算機以最大程度提高其性能的網(wǎng)站 Overclockers.ru。此實踐的基本方式是使用較為昂貴的計算機組件來提高 CPU 和 RAM 內(nèi)存的時鐘速度。
完成此項工作之后,對于此超頻 CPU,使用更加昂貴的水冷卻系統(tǒng),甚至浸入液氮處理器。此類操作的結(jié)果是 PC 性能增加兩倍甚至三倍。
以經(jīng)濟方式編寫的勝任代碼通常能夠幫助我們事半功倍。當然,此方法不能將 Celleron300A 轉(zhuǎn)換為 Core 2 Quad Q6600,但是它確實允許我們使一臺常規(guī)的標準預(yù)算 PC 以頂配計算機才具備的性能工作!
在某些不是很經(jīng)典的指標中反復(fù)重新計算當前已收盤的指標柱
如果程序代碼優(yōu)化的這一方法無差別地適合所有指標,則一切事情都會很美好了!但是,唉,這不是真的。有一組指標,如果采用此類方法,在將指標加載到已經(jīng)存在的歷史數(shù)據(jù)期間僅開始正常計算一次。
在加載指標后出現(xiàn)的所有指標柱上,其值變?yōu)橥耆徽_。發(fā)生這種情況的主要原因在于來自指標的某些變量取決于在前一指標柱上計算指標之后具有相同變量的那些指標。正式地,它看起來如下所示:
SomeVariable(bar) = Function(SomeVariable(bar - 1))
其中:
SomeVariable() — 某指標柱的某個變量的值;
bar - 在其上進行計算的指標柱的編號。
出于顯而易見的原因,在實際代碼中,此類依存關(guān)系具有很少的清晰函數(shù)形式。但是本質(zhì)并沒有改變,例如,對于移動 T3(未經(jīng)優(yōu)化的指標 - T3 !!!!!!. mq5),與我們相關(guān)的代碼部分看起來如下所示:
? e1 = w1 * series + w2 * e1; ? e2 = w1 * e1 + w2 * e2; ? e3 = w1 * e2 + w2 * e3; ? e4 = w1 * e3 + w2 * e4; ? e5 = w1 * e4 + w2 * e5; ? e6 = w1 * e5 + w2 * e6; ? //---- ? ? T3 = c1 * e6 + c2 * e5 + c3 * e4 + c4 * e3;
變量 e1、e2、e3、e4、e5、e6 正好有此類函數(shù)依存關(guān)系,涉及使用此代碼來計算新的指標柱一次!但是當前指標柱,通過類似的計算,將反復(fù)被跳過,直到其收盤為止。
這些變量在當前指標柱上的值將一直變化,盡管對于當前指標柱,在改變之前,它們應(yīng)該在計算上一指標柱之后保持不變!
因此,這些變量在上一指標柱(相對于當前指標柱)的值應(yīng)保存在靜態(tài)變量中,并且轉(zhuǎn)移它們以便在下一次指標柱改變時重復(fù)使用,在新的指標柱上,變量的倒數(shù)第二個值應(yīng)再次保存為 e1、e2、e3、e4、e5、e6。
對值進行類似處理的其他代碼也很簡單。首先,您應(yīng)在函數(shù) OnCalculate () 內(nèi)聲明用于存儲值的局部靜態(tài)變量
? //---- 聲明用于存儲系數(shù)有效值的靜態(tài)變量 ? static double e1_, e2_, e3_, e4_, e5_, e6_;
之后,在新出現(xiàn)的指標柱的編號大于零時,在當前柱上進行任何計算之前,在循環(huán)中記住變量的值:
? ? //---- 在運行至當前柱之前記錄變量值 ? ? if (rates_total != prev_calculated && bar == rates_total - 1) ? ? ?{ ? ? ? e1_ = e1; ? ? ? e2_ = e2; ? ? ? e3_ = e3; ? ? ? e4_ = e4; ? ? ? e5_ = e5; ? ? ? e6_ = e6; ? ? ?}
在循環(huán)運算符塊之前,通過反轉(zhuǎn)恢復(fù)變量的值:
? //---- 恢復(fù)變量值 ? e1 = e1_; ? e2 = e2_; ? e3 = e3_; ? e4 = e4_; ? e5 = e5_; ? e6 = e6_;
很自然地,現(xiàn)在,計算系數(shù)的開始初始化在函數(shù) OnCalculate () 第一次啟動時只進行一次,并且現(xiàn)在并不是用系數(shù)本身進行計算,而是用對應(yīng)的靜態(tài)變量進行計算。
//---- 首先計算用于重新計算柱的起始編號 ? if (prev_calculated == 0) // 確認這是指標的第一次計算 ? ?{ ? ? first = begin; // 計算所有柱的起始編號 ? ? //---- 開始所計算系數(shù)的初始化 ? ? e1_ = price[first]; ? ? e2_ = price[first]; ? ? e3_ = price[first]; ? ? e4_ = price[first]; ? ? e5_ = price[first]; ? ? e6_ = price[first]; ? ?}
結(jié)果,最終的指標 T3.mq5 開始以最符合成本效益的方式進行計算。所有一切都不重要,但并不是始終都能輕松識別類似的函數(shù)依存關(guān)系。在這個例子中,所有指標變量的值都可以保存在靜態(tài)變量中,并且以相同的方式恢復(fù)。
并且我們只能在后來開始指出哪些變量真的需要恢復(fù),哪些變量沒有此需要。為此,赫茲量化需要將未經(jīng)優(yōu)化的指標和經(jīng)過優(yōu)化的指標掛在圖表上,并且檢查它們的工作,逐漸地從恢復(fù)列表中一次移除一個變量。最后,我們僅剩下那些真正需要恢復(fù)的變量。
自然地,我提供這一版本的邏輯來處理普通指標的程序代碼,在代碼中會重新統(tǒng)計當前指標柱和新出現(xiàn)的指標柱。對于重繪并著眼將來的指標,由于這些指標的特點,赫茲量化不能創(chuàng)建一個類似的、非常標準和簡單的代碼優(yōu)化方法。并且大多數(shù)經(jīng)驗豐富的 EA 交易程序編寫者并不認為有這種需要。因此,這是我們可以考慮詳細分析這些指標完成的地方。
可能導(dǎo)致 MQL5 代碼異常緩慢的指標調(diào)用的特點
似乎任務(wù)已經(jīng)完成,赫茲量化得到了經(jīng)過優(yōu)化的指標,該指標以最經(jīng)濟的方式統(tǒng)計指標柱,并且現(xiàn)在足以編寫幾行代碼,在 EA 交易程序或指標的代碼中調(diào)用此指標,以從指標緩存中獲得計算值。
但是如果到了正式階段,都沒有指出這幾行代碼包含什么類型的操作,這就并不像看起來的那么簡單。
正如在 MQL5 中從時間序列獲取值一樣,從用戶指標和技術(shù)指標獲取值的特點是通過將數(shù)據(jù)復(fù)制到用戶數(shù)組變量來進行。對于當前帳戶,這可能導(dǎo)致建立完全不必要的數(shù)據(jù)。
最容易的方式是從某些技術(shù)指標在某個數(shù)據(jù)接收關(guān)系上驗證所有這一切。作為一個例子,赫茲量化可以采用移動 iAMA,并依據(jù)此技術(shù)指標建立一個自定義指標 AMkA。
為了復(fù)制數(shù)據(jù),赫茲量化將使用函數(shù)調(diào)用 CopyBuffer () 的第一版本,并為復(fù)制請求起始位置和需要的元素數(shù)量。在 AMkA 指標中,使用技術(shù)指標標準方差在當前指標柱上處理移動增量,之后,為了獲取交易信號,將此增量與已經(jīng)處理的總標準方差值進行比較。
因此,在針對 AMkA 指標實施的最簡單的情況中,您應(yīng)首先創(chuàng)建一個指標,在該指標中,指標緩存包含移動增量的值(指標 dAMA)。接著,在另一指標中,赫茲量化使用指標句柄及 AMA 增量獲得經(jīng)過標準方差指標處理而得到的值。
創(chuàng)建類似指標的過程已經(jīng)在有關(guān)這些主題的各種文章中得到詳細解釋,因此我將不會暫停于此,并且將僅分析在其他指標的代碼中存取所調(diào)用指標的指標緩存的細節(jié)。
在廣闊的互聯(lián)網(wǎng)資源中,赫茲量化已經(jīng)看到 MQL5 例子的出現(xiàn),它們的作者照字面將指標緩存的整個內(nèi)容復(fù)制到中間動態(tài)數(shù)組。之后,使用循環(huán)運算符,從這些中間數(shù)組將所有的值逐個傳輸?shù)阶罱K的指標緩存。
要解決我們的問題,此方法看起來非常簡單
? if (CopyBuffer(AMA_Handle, 0, 0, rates_total, Array) <= 0) return(0); ? ArrayCopy(AMA_Buffer, Array, 0, 0, WHOLE_ARRAY);
(指標 dAMA !!!!!!. mq5)或如下所示
? if (CopyBuffer(AMA_Handle, 0, 0, rates_total, Array) <= 0) return(0); ? ? for(bar = 0; bar < rates_total; bar++) ? ?{ ? ? AMA_Buffer[bar] = Array[bar]; ? ? /* ? ? ?這里是指標計算的代碼 ? ?*/ ? ? ? ?}
但是此類自然的解決方案價值如何呢?首先,最好進一步了解操作的最佳合理過程是什么。第一,使用中間數(shù)組 Array [] 并不是必須的,數(shù)據(jù)應(yīng)直接復(fù)制到指標緩存 AMA []。
第二,在每一次指標價格變動時,只需要復(fù)制以下三種情形下中的值:
從新出現(xiàn)的指標柱
從已收盤的指標柱
從當前未收盤的指標柱
指標緩存中剩余的值已經(jīng)存在,并沒有多次重寫它們的必要。