02 พฤศจิกายน, 2026

ฟีเจอร์ที่ซ่อนอยู่ของ MQL4 ในการเขียน Expert Advisor

Oleg Ciruluk
RU TH

นิตยสารนี้ตีพิมพ์บทวิเคราะห์จำนวนมากเกี่ยวกับ Expert Advisor (EA) และผลลัพธ์ของการใช้งาน: บางตัวประสบความสำเร็จอย่างมาก ในขณะที่บางตัวถูกประเมินว่าขาดทุนทันที — ซึ่งก็ถือเป็นผลลัพธ์หนึ่งเช่นกัน แต่หากพิจารณาอย่างละเอียด ประสิทธิภาพของ EA นั้นเป็นผลรวมเชิงซ้อนจากปัจจัยต่อไปนี้:

1) กลยุทธ์การซื้อขาย ที่ใช้ (อัลกอริทึมในการสร้างสัญญาณเปิด-ปิดตำแหน่ง);

2) เทคนิคการบริหารเงินทุน (อัลกอริทึมในการควบคุมขนาดล็อต);

3) ความถูกต้องของโค้ด MQL ตามกลยุทธ์และ Money Management (การมีหรือไม่มีข้อผิดพลาดเชิงตรรกะ) และการไม่มี side effect ที่ไม่ได้ตั้งใจในโปรแกรม

ประเด็นสองข้อแรกมีการเขียนอธิบายไว้มากมายแล้ว แต่ในบทความนี้ ผมจะเน้นเฉพาะ ประเด็นเชิงลึกเกี่ยวกับการใช้ภาษา MQL ซึ่งไม่ปรากฏในเอกสารทางการของภาษา (เช่น เอกสารที่เผยแพร่ที่นี่: http://docs.mql4.com/ru/)

เหตุใดจึงสำคัญ?

  • ประการแรก การรู้พฤติกรรมของโครงสร้างภาษาที่ไม่ระบุไว้ในเอกสารช่วยป้องกันผลกระทบที่ไม่คาดคิดและยากต่อการวิเคราะห์ในโปรแกรม
  • ประการที่สอง ช่วยให้เขียน โค้ดที่มีประสิทธิภาพสูงขึ้น และมีแนวโน้มเกิดข้อผิดพลาดแฝงน้อยลง การใช้ความสามารถเหล่านี้ใน Expert Advisor ของคุณไม่จำเป็นต้องมีทักษะพิเศษ เพียงแค่รู้จักและเข้าใจว่าสามารถใช้งานได้จริง หากบางส่วนของเนื้อหาต่อไปนี้ดูซับซ้อนเกินไปสำหรับคุณ ก็สามารถข้ามผ่านได้อย่างปลอดภัย — ข้อความเหล่านั้นจัดทำขึ้นเพื่อความสมบูรณ์ของเนื้อหาเท่านั้น

การส่งพารามิเตอร์แบบ Pass by Reference

หลักการนี้เป็นที่คุ้นเคยดีสำหรับโปรแกรมเมอร์ทั่วไป โดยเฉพาะผู้ที่ใช้ภาษา C หรือ C++ เป็นหลัก ลองเขียน Expert Advisor แบบง่ายที่สุด (ไฟล์ t2.mq4 — ชื่อไฟล์ตัวอย่างทั้งหมดในไฟล์แนบจะใช้รูปแบบนี้) แล้วนำไปแนบกับกราฟของคู่เงินใดๆ:

int start() {
static int nTick = 0;
Inc( nTick );
Alert( “tick ใหม่ลำดับที่ ” + nTick );
}
//————————————–
void Inc( int n ) { n++; }

ผลลัพธ์ที่ได้คือการแสดงข้อความเดิมซ้ำๆ ไม่สิ้นสุด: “tick ใหม่ลำดับที่ 0″… ค่า n ที่ส่งเข้าฟังก์ชันจะเปลี่ยนแปลงภายในฟังก์ชัน แต่ไม่มีผลต่อตัวแปรภายนอก (ฟังก์ชันที่เรียกใช้ คือ start()) — เพราะค่าที่ส่งเข้าไปคือ สำเนา ของตัวแปร nTick ซึ่งเป็นพฤติกรรม ปกติ ของการเรียกฟังก์ชันใน MQL4 ให้เพิ่มสัญลักษณ์ตัวเดียว (&) ลงใน declaration ของฟังก์ชัน Inc():

void Inc( int& n ) { n++; }

ผลลัพธ์ของการรันโปรแกรมจะเปลี่ยนแปลงอย่างสิ้นเชิง:
tick ใหม่ลำดับที่ 1
tick ใหม่ลำดับที่ 2
tick ใหม่ลำดับที่ 3
…

สัญลักษณ์ & หมายความว่า แทนที่จะส่งสำเนา ให้ส่ง reference ของตัวแปร nTick เข้าไป และการเปลี่ยนแปลงใดๆ ที่กระทำกับตัวแปรนี้ภายในฟังก์ชัน จะส่งผลต่อค่าตัวแปรภายนอกฟังก์ชันทันที ลักษณะเช่นนี้เรียกว่า side effect

ข้อสรุป: ความแตกต่างเล็กนี้มีผลตามมาอย่างลึกซึ้งมาก เพราะฟังก์ชันใน MQL มีข้อจำกัดสูง — สามารถคืนค่าได้เพียงค่าเดียวเท่านั้น และต้องเป็นชนิดข้อมูลพื้นฐาน (primitive type) เช่น int, string, datetime เป็นต้น แต่ด้วย side effect เราสามารถให้ฟังก์ชันที่มีพารามิเตอร์หลายตัว ทำการแก้ไขพารามิเตอร์ทั้งหมดพร้อมกัน ซึ่งเทียบเท่ากับการ return ค่าได้มากกว่าหนึ่งค่าหลังการดำเนินการเสร็จสิ้น (หรืออาจมองว่าเป็นการคืนค่าข้อมูลแบบมีโครงสร้าง) ตัวอย่างเช่น ฟังก์ชันนี้:

double Func( int& n, string& s, datetime& t ) { … }

การส่ง Array แบบ Pass by Reference

ตัวอย่างต่อไปนี้ (ไฟล์ t3.mq4) คล้ายกับตัวอย่างก่อนหน้า แต่ side effect และศักยภาพที่เกิดขึ้นมีมากกว่าอย่างมีนัยสำคัญ:

extern int N = 7;
int A[];
//——————————————————————
int init() {
ArrayResize( A, N );
Alert( “ขนาดอาร์เรย์ ” + ArraySize( A ) );
for( int i = 0; i < N; i++ ) A[ i ] = i + 1;
}
//——————————————————————
int start() {
static nBars = 0; // เพื่อลดความถี่ — ตอนนี้ทำงานต่อเมื่อมีบาร์ใหม่ ไม่ใช่ทุก tick
if( nBars != Bars ) { // แนะนำให้รันบนไทม์เฟรม M1
nBars = Bars;
IncArray( A );
ShowArray( A );
}
}
//——————————————————————
void IncArray( int& Array[] ) {
int n = ArraySize( Array );
for( int i = 0; i < n; i++ ) Array[ i ]++;
}
void ShowArray( int Array[] ) {
int n = ArraySize( Array );
string Msg = “”;
for( int i = 0; i < n; i++ ) {
Msg = Msg + Array[ i ];
if( i < n – 1 ) Msg = Msg + ” , “;
}
Alert( Msg );
}

นำโปรแกรมนี้ไปแนบกับกราฟ (แนะนำให้ใช้ไทม์เฟรม M1) คุณจะเห็นข้อความแจ้งเตือนแสดงผลดังนี้:
2, 3, 4, 5, 6, 7, 8
3, 4, 5, 6, 7, 8, 9
4, 5, 6, 7, 8, 9, 10
…

ข้อสรุป: ตอนนี้แต่ละ element ของ array ที่ส่งเข้าไปจะถูกปรับเปลี่ยนแยกกัน

หมายเหตุ: น่าสนใจว่า หากเราไม่ส่ง array เข้าฟังก์ชัน IncArray() โดย reference (คือไม่ใส่ &) พฤติกรรมของโปรแกรมจะเปลี่ยนไปอย่างไร? ระบบจะสร้างสำเนาของ array เพื่อใช้งานภายในฟังก์ชันหรือไม่ (เช่นเดียวกับกรณีของตัวแปรเดี่ยวในตัวอย่างก่อนหน้า)? เราสามารถทดสอบได้โดยเปลี่ยนหัวฟังก์ชันเป็นเพียงเท่านี้ (เพียงแค่ลบ &):

void IncArray( int Array[] ) {
…

ผลลัพธ์ที่ได้จะน่าประหลาดใจแม้แต่สำหรับโปรแกรมเมอร์ C หรือ C++ ที่มีประสบการณ์! ระหว่างขั้นตอน compile time (ก่อนรันโปรแกรม) เราจะได้รับข้อความแสดงข้อผิดพลาดดังนี้:

‘Array’ – ไม่สามารถกำหนดค่าให้กับ element ของ array ได้ C:\Program Files\MetaTrader – Alpari\experts 3.mq4 (22, 33)

นี่หมายความว่า ตัว compiler ตรวจสอบความเป็น L-value (ความสามารถในการกำหนดค่า) ของ element ของ array ล่วงหน้า! แต่สำหรับ scalar parameter กลับไม่มีการตรวจสอบเช่นนี้

การ resize array ที่ส่งเข้าฟังก์ชันแบบ Pass by Reference

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

int A[ 5 ];
for( i = 0; i < ArraySize( A ); i++ ) A[ i ] = 333;
ShowArray( A );
ArrayResize( A, 7 );
for( i = 0; i < ArraySize( A ); i++ ) A[ i ] = 333;
ShowArray( A );

หลังรันโปรแกรม จะได้ผลลัพธ์ที่ดูแปลกประหลาด: 333, 333, 333, 333, 333, 0, 0

นั่นคือ array ไม่ได้ถูก reallocate memory ใหม่ทั้งหมด แต่จะถูก resize ให้ได้ขนาดตามที่ต้องการ — โดยค่าที่กำหนดไว้ก่อนหน้านี้จะยังคงถูกเก็บไว้ทั้งหมด! ถ้าไม่มีคุณสมบัตินี้ ผู้พัฒนา MQL4 คงจะไม่สามารถออกแบบการจัดเก็บ time series data ใน MetaTrader 4 ได้เลย

และนี่คือเทคนิคที่สัญญาว่าจะนำเสนอ — การเปลี่ยนขนาดของ array ภายในฟังก์ชัน
เพียงแค่แก้ไขฟังก์ชันเดียวจากตัวอย่างก่อนหน้า:

void IncArray( int& Array[] ) {
int n = ArraySize( Array );
ArrayResize( A, n + 1 );
for( int i = 0; i <= n; i++ ) Array[ i ] = i + 1;
}

ผลลัพธ์จากการรัน (คัดลอกจากบันทึกของเทอร์มินัล MetaTrader 4 ซึ่งแสดงผลจากล่างขึ้นบน):

…
2011.02.10 19:54:00 @t3 EURUSD,M1: alert: 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14
2011.02.10 19:53:02 @t3 EURUSD,M1: alert: 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13
2011.02.10 19:52:08 @t3 EURUSD,M1: alert: 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12
…

หมายเหตุ: ขอพาท่านย้อนกลับไปยังคำถามที่กล่าวถึงก่อนหน้าอีกครั้ง: array ใน MQL4 ส่งเข้าฟังก์ชันโดยสำเนาหรือโดย reference? เพื่อตรวจสอบ ให้เขียนหัวฟังก์ชันใหม่ดังนี้:

void IncArray( int Array[] ) {
int n = ArraySize( Array );
ArrayResize( A, n + 1 );
}

ผลลัพธ์ที่ได้ค่อนข้างน่าประหลาดใจ:

2011.03.07 20:17:02 t4 EURUSD,M1: Alert: 1 , 2 , 3 , 4 , 5 , 6 , 7 , 0 , 0 , 0
2011.03.07 20:16:01 t4 EURUSD,M1: Alert: 1 , 2 , 3 , 4 , 5 , 6 , 7 , 0 , 0
2011.03.07 20:15:44 t4 EURUSD,M1: Alert: 1 , 2 , 3 , 4 , 5 , 6 , 7 , 0

ดังนั้น สรุปได้ว่า array ใน MQL4 จะถูกส่งเข้าฟังก์ชัน เสมอ โดย reference แต่หากคุณไม่ระบุ & อย่างชัดเจน ตัว compiler จะตรวจสอบและห้ามไม่ให้คุณเปลี่ยนแปลง element ของ array ภายในฟังก์ชัน

ติดตามเราทาง Telegram

Fortrader contentUrl Suite 11, Second Floor, Sound & Vision House, Francis Rachel Str. Victoria Victoria, Mahe, Seychelles +7 10 248 2640568

เพิ่มเติมจากหมวดหมู่นี้

บทความทั้งหมด

ออร์เดอร์ BuyStopLimit และ SellStopLimit

ออร์เดอร์ BuyStopLimit และ SellStopLimit คือฟีเจอร์ใหม่ใน MetaTrader 5 ที่สามารถจำลองการทำงานได้ใน MT4 ผ่านสคริปต์เฉพาะทาง

การดัดแปลงโปรแกรม MetaTrader4 ด้วยตนเอง (ตอนที่ 2)

บทความตอนที่ 2 ของซีรีส์การเขียนโปรแกรม MQL4 ที่อธิบายแนวคิดพื้นฐาน เช่น ตัวแปร ตัวดำเนินการ เงื่อนไข ลูป และฟังก์ชัน พร้อมแนวทางปฏิบัติสำหรับการดัดแปลงโปรแกรมด้วยตนเอง

ปรับแต่งโปรแกรม MetaTrader 4 ด้วยตนเอง (ตอนที่ 1)

เรียนรู้วิธีปรับแต่งโปรแกรมสำหรับ MetaTrader 4 ด้วยตนเอง: ตั้งแต่การหาซอร์สโค้ด วางไฟล์ให้ถูกต้อง ไปจนถึงเข้าใจโครงสร้างและอัลกอริทึมของ MQL4

Expert เทรดอัตโนมัติ “Forex Agent”

วิเคราะห์และปรับปรุง Expert Forex Agent v07 ให้กลายเป็น v08 โดยเปลี่ยนจังหวะการวาง Pending Order พร้อมผลลัพธ์ที่ดีขึ้นอย่างชัดเจน

บทความเพื่อการศึกษาล่าสุด

บทความทั้งหมด

บรรณาธิการแนะนำ

บทความทั้งหมด