股票量化交易軟件:自定義品種實(shí)踐基礎(chǔ)

等量/范圍圖表

等量圖表是基于相鄰柱線交易量相等原理的柱線圖表。 在常規(guī)圖表上,每根新柱線均以指定的時間間隔形成,其與時間幀大小相匹配。 在等量圖表上,當(dāng)價(jià)格變動或?qū)嶋H交易量的總和達(dá)到預(yù)設(shè)值時,每根柱線均視為已形成。 之后,程序開始計(jì)算下一根柱線的額度。 當(dāng)然,在計(jì)算交易量時價(jià)格走勢依然受控,因此您會在圖表上得到通常的四個價(jià)格值:開盤價(jià)、最高價(jià)、最低價(jià)和收盤價(jià)。
盡管等量圖表上的水平軸仍然表示時序,但每根柱線的時間戳是任意的,且取決于每個時間區(qū)間的波動性(交易的數(shù)量或大?。?許多交易者認(rèn)為,與固定時間幀相比,這種柱形法方法能更準(zhǔn)確地描述不斷變化的行情。
不幸的是,赫茲股票量化都未提供開箱即用的等量圖表。 它們應(yīng)以特殊方式生成。
在 赫茲股票量化當(dāng)中,這可以利用離線圖表來達(dá)成。 等量圖表復(fù)查中介紹了該方法。
而在 赫茲股票量化 當(dāng)中,相同的算法可利用自定義品種來實(shí)現(xiàn)。 為了簡化任務(wù),我們借用指定文章中的非交易型智能交易系統(tǒng),并令其與赫茲股票量化 MQL API 適配。
原始文件 EqualVolumeBars.mq4 已重命名為 EqualVolumeBars.mq5,并對其進(jìn)行了少許修改。 特別是,描述輸入?yún)?shù)的 'extern' 關(guān)鍵字已替換為 'input'。 用一個 StartDate 參數(shù)取代了 StartYear 和 StartMonth 兩個參數(shù)。 在 赫茲股票量化 中設(shè)置非標(biāo)準(zhǔn)時間幀的 CustomPeriod 參數(shù)現(xiàn)在不需要了,故將其刪除。
請注意,赫茲股票量化 的交易量是全部即時報(bào)價(jià)的交易量,即,它們代表柱線內(nèi)即時報(bào)價(jià)的數(shù)量(價(jià)格變動次數(shù))。 原本的想法是處理 M1 柱線(帶有其即時報(bào)價(jià)量),或含有其他經(jīng)紀(jì)商提供的即時報(bào)價(jià)的外部 csv 文件,為了計(jì)數(shù)每次輸入的即時報(bào)價(jià),以及一旦達(dá)到指定的即時報(bào)價(jià)次數(shù)后,立即形成新的等量柱線。 柱線被寫入一個 hst 文件,該文件可以在 赫茲股票量化中作為脫機(jī)圖表打開。
讀取 csv 文件并寫入 hst 文件的相關(guān)代碼在赫茲股票量化 中已不再需要。 取而代之,我們可用自定義品種 API 讀取真實(shí)的即時報(bào)價(jià)歷史記錄,并形成柱線。 此外,赫茲股票量化 允許經(jīng)紀(jì)商提供真實(shí)的交易量和即時報(bào)價(jià)(針對交易所金融產(chǎn)品,但它們通常不適用于外匯金融產(chǎn)品)。 如果啟用此模式,則構(gòu)建等量柱線時可以不依據(jù)即時報(bào)價(jià)次數(shù),而是依據(jù)實(shí)際交易量。
FromM1 輸入?yún)?shù)判斷 EA 是否需要處理 M1 柱線(默認(rèn)為 “true”),亦或是即時報(bào)價(jià)歷史記錄(“false”)。 開始處理即時報(bào)價(jià)時,請勿選擇距離太遠(yuǎn)的起點(diǎn),因?yàn)檫@可能需要大量時間和磁盤空間。 如果您已采用即時報(bào)價(jià)記錄操作,那么您要了解您的 PC 能力和可用資源。
以相同的方式繪制等范圍柱線。 然而,當(dāng)價(jià)格覆蓋了指定的點(diǎn)數(shù)時,將在此處開立一根新的柱線。 請注意,這些柱線僅在即時報(bào)價(jià)模式下可用(FromM1 == false)。
Chart type — EqualTickVolumes, EqualRealVolumes, RangeBars — 由 WorkMode 輸入?yún)?shù)設(shè)置。
最方便的方法是利用 Symbol 函數(shù)(由 fxsaber 開發(fā))來操控自定義品種。 可用 #include 指令將其連接到智能交易系統(tǒng):
?#include <Symbol.mqh>
現(xiàn)在,我們可以基于當(dāng)前圖表品種創(chuàng)建自定義品種。 如下完成:
?if(!SymbolSelect(symbolName, true)) ?{ ? ?const SYMBOL Symb(symbolName); ? ?Symb.CloneProperties(_Symbol); ? ? ? ?if(!SymbolSelect(symbolName, true)) ? ?{ ? ? ?Alert("Can't select symbol:", symbolName, " err:", GetLastError()); ? ? ?return INIT_FAILED; ? ?} ?}
其中 symbolName 是含有自定義品種名稱的字符串。
與所有自定義品種管理相關(guān)的初始化片段,和許多其他輔助任務(wù)(特別是重置現(xiàn)有歷史記錄,使用新的自定義品種打開圖表)均會以類似方式在所有程序中執(zhí)行。 您可以在下面的附件中查看相關(guān)的源代碼。 我將在本文中忽略它們,因?yàn)樗鼈冎皇谴我獌?nèi)容。
當(dāng)出現(xiàn)一根新的等量柱線,或當(dāng)前的等量柱線變化時,將調(diào)用 WriteToFile 函數(shù)。 經(jīng)調(diào)用 赫茲股票量化 中的 CustomRatesUpdate 來實(shí)現(xiàn)此函數(shù):
?void WriteToFile(datetime t, double o, double l, double h, double c, long v, long m = 0) ?{ ? ?MqlRates r[1]; ? ? ? ?r[0].time = t; ? ?r[0].open = o; ? ?r[0].low = l; ? ?r[0].high = h; ? ?r[0].close = c; ? ?r[0].tick_volume = v; ? ?r[0].spread = 0; ? ?r[0].real_volume = m; ? ? ? ?int code = CustomRatesUpdate(symbolName, r); ? ?if(code < 1) ? ?{ ? ? ?Print("CustomRatesUpdate failed: ", GetLastError()); ? ?} ?}
令人驚訝的是,M1 柱線周期(FromM1 = true 模式)與 MQL4 版本幾乎相同,這意味著只需修改 WriteToFile 函數(shù),我們就可以得到 MQL5 版本的 M1 柱線函數(shù)代碼。 唯一需要更改的部分是 RefreshWindow 中即時報(bào)價(jià)的生成。 在 赫茲股票量化之中,這是通過發(fā)送 Windows 消息來模擬脫機(jī)圖表上的即時報(bào)價(jià)柱線來完成的。 赫茲股票量化則利用 CustomTicksAdd 函數(shù):
?void RefreshWindow(const datetime t) ?{ ? ?MqlTick ta[1]; ? ?SymbolInfoTick(_Symbol, ta[0]); ? ?ta[0].time = t; ? ?ta[0].time_msc = ta[0].time * 1000; ? ?if(CustomTicksAdd(symbolName, ta) == -1) ? ?{ ? ? ?Print("CustomTicksAdd failed:", GetLastError(), " ", (long) ta[0].time); ? ? ?ArrayPrint(ta); ? ?} ?}
即時報(bào)價(jià)生成會在自定義品種圖表上調(diào)用 OnTick 事件,其允許在此類圖表上運(yùn)行智能交易系統(tǒng)進(jìn)行交易。 不過,這項(xiàng)技術(shù)需要采取一些額外的措施,我們稍后再加以研究。
從即時報(bào)價(jià)歷史中生成等量柱線的模式(FromM1 = false)要復(fù)雜一些。 這需要調(diào)用標(biāo)準(zhǔn) CopyTicks/CopyTicksRange 函數(shù)讀取真實(shí)的即時報(bào)價(jià)。 所有這些功能都已在 TicksBuffer 類中實(shí)現(xiàn)。
?#define TICKS_ARRAY 10000 ? ?class TicksBuffer ?{ ? ?private: ? ? ?MqlTick array[]; ? ? ?int tick; ? ? ? ?public: ? ? ?bool fill(ulong &cursor, const bool history = false) ? ? ?{ ? ? ? ?int size = history ? CopyTicks(_Symbol, array, COPY_TICKS_ALL, cursor, TICKS_ARRAY) : CopyTicksRange(_Symbol, array, COPY_TICKS_ALL, cursor); ? ? ? ?if(size == -1) ? ? ? ?{ ? ? ? ? ?Print("CopyTicks failed: ", GetLastError()); ? ? ? ? ?return false; ? ? ? ?} ? ? ? ?else if(size == 0) ? ? ? ?{ ? ? ? ? ?if(history) Print("End of CopyTicks at ", (datetime)(cursor / 1000)); ? ? ? ? ?return false; ? ? ? ?} ? ? ? ? ? ? ? ?cursor = array[size - 1].time_msc + 1; ? ? ? ?tick = 0; ? ? ? ? ? ? ?return true; ? ? ?} ? ? ? ? ? ?bool read(MqlTick &t) ? ? ?{ ? ? ? ?if(tick < ArraySize(array)) ? ? ? ?{ ? ? ? ? ?t = array[tick++]; ? ? ? ? ?return true; ? ? ? ?} ? ? ? ?return false; ? ? ?} ?};
在 TICKS_ARRAY 片段的 “fill” 方法中請求即時報(bào)價(jià),然后將其添加到“數(shù)組”中,然后調(diào)用 “read” 方法逐一讀取。 該方法所實(shí)現(xiàn)的操控即時報(bào)價(jià)歷史記錄的算法類似于 M1 歷史記錄柱線(附件中提供了完整的源代碼)。
? ?TicksBuffer tb; ? ? ? ?while(tb.fill(cursor, true) && !IsStopped()) ? ?{ ? ? ?MqlTick t; ? ? ?while(tb.read(t)) ? ? ?{ ? ? ? ?... ? ? ? ?// New or first bar ? ? ? ?if(IsNewBar() || now_volume < 1) ? ? ? ?{ ? ? ? ? ?WriteToFile(...); ? ? ? ?} ? ? ?} ? ?}
每次啟動智能交易系統(tǒng)時,它都會調(diào)用 “Reset” 函數(shù)清除指定自定義品種的現(xiàn)有歷史記錄(如果存在的話)。 如有必要,可改進(jìn)此行為,即保存歷史記錄,并在前一此生成結(jié)束的位置繼續(xù)生成柱線。