MQL4:Expert Advisor 101 — OnCalculate() ทำงานอย่างไร ?— EP10

Attaphon One
3 min readMay 8, 2021

--

จาก EP9 ที่เราพูดถึงเรื่องของการสร้าง indicator ผมยังติดค้างการอธิบายการทำงานของ OnCalculate() โดยละเอียด ถ้าเขียนต่อบทความน่าจะยาวจะเกิน 3 นาทีแน่ เลยขอแบ่งมาเป็นอีกบท ซึ่งบทนี้จะเป็นเรื่องของ OnCalculate() อย่างเดียวเลยครับ

ใครเพิ่งเข้ามา อยากให้เข้าไปอ่านบทความย้อนหลังการสร้าง custom indicator ใน EP9 ก่อนครับ แล้วค่อยมาอ่านบทนี้ต่อ

OnCalculate()

ฟังก์ชัน OnCalculate() จะทำงานเมื่อ ได้รับข้อมูลราคาที่เปลี่ยนแปลง (ข้อมูล Tick) จากฝั่ง server ซึ่ง OnCalculate() มีรายละเอียดตัวแปรที่ใช้งานภายในฟังก์ชั่น ดังนี้

int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
//--- return value of prev_calculated for next call
return(rates_total);
}

โดยปกติ หากเรานำ code ส่วนที่ทำการคำนวนสร้าง indicator ใส่เข้าไปตรงๆ ภายในฟังก์ชั่น OnCalculate() นี้ นั่นหมายความว่า ทุกครั้งที่ราคามีการเปลี่ยนแปลง ฟังก์ชัน OnCalculate() จะคำนวน indicator ใหม่ทุกครั้ง โดยคำนวนตั้งแต่แท่งเทียนแรกไปจนแท่งเทียนสุดท้าย ซ้ำๆ …

เพื่อป้องกันปัญหานี้ (ที่จะทำให้คอมพิวเตอร์เราหัวร้อน และโปรแกรมทำงานช้า) ตัวฟังก์ชัน OnCalculate() จึงมีตัวแปร 2 ตัวสำคัญ ให้เราใช้งานตามนี้

  • rates_total : จำนวนแท่งเทียน (ฺBar) ที่อยู่บน chart กราฟคู่เงิน
  • prev_calculated : จำนวนแท่งเทียน (Bar) ที่ถูกนำมาคำนวน indicator ไปแล้ว

เราสามารถใช้ 2 ตัวแปรนี้ สร้าง logic ที่ทำให้ indicator ไม่คำนวนแท่งเทียนซ้ำได้ ตัวอย่างดัง code นี้

int unCalculateBar = rates_total - prev_calculated;
for(int i=0;i<unCalculateBar;i++)
{
// put your code for calculating indicator here...
}

หลักการคือ สร้างตัวแปร unCalculateBar ที่เก็บจำนวนแท่งเทียนที่ยังไม่ได้เอามาคำนวนค่า indicator และถ้าค่าของตัวแปร unCalculateBar มีค่ามากกว่า 0 เมื่อไร ก็จะให้ทำงานคำนวน indicator ใหม่อีกครั้ง ( คำนวนเฉพาะแท่งเทียนที่เพิ่งเกิดใหม่ )

int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
int unCalculateBar = rates_total - prev_calculated;
for(int i=0;i<unCalculateBar;i++)
{
// put your code for calculating indicator here...
}

//--- return value of prev_calculated for next call
return(rates_total);
}

เพื่อความเข้าใจที่มากขึ้น ขออธิบายตามรูปต่อไปนี้

สมมุติว่า เรามีแท่งเทียนในกราฟปัจจุบัน 33 แท่งเทียน แล้วเราเพิ่งจะโยน Custom Indicator ตัวหนึ่งเข้าไปในกราฟ เพื่อให้มันให้เริ่มทำงาน

กรณีที่ 1 : เมื่อเริ่มต้นใช้งาน indicator และ OnCalculate() เริ่มทำงานครั้งแรก

  • ค่าตัวแปร rates_total จะเท่ากับจำนวนแท่งเทียน(Bar)ที่อยู่บน Chart ซึ่งในกรณีรูปนี้ จะมีอยู่ 33 แท่งเทียน ดังนั้นเมื่อเริ่มต้น rates_total = 33
  • ค่าตัวแปร prev_calculated จะจำนวนแท่งเทียน (bar) ที่ถูกนำมาคำนวน indicator ไปแล้ว ซึ่งในกรณีนี้ ทุกแท่งเทียนยังไม่เคยถูกใช้คำนวนเลย ดังนั้นเมื่อเริ่มต้น prev_calculated = 0
  • ตัวแปร unCalculateBar (จำนวนแท่งเทียนที่ยังไม่ถูกคำนวนค่า) = 33
  • เมื่อตัวแปร unCalculateBar ( ที่มีค่าเท่ากับ 33 ) นั้นมีค่ามากกว่า 0 โปรแกรมจะเริ่มทำการคำนวนสร้าง indicator ภายใน for loop ซึ่งจะทำการคำนวนค่าทีละแท่งเทียนไปจนถึงแท่งเทียนสุดท้าย ( ตามรูปนี้ก็จะทำงานวน loop ไป 33 รอบ )

ถ้าเราสังเกต จะเห็นว่าฟังก์ชั่น OnCalculate() เมื่อทำงานจบฟังก์ชันจะมีการ return ค่าตัวแปร rates_total ออกไปด้วย ซึ่งค่า rates_total ที่ทำงานในรอบนี้ จะไปเป็นค่า prev_calculated ในการทำงานรอบหน้า

กรณีที่ 2: เมื่อ OnCalculate() อีกครั้งหลังจากทำงานรอบแรกไปแล้ว

  • เมื่อมีสัญญาณข้อมูล Tick เข้ามา ฟังก์ชัน OnCalculate() จะถูกเรียกใช้งาน
  • เนื่องจากจำนวนแท่งเทียน(Bar)ที่อยู่บน Chart ยังมี 33 แท่งเทียนเท่าเดิม ดังนั้นการทำงานรอบนี้ rates_total = 33
  • เนื่องจากเป็นการทำงานในรอบใหม่ ค่า prev_calculated จะถูกอัพเดทว่าทุกแท่งเทียนถูกนำมาคำนวน indicator ไปหมดแล้ว ดังนั้นการทำงานรอบนี้ prev_calculated = 33
  • ตัวแปร unCalculateBar (จำนวนแท่งเทียนที่ยังไม่ถูกคำนวนค่า) = 0
  • เพราะเนื่องจาก unCalculateBar มีค่าเท่ากับ 0 นั่นทำให้ For loop จะไม่ทำงาน

การคำนวน indicator จะเริ่มทำงานอีกครั้งเมื่อมีแท่งเทียนใหม่เกิดขึ้น ( new Bar )

กรณีที่ 3: เมื่อ OnCalculate() เริ่มทำงานใหม่อีกช่วงเกิดแท่งเทียนใหม่

  • เมื่อมี new Bar เกิดขึ้นมาใหม่ ฟังก์ชัน OnCalculate() จะถูกเรียกใช้งาน
  • จำนวนแท่งเทียน(Bar)ที่อยู่บน Chart ตอนนี้เพิ่มขึ้นมาเป็น 34 แท่งเทียน ดังนั้นการทำงานรอบนี้ rates_total = 34
  • ค่า prev_calculated ยังคงเป็นค่าเดิม ( คำนวนไว้ที่ 33 ในรอบก่อนๆ )ดังนั้นการทำงานรอบนี้ prev_calculated = 33
  • ตัวแปร unCalculateBar (จำนวนแท่งเทียนที่ยังไม่ถูกคำนวนค่า) = 1
  • เมื่อตัวแปร unCalculateBar ( ที่มีค่าเท่ากับ 1) นั้นมีค่ามากกว่า 0 โปรแกรมจะเริ่มทำการคำนวนสร้าง indicator ภายใน for loop ซึ่งจะทำการคำนวนเฉพาะแท่งเทียนล่าสุดเท่านั้น ( ตามรูปนี้ก็จะทำงานวน loop แค่ 1 รอบ )

เมื่อทำงานจบฟังก์ชันจะมีการ return ค่าตัวแปร rates_total ในรอบนี้ จะไปเป็นค่า prev_calculated ในการทำงานรอบหน้า

กรณีที่ 4: เมื่อ OnCalculate() ทำงานอีกครั้งหลังจากคำนวนค่าแท่งเทียนใหม่ไปแล้ว

  • เมื่อมีสัญญาณข้อมูล Tick เข้ามา ฟังก์ชัน OnCalculate() จะถูกเรียกใช้งาน
  • เนื่องจากจำนวนแท่งเทียน(Bar)ที่อยู่บน Chart ยังมี 34 แท่งเทียนเท่าเดิม ดังนั้นการทำงานรอบนี้ rates_total = 34
  • เนื่องจากเป็นการทำงานในรอบใหม่ ค่า prev_calculated จะถูกอัพเดทว่าทุกแท่งเทียนถูกนำมาคำนวน indicator ไปหมดแล้ว ดังนั้นการทำงานรอบนี้ prev_calculated = 34
  • ตัวแปร unCalculateBar (จำนวนแท่งเทียนที่ยังไม่ถูกคำนวนค่า) = 0
  • เพราะเนื่องจาก unCalculateBar มีค่าเท่ากับ 0 นั่นทำให้ For loop จะไม่ทำงาน

การทำงานของ indicator ก็จะทำงานวนไปใน 4 กรณีนี้ ต่อไปเรื่อยๆจนกว่าจะหยุดใช้ indicator ครับ

หวังว่าผู้อ่านคงจะเข้าใจวิธีการทำงานของ OnCalculate() มากขึ้นนะครับ จริงๆ บทนี้ค่อนข้างจะยากสำหรับมือใหม่ อาจจะต้องลองลงมือทดลงเขียน code เพื่อความเข้าใจที่เพิ่มขึ้น เพราะการเขียน code ให้เก่ง ไม่ได้มาจากการอ่านครับ แต่มาจากการลงมือทำ ลงมือเขียน code

ยังไงก็รบกวนเหมือนเดิมครับ ฝากกดปุ่มตบมือถ้าถูกใจบทความนี้ หรือถ้ามีข้อสงสัยก็เขียน comment ไว้ครับ เจอกันต่อใน EP หน้าครับ …

--

--