หน้าหลัก > บล็อก > แก้ปัญหา Invalid Stops (10016/130) — SL บน MT5

MT5MQL5ข้อผิดพลาดแก้ปัญหาEA

แก้ปัญหา Invalid Stops (10016/130) — SL บน MT5

เผยแพร่: 2026-07-07เวลาอ่าน: ประมาณ 4 นาที
This article reflects information as of its publish date. EA performance figures (PF, DD, annual return) change with live trading and re-validation — check the latest on the EA pages. See the latest EA results

สารบัญ

  1. Invalid Stops คืออะไร (ความแตกต่างระหว่าง 10016 กับ 130)
  2. ① TRADE_RETCODE_INVALID_STOPS = 10016 (รหัสคืนค่าของ `OrderSend()` บน MT5)
  3. ② ERR_INVALID_STOPS = 130 (`GetLastError()` บน MT4)
  4. วิธีตรวจสอบเบื้องต้นใน 30 วินาที
  5. สาเหตุและวิธีแก้ไข (6 รูปแบบ)
  6. ① SL/TP อยู่ใกล้ราคาปัจจุบันเกินไป (น้อยกว่า Stop Level)
  7. ② ทิศทางของ SL/TP กลับด้าน (สลับ BUY/SELL)
  8. ③ การแก้ไขคำสั่ง/โพซิชันภายในโซน Freeze Level
  9. ④ ความสับสนระหว่างราคากับพอยท์ (ระยะห่าง)
  10. ⑤ บัญชีที่ Execution แบบ Market ไม่ยอมรับ SL/TP ตอนเปิดออร์เดอร์
  11. ⑥ ลักษณะเฉพาะของสัญลักษณ์ (ทองคำและดัชนีมี Stop Level กว้างกว่า)
  12. ความแตกต่างระหว่างโบรกเกอร์ (ข้อควรระวัง)
  13. โค้ดป้องกันข้อผิดพลาดนี้ใน MQL5 (สำหรับผู้พัฒนา EA)
  14. ตรวจสอบและ Clamp ค่า SL/TP ก่อนส่งคำสั่ง
  15. จัดการ retcode 10016 แยกเฉพาะ
  16. ตรวจสอบ Freeze Level ก่อนแก้ไข Trail
  17. ตารางตรวจสอบตามลำดับความสำคัญ
  18. สรุป
  19. คำถามที่พบบ่อย (FAQ)
  20. Q: คำนวณ SL/TP ถูกต้องแล้ว แต่ยังเจอ Invalid stops เพราะอะไร?
  21. Q: 10016 กับ 130 ต่างกันอย่างไร?
  22. Q: ปกติไม่เจอปัญหา แต่พอมีการประกาศข่าวสำคัญกลับเจอ Invalid stops?
  23. Q: ไม่มี SL/TP แล้วส่งคำสั่งผ่าน แต่พอใส่ SL/TP กลับถูกปฏิเสธ?
  24. Q: EA ที่ทำงานปกติบน EURUSD กลับเจอ Invalid stops ซ้ำๆ บนทองคำ?

แก้ปัญหา Invalid Stops (10016/130) แบบครบวงจร

เวลารัน EA แล้วเห็น Invalid stops หรือ OrderSend error 130 ขึ้นในแท็บ Expert หลายคนมักสับสนว่า "ค่า SL ผิดหรือเปล่า? แต่คำนวณถูกแล้วนี่..." ทว่าความจริงแล้ว ข้อผิดพลาดนี้ส่วนใหญ่ไม่ได้เกิดจากการคำนวณ SL/TP ผิด แต่เป็นการละเมิด "กฎระยะห่างขั้นต่ำที่โบรกเกอร์กำหนด" แม้ค่าที่ส่งไปจะถูกต้อง แต่ถ้าอยู่ใกล้ราคาปัจจุบันเกินไป ทิศทางกลับด้าน หรืออยู่ในโซนห้ามแก้ไข ก็จะถูกปฏิเสธทันที

บทความนี้เขียนขึ้นสำหรับทั้งผู้ใช้ EA บน MT5/MT4 และผู้พัฒนา EA ด้วย MQL5 โดยรวบรวมเนื้อหาตั้งแต่ ตัวตนที่แท้จริงของ Invalid stops, สาเหตุทั้ง 6 ข้อ, วิธีตรวจสอบใน 30 วินาที ไปจนถึงการป้องกันถาวรในโค้ด ไว้ในบทความเดียว สำหรับรายการข้อผิดพลาดทั้งหมด สามารถดูได้ที่ คู่มือรวมวิธีแก้ปัญหารหัสข้อผิดพลาด MQL5 / MT5

บทความนี้อ้างอิงจาก MT5 (build 4xxx) ณ เดือนกรกฎาคม 2026 ค่า Stop Level ที่แท้จริงจะแตกต่างกันไปตามโบรกเกอร์และสัญลักษณ์การเทรด


Invalid Stops คืออะไร (ความแตกต่างระหว่าง 10016 กับ 130)

ค่าที่แสดงถึง "Stop ที่ไม่ถูกต้อง" มี 2 แบบตาม MT5 และ MT4 (คนละเจเนอเรชัน) การแยกให้ชัดว่าเกิดขึ้นบนแพลตฟอร์มไหนและในขั้นตอนใดจะช่วยให้หาสาเหตุได้เร็วขึ้นมาก

① TRADE_RETCODE_INVALID_STOPS = 10016 (รหัสคืนค่าของ OrderSend() บน MT5)

ผลลัพธ์ของ OrderSend() ใน MQL5 จะถูกเก็บไว้ใน MqlTradeResult.retcode หาก SL/TP ในคำสั่ง (หรือความสัมพันธ์กับราคาของ pending order) ไม่ตรงตามกฎของเซิร์ฟเวอร์ คำสั่งจะถูกปฏิเสธด้วยรหัส 10016 (TRADE_RETCODE_INVALID_STOPS) ซึ่งเป็น การแจ้งปฏิเสธจากฝั่งเทรดเซิร์ฟเวอร์

ความหมาย: Stop (SL/TP) ในคำสั่งไม่ถูกต้อง (Invalid stops in the request)
ค่าคงที่ : TRADE_RETCODE_INVALID_STOPS
ค่า      : 10016
// ตัวอย่าง output ที่พบได้บ่อยในล็อก
2026.07.07 09:15:32.441 EA_NAME XAUUSD,M5: OrderSend error: retcode=10016 (invalid stops)

② ERR_INVALID_STOPS = 130 (GetLastError() บน MT4)

บนเจเนอเรชัน MT4 (MQL4) เมื่อ OrderSend() / OrderModify() ล้มเหลว ค่า GetLastError() จะคืนค่า 130 (ERR_INVALID_STOPS) ข้อความ OrderSend error 130 ที่เห็นในแท็บ Expert ก็คือค่านี้เอง หากใช้งาน EA เวอร์ชัน MT4 ควบคู่กันก็จะเจอค่านี้

ความหมาย: Stop ไม่ถูกต้อง (invalid stops)
ค่าคงที่ : ERR_INVALID_STOPS
ค่า      : 130

สรุปการใช้งานจริง:

แพลตฟอร์มแหล่งที่มาค่าเกิดขึ้นในกรณีใด
MT5MqlTradeResult.retcode10016 (TRADE_RETCODE_INVALID_STOPS)OrderSend / PositionModify ถูกเซิร์ฟเวอร์ปฏิเสธ
MT5CTrade.ResultRetcode()10016การส่งคำสั่ง/แก้ไขผ่าน CTrade ถูกปฏิเสธ
MT4GetLastError()130 (ERR_INVALID_STOPS)หลังจาก OrderSend / OrderModify ล้มเหลว

แม้ตัวเลขจะต่างกัน แต่ ความหมายและสาเหตุแทบจะเหมือนกัน วิธีแก้ไขจึงใช้ร่วมกันได้ นอกจากนี้ ค่าที่มักสับสนกันบ่อยบน MT5 คือ 10015 (TRADE_RETCODE_INVALID_PRICE) ซึ่งหมายถึง "ราคาคำสั่งเองไม่ถูกต้อง" ซึ่งเป็นคนละกรณี ส่วน 10016 เป็นปัญหาที่ "ตำแหน่งของ SL/TP (Stop)" เท่านั้น


วิธีตรวจสอบเบื้องต้นใน 30 วินาที

เปิดเมนู "Market Watch → คลิกขวาที่สัญลักษณ์ → Specification" บน MT5 แล้วดู 2 รายการนี้

Stops level  : ระยะห่างขั้นต่ำ (หน่วยพอยท์) ที่ SL/TP ต้องอยู่ห่างจากราคาปัจจุบัน
Freeze level : ระยะห่าง (หน่วยพอยท์) ที่ห้ามแก้ไข/ยกเลิกคำสั่งที่ใกล้จะทำงาน

จากนั้นตรวจสอบล็อกในจังหวะที่เกิดข้อผิดพลาด โดยเทียบ ค่า SL/TP ที่พยายามส่งกับ Bid/Ask ปัจจุบัน

  • SL หรือ TP อยู่ห่างจากราคาปัจจุบันน้อยกว่า Stop Level → เกือบแน่นอนว่าเป็นสาเหตุนี้ (สาเหตุ ①)
  • เป็น BUY แต่ SL อยู่สูงกว่า Bid หรือ TP อยู่ต่ำกว่า Bid (ถ้าเป็น SELL ก็กลับกัน) → ทิศทางสลับกัน (สาเหตุ ②)
  • การแก้ไขโพซิชันที่ถืออยู่ล้มเหลวเท่านั้น → อาจเป็น Freeze Level หรือข้อกำหนดตั้ง SL/TP ภายหลัง (สาเหตุ ③ / ⑤)
  • ค่า SL เป็นตัวเลขที่เห็นชัดว่าไม่ใช่ราคา เช่น "50" → ความสับสนระหว่างราคากับพอยท์ (สาเหตุ ④)

หากต้องการตรวจสอบผ่านโค้ด เพียงบรรทัดเดียว

Print("StopsLevel=", SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL),
      " FreezeLevel=", SymbolInfoInteger(_Symbol, SYMBOL_TRADE_FREEZE_LEVEL));

สาเหตุและวิธีแก้ไข (6 รูปแบบ)

① SL/TP อยู่ใกล้ราคาปัจจุบันเกินไป (น้อยกว่า Stop Level)

อาการ: พบบ่อยใน EA แนวสแกลป์ที่วาง SL แบบชิดราคา หรือ EA ที่มี Trail แคบ แม้แต่การเทรดด้วยมือ หากพยายามวาง SL "ใกล้ราคามาก" ปุ่มส่งคำสั่งอาจกดไม่ได้หรือถูกปฏิเสธ

สาเหตุ: โบรกเกอร์กำหนดค่า SYMBOL_TRADE_STOPS_LEVEL (ระยะ Stop ขั้นต่ำ หน่วยพอยท์) แยกตามแต่ละสัญลักษณ์ และ SL/TP หรือราคา pending order ที่อยู่ห่างจากราคาปัจจุบันน้อยกว่าระยะนี้จะถูกเซิร์ฟเวอร์ปฏิเสธทั้งหมด ราคาอ้างอิงที่ใช้ตรวจสอบคือ Bid สำหรับ SL/TP ของโพซิชัน BUY และ Ask สำหรับ SELL เมื่อสเปรดขยายตัว ระยะห่างระหว่าง Bid กับ Ask จะกว้างขึ้น ทำให้บางครั้งระยะที่ผ่านได้ในภาวะปกติอาจถูกปฏิเสธทันทีในช่วงข่าวสำคัญหรือช่วงเช้าตรู่

วิธีแก้ไข:

  1. ตรวจสอบ Stop Level จากหน้าต่าง Specification แล้วปรับ SL/TP หรือระยะ Trail ของ EA ให้กว้างกว่านั้น
  2. ทำการ Clamp ค่าฝั่ง EA ก่อนส่งคำสั่ง (ดูโค้ดด้านล่าง)
  3. หากจำเป็นต้องใช้ SL แบบชิดราคาจริงๆ ควรพิจารณาโบรกเกอร์หรือประเภทบัญชีที่มี Stop Level ต่ำ

② ทิศทางของ SL/TP กลับด้าน (สลับ BUY/SELL)

อาการ: เกิด Invalid stops ทุกครั้งเมื่อเทรดในทิศทางใดทิศทางหนึ่ง (ซื้ออย่างเดียว หรือขายอย่างเดียว) เป็นรูปแบบที่พบบ่อยที่สุดในการทดสอบ EA ที่เขียนเองครั้งแรก

สาเหตุ: กฎเรียบง่ายคือ SL ของ BUY ต้องต่ำกว่าราคาปัจจุบัน (Bid) และ TP ต้องสูงกว่า ส่วน SL ของ SELL ต้องสูงกว่าราคาปัจจุบัน (Ask) และ TP ต้องต่ำกว่า ข้อผิดพลาดทั่วไปคือการคัดลอกสูตรคำนวณของ BUY มาใช้กับ SELL แล้วลืมแก้เครื่องหมาย หรือสลับ price - sl กับ price + sl ผิด ซึ่งเซิร์ฟเวอร์จะคืนค่า 10016/130 ทันที

วิธีแก้ไข:

  1. เมื่อเกิดข้อผิดพลาด ให้ใช้ PrintFormat("type=%s sl=%.5f tp=%.5f bid=%.5f ask=%.5f", ...) เพื่อแสดงค่าจริงและตรวจสอบทิศทางด้วยตา
  2. รวมการคำนวณ SL/TP ของ BUY/SELL ไว้ในฟังก์ชันเดียวกัน โดยแยกเครื่องหมายไว้จุดเดียว (ไม่ให้เกิดการแยกโค้ดด้วยการคัดลอกวาง)

③ การแก้ไขคำสั่ง/โพซิชันภายในโซน Freeze Level

อาการ: คำสั่งใหม่ไม่มีปัญหา แต่ การแก้ไข/ยกเลิกคำสั่งที่ใกล้จะถึง TP, ใกล้ถึง SL หรือใกล้ถึงจุดทำงานของ pending order เท่านั้นที่ถูกปฏิเสธ

สาเหตุ: สำหรับสัญลักษณ์ที่มีการตั้งค่า SYMBOL_TRADE_FREEZE_LEVEL เมื่อ ราคาปัจจุบันเข้าใกล้ราคาที่จะทำงาน (ราคาทริกเกอร์ของ TP/SL/pending) ในระยะหนึ่ง การแก้ไข/ยกเลิกคำสั่งนั้นจะถูกล็อก นี่เป็นข้อกำหนดของเซิร์ฟเวอร์เพื่อป้องกันความขัดแย้งระหว่างกระบวนการ Execution กับคำขอแก้ไข ปัญหานี้มักปรากฏในรูปแบบที่ Trail ของ EA "พยายามอัปเดตอีกขั้นตอนตอนใกล้ถึง TP แต่ถูกปฏิเสธ"

วิธีแก้ไข:

  1. ก่อนแก้ไขคำสั่ง ให้อ่านค่า SYMBOL_TRADE_FREEZE_LEVEL และหากระยะห่างถึงราคาทริกเกอร์น้อยกว่าหรือเท่ากับ Freeze Level ให้ ข้ามการแก้ไขในรอบนั้น
  2. ขยายช่วงเวลา/ระยะการอัปเดต Trail เพื่อลดคำขอแก้ไขที่ไม่จำเป็นในช่วงใกล้ทำงาน
  3. แม้ถูกปฏิเสธก็ไม่ร้ายแรง (ใกล้ทำงาน = กำลังจะ Execute อยู่แล้ว) จึงออกแบบให้ดักข้อผิดพลาดแล้วบันทึกล็อกไว้เฉยๆ ได้

④ ความสับสนระหว่างราคากับพอยท์ (ระยะห่าง)

อาการ: ค่า SL ถูกใส่เป็นตัวเลข "ที่ตั้งใจให้เป็นระยะทาง" โดยตรง เช่น 50 หรือ 0.0050 ค่า sl=50.00000 ที่ปรากฏในล็อกเห็นได้ชัดว่าไม่ใช่ราคา

สาเหตุ: ค่าที่ใส่ใน MqlTradeRequest.sl / .tp ต้องเป็น ราคาสัมบูรณ์ (ไม่ใช่ "ต่ำกว่าราคาเข้า 50 พอยท์") EA ที่บริหารด้วยระยะทางต้องแปลงเป็น entry ± distance * _Point ก่อนส่งค่า ในทางกลับกัน หากส่งระยะทางไปในตำแหน่งที่ควรเป็นราคาสัมบูรณ์ตามความเคยชินของฟังก์ชันยุค MT4 ค่านั้นจะไม่สามารถเป็นราคาที่ถูกต้องได้ และกลายเป็น 10016/130

นอกจากนี้ ความสับสนระหว่าง pips กับพอยท์ ก็เป็นอีกกรณีคลาสสิก โบรกเกอร์ที่แสดงราคา 5 หลัก (เช่น USDJPY แสดง 3 หลัก, EURUSD แสดง 5 หลัก) จะมี 1 pip = 10 พอยท์ หากไม่แน่ใจว่า "SL 50" หมายถึง pips หรือพอยท์ ระยะทางอาจคลาดเคลื่อนถึง 10 เท่า จนต่ำกว่า Stop Level และถูกปฏิเสธ

วิธีแก้ไข:

  1. คำนวณ SL/TP ด้วยรูปแบบ NormalizeDouble(price ± dist * _Point, _Digits) เสมอ
  2. ระบุหน่วยของพารามิเตอร์อินพุต (pips / points) ไว้ในคอมเมนต์ให้ชัดเจน และรวมการแปลงหน่วย _Point ไว้ในจุดเดียวภายในโค้ด

⑤ บัญชีที่ Execution แบบ Market ไม่ยอมรับ SL/TP ตอนเปิดออร์เดอร์

อาการ: คำสั่งใหม่จะสำเร็จก็ต่อเมื่อไม่มี SL/TP แต่คำสั่งที่มี SL/TP แนบไปด้วยจะเกิด Invalid stops โดยเฉพาะบัญชีประเภท ECN/Market Execution

สาเหตุ: ในระบบ Market Execution "ราคาตอนส่งคำสั่ง" กับ "ราคาที่ Execute จริง" อาจไม่ตรงกัน ทำให้ เซิร์ฟเวอร์บางแห่งไม่รับ SL/TP ที่แนบไปในคำสั่งเปิดใหม่ และกำหนดให้ตั้งค่าผ่านการแก้ไขโพซิชันภายหลังจาก Execute แล้วเท่านั้น (เป็นข้อกำหนดคลาสสิกที่ทำให้บัญชี ECN ยุค MT4 เจอ error 130 บ่อยครั้ง และยังมีเซิร์ฟเวอร์บน MT5 บางแห่งที่มีพฤติกรรมเดียวกันหลงเหลืออยู่)

วิธีแก้ไข:

  1. เปลี่ยนมาใช้รูปแบบ 2 ขั้นตอน คือ ส่งคำสั่งโดยไม่มี SL/TP ก่อน → หลังยืนยัน Execute แล้วจึงใช้ PositionModify() (หรือ trade.PositionModify() ถ้าใช้ CTrade) เพื่อตั้ง SL/TP
  2. วิธีนี้จะเกิดช่วงเวลาที่ "ส่งคำสั่งสำเร็จแต่ตั้ง SL ไม่สำเร็จ" ดังนั้นต้องมีระบบป้องกัน ลองซ้ำจนกว่าตั้ง SL สำเร็จ และหากล้มเหลวครบจำนวนครั้งที่กำหนดให้ปิดออร์เดอร์ทันที (การปล่อยไว้โดยไม่มี SL คือผลลัพธ์ที่แย่ที่สุด)
  3. สามารถตรวจสอบรูปแบบ Execution ได้ด้วย SymbolInfoInteger(_Symbol, SYMBOL_TRADE_EXEMODE)

⑥ ลักษณะเฉพาะของสัญลักษณ์ (ทองคำและดัชนีมี Stop Level กว้างกว่า)

อาการ: EA ตัวเดียวกันทำงานปกติบน EURUSD แต่พอนำไปใช้กับ XAUUSD (ทองคำ) หรือ CFD ดัชนีหุ้น กลับเกิด Invalid stops ซ้ำๆ

สาเหตุ: Stop Level ถูกตั้งค่าแยกกันในแต่ละสัญลักษณ์ และโดยทั่วไป ทองคำ ดัชนี และสกุลเงินแปลกใหม่ (Exotic) มักมี Stop Level กว้างกว่าคู่เงินหลัก หากนำระยะ SL/Trail ที่ปรับไว้แคบสำหรับ Forex คู่หลักมาใช้ตรงๆ จะไม่ถึงระยะขั้นต่ำของสัญลักษณ์นั้นและถูกปฏิเสธ นอกจากนี้จำนวนหลักทศนิยมก็แตกต่างกันตามสัญลักษณ์ (เช่น ทองคำมี 2-3 หลัก) ดังนั้นโค้ดที่กำหนดค่า _Digits ตายตัวก็จะพังเช่นกัน

วิธีแก้ไข:

  1. เมื่อเปลี่ยนสัญลักษณ์ ให้ตรวจสอบ Stop Level และจำนวนหลักจากหน้าต่าง Specification ทุกครั้ง
  2. กำหนดระยะ SL/TP ด้วย เกณฑ์ความผันผวน เช่น ATR แทนการใช้พอยท์ตายตัว จะช่วยลดปัญหาเมื่อใช้งานข้ามสัญลักษณ์
  3. ในโค้ดควรดึงค่า _Point / _Digits / SYMBOL_TRADE_STOPS_LEVEL แบบไดนามิกเสมอ (ห้าม Hardcode)

ความแตกต่างระหว่างโบรกเกอร์ (ข้อควรระวัง)

Stop Level และ Freeze Level แตกต่างกันโดยสิ้นเชิงตามแต่ละคู่ของโบรกเกอร์และสัญลักษณ์ แม้จะใช้ EA และการตั้งค่าเดียวกัน แต่ข้อผิดพลาดที่ไม่เคยเกิดขึ้นเลยกับโบรกเกอร์ A อาจเกิดขึ้นทุกวันกับโบรกเกอร์ B ก็เป็นเรื่องปกติ

สิ่งที่ต้องระวังเพิ่มเติมคือ โบรกเกอร์ที่แสดง Stop Level เป็น "0" ค่า 0 มักไม่ได้หมายถึง "ไม่มีข้อจำกัด" แต่หมายถึง "ตัดสินแบบไดนามิก" ในหลายกรณี ทำให้ SL ที่วางใกล้แค่ไหนก็ผ่านได้ในภาวะปกติ แต่ จะถูกปฏิเสธเฉพาะช่วงที่สเปรดขยายตัว เช่น ตอนประกาศข่าวสำคัญหรือช่วงเช้าตรู่ ซึ่งมักเป็นต้นตอของ "Invalid stops ที่นานๆ เกิดที"

การตรวจสอบควรทำกับบัญชีจริงเสมอ

long stops  = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);   // หน่วยพอยท์
long freeze = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_FREEZE_LEVEL);  // หน่วยพอยท์

ตัวเลขที่แท้จริงขึ้นอยู่กับประเภทบัญชีและสเปคของสัญลักษณ์ในแต่ละโบรกเกอร์ บทความนี้จึงไม่ได้ระบุตัวเลขที่ชัดเจน ค่าที่ได้จากการรันโค้ดข้างต้นในบัญชีของคุณเองคือคำตอบที่ถูกต้องเพียงหนึ่งเดียว


โค้ดป้องกันข้อผิดพลาดนี้ใน MQL5 (สำหรับผู้พัฒนา EA)

การออกแบบที่ถูกต้องไม่ใช่ "แก้เมื่อเกิดข้อผิดพลาด" แต่คือ Clamp ค่า SL/TP ให้อยู่ในระยะขั้นต่ำของโบรกเกอร์ก่อนส่งคำสั่ง เพื่อไม่ให้เกิด 10016 ตั้งแต่แรก

ตรวจสอบและ Clamp ค่า SL/TP ก่อนส่งคำสั่ง

// Clamp ค่า SL/TP ให้อยู่ในระยะที่มากกว่าหรือเท่ากับ Stop Level ก่อนส่งคำสั่ง
// คืนค่า false = ทิศทางกลับด้าน (ข้อผิดพลาดในการออกแบบ) ดังนั้นไม่ควรส่งคำสั่ง
bool ClampStops(ENUM_ORDER_TYPE type, double &sl, double &tp)
{
   double point   = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   int    digits  = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
   long   stopsPt = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
   double spread  = SymbolInfoDouble(_Symbol, SYMBOL_ASK)
                  - SymbolInfoDouble(_Symbol, SYMBOL_BID);
   // เผื่อระยะเพิ่มเติมเท่ากับสเปรด นอกเหนือจาก Stop Level (รองรับโบรกเกอร์ที่ stops=0 และตัดสินแบบไดนามิก)
   double minDist = stopsPt * point + spread;

   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

   if(type == ORDER_TYPE_BUY)
   {
      // SL/TP ของ BUY จะถูกตรวจสอบโดยอ้างอิง Bid
      if(sl > 0 && sl >= bid) return false;              // ทิศทางกลับด้าน
      if(tp > 0 && tp <= bid) return false;
      if(sl > 0 && (bid - sl) < minDist) sl = bid - minDist;
      if(tp > 0 && (tp - bid) < minDist) tp = bid + minDist;
   }
   else if(type == ORDER_TYPE_SELL)
   {
      // SL/TP ของ SELL จะถูกตรวจสอบโดยอ้างอิง Ask
      if(sl > 0 && sl <= ask) return false;              // ทิศทางกลับด้าน
      if(tp > 0 && tp >= ask) return false;
      if(sl > 0 && (sl - ask) < minDist) sl = ask + minDist;
      if(tp > 0 && (ask - tp) < minDist) tp = ask - minDist;
   }
   sl = NormalizeDouble(sl, digits);
   tp = NormalizeDouble(tp, digits);
   return true;
}

มี 3 ประเด็นสำคัญ

  1. ดึงค่า SYMBOL_TRADE_STOPS_LEVEL และ SYMBOL_POINT แบบไดนามิกทุกครั้ง (ไม่ผูกกับสัญลักษณ์หรือโบรกเกอร์ใดโบรกเกอร์หนึ่ง)
  2. เผื่อระยะเพิ่มเติมเท่ากับสเปรด (ช่วยให้ผ่านได้ง่ายขึ้นแม้กับโบรกเกอร์ที่ Stop Level เป็น 0 และตัดสินแบบไดนามิก)
  3. สุดท้ายต้องปรับจำนวนหลักด้วย NormalizeDouble(ราคา, _Digits) เสมอ (จำนวนทศนิยมที่เกินก็เป็นสาเหตุของการถูกปฏิเสธได้เช่นกัน)

จัดการ retcode 10016 แยกเฉพาะ

การบันทึกล็อกว่า "ส่งอะไรไป และตอนนั้นระยะห่างเท่าไร" เมื่อถูกปฏิเสธ จะช่วยให้เห็นได้ทันทีว่าตรงกับสาเหตุ ① ถึง ⑥ ข้อใด

MqlTradeRequest req; MqlTradeResult res;
ZeroMemory(req); ZeroMemory(res);
// ... จัดเตรียม req (sl/tp ผ่าน ClampStops แล้ว) ...
if(!OrderSend(req, res) || res.retcode != TRADE_RETCODE_DONE)
{
   if(res.retcode == TRADE_RETCODE_INVALID_STOPS)   // 10016
      PrintFormat("Invalid stops: sl=%s tp=%s bid=%s stopsLevel=%d",
                  DoubleToString(req.sl, _Digits),
                  DoubleToString(req.tp, _Digits),
                  DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits),
                  (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL));
   else
      PrintFormat("OrderSend failed: retcode=%d, lastError=%d", res.retcode, GetLastError());
}

ตรวจสอบ Freeze Level ก่อนแก้ไข Trail

// ก่อนแก้ไขโพซิชัน ให้ตรวจสอบว่าราคาทริกเกอร์อยู่ในโซน Freeze หรือไม่
bool CanModify(double triggerPrice)
{
   long freeze = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_FREEZE_LEVEL);
   if(freeze <= 0) return true;
   double dist = MathAbs(SymbolInfoDouble(_Symbol, SYMBOL_BID) - triggerPrice);
   return (dist > freeze * SymbolInfoDouble(_Symbol, SYMBOL_POINT));
}

สำหรับ EA เวอร์ชัน MT4 สามารถดึงค่าเดียวกันได้ด้วย MarketInfo(Symbol(), MODE_STOPLEVEL) / MODE_FREEZELEVEL แนวคิดเหมือนกันทุกประการ

EA ที่ FXEA365 แจกจ่ายนั้นมี การตรวจสอบ SL/TP ก่อนส่งคำสั่ง การ Clamp ตาม Stop Level และการดึงค่าแบบไดนามิกที่ไม่ผูกกับสัญลักษณ์ เป็นมาตรฐานอยู่แล้ว จึงออกแบบมาไม่ให้หยุดทำงานเพราะ Invalid stops แม้เปลี่ยนโบรกเกอร์หรือสัญลักษณ์


ตารางตรวจสอบตามลำดับความสำคัญ

ลำดับตรวจสอบวิธีแก้ไข
🚨 อันดับแรกระยะห่างระหว่าง SL/TP กับราคาปัจจุบัน < Stop Level หรือไม่ขยายระยะ SL/TP / ใช้โค้ด Clamp
🚨 อันดับแรกทิศทางของ SL/TP สลับกันระหว่าง BUY/SELL หรือไม่แสดงค่าจริงในล็อกแล้วตรวจสอบด้วยตา
⚠️ ถัดไปเฉพาะการแก้ไขล้มเหลว → อยู่ในโซน Freeze Level หรือไม่ข้ามการแก้ไขในช่วงใกล้ทำงาน
⚠️ ถัดไปใส่ระยะทาง (พอยท์) ลงใน sl/tp โดยตรงหรือไม่แปลงเป็นราคาสัมบูรณ์ price ± dist*_Point
✅ ตรวจสอบเป็นบัญชี Market Execution ที่ต้องตั้ง SL/TP ภายหลังหรือไม่ใช้รูปแบบ 2 ขั้นตอน ส่งคำสั่ง → PositionModify
🛠 พัฒนาดึงค่าสเปคของสัญลักษณ์แบบไดนามิกหรือไม่นำ ClampStops ด้านบนไปใช้งาน

สรุป

  • Invalid stops คือ MT5 = 10016 (TRADE_RETCODE_INVALID_STOPS), MT4 = 130 (ERR_INVALID_STOPS) ตัวเลขต่างกันแต่ความหมายและวิธีแก้ไขเหมือนกัน
  • สาเหตุส่วนใหญ่ไม่ใช่ความผิดพลาดในการคำนวณ แต่มาจาก 6 สาเหตุนี้ ต่ำกว่า Stop Level, ทิศทางกลับด้าน, Freeze Level, ความสับสนระหว่างราคากับพอยท์, ข้อกำหนดตั้ง SL/TP ภายหลัง และลักษณะเฉพาะของสัญลักษณ์
  • ผู้พัฒนาสามารถป้องกันถาวรได้ด้วยการ "อ่านค่า SYMBOL_TRADE_STOPS_LEVEL แล้ว Clamp พร้อมใช้ NormalizeDouble ก่อนส่งคำสั่ง" แม้กับโบรกเกอร์ที่ Stop Level เป็น 0 ก็สามารถรองรับได้ด้วยการเผื่อระยะเท่ากับสเปรด

สำหรับรหัสข้อผิดพลาดทั้งหมด สามารถดูได้ที่ คู่มือรวมวิธีแก้ปัญหารหัสข้อผิดพลาด MQL5 / MT5 ส่วนข้อผิดพลาดยอดนิยมอีกแบบที่ทำให้คำสั่งถูกปฏิเสธเช่นกันคือ "เงินทุนไม่เพียงพอ" ซึ่งสรุปไว้ใน บทความอธิบาย ERR_NO_MONEY (134/10019)


คำถามที่พบบ่อย (FAQ)

Q: คำนวณ SL/TP ถูกต้องแล้ว แต่ยังเจอ Invalid stops เพราะอะไร?

มีความเป็นไปได้สูงว่าไม่ได้ถูกปฏิเสธเพราะค่าผิด แต่เป็นเพราะ ระยะห่างจากราคาปัจจุบัน SL/TP ที่มีระยะห่างน้อยกว่าระยะ Stop ขั้นต่ำของโบรกเกอร์ (Stop Level) จะถูกปฏิเสธทั้งหมดแม้ค่าจะถูกต้องก็ตาม กรุณาตรวจสอบระยะขั้นต่ำได้จากหน้าต่าง Specification หรือ SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL)

Q: 10016 กับ 130 ต่างกันอย่างไร?

10016 (TRADE_RETCODE_INVALID_STOPS) คือรหัสผลลัพธ์จาก OrderSend() / PositionModify() บน MT5 ส่วน 130 (ERR_INVALID_STOPS) คือค่าที่ GetLastError() คืนค่าบน MT4 เป็นเพียงความแตกต่างของแพลตฟอร์ม ส่วนความหมาย (ตำแหน่ง SL/TP ไม่ถูกต้อง) และวิธีแก้ไขเหมือนกัน

Q: ปกติไม่เจอปัญหา แต่พอมีการประกาศข่าวสำคัญกลับเจอ Invalid stops?

สาเหตุคือสเปรดที่ขยายตัว เนื่องจาก SL/TP ของ BUY จะถูกตรวจสอบโดยอ้างอิง Bid ส่วน SELL อ้างอิง Ask เมื่อสเปรดขยายตัวในจังหวะนั้น ระยะห่างที่มีอยู่อาจไม่เพียงพอ แม้แต่โบรกเกอร์ที่ Stop Level เป็น 0 ก็มีพฤติกรรมปฏิเสธแบบไดนามิกเฉพาะช่วงที่ราคาผันผวนรุนแรง ควรเผื่อระยะ SL/TP เพิ่มเท่ากับสเปรด (ดูโค้ดในเนื้อหา)

Q: ไม่มี SL/TP แล้วส่งคำสั่งผ่าน แต่พอใส่ SL/TP กลับถูกปฏิเสธ?

มีความเป็นไปได้ว่าเป็นบัญชีประเภท Market Execution ที่เซิร์ฟเวอร์ไม่รับ SL/TP ที่แนบไปในคำสั่งเปิดใหม่ กรุณาเปลี่ยนเป็นรูปแบบ 2 ขั้นตอน คือส่งคำสั่งโดยไม่มี SL/TP ก่อน แล้วตั้งค่าด้วย PositionModify() หลังจาก Execute สำเร็จ แต่ต้องมีระบบลองซ้ำเมื่อตั้ง SL ไม่สำเร็จ และมีมาตรการปิดออร์เดอร์ทันทีหากล้มเหลวต่อเนื่องด้วย

Q: EA ที่ทำงานปกติบน EURUSD กลับเจอ Invalid stops ซ้ำๆ บนทองคำ?

โดยทั่วไปทองคำและดัชนีหุ้นมี Stop Level กว้างกว่าคู่เงินหลัก และจำนวนหลักก็แตกต่างกันด้วย ควรขยายระยะ SL/Trail ให้สอดคล้องกับสเปคของสัญลักษณ์นั้น หรือเปลี่ยนไปใช้ระยะที่อิงตามความผันผวน เช่น ATR

📧 แจ้งเตือนก่อนขึ้นราคา + คอร์สอีเมลฟรี 5 วัน

EA ทั้งหมดจำหน่ายในราคาเปิดตัวและจะปรับขึ้นเป็นขั้นตามยอดขาย รับการแจ้งเตือนก่อนการขึ้นราคาแต่ละครั้ง พร้อมอีเมลรายวันเกี่ยวกับการเทรดอัตโนมัติ การอ่านแบ็กเทสต์ และการเลือกโบรกเกอร์

* ปกป้องความเป็นส่วนตัวอย่างเคร่งครัด คุณสามารถยกเลิกการสมัครได้ตลอดเวลา

ความคิดเห็นและคำถาม