赫茲股票量化交易軟件:開發(fā)一個(gè)跨平臺(tái)網(wǎng)格 EA

網(wǎng)格交易系統(tǒng)
在開始EA開發(fā)之前,讓赫茲股票量化描述一下網(wǎng)格交易策略的基礎(chǔ)知識(shí)。
網(wǎng)格是指將多個(gè)限價(jià)訂單置于當(dāng)前價(jià)格之上,同時(shí)將相同數(shù)量的限價(jià)訂單置于當(dāng)前價(jià)格之下的 EA 交易。
限價(jià)訂單是通過(guò)一定的步驟而不是單一的價(jià)格來(lái)設(shè)定的。換言之,第一個(gè)限價(jià)訂單被設(shè)定在當(dāng)前價(jià)格之上的某個(gè)距離,第二個(gè)限價(jià)設(shè)置在第一個(gè)限價(jià)之上的相同距離處,以此類推。訂單數(shù)量和應(yīng)用的步驟各不相同,
一個(gè)方向的訂單高于當(dāng)前價(jià)格,而另一個(gè)方向的訂單低于當(dāng)前價(jià)格。應(yīng)該考慮的是:
在一個(gè)趨勢(shì)中,買入訂單應(yīng)該高于當(dāng)前價(jià)格,而賣出訂單應(yīng)該低于當(dāng)前價(jià)格;
在盤整期間,賣出訂單應(yīng)高于當(dāng)前價(jià)格,而買入訂單應(yīng)低于當(dāng)前價(jià)格。
您可以應(yīng)用止損水平,也可以不使用它們。
如果你不使用止損和獲利,所有未平倉(cāng)合約,無(wú)論是盈利的還是虧損的,都會(huì)存在,直到整體利潤(rùn)達(dá)到一定水平。之后,所有未結(jié)頭寸以及不受價(jià)格影響的限價(jià)訂單都將關(guān)閉,并設(shè)置新的網(wǎng)格。
下面的屏幕截圖顯示了一個(gè)開啟的網(wǎng)格:

編輯切換為居中
因此,在理論上,網(wǎng)格交易系統(tǒng)可以讓你在任何市場(chǎng)中獲利,而不需要等待任何獨(dú)特的進(jìn)入點(diǎn),也不需要使用任何指標(biāo)。
如果使用止損和獲利,那么利潤(rùn)是由于一個(gè)頭寸的虧損被價(jià)格朝一個(gè)方向移動(dòng)時(shí)的整體利潤(rùn)所覆蓋而獲得的。
沒有止損水平,利潤(rùn)是由于在正確的方向上打開更多的訂單而獲得的。即使最初價(jià)格觸及一個(gè)方向的倉(cāng)位,然后轉(zhuǎn)向,正確方向的新倉(cāng)位將彌補(bǔ)先前開啟的倉(cāng)位的損失,因?yàn)樽罱K會(huì)有更多的倉(cāng)位。
赫茲股票量化的網(wǎng)格 EA 的工作原理
赫茲股票量化已經(jīng)描述了上面最簡(jiǎn)單網(wǎng)格的工作原理,您可以為更改打開訂單的方向、添加以相同價(jià)格打開多個(gè)訂單的能力、添加指標(biāo)等網(wǎng)格提供自己的選項(xiàng)。
在本文中,赫茲股票量化將嘗試實(shí)現(xiàn)最簡(jiǎn)單的網(wǎng)格化版本,而不會(huì)造成停止損失,因?yàn)樗诘乃枷敕浅UT人。
事實(shí)上,認(rèn)為價(jià)格遲早會(huì)在向一個(gè)方向移動(dòng)時(shí)達(dá)到利潤(rùn),即使最初的開倉(cāng)方向是錯(cuò)誤的,這似乎是合理的。假設(shè)在一開始,價(jià)格就經(jīng)歷了調(diào)整,觸發(fā)了兩個(gè)訂單,此后,價(jià)格開始向相反(主要趨勢(shì))的方向移動(dòng)。在這種情況下,早晚兩個(gè)以上的訂單將朝著正確的方向打開,我赫茲股票量化們的初始損失將在一段時(shí)間后變成利潤(rùn)。
似乎交易系統(tǒng)造成損失的唯一情況是,當(dāng)價(jià)格首先觸及一個(gè)訂單,然后返回并觸及另一個(gè)訂單,然后再次改變方向并觸及另一個(gè)訂單,并反復(fù)改變其方向,觸及越來(lái)越遠(yuǎn)的訂單。但這種價(jià)格行為在實(shí)際情況下是可能的嗎?
EA 模板
赫茲股票量化將從模板開始開發(fā)EA,這將使我們能夠立即看到將涉及哪些標(biāo)準(zhǔn)MQL函數(shù)。
#property copyright "Klymenko Roman (needtome@icloud.com)" #property link ? ? ?"https://www.mql5.com/en/users/needtome" #property version ? "1.00" #property strict //+------------------------------------------------------------------+ //| EA 交易初始化函數(shù) ? ? ? ? ? ? ? ? ? ? ? ??????????????????????????????????????????????? ? ? ? ? ? | //+------------------------------------------------------------------+ int OnInit() ?{ //--- ? //--- ? return(INIT_SUCCEEDED); ?} //+------------------------------------------------------------------+ //| EA 交易去初始化函數(shù) ? ? ? ? ? ? ? ? ? ? ? ? ? ???????????????????????????????????????????????? ? ? | //+------------------------------------------------------------------+ void OnDeinit(const int reason) ?{ ?} //+------------------------------------------------------------------+ //| EA交易分時(shí)函數(shù) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???????????????????????? ?| //+------------------------------------------------------------------+ void OnTick() ?{ ?} void OnChartEvent(const int id, ? ? ? ? // 事件 ID ? ? ? ? ? ? ? ? ? ?const long& lparam, ? // 長(zhǎng)整型的事件參數(shù) ? ? ? ? ? ? ? ? ?const double& dparam, // 雙精度浮點(diǎn)型的事件參數(shù) ? ? ? ? ? ? ? ? ?const string& sparam) // 字符串類型的事件參數(shù) ? { ? }
它與使用 MQL5 向?qū)?chuàng)建 EA 時(shí)生成的標(biāo)準(zhǔn)模板的唯一區(qū)別是 #property strict 這一行,赫茲股票量化添加它以便 EA 也在 MQL4 中工作。
赫茲股票量化需要 OnChartEvent() 函數(shù)來(lái)響應(yīng)單擊按鈕,接下來(lái),我們將實(shí)現(xiàn)Close All(全部關(guān)閉)按鈕,以便在達(dá)到所需利潤(rùn)或只想停止 EA 時(shí)手動(dòng)關(guān)閉所有交易品種倉(cāng)位和訂單。
開啟倉(cāng)位函數(shù)
可能任何EA最重要的功能都是下訂單的能力,第一個(gè)問(wèn)題就在這里等著我們。在 MQL5 和 MQL4 中,訂單的設(shè)置方式非常不同。為了以某種方式統(tǒng)一這個(gè)功能,我們必須開發(fā)一個(gè)自定義函數(shù)來(lái)下訂單。
每個(gè)訂單都有自己的類型:買入訂單、賣出訂單、限價(jià)買入或賣出訂單。在下訂單時(shí)設(shè)置此類型的變量在MQL5和MQL4中也不同。
在 MQL4 中,訂單類型由 int 類型變量指定,而在 MQL5 中,則使用ENUM_ORDER_TYPE枚舉,在 MQL4 中沒有這樣的枚舉。因此,為了組合這兩種方法,赫茲股票量化應(yīng)該創(chuàng)建一個(gè)自定義枚舉來(lái)設(shè)置訂單類型。因此,我們將來(lái)要?jiǎng)?chuàng)建的函數(shù)將不依賴于MQL版本:
enum TypeOfPos{ ? MY_BUY, ? MY_SELL, ? MY_BUYSTOP, ? MY_BUYLIMIT, ? MY_SELLSTOP, ? MY_SELLLIMIT, };
現(xiàn)在赫茲股票量化可以創(chuàng)建一個(gè)自定義函數(shù)來(lái)下訂單了,讓我們稱它為 pdxSendOrder()。我們將傳遞下訂單所需的所有信息:訂單類型、未平倉(cāng)價(jià)格、止損(未設(shè)定為0)、獲利(未設(shè)定為0)、成交量、未平倉(cāng)訂單(如果在MQL5中應(yīng)修改未平倉(cāng))、注釋和交易品種(如果您需要為不是當(dāng)前打開的交易品種開啟訂單):
// 發(fā)送訂單的函數(shù) bool pdxSendOrder(TypeOfPos mytype, double price, double sl, double tp, double volume, ulong position=0, string comment="", string sym=""){ ? // 檢查傳入的數(shù)值 ? if( !StringLen(sym) ){ ? ? ?sym=_Symbol; ? } ? int curDigits=(int) SymbolInfoInteger(sym, SYMBOL_DIGITS); ? if(sl>0){ ? ? ?sl=NormalizeDouble(sl,curDigits); ? } ? if(tp>0){ ? ? ?tp=NormalizeDouble(tp,curDigits); ? } ? if(price>0){ ? ? ?price=NormalizeDouble(price,curDigits); ? } ? ? #ifdef __MQL5__ ? ? ?ENUM_TRADE_REQUEST_ACTIONS action=TRADE_ACTION_DEAL; ? ? ?ENUM_ORDER_TYPE type=ORDER_TYPE_BUY; ? ? ?switch(mytype){ ? ? ? ? case MY_BUY: ? ? ? ? ? ?action=TRADE_ACTION_DEAL; ? ? ? ? ? ?type=ORDER_TYPE_BUY; ? ? ? ? ? ?break; ? ? ? ? case MY_BUYSTOP: ? ? ? ? ? ?action=TRADE_ACTION_PENDING; ? ? ? ? ? ?type=ORDER_TYPE_BUY_STOP; ? ? ? ? ? ?break; ? ? ? ? case MY_BUYLIMIT: ? ? ? ? ? ?action=TRADE_ACTION_PENDING; ? ? ? ? ? ?type=ORDER_TYPE_BUY_LIMIT; ? ? ? ? ? ?break; ? ? ? ? case MY_SELL: ? ? ? ? ? ?action=TRADE_ACTION_DEAL; ? ? ? ? ? ?type=ORDER_TYPE_SELL; ? ? ? ? ? ?break; ? ? ? ? case MY_SELLSTOP: ? ? ? ? ? ?action=TRADE_ACTION_PENDING; ? ? ? ? ? ?type=ORDER_TYPE_SELL_STOP; ? ? ? ? ? ?break; ? ? ? ? case MY_SELLLIMIT: ? ? ? ? ? ?action=TRADE_ACTION_PENDING; ? ? ? ? ? ?type=ORDER_TYPE_SELL_LIMIT; ? ? ? ? ? ?break; ? ? ?} ? ? ? ? ? ?MqlTradeRequest mrequest; ? ? ?MqlTradeResult mresult; ? ? ?ZeroMemory(mrequest); ? ? ? ? ? ?mrequest.action = action; ? ? ?mrequest.sl = sl; ? ? ?mrequest.tp = tp; ? ? ?mrequest.symbol = sym; ? ? ?if(position>0){ ? ? ? ? mrequest.position = position; ? ? ?} ? ? ?if(StringLen(comment)){ ? ? ? ? mrequest.comment=comment; ? ? ?} ? ? ?if(action!=TRADE_ACTION_SLTP){ ? ? ? ? if(price>0){ ? ? ? ? ? ?mrequest.price = price; ? ? ? ? } ? ? ? ? if(volume>0){ ? ? ? ? ? ?mrequest.volume = volume; ? ? ? ? } ? ? ? ? mrequest.type = type; ? ? ? ? mrequest.magic = EA_Magic; ? ? ? ? switch(useORDER_FILLING_RETURN){ ? ? ? ? ? ?case FOK: ? ? ? ? ? ? ? mrequest.type_filling = ORDER_FILLING_FOK; ? ? ? ? ? ? ? break; ? ? ? ? ? ?case RETURN: ? ? ? ? ? ? ? mrequest.type_filling = ORDER_FILLING_RETURN; ? ? ? ? ? ? ? break; ? ? ? ? ? ?case IOC: ? ? ? ? ? ? ? mrequest.type_filling = ORDER_FILLING_IOC; ? ? ? ? ? ? ? break; ? ? ? ? } ? ? ? ? mrequest.deviation=100; ? ? ?} ? ? ?if(OrderSend(mrequest,mresult)){ ? ? ? ? if(mresult.retcode==10009 || mresult.retcode==10008){ ? ? ? ? ? ?return true; ? ? ? ? }else{ ? ? ? ? ? ?msgErr(GetLastError(), mresult.retcode); ? ? ? ? } ? ? ?} ? #else ? ? ?int type=OP_BUY; ? ? ?switch(mytype){ ? ? ? ? case MY_BUY: ? ? ? ? ? ?type=OP_BUY; ? ? ? ? ? ?break; ? ? ? ? case MY_BUYSTOP: ? ? ? ? ? ?type=OP_BUYSTOP; ? ? ? ? ? ?break; ? ? ? ? case MY_BUYLIMIT: ? ? ? ? ? ?type=OP_BUYLIMIT; ? ? ? ? ? ?break; ? ? ? ? case MY_SELL: ? ? ? ? ? ?type=OP_SELL; ? ? ? ? ? ?break; ? ? ? ? case MY_SELLSTOP: ? ? ? ? ? ?type=OP_SELLSTOP; ? ? ? ? ? ?break; ? ? ? ? case MY_SELLLIMIT: ? ? ? ? ? ?type=OP_SELLLIMIT; ? ? ? ? ? ?break; ? ? ?} ? ? ?if(OrderSend(sym, type, volume, price, 100, sl, tp, comment, EA_Magic, 0)<0){ ? ? ? ? msgErr(GetLastError()); ? ? ?}else{ ? ? ? ? return true; ? ? ?} ? ? #endif ? return false; }
首先,檢查傳遞給函數(shù)的數(shù)值并規(guī)范化價(jià)格。
輸入?yún)?shù). 之后,使用條件編譯來(lái)定義當(dāng)前的MQL版本,并根據(jù)其規(guī)則設(shè)置順序。多出的 useORDER_FILLING_RETURN 輸入?yún)?shù)是為 MQL5 使用的,在它的幫助下,赫茲股票量化根據(jù)代理支持的模式配置訂單執(zhí)行模式。因?yàn)?useORDER_FILLING_RETURN 輸入?yún)?shù)只對(duì) MQL5 EA 才是必須的,再次使用條件編譯來(lái)添加它:
#ifdef __MQL5__ ? enum TypeOfFilling //執(zhí)行模式 ? ? { ? ? ?FOK,//ORDER_FILLING_FOK ? ? ?RETURN,// ORDER_FILLING_RETURN ? ? ?IOC,//ORDER_FILLING_IOC ? ? }; ? input TypeOfFilling ?useORDER_FILLING_RETURN=FOK; //執(zhí)行模式 #endif
此外,下訂單時(shí),還使用包含EA幻數(shù)的EA_Magic輸入?yún)?shù)。
如果在EA設(shè)置中未設(shè)置此參數(shù),則 EA 已啟動(dòng)的交易品種上的任何倉(cāng)位都被視為屬于EA,這樣,EA就完全控制了它們。
如果設(shè)置了幻數(shù),則EA只考慮在其工作中具有此幻數(shù)的倉(cāng)位。
顯示錯(cuò)誤. 如果訂單成功設(shè)置, 就返回 true。否則,會(huì)將適當(dāng)?shù)腻e(cuò)誤代碼傳遞給 msgErr() 的函數(shù),以便進(jìn)一步分析并顯示可理解的錯(cuò)誤消息。這個(gè)函數(shù)顯示包含詳細(xì)錯(cuò)誤描述的本地化消息,在這里提供完整的代碼是沒有意義的,所以我只展示其中的一部分:
void msgErr(int err, int retcode=0){ ? string curErr=""; ? switch(err){ ? ? ?case 1: ? ? ? ? curErr=langs.err1; ? ? ? ? break; // ? ? ?case N: // ? ? ? ? curErr=langs.errN; // ? ? ? ? break; ? ? ?default: ? ? ? ? curErr=langs.err0+": "+(string) err; ? } ? if(retcode>0){ ? ? ?curErr+=" "; ? ? ?switch(retcode){ ? ? ? ? case 10004: ? ? ? ? ? ?curErr+=langs.retcode10004; ? ? ? ? ? ?break; // ? ? ? ? case N: // ? ? ? ? ? ?curErr+=langs.retcodeN; // ? ? ? ? ? ?break; ? ? ?} ? } ? ? Alert(curErr); }
我們將在下一節(jié)詳細(xì)討論本地化。