Moving Average EA เป็นเครื่องมือที่มีอยู่ในแพคเกจมาตรฐานของ MetaTrader 5 และเป็นตัวอย่างของ EA ที่ใช้ในการเทรดโดยอิงจาก Moving Average indicator.
ไฟล์ EA ที่ชื่อ Moving Average.mq5 จะอยู่ในโฟลเดอร์ "terminal_data_folder\MQL5\Experts\Examples\Moving Average" โดย EA นี้เป็นตัวอย่างการใช้ ตัวชี้วัดทางเทคนิค, ฟังก์ชัน ประวัติการซื้อขาย และ คลาสการเทรด ที่มีอยู่ใน Standard Library นอกจากนี้ EA ยังมีระบบการจัดการเงินที่อิงจากผลการเทรดอีกด้วย。
มาดูกันว่าโครงสร้างของ Expert Advisor นี้ทำงานอย่างไรบ้าง
1. คุณสมบัติของ EA
//+------------------------------------------------------------------+ //| Moving Averages.mq5 | //| Copyright 2009-2013, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2009-2013, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00"
ใน 5 บรรทัดแรกจะประกอบไปด้วยความคิดเห็น ส่วน 3 บรรทัดถัดไปจะตั้งค่าคุณสมบัติของโปรแกรม MQL5 (ลิขสิทธิ์, ลิงก์, เวอร์ชัน) โดยใช้ #property directives
เมื่อคุณรัน Expert Advisor เหล่านี้จะถูกแสดงในแท็บ "Common"

รูปที่ 1. พารามิเตอร์ทั่วไปของ Moving Average EA
1.2. ไฟล์ที่รวมเข้า
ต่อไปนี้ #include directive จะบอกให้คอมไพเลอร์รวมไฟล์ "Trade.mqh"
ไฟล์นี้เป็นส่วนหนึ่งของ Standard Library ซึ่งมี CTrade class สำหรับเข้าถึงฟังก์ชันการเทรดได้ง่าย
#include <Trade\Trade.mqh>ชื่อไฟล์ที่รวมเข้าแสดงในวงเล็บ "<>;" ดังนั้นเส้นทางจึงถูกตั้งค่าให้สัมพันธ์กับไดเรกทอรี: "terminal_data_folder\Include"
1.3 พารามิเตอร์นำเข้า
ถัดไปคือประเภท, ชื่อ, ค่าปริยายและความคิดเห็น บทบาทของพวกเขาจะเห็นในรูปที่ 2
input double MaximumRisk = 0.02; // ความเสี่ยงสูงสุดเป็นเปอร์เซ็นต์ input double DecreaseFactor = 3; // ปัจจัยการลดลง input int MovingPeriod = 12; // ระยะเวลาของ Moving Average input int MovingShift = 6; // การเลื่อนของ Moving Average
พารามิเตอร์ MaximumRisk และ DecreaseFactor จะถูกใช้สำหรับการจัดการเงิน, MovingPeriod และ MovingShift จะตั้งค่าช่วงและการเลื่อนของ Moving Average ตัวชี้วัดทางเทคนิคที่จะใช้ในการตรวจสอบเงื่อนไขการเทรด
ข้อความในความคิดเห็นในบรรทัดพารามิเตอร์นำเข้า พร้อมค่าปริยายจะแสดงในแท็บ "Options" แทนชื่อของพารามิเตอร์นำเข้า:

รูปที่ 2. พารามิเตอร์นำเข้าสำหรับ Moving Average EA
1.4. ตัวแปรทั่วโลก
จากนั้นประกาศตัวแปรทั่วโลก ExtHandle เพื่อใช้เก็บข้อมูลของ Moving Average indicator
//--- int ExtHandle=0;
ในส่วนนี้จะมีฟังก์ชัน 6 ตัว โดยแต่ละตัวจะมีวัตถุประสงค์ที่อธิบายไว้ในความคิดเห็นก่อนร่างฟังก์ชัน:
- TradeSizeOptimized() - คำนวณขนาดล็อตที่เหมาะสม;
- CheckForOpen() - ตรวจสอบเงื่อนไขเปิดตำแหน่ง;
- CheckForClose() - ตรวจสอบเงื่อนไขปิดตำแหน่ง;
- OnInit() - ฟังก์ชันเริ่มต้น Expert;
- OnTick() - ฟังก์ชันการประมวลผลแต่ละเทียน;
- OnDeinit() - ฟังก์ชันการยกเลิกการใช้งาน Expert;
ฟังก์ชันสุดท้ายทั้งสามเป็น ฟังก์ชันจัดการเหตุการณ์; ฟังก์ชันบริการทั้งสามจะถูกเรียกในโค้ดของพวกมัน
2. ฟังก์ชันจัดการเหตุการณ์
2.1. ฟังก์ชันเริ่มต้น OnInit()
ฟังก์ชัน OnInit() จะถูกเรียกเพียงครั้งเดียวเมื่อเริ่ม Expert Advisor เป็นครั้งแรก โดยทั่วไปใน OnInit() จะเตรียม EA ให้พร้อมทำงาน: ตรวจสอบพารามิเตอร์นำเข้า, ตั้งค่าตัวชี้วัดและพารามิเตอร์, ฯลฯ ในกรณีที่เกิดข้อผิดพลาดร้ายแรงเมื่อการทำงานต่อไปไม่มีความหมาย ฟังก์ชันจะออกด้วยรหัสคืนค่า INIT_FAILED
//+------------------------------------------------------------------+ //| ฟังก์ชันเริ่มต้น Expert | //+------------------------------------------------------------------+ int OnInit(void) { //--- ExtHandle=iMA(_Symbol,_Period,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE); if(ExtHandle==INVALID_HANDLE) { printf("เกิดข้อผิดพลาดในการสร้าง MA indicator"); return(INIT_FAILED); } //--- return(INIT_SUCCEEDED); }
เนื่องจากการเทรด EA อิงจากตัวชี้วัด Moving Average โดยการเรียก iMA() ตัวชี้วัด Moving Average จะถูกสร้างและจับมือได้ในตัวแปรทั่วโลก ExtHandle
ในกรณีที่เกิดข้อผิดพลาด, OnInit() จะถูกออกด้วยรหัสคืนค่า INIT_FAILED - เป็นวิธีที่ถูกต้องในการสิ้นสุดการทำงานของ EA/indicator ในกรณีที่การเริ่มต้นไม่สำเร็จ
2.2. ฟังก์ชัน OnTick()
ฟังก์ชัน OnTick() จะถูกเรียกทุกครั้งที่ได้รับราคาข้อเสนอใหม่สำหรับสัญลักษณ์ของกราฟที่ EA ทำงานอยู่
//+------------------------------------------------------------------+ //| ฟังก์ชันการประมวลผลแต่ละเทียน | //+------------------------------------------------------------------+ void OnTick(void) { //--- if(PositionSelect(_Symbol)) CheckForClose(); else CheckForOpen(); //--- }
ฟังก์ชัน PositionSelect() ใช้สำหรับกำหนดว่ามีตำแหน่งเปิดอยู่สำหรับสัญลักษณ์ปัจจุบันหรือไม่
หากมีตำแหน่งเปิดอยู่ ฟังก์ชัน CheckForClose() จะถูกเรียกซึ่งวิเคราะห์สถานะปัจจุบันของตลาดและปิดตำแหน่งที่เปิดอยู่ มิฉะนั้นจะเรียก CheckForOpen() ซึ่งจะตรวจสอบเงื่อนไขการเข้าตลาดและเปิดตำแหน่งใหม่หากมีเงื่อนไขเกิดขึ้น
2.3. ฟังก์ชัน OnDeInit()
ฟังก์ชัน OnDeInit() จะถูกเรียกเมื่อ EA ถูกลบออกจากกราฟ หากโปรแกรมวางวัตถุกราฟิกระหว่างการทำงาน สามารถลบได้จากกราฟ
//+------------------------------------------------------------------+ //| ฟังก์ชันยกเลิกการใช้งาน Expert | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { } //+------------------------------------------------------------------+
ในกรณีนี้ไม่มีการดำเนินการใด ๆ ในระหว่างการยกเลิกการใช้งาน Expert Advisor
3. ฟังก์ชันบริการ
3.1. ฟังก์ชัน TradeSizeOptimized()
ฟังก์ชันนี้คำนวณและคืนค่าขนาดล็อตที่เหมาะสมสำหรับการเปิดตำแหน่งด้วยระดับความเสี่ยงที่กำหนดและผลการซื้อขาย
//+------------------------------------------------------------------+ //| คำนวณขนาดล็อตที่เหมาะสม | //+------------------------------------------------------------------+ double TradeSizeOptimized(void) { double price=0.0; double margin=0.0; //--- คำนวณขนาดล็อต if(!SymbolInfoDouble(_Symbol,SYMBOL_ASK,price)) return(0.0); if(!OrderCalcMargin(ORDER_TYPE_BUY,_Symbol,1.0,price,margin)) return(0.0); if(margin<=0.0) return(0.0); double lot=NormalizeDouble(AccountInfoDouble(ACCOUNT_FREEMARGIN)*MaximumRisk/margin,2); //--- คำนวณความยาวของชุดการซื้อขายที่ขาดทุนติดต่อกัน if(DecreaseFactor>0) { //--- ขอประวัติการเทรดทั้งหมด HistorySelect(0,TimeCurrent()); //-- int orders=HistoryDealsTotal(); // จำนวนดีลทั้งหมด int losses=0 // จำนวนดีลที่ขาดทุนในชุด for(int i=orders-1;i>=0;i--) { ulong ticket=HistoryDealGetTicket(i); if(ticket==0) { Print("HistoryDealGetTicket ล้มเหลว ไม่มีประวัติการเทรด"); break; } //--- ตรวจสอบสัญลักษณ์ของดีล if(HistoryDealGetString(ticket,DEAL_SYMBOL)!=_Symbol) continue; //--- ตรวจสอบกำไร double profit=HistoryDealGetDouble(ticket,DEAL_PROFIT); if(profit>0.0) break; if(profit<0.0) losses++; } //--- if(losses>1) lot=NormalizeDouble(lot-lot*losses/DecreaseFactor,1); } //--- ปรับขนาดล็อตให้ถูกต้องและตรวจสอบค่าที่อนุญาตของปริมาณการซื้อขาย double stepvol=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP); lot=stepvol*NormalizeDouble(lot/stepvol,0); double minvol=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN); if(lot<minvol) lot=minvol; double maxvol=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX); if(lot>maxvol) lot=maxvol; //--- คืนค่าขนาดปริมาณการซื้อขาย return(lot); }
ฟังก์ชัน SymbolInfoDouble() ใช้สำหรับตรวจสอบความพร้อมของราคาสำหรับสัญลักษณ์ปัจจุบัน ถัดไปฟังก์ชัน OrderCalcMargin() จะใช้สำหรับการขอจำนวนมาร์จินที่ต้องการในการวางคำสั่ง (ในกรณีนี้คำสั่งซื้อ) ขนาดล็อตเริ่มต้นจะถูกกำหนดจากมูลค่าของมาร์จินที่ต้องการสำหรับการวางคำสั่ง, มาร์จินฟรีของบัญชี (AccountInfoDouble(ACCOUNT_FREEMARGIN)) และค่าความเสี่ยงสูงสุดที่อนุญาตซึ่งระบุในพารามิเตอร์นำเข้า MaximumRisk
หากค่าของพารามิเตอร์นำเข้า DecreaseFactor เป็นบวก จะมีการวิเคราะห์ดีลในประวัติศาสตร์และขนาดล็อตจะถูกปรับตามข้อมูลเกี่ยวกับชุดการซื้อขายที่ขาดทุนสูงสุด: ขนาดล็อตเริ่มต้นจะถูกคูณด้วยขนาด (1-losses/DecreaseFactor)
จากนั้นปริมาณการซื้อขายจะ "ปัด" ไปยังค่าที่เป็นไปตามจำนวนขั้นต่ำที่อนุญาต (stepvol) สำหรับสัญลักษณ์ปัจจุบัน นอกจากนี้ยังมีการขอค่าต่ำสุด (minvol) และค่ามากที่สุด (maxvol) ของปริมาณการซื้อขาย และหากค่าล็อตออกนอกขอบเขตที่อนุญาต จะต้องปรับให้เหมาะสม โดยผลสุดท้ายจะคืนค่าขนาดปริมาณการซื้อขายที่คำนวณ
3.2. ฟังก์ชัน CheckForOpen()
CheckForOpen() ใช้ในการตรวจสอบเงื่อนไขการเปิดตำแหน่งและเปิดมันเมื่อมีเงื่อนไขการเทรดเกิดขึ้น (ในกรณีนี้เมื่อราคาข้าม Moving Average)
//+------------------------------------------------------------------+ //| ตรวจสอบเงื่อนไขเปิดตำแหน่ง | //+------------------------------------------------------------------+ void CheckForOpen(void) { MqlRates rt[2]; //--- คัดลอกค่าราคา if(CopyRates(_Symbol,_Period,0,2,rt)!=2) { Print("CopyRates ของ ",_Symbol," ล้มเหลว ไม่มีประวัติ"); return; } //--- เทรดเฉพาะในแท็บแรกของแท่งใหม่ if(rt[1].tick_volume>1) return; //--- รับค่าปัจจุบันของ Moving Average indicator double ma[1]; if(CopyBuffer(ExtHandle,0,0,1,ma)!=1) { Print("CopyBuffer จาก iMA ล้มเหลว ไม่มีข้อมูล"); return; } //--- ตรวจสอบสัญญาณ ENUM_ORDER_TYPE signal=WRONG_VALUE; if(rt[0].open>ma[0] && rt[0].close<ma[0]) signal=ORDER_TYPE_SELL // เงื่อนไขขาย else { if(rt[0].open<ma[0] && rt[0].close>ma[0]) signal=ORDER_TYPE_BUY // เงื่อนไขซื้อ } //--- ตรวจสอบเพิ่มเติม if(signal!=WRONG_VALUE) if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) if(Bars(_Symbol,_Period)>100) { CTrade trade; trade.PositionOpen(_Symbol,signal,TradeSizeOptimized(), SymbolInfoDouble(_Symbol,signal==ORDER_TYPE_SELL ? SYMBOL_BID:SYMBOL_ASK), 0,0); } //--- }
เมื่อทำการเทรดโดยใช้ Moving Average จะต้องตรวจสอบว่าราคาได้ข้าม Moving Average หรือไม่ โดยใช้ฟังก์ชัน CopyRates() จะคัดลอกค่าราคาสองค่าลงในอาร์เรย์ของโครงสร้าง rt[], rt[1] จะสอดคล้องกับแท่งปัจจุบัน, rt[0] - แท่งที่เสร็จสมบูรณ์
การเริ่มต้นแท่งใหม่จะตรวจสอบจากปริมาณการติ๊กของแท่งปัจจุบันว่าเท่ากับ 1 หรือไม่ ถ้าเท่ากับ 1 ก็แสดงว่าแท่งใหม่ได้เริ่มต้นขึ้น ควรทราบว่าวิธีการนี้อาจล้มเหลวในบางกรณี (เมื่อราคามาเป็นกลุ่ม) ดังนั้นการเริ่มต้นการสร้างแท่งใหม่จะต้องบันทึกและเปรียบเทียบเวลาของราคาปัจจุบัน (ดู IsNewBar)
ค่าปัจจุบันของ Moving Average indicator จะถูกเรียกโดยใช้ฟังก์ชัน CopyBuffer() และบันทึกในอาร์เรย์ ma[] ที่มีค่าเพียงค่าเดียว จากนั้นโปรแกรมจะตรวจสอบว่าราคาได้ข้าม Moving Average หรือไม่และทำการตรวจสอบเพิ่มเติม (หากการเทรดโดยใช้ EA เป็นไปได้และมีแท่งในประวัติ) หากสำเร็จจะเปิดตำแหน่งที่เหมาะสมสำหรับสัญลักษณ์โดยการเรียก PositionOpen() วิธีการของวัตถุการค้า (อินสแตนซ์ของ CTrade)
ราคาการเปิดตำแหน่งจะถูกตั้งค่าด้วยฟังก์ชัน SymbolInfoDouble() ซึ่งคืนค่าราคาประมูลหรือราคาเสนอขึ้นอยู่กับค่าของตัวแปร signal ขนาดของตำแหน่งจะถูกกำหนดโดยเรียก TradeSizeOptimized() ที่ได้กล่าวถึงข้างต้น
3.3. ฟังก์ชัน CheckForClose()
CheckForClose() ตรวจสอบเงื่อนไขสำหรับการปิดตำแหน่งและปิดมันหากมีเงื่อนไขในการปิดเกิดขึ้น
//+------------------------------------------------------------------+ //| ตรวจสอบเงื่อนไขปิดตำแหน่ง | //+------------------------------------------------------------------+ void CheckForClose(void) { MqlRates rt[2]; //--- คัดลอกค่าราคา if(CopyRates(_Symbol,_Period,0,2,rt)!=2) { Print("CopyRates ของ ",_Symbol," ล้มเหลว ไม่มีประวัติ"); return; } //--- เทรดเฉพาะในแท็บแรกของแท่งใหม่ if(rt[1].tick_volume>1) return; //--- รับค่าปัจจุบันของ Moving Average indicator double ma[1]; if(CopyBuffer(ExtHandle,0,0,1,ma)!=1) { Print("CopyBuffer จาก iMA ล้มเหลว ไม่มีข้อมูล"); return; } //--- รับประเภทของตำแหน่งที่เลือกก่อนหน้านี้โดยใช้ PositionSelect() bool signal=false; long type=PositionGetInteger(POSITION_TYPE); if(type==(long)POSITION_TYPE_BUY && rt[0].open>ma[0] && rt[0].close<ma[0]) signal=true; if(type==(long)POSITION_TYPE_SELL && rt[0].open<ma[0] && rt[0].close>ma[0true; //--- ตรวจสอบเพิ่มเติม if(signal) if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) if(Bars(_Symbol,_Period)>100) { CTrade trade; trade.PositionClose(_Symbol,3); } //--- }
อัลกอริธึมของฟังก์ชัน CheckForClose() จะคล้ายคลึงกับอัลกอริธึมของ CheckForOpen() ขึ้นอยู่กับทิศทางของตำแหน่งที่เปิดอยู่ จะมีการตรวจสอบเงื่อนไขในการปิด (ราคาข้าม MA ลงไปสำหรับการซื้อ หรือ ขึ้นไปสำหรับการขาย) ตำแหน่งที่เปิดอยู่จะถูกปิดโดยการเรียก PositionClose() วิธีการของวัตถุการค้า (อินสแตนซ์ของ CTrade)
4. การทดสอบย้อนหลัง
ค่าที่ดีที่สุดของพารามิเตอร์สามารถค้นหาได้โดยใช้ Strategy Tester ของ MetaTrader 5 terminal
ตัวอย่างเช่น เมื่อทำการปรับแต่งพารามิเตอร์ MovingPeriod ในช่วงวันที่ 2012.01.01-2013.08.01 ผลลัพธ์ที่ดีที่สุดจะได้แก่ MovingPeriod=45:

ผลการทดสอบย้อนหลังของ Moving Average Expert Advisor
บทสรุป:
Moving Average Expert Advisor ที่รวมอยู่ในแพคเกจมาตรฐานของ MetaTrader 5 เป็นตัวอย่างของการใช้ ตัวชี้วัดทางเทคนิค, ฟังก์ชัน ประวัติการเทรด และ คลาสการเทรด ของ Standard Library นอกจากนี้ EA ยังมีระบบการจัดการเงินที่อิงจากผลการเทรดอีกด้วย
โพสต์ที่เกี่ยวข้อง
- สร้าง Expert Advisor ด้วย MQL5 Wizard: สัญญาณซื้อขายจาก Morning/Evening Stars + MFI
- สร้างสัญญาณการซื้อขายด้วย MQL5 Wizard: การใช้ EMA และกรองเวลาภายใน
- สร้างสัญญาณการซื้อขายด้วย MQL5 Wizard: เทคนิค Hammer/Hanging Man และ CCI
- สร้าง Expert Advisor ด้วย MQL5 Wizard สำหรับสัญญาณการเทรด Bullish Harami/Bearish Harami และ RSI
- สร้าง EA บน MQL5 Wizard ด้วยสัญญาณการซื้อขายจากแท่งเทียน 3 Black Crows/3 White Soldiers + Stochastic