บทความนี้เป็นส่วนต่อเนื่องของซีรีส์ที่เริ่มต้นไว้ก่อนหน้านี้เกี่ยวกับการพัฒนา Expert Advisor (EA) (ดูฉบับที่ 61 และ 63 ของนิตยสาร ForTraders.org) น่าเสียดายที่งานด้านนี้ต้องหยุดชะงักไปหลายเดือน ทั้งสำหรับผมและผู้อ่านจำนวนมาก ด้วยเหตุผลหลายประการ แต่ที่สำคัญคือ ผมได้กลับมาสานต่องานนี้อีกครั้ง
สรุปผลจากการทดสอบที่ผ่านมา
ช่วงทดสอบสั้นๆ แสดงให้เห็นว่าแนวคิดนี้มีศักยภาพ แม้กำไรจะไม่สูง แต่สิ่งสำคัญกว่าคือไม่เกิดขาดทุน บทความนี้จะเสนอการปรับปรุงเพื่อเพิ่มประสิทธิภาพโปรแกรม โดยเริ่มจากชี้แจงจุดที่ไม่ชัดเจนในโค้ด และแก้ไขข้อผิดพลาดเล็กน้อย
ข้อผิดพลาดและจุดที่ไม่ชัดเจนในโค้ดของ EA

บรรทัดที่ 18 ของโค้ด:
Pause_Of_Trade_Try = 2; // ระยะเวลาพักระหว่างการพยายามเปิดออเดอร์ (หน่วย: วินาที)
ที่จริงแล้ว ค่าพารามิเตอร์นี้ไม่ได้ระบุเป็นวินาที แต่เป็น หนึ่งในสิบของวินาที ดังนั้นค่า 2 หมายถึง 0.2 วินาที
บรรทัดที่ 118–119 (และโค้ดลักษณะเดียวกันที่ปรากฏต่อไปในโปรแกรม):
return(OrderTicket());
break; // ออกจากลูป
อาจดูไม่ชัดเจนว่าทำไมจึงต้องใส่คำสั่ง break เนื่องจากเมื่อเรียก return แล้ว เราจะออกจากฟังก์ชันทันที ดังนั้นคำสั่ง break จึงไม่ถูกดำเนินการเลย นี่เป็นเพียงนิสัยส่วนตัวของผมที่มักใส่ break ทุกครั้งที่ต้องออกจากลูป อย่างไรก็ตาม ผมเคยพบภาษาโปรแกรมบางภาษาที่ลูปยังคงทำงานต่อไปหลังจาก return หากไม่มี break นอกจากนี้ นักเขียนโปรแกรมทุกคนทราบดีว่าในโครงสร้าง switch ของภาษา C จะเกิดกรณี “fall-through” (กระโดดไปยังคำสั่งถัดไปโดยอัตโนมัติ) หากไม่ใส่ break ดังนั้น การใส่ break ในที่นี้จึง ไม่จำเป็น
บรรทัดที่ 169 (และเนื้อหาของฟังก์ชัน sl):
double sl(int sl_value, int type, string symb=”NONE”, int rmode=1) {
อาจไม่ชัดเจนว่าพารามิเตอร์ rmode ใช้ทำอะไร และทำไมการคำนวณราคา Stop Loss (SL) จึงขึ้นกับค่านี้ โดยหาก rmode=1 จะคำนวณ SL ของออเดอร์ Buy จากราคา Ask และออเดอร์ Sell จากราคา Bid แต่ถ้า rmode=2 จะคำนวณ SL ของออเดอร์ Buy จากราคา Bid และออเดอร์ Sell จากราคา Ask (ดูเพิ่มเติมเรื่อง ราคา Bid)
ประเด็นนี้เป็นที่ถกเถียงกันว่า ควรคำนวณราคา Stop Loss จากราคาใด ฝ่ายหนึ่งเชื่อว่าเมื่อ触及 SL แล้ว ควรได้ขาดทุนตามจำนวนจุดที่ตั้งไว้พอดี (เช่น ตั้ง SL=100 ก็ขาดทุน 100 จุดพอดี) อีกฝ่ายเชื่อว่าควรรวมสเปรดเข้าไปด้วย เพื่อให้ตั้ง SL ได้ตามระยะที่ต้องการ โดยเฉพาะเมื่อสเปรดกว้างจนตั้ง SL ตามระยะที่ระบุไม่ได้ ซึ่งกรณีนี้ SL จะเท่ากับ 100 + <สเปรด> จุด
ผมส่วนใหญ่ใช้วิธีแรก และฟังก์ชันนี้ก็ออกแบบมาให้ทำงานตามวิธีนั้นเมื่อ rmode = 1 (ค่าเริ่มต้น) การเพิ่มพารามิเตอร์นี้แสดงให้เห็นว่าผมรับรู้ทั้งสองแนวทาง และไม่ได้บังคับให้ใช้วิธีใดวิธีหนึ่งโดยเฉพาะ (หากคุณสนับสนุนวิธีที่สอง ก็เพียงแค่เปลี่ยนค่า 1 เป็น 2 ในโค้ด) อย่างไรก็ตาม ผมเองใช้วิธีแรกในการเทรดจริง

ส่วนอื่น ๆ ของโค้ดดูมีตรรกะชัดเจนและเข้าใจง่าย แต่ในอนาคตผมอาจอธิบายเพิ่มเติมตามความจำเป็น
การปรับปรุงใหม่ใน EA
แม้จะได้ผลลัพธ์เบื้องต้นในช่วงเวลาสั้นๆ แต่ยังเร็วเกินไปที่จะกล่าวถึงการเปลี่ยนหรือปรับแต่งอัลกอริทึมค้นหาสัญญาณเทรด จึงขอเพิ่มฟังก์ชันใหม่สองรายการ ฟังก์ชันแรกคือ Trailing Stop ซึ่งผมจะนำฟังก์ชันมาตรฐานที่ใช้ใน EA อื่นๆ มาปรับเล็กน้อย เพื่อให้สามารถใช้งานพร้อมกันได้กับหลายคู่เงิน ฟังก์ชันนี้มีพารามิเตอร์เข้าดังนี้:
//+———————————————————————-+
//+ ฟังก์ชัน Trailing Stop +
extern bool UseTrailing = true; // เปิด/ปิดการใช้งาน T-SL
extern int TrailingStop = 50; // ระยะ Trailing Stop คงที่
extern int TrailingStep = 1; // ขั้นบันไดของ Trailing Stop
//+ ฟังก์ชัน Trailing Stop +
//+———————————————————————-+
โค้ดสำหรับทำงานกับคู่เงินปัจจุบันมีดังนี้:
//+——————————————————————————————————————-+
//| ฟังก์ชัน Trailing Stop Loss |
void T_SL() {
if(!UseTrailing) return;
int i = 0;
for(i=0; i<OrdersTotal(); i++) {
if(!(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))) continue;
if(OrderSymbol() != Symbol() || OrderMagicNumber()!=Magic_Number) continue;
if(OrderType()==OP_BUY) {
if(NormalizeDouble(Bid-OrderOpenPrice(),Digits)>NormalizeDouble(TrailingStop*Point,Digits)) {
if(NormalizeDouble(OrderStopLoss(),Digits)<NormalizeDouble(Bid-(TrailingStop+TrailingStep-1)*Point,Digits))
OrderModify(OrderTicket(), OrderOpenPrice(), NormalizeDouble(Bid-TrailingStop*Point,Digits), OrderTakeProfit(), 0, CLR_NONE);
} //end if(NormalizeDouble(Bid-OrderOpenPrice(),Digits)>NormalizeDouble(TrailingStop*Point,Digits))
} //end if(OrderType()==OP_BUY)
if(OrderType()==OP_SELL) {
if(NormalizeDouble(OrderOpenPrice()-Ask,Digits)>NormalizeDouble(TrailingStop*Point,Digits)) {
if(NormalizeDouble(OrderStopLoss(),Digits)>NormalizeDouble(Ask+(TrailingStop+TrailingStep-1)*Point,Digits))
OrderModify(OrderTicket(), OrderOpenPrice(), NormalizeDouble(Ask+TrailingStop*Point,Digits), OrderTakeProfit(), 0, CLR_NONE);
} //end if(NormalizeDouble(OrderOpenPrice()-Ask,Digits)>NormalizeDouble(TrailingStop*Point,Digits))
} //end if(OrderType()==OP_SELL)
} //end for(i=0; i<OrdersTotal(); i++)
} //end void T_SL()
//| ฟังก์ชัน Trailing Stop Loss |
//+——————————————————————————————————————-+
สำหรับ EA นี้ ต้องปรับโค้ดให้จัดการออเดอร์ทั้งหมด (ที่มี Magic Number ตรงกัน) ในทุกคู่เงินได้ ดังนี้:
//+——————————————————————————————————————-+
//| ฟังก์ชัน Trailing Stop Loss |
void T_SL() {
if(!UseTrailing) return;
string symb=””;
int i = 0;
double ask=0.0, bid=0.0, dig=0.0, pp=0.0;
for(i=0; i<OrdersTotal(); i++) {
if(!(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))) continue;
if(OrderMagicNumber()!=Magic_Number) continue;
symb=OrderSymbol();
pp=MarketInfo(symb,MODE_POINT);
dig=MarketInfo(symb,MODE_DIGITS);
ask=MarketInfo(symb,MODE_ASK);
bid=MarketInfo(symb,MODE_BID);
if(OrderType()==OP_BUY) {
if(NormalizeDouble(bid-OrderOpenPrice(),dig)>NormalizeDouble(TrailingStop*pp,dig)) {
if(NormalizeDouble(OrderStopLoss(),dig)<NormalizeDouble(bid-(TrailingStop+TrailingStep-1)*pp,dig))
OrderModify(OrderTicket(), OrderOpenPrice(), NormalizeDouble(bid-TrailingStop*pp,dig), OrderTakeProfit(), 0, CLR_NONE);
} //end if(NormalizeDouble(Bid-OrderOpenPrice(),Digits)>NormalizeDouble(TrailingStop*Point,Digits))
} //end if(OrderType()==OP_BUY)
if(OrderType()==OP_SELL) {
if(NormalizeDouble(OrderOpenPrice()-ask,dig)>NormalizeDouble(TrailingStop*pp,dig)) {
if(NormalizeDouble(OrderStopLoss(),dig)>NormalizeDouble(ask+(TrailingStop+TrailingStep-1)*pp,dig))
OrderModify(OrderTicket(), OrderOpenPrice(), NormalizeDouble(ask+TrailingStop*pp,dig), OrderTakeProfit(), 0, CLR_NONE);
} //end if(NormalizeDouble(OrderOpenPrice()-Ask,Digits)>NormalizeDouble(TrailingStop*Point,Digits))
} //end if(OrderType()==OP_SELL)
} //end for(i=0; i<OrdersTotal(); i++)
} //end void T_SL()
//| ฟังก์ชัน Trailing Stop Loss |
//+——————————————————————————————————————-+
จากนั้น เรียกใช้ฟังก์ชันนี้ภายในฟังก์ชัน start() (ดูในโค้ดฉบับสมบูรณ์)
ฟังก์ชันที่สองซึ่งมีประโยชน์และผมตัดสินใจเพิ่มลงใน EA คือ ส่งเสียงเตือนเมื่อเปิดออเดอร์ เนื่องจากเป็นส่วนเสริมขนาดเล็ก จึงไม่ขออธิบายรายละเอียด สามารถดูโค้ด EA ฉบับเต็มได้ที่ นี่
สรุป
ในเวอร์ชันถัดไปของ EA จะมีฟังก์ชันที่น่าสนใจมากขึ้น โดยมุ่งเน้นการควบคุมความเสี่ยงและการบริหารขนาดสถานะ (Position Sizing) สำหรับขณะนี้ ขอติดตามผลการทดสอบของเวอร์ชันนี้ก่อน