看板 ForeignEX 關於我們 聯絡資訊
HIHI大家好,我又再度出現了,很久沒發文前陣子都待在中國, 最近很多人發文有關MT4 EA的文章,看到了一些也想提供一些撰寫方式, 我自己本身不是IT出身,CODING在學MQL4之前幾乎沒學過,所以算是從0學起吧。 但過程我就不多加贅述了,我這篇文章將提供一般的策略如何撰寫, 讓你不用有求於人,也能輕鬆寫出自己的EA,至於大神們就可以直接跳過了, 若有錯誤還請多指教。 在本文開始前額外插個小話題: 10/1開始MATE QUOTE將停止更新MT4,並上調MT4對外匯商售價以及維護費, 並且相對調低MT5價格,試圖逼所有外匯商改用MT5,目前今年使用MT5券商, 已經增加百家,如果照這樣下去,MT5的使用將會越來越大,如果有懂IT的大人, 可以改用MQL5試試看了,我還在學覺得很困難。 另外MT5為人詬病的"不可對鎖"這件事情在新版本已經改善,將由外匯商自行決定 是否給投資人對鎖模式。 -------------------------本文開始----------------------------- 在MQL4編輯介面新開一個EA,甚麼都不選擇的情況下按下一步, 將會出現一個空白的EA編寫介面包含以下三個區塊: Oninit(首次啟動EA執行內部程序"只執行一次") OnDeinit(關閉EA時執行內部程序) Ontick(價格"每次"跳動都會執行裡面的程序一次) 在這邊我會先給大家一個概念,建議將所有的程序都寫在這三個區塊以外, 有人可能會覺得神經病那這樣怎麼執行,且聽老納娓娓道來。 "所有的程序寫成"子函數"並在ONTICK中調用" "所有的程序寫成"子函數"並在ONTICK中調用" "所有的程序寫成"子函數"並在ONTICK中調用" 很重要說三次 解釋:有需要用到她的時候再把他叫出來,沒需要用到叫他滾邊睡。 曾經下載過網路上其他程式的人,不知道有沒有這樣的經驗, 兩支EA有的時候回測速度就是有一支比別支慢很多,明明同樣時間和歷史, 先忽略掉程序本身真的很複雜的部份,大約有70%的情況都是因為, 設計師把所有的程序全部放在ONTICK裡面,不管是不是立即要用到的, 這會導致電腦程序的執行非常緩慢,假設你的程序有500行, 也就是說價格每跳動一次你就要執行一次500行的程序,非常佔資源與時間, 所以謹記這點。 回到EA部分,一支EA裡面最關鍵的幾個要素是: 下單 平倉 我這次就以這兩個來做示範,基本上你想要直接複製走也可以,很多IT到最後都是這樣, 下單: 下單最重要的就是"條件",連接到剛剛的調用的概念,我會先做一個子函數出來, void CheckForOpen() { if(這邊很多人會直接開始寫下單條件)//但我不會這麼做 //我會在外面再增加一個子函數 //bool buy_rule() //bool sell_rule() } 為什麼會這樣做呢?,當我在設定一個buyrule sell rule 當之後我的下單條件 有所更改,我不用一行一行去找,我只需要去更改這兩個子函數內的條件就可以了。 額外說:如果您要問bool是甚麼void是甚麼,可以去估狗 myeatrade的網站, 上面有完整的教科文。 輪到平倉部分,當然也是用同樣方式,在外面製作一個子函數來調用 void CheckForclose() { 一樣在開始下單之前設定一個 bool buy_crule() //bool後面的字不一定要跟我一樣,我這只是方便我自己看 //你可以改成你自己想要的名子,中文也可以。 bool sell_crule() //同樣的 } 這邊得解釋一下平倉這件事情,在下單中因為你場上沒有任何單子, 再加上你的下單策略可能是不需要看場上單子的, 所以你不需要用到"選單子"這件事情, 但在平倉你必須先選擇到你要平的部位才能進行砍單。 所以你必須有這一段選單過程: for(int i= OrdersTotal()-1; i>=0; i--) { if(!OrderSelect(i, SELECT_BY_POS)) { if(OrderMagicNumber() == magic)(魔術碼) 提醒一下(IF裡面都是兩個==) { if(OrderSymbol()==symbol) (商品確認) { if(OrderType() == OP_SELL ) (確認方向) { OrderClose(OrderTicket(),OrderLots(),Ask,3,White) 最後在ORDERCLOSE平倉,如果是BUY單就是Bid 如果是sell單就是Ask,這邊要小心常錯。 } } } } } 可能有人注意到我的for迴圈中是使用i--而非mql4範例中的i++ 主要是因為電腦程序的第一個數位都是從0開始,如果你今天是多張單子策略, 他平倉完第一張(在程序編號上是0),前面的單字會往前補,也就是原本編號1的單子 會變成編號0,但這時候for迴圈會開始執行編號1的單子,這樣一直循環下去, 導致你的倉位會有些部位沒平倉到,為了解決這個問題, 最好的方式就是我從最後一張來,一路遞減,這樣甚麼煩惱都沒有。 回到Ontick上我們要調用這兩個勢必也有些條件,這個條件其實就不用像剛剛那個 再額外設置一個子函數了,直接寫在Ontick裡即可。 void OnTick() { if(CalculateCurrentOrders()==0) //計算場上部位數量的子函數等下附上 CheckForOpen(); if(CalculateCurrentOrders()>=1) CheckForClose(); //--- } /*如果if條件式下沒有{大括號},則程式只會執行條件式下一句, 所以如果有多行要執行,請加{}並左右對稱。 本來我還想提到追蹤止損部分,但是因為感冒打得有點累了所以下次再提吧, 最後送給大家一個簡易版的程式碼,可以直接複製過去的,並附上註解。 int MAGICMA = 378354385; extern int 停利 =2900; extern int 停損 =50; extern int 追蹤止損距離 = 500; extern int 均線數值 = 288; extern double Lots = 0.1; int bars = 1; //+--------------------------買單條件--------------------------------+ bool buy_rule() { double ma=iMA(NULL,0,均線數值,0,MODE_SMA,PRICE_CLOSE,0); if(Open[0]>ma) return (true); else return(false); } //+--------------------------賣單條件--------------------------------+ bool sell_rule() { double ma=iMA(NULL,0,均線數值,0,MODE_SMA,PRICE_CLOSE,0); if(Open[0]<ma) return (true); else return(false); } bool buy_crule() { double ma=iMA(NULL,0,均線數值,0,MODE_SMA,PRICE_CLOSE,0); if(Open[0]<ma) return (true); else return(false); } //+--------------------------賣單條件--------------------------------+ bool sell_crule() { double ma=iMA(NULL,0,均線數值,0,MODE_SMA,PRICE_CLOSE,0); if(Open[0]>ma) return (true); else return(false); } //+--------------------------平倉子涵數--------------------------------+ void CheckForClose() { double ma=iMA(NULL,0,均線數值,0,MODE_SMA,PRICE_CLOSE,0); for(int i=0;i<OrdersTotal();i++) { if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) break; if(OrderMagicNumber()==MAGICMA || OrderSymbol()==Symbol()) if(OrderType()==OP_BUY && buy_crule()==True) { OrderClose(OrderTicket(),OrderLots(),Bid,3,White) ; } if(OrderType()==OP_SELL && sell_crule() == True) { OrderClose(OrderTicket(),OrderLots(),Ask,3,White); } } } //+--------------------------開新部位--------------------------------+ void CheckForOpen() { double ticket; if(buy_rule()==true && bars!=Bars) { ticket=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,Bid-停損*Point,Ask+停 利*Point,"",MAGICMA,0,Blue); bars=Bars; } if(sell_rule()==true && bars!=Bars) { ticket=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,Ask+停損*Point,Bid-停 利*Point,"",MAGICMA,0,Red); bars=Bars; } } int OnInit() { bars=Bars; return(INIT_SUCCEEDED); } //+--------------------------計算部位數量--------------------------------+ int CalculateCurrentOrders() { int orders = 0; for(int i=0;i<OrdersTotal();i++) { if(!OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) continue; if(OrderSymbol()!=Symbol() || OrderMagicNumber()!=MAGICMA) continue; orders++; } return(orders); } //關閉EA時所執行之程序 void OnDeinit(const int reason) { } //+--------------------------移動停損--------------------------------+ void modify() { double Ma =iMA(NULL,0,均線數值,0,MODE_SMA,PRICE_CLOSE,0); for(int i=0;i<OrdersTotal();i++) { if(!OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) continue; if(OrderType()==OP_BUY) { if(OrderMagicNumber() == MAGICMA ) { if(追蹤止損距離>0 ) { if(Bid-OrderOpenPrice()>Point*追蹤止損距離) { OrderModify(OrderTicket(),OrderOpenPrice(),Bid-追蹤止 損距離*Point,OrderTakeProfit(),0,Green); } } } } if(OrderType()==OP_SELL) { if(追蹤止損距離>0 ) { if((OrderOpenPrice()-Ask)>(Point*追蹤止損距離)) { OrderModify(OrderTicket(),OrderOpenPrice(),Ask+追蹤止 損距離*Point,OrderTakeProfit(),0,Red); return; } } } } } //+--------------------------價格跳動執行一次 --------------------------------+ void OnTick() { if(CalculateCurrentOrders()<1) CheckForOpen(); if(CalculateCurrentOrders()>=1) modify(); CheckForClose() ; } /*關於程序中的Bars這邊做個說明,Bars在mql4中定義為當前這根k棒, /*而我自己在設定一個自定義變數bars,在開倉時用到, /*當bars != Bars 代表著當前這根k棒跟之前紀錄的那一根不同了, /*在ordersend之後 bars=Bars 代表著我這根k棒下過單了,避免重複開單, /*每更新一根k棒才會下單。 這只是一個很簡易的模組版本ea,如果有比較深入的想了解, 可以把問題來信,我看我有沒有辦法解決,如果無法就說抱歉了, 下次文章:馬丁寫法。 -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 220.137.10.223 ※ 文章網址: https://www.ptt.cc/bbs/ForeignEX/M.1499351000.A.122.html ※ 編輯: n33222 (220.137.10.223), 07/06/2017 22:31:29
henry46277: 子涵數的涵 寫錯 應該是函 07/06 22:37
不好意思,電腦選字忘記改,已修正 ※ 編輯: n33222 (220.137.10.223), 07/06/2017 22:40:46
fritrescond: 認真推 07/06 22:54
※ 編輯: n33222 (220.137.10.223), 07/06/2017 23:04:21
VCLee: 推.. 07/07 00:10
ary9872: 感謝撰文分享 07/07 02:11
wwchocolate: 推認真 07/07 03:03
liker: 推 07/07 07:04
ducati5566: 推! 07/07 08:18
hobbylin: 推 07/07 08:21
willy84222: 推 07/07 08:53
angrywater: 推一下,謝分享 07/07 13:08
Theseus914: 推,對新手很有幫助,感謝 07/08 13:11
yzfr6: 函數、函式即可,不需要【子】 07/09 01:00
Hermanda: 推 超專業 07/09 21:15
liuwen: 推推 07/11 22:18
roger2232: 感謝分享 07/15 03:11