Author
Trader Tan
Introduction (บทนำ)
ในบทความนี้เรายังคงเขียนโปรแกรมกลยุทธ์การซื้อขายที่อธิบายไว้ในส่วนของหนังสือโดย
L. Raschke และ L. Connors Street Smarts:
กลยุทธ์การซื้อขายระยะสั้นความน่าจะเป็นสูงซึ่งมุ่งเน้นการทดสอบขีด จำกัด
ตามช่วงราคา ส่วนสุดท้ายของ TS เต็มรูปแบบในส่วนนี้คือ Pinum Momentum ซึ่งใช้รูปแบบประกอบด้วยแท่งสองสีทุกวัน โดยแถบแรกทิศทางการค้าในวันที่สองมีการกำหนดและการเคลื่อนไหวของราคาในช่วงเริ่มต้นของแถบที่สองควรระบุระดับการค้าบางอย่างสำหรับรายการและออกจากตลาดจุดประสงค์ของบทความนี้คือเพื่อแสดงให้เห็นถึงโปรแกรมเมอร์ที่เข้าใจถึง
MQL5 ซึ่งเป็นหนึ่งในตัวแปรที่ใช้เพื่อทราบถึง Momentum Pinball TS
ซึ่งจะใช้วิธีการแบบง่าย ๆ ของการเขียนโปรแกรมเชิงวัตถุ จาก OOP รหัสจะแตกต่างกันโดยไม่มีคลาส - พวกเขาจะถูกแทนที่ด้วยโครงสร้าง ในทางตรงกันข้ามกับคลาสการออกแบบในโค้ดและการประยุกต์ใช้อ็อบเจ็กต์ประเภทนี้แตกต่างจากการเขียนโปรแกรมขั้นตอนที่คุ้นเคยกับผู้สร้างเริ่มต้นมากที่สุด
ในทางกลับกันคุณลักษณะที่มีให้โดยโครงสร้างมากกว่าพอที่จะแก้ปัญหาดังกล่าวได้เช่นเดียวกับในบทความก่อนหน้านี้ก่อนสร้างโมดูลบล็อกสัญญาณแล้ว
-
ตัวบ่งชี้สำหรับการซื้อขายด้วยตนเองและการทำเครื่องหมายประวัติซึ่งใช้โมดูลนี้
โปรแกรมที่สามจะเป็น Expert Advisor สำหรับการซื้อขายอัตโนมัติ นอกจากนี้ยังจะใช้โมดูลสัญญาณ สรุปได้ว่าเราจะทดสอบที่ปรึกษาผู้เชี่ยวชาญเรื่องราคาสดเนื่องจากผู้เขียนหนังสือทำงานกับคำพูดอายุ 20 ปี
Rules of the Momentum Pinball TS
L. Raschke และ L. Connors ต้องเผชิญกับความไม่แน่นอนของคอนเนอร์เมื่อใช้เทคนิคการซื้อขายที่อธิบายไว้โดยจอร์จเทย์เลอร์ซึ่งกลายเป็นเหตุผลสำหรับการรวบรวมกฎของกลยุทธ์การซื้อขายนี้
กลยุทธ์ของเทย์เลอร์ก่อนวันเริ่มต้นของวันอื่นจะกำหนดทิศทางของการค้า - ไม่ว่าจะเป็นวันขายหรือวันที่ซื้อ อย่างไรก็ตามการค้าที่แท้จริงของผู้เขียนมักเป็นการละเมิดข้อตกลงนี้ซึ่งในความเห็นของผู้เขียนหนังสือจะได้รับกฎการซื้อขายที่ยุ่งเหยิงในการกำหนดทิศทางการซื้อขายของวันรุ่งขึ้นผู้เขียนใช้ตัวบ่งชี้ ROC (อัตราการเปลี่ยนแปลง) RSI (Relative Strength Index) oscillator ถูกนำมาประยุกต์ใช้กับค่าของมันและค่า ROC ของ Cyclicity ก็สามารถมองเห็นได้เป็นอย่างดี สุดท้ายผู้เขียนของ TS เพิ่มระดับสัญญาณ - เส้นขอบของพื้นที่ที่ซื้อจนเกินไปและขายในแผนภูมิ RSI การปรากฏตัวของเส้นของตัวบ่งชี้ดังกล่าว
(มันเป็นชื่อ LBR / RSI จากลินดาแบรดฟอ Raschke)
ในบริเวณนั้นถูกกำหนดวันในการตรวจสอบการขายน่าจะเป็นที่สุดและซื้อวัน LBR / RSI มีรายละเอียดดังนี้
กฎสมบูรณ์ของ Momentum Pinball TS สำหรับรายการซื้อมีสูตรดังนี
- ในวันที่ D1 มูลค่าของ LBR / RSI ของวันที่ปิดล่าสุดควรอยู่ในพื้นที่ที่ขายเกิน - ต่ำกว่า 30
- หลังจากปิดบาร์รายชั่วโมงแรกของวันใหม่แล้วให้วางคำสั่งซื้อที่รอดำเนินการเกินกว่าขีดสูงสุดดังกล่าว
- หลังจากที่เรียกใช้คำสั่งซื้อที่ค้างอยู่ให้วางการสูญเสียการหยุดพักไปที่ระดับต่ำสุดของแถบรายชั่วโมงแรก
- ถ้าตำแหน่งถูกปิดด้วยความสูญเสียให้วางคำสั่งซื้อที่รอดำเนินการอีกครั้งไว้ที่ระดับเดียวกัน
- ถ้าในตอนท้ายของวันตำแหน่งยังคงทำกำไรได้ทิ้งไว้ในวันถัดไป ในวันซื้อขายวันที่สองต้องปิดสถานะดังกล่าวการแสดงกฎการเข้าเมืองด้วยความช่วยเหลือของสองตัวชี้วัดที่อธิบายไว้ด้านล่างมีลักษณะดังนี้:กฎสำหรับการออกจากตลาดไม่ได้ระบุไว้อย่างชัดเจนในหนังสือ: ผู้เขียนพูดเกี่ยวกับการใช้ต่อท้ายเกี่ยวกับการปิดในเช้าวันรุ่งขึ้นและเกี่ยวกับออกจากที่สูงขึ้นแล้ววันซื้อขายวันแรกสูงกฎสำหรับรายการขายมีความคล้ายคลึงกัน - LBR / RSI ควรอยู่ในพื้นที่ที่ซื้อเกิน (สูงกว่า 70) การสั่งซื้อที่ค้างอยู่ควรวางไว้ที่ระดับต่ำสุดของแถบรายชั่วโมงแรก
แน่นอนการคำนวณทั้งหมดที่จำเป็นสำหรับการรับสัญญาณอาจทำได้ในโมดูลสัญญาณมาก แต่นอกเหนือจากการซื้อขายอัตโนมัติในแผนของบทความนี้ให้การซื้อขายด้วยตนเองเช่นกัน การมีตัวบ่งชี้แยกต่างหาก LBR / RSI พร้อมกับเน้นพื้นที่ที่ซื้อเกินหรือซื้อเกินกำหนดจะเป็นประโยชน์เพื่อความสะดวกในการระบุรูปแบบเวอร์ชันด้วยตนเอง และเพื่อเพิ่มประสิทธิภาพความพยายามของเราเราจะไม่ใช้โปรแกรมการประมาณค่า LBR / RSI หลายรุ่นในหลายรูปแบบ ('buffer' สำหรับตัวบ่งชี้และ 'bufferless' สำหรับหุ่นยนต์) เชื่อมต่อไฟแสดงสถานะภายนอกเข้ากับโมดูลสัญญาณผ่านฟังก์ชัน iCustom มาตรฐาน ตัวบ่งชี้นี้จะไม่ทำการประเมินโดยใช้ทรัพยากรมากและไม่จำเป็นต้องตั้งคำถามในแต่ละจุดที่ TS ระบุค่าของตัวบ่งชี้ที่แถบรายวันที่ปิดไว้ เราไม่สนใจเกี่ยวกับการเปลี่ยนแปลงค่าปัจจุบันอย่างต่อเนื่อง ดังนั้นจึงไม่มีอุปสรรคสำคัญสำหรับการแก้ปัญหาดังกล่าว
ที่นี่รวมเอาอัลกอริทึมการคำนวณ ROC และ RSI ซึ่งจะวาดเส้นโค้งออสซิลเลเตอร์ที่เป็นผลลัพธ์ เพื่อที่จะตรวจจับค่าที่ต้องการได้อย่างง่ายดายให้เพิ่มการเติมพื้นที่ที่ซื้อจนเกินไปและขายในหลายสี สำหรับการทำเช่นนี้เราต้องใช้ 5 บัฟเฟอร์เพื่อแสดงและอีก 4 ตัวสำหรับการคำนวณเสริม
เพิ่มการตั้งค่ามาตรฐาน (ระยะเวลา RSI และค่าขอบของสองพื้นที่) ด้วยกฎอื่นที่ไม่ได้มาจากกฎของระบบการซื้อขายต้นฉบับ สำหรับการคำนวณคุณจะสามารถใช้ราคาปิดบาร์ได้ทุกวัน แต่ยังมีค่ามัธยฐานข้อมูลเพิ่มเติมโดยทั่วไปราคาปกติหรือถัวเฉลี่ย อันที่จริงแล้วสำหรับการทดลองของเขาผู้ใช้สามารถเลือกได้ระหว่างเจ็ดตัวแปรที่จัดหาโดย ENUM_APPLIED_PRICE
การประกาศบัฟเฟอร์กล่องข้อความของผู้ใช้และบล็อกการเตรียมใช้งานจะมีลักษณะดังนี้:
[
ในตัวจัดการเหตุการณ์มาตรฐาน OnCalculate จัดเรียงลูปสองอันแยกกัน: กลุ่มแรกเตรียมอาร์เรย์ข้อมูล ROC ค่าที่สองจะคำนวณค่า oscillator จากข้อมูลอาร์เรย์นี้
#property indicator_separate_window #property indicator_buffers 9 #property indicator_plots 3 #property indicator_label1 “Overbought area" #property indicator_type1 DRAW_FILLING #property indicator_color1 C'255,208,234' #property indicator_width1 1 #property indicator_label2 “Oversold area" #property indicator_type2 DRAW_FILLING #property indicator_color2 C'179,217,255' #property indicator_width2 1 #property indicator_label3 "RSI от ROC" #property indicator_type3 DRAW_LINE #property indicator_style3 STYLE_SOLID #property indicator_color3 clrTeal #property indicator_width3 2 #property indicator_minimum 0 #property indicator_maximum 100 input ENUM_APPLIED_PRICE TS_MomPin_Applied_Price = PRICE_CLOSE; // Price for ROC calculation input uint TS_MomPin_RSI_Period = 3; // RSI Period input double TS_MomPin_RSI_Overbought = 70; // RSI Oversold level input double TS_MomPin_RSI_Oversold = 30; // RSI overbought level double buff_Overbought_High[], buff_Overbought_Low[], // overbought area background buff_Oversold_High[], buff_Oversold_Low[], // oversold area background buff_Price[], // array of bar calculated prices buff_ROC[], // ROC array from calculated prices buff_RSI[], // RSI from ROC buff_Positive[], buff_Negative[] // auxiliary arrays for RSI calculation ; int OnInit() { // designation of indicator buffers: // overbought area SetIndexBuffer(0, buff_Overbought_High, INDICATOR_DATA); PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetInteger(0, PLOT_SHOW_DATA, false); SetIndexBuffer(1, buff_Overbought_Low, INDICATOR_DATA); // oversold area SetIndexBuffer(2, buff_Oversold_High, INDICATOR_DATA); PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetInteger(1, PLOT_SHOW_DATA, false); SetIndexBuffer(3, buff_Oversold_Low, INDICATOR_DATA); // RSI curve SetIndexBuffer(4, buff_RSI, INDICATOR_DATA); PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE); // auxiliary buffers for RSI calculation SetIndexBuffer(5, buff_Price, INDICATOR_CALCULATIONS); SetIndexBuffer(6, buff_ROC, INDICATOR_CALCULATIONS); SetIndexBuffer(7, buff_Negative, INDICATOR_CALCULATIONS); SetIndexBuffer(8, buff_Positive, INDICATOR_CALCULATIONS); IndicatorSetInteger(INDICATOR_DIGITS, 2); IndicatorSetString(INDICATOR_SHORTNAME, "LBR/RSI"); return(INIT_SUCCEEDED); }]
ในรายงานฉบับที่มีการเปลี่ยนแปลงโดย Linda Raschke เราไม่ควรเปรียบเทียบราคาของบาร์ต่อกัน แต่ก็หายไปหนึ่งบาร์ระหว่างกัน กล่าวอีกนัยหนึ่งใน TS ใช้รูปแบบราคาของวันที่ยืนปิดจากวันซื้อขายวันหนึ่งและสามวันทำการตามลำดับ นี้ค่อนข้างง่ายที่จะทำ; ตลอดเส้นทางในวงนี้จัดเตรียมพื้นหลังของพื้นที่ที่ซื้อจนเกินไปและขายเกิน นอกจากนี้โปรดใช้คุณลักษณะการเลือกประเภทราคาดังนี้
[
ลูปที่สอง (การคำนวณ RSI) ไม่มีลักษณะเฉพาะอย่างยิ่งมันเกือบจะทำซ้ำขั้นตอนของออสซิลเลเตอร์มาตรฐานของประเภทนี้:
int i_RSI_Period = int(TS_MomPin_RSI_Period), // transfer of RSI period into int type i_Bar, i_Period_Bar // two bar indices for simultaneous application ; double d_Sum_Negative, d_Sum_Positive, // auxiliary variables for RSI calculation d_Change // auxiliary variable for ROC calculation ; // Fill in ROC buffer and fill areas: i_Period_Bar = 1; while(++i_Period_Bar < rates_total && !IsStopped()) { // calculated bar price: switch(TS_MomPin_Applied_Price) { case PRICE_CLOSE: buff_Price[i_Period_Bar] = Close[i_Period_Bar]; break; case PRICE_OPEN: buff_Price[i_Period_Bar] = Open[i_Period_Bar]; break; case PRICE_HIGH: buff_Price[i_Period_Bar] = High[i_Period_Bar]; break; case PRICE_LOW: buff_Price[i_Period_Bar] = Low[i_Period_Bar]; break; case PRICE_MEDIAN: buff_Price[i_Period_Bar] = 0.50000 * (High[i_Period_Bar] + Low[i_Period_Bar]); break; case PRICE_TYPICAL: buff_Price[i_Period_Bar] = 0.33333 * (High[i_Period_Bar] + Low[i_Period_Bar] + Open[i_Period_Bar]); break; case PRICE_WEIGHTED: buff_Price[i_Period_Bar] = 0.25000 * (High[i_Period_Bar] + Low[i_Period_Bar] + Open[i_Period_Bar] + Open[i_Period_Bar]); break; } // difference of bar calculated prices (ROC value): if(i_Period_Bar > 1) buff_ROC[i_Period_Bar] = buff_Price[i_Period_Bar] - buff_Price[i_Period_Bar - 2]; // background filling: buff_Overbought_High[i_Period_Bar] = 100; buff_Overbought_Low[i_Period_Bar] = TS_MomPin_RSI_Overbought; buff_Oversold_High[i_Period_Bar] = TS_MomPin_RSI_Oversold; buff_Oversold_Low[i_Period_Bar] = 0; }]
[
ลองตั้งชื่อตัวบ่งชี้ LBR_RSI.mq5 แล้ววางลงในโฟลเดอร์ตัวบ่งชี้มาตรฐานของแคตตาล็อกข้อมูลเทอร์มินัล เป็นชื่อที่จะระบุในฟังก์ชัน iCustom ของโมดูลสัญญาณดังนั้นคุณจึงไม่ควรเปลี่ยน
i_Period_Bar = prev_calculated - 1; if(i_Period_Bar <= i_RSI_Period) { buff_RSI[0] = buff_Positive[0] = buff_Negative[0] = d_Sum_Positive = d_Sum_Negative = 0; i_Bar = 0; while(i_Bar++ < i_RSI_Period) { buff_RSI[0] = buff_Positive[0] = buff_Negative[0] = 0; d_Change = buff_ROC[i_Bar] - buff_ROC[i_Bar - 1]; d_Sum_Positive += (d_Change > 0 ? d_Change : 0); d_Sum_Negative += (d_Change < 0 ? -d_Change : 0); } buff_Positive[i_RSI_Period] = d_Sum_Positive / i_RSI_Period; buff_Negative[i_RSI_Period] = d_Sum_Negative / i_RSI_Period; if(buff_Negative[i_RSI_Period] != 0) buff_RSI[i_RSI_Period] = 100 - (100 / (1. + buff_Positive[i_RSI_Period] / buff_Negative[i_RSI_Period])); else buff_RSI[i_RSI_Period] = buff_Positive[i_RSI_Period] != 0 ? 100 : 50; i_Period_Bar = i_RSI_Period + 1; } i_Bar = i_Period_Bar - 1; while(++i_Bar < rates_total && !IsStopped()) { d_Change = buff_ROC[i_Bar] - buff_ROC[i_Bar - 1]; buff_Positive[i_Bar] = (buff_Positive[i_Bar - 1] * (i_RSI_Period - 1) + (d_Change> 0 ? d_Change : 0)) / i_RSI_Period; buff_Negative[i_Bar] = (buff_Negative[i_Bar - 1] * (i_RSI_Period - 1) + (d_Change <0 ? -d_Change : 0)) / i_RSI_Period; if(buff_Negative[i_Bar] != 0) buff_RSI[i_Bar] = 100 - 100. / (1. + buff_Positive[i_Bar] / buff_Negative[i_Bar]); else buff_RSI[i_Bar] = buff_Positive[i_Bar] != 0 ? 100 : 50; }]
Signal module
ในโมดูลสัญญาณที่เชื่อมต่อกับ Expert Advisor และ Indicator ให้วางการตั้งค่าผู้ใช้ของกลยุทธ์การซื้อขาย Momentum Pinball ผู้เขียนให้ค่าคงที่สำหรับการคำนวณตัวบ่งชี้ LBR / RSI (ระยะเวลา RSI = 3, ระดับซื้อเกิน (= ซื้อ 30), ระดับขาย (oversold) = 70) แต่เราจะทำให้พวกเขาสามารถเปลี่ยนแปลงได้สำหรับการทดลองเช่นเดียวกับวิธีการปิดบัญชี - หนังสือกล่าวถึงสามรูปแบบ เราจะจัดโปรแกรมทั้งหมดและผู้ใช้จะมีคุณสมบัติในการเลือกตัวเลือกที่ต้องการ:
- to close position by Stop Loss level trailing;
- to close it in the morning of the following day;
- to wait on the second day for breakthrough of extremum of the position opening day.
“Morning”เป็นความคิดที่ค่อนข้างไม่มีตัวตนเพื่อทำให้กฎระเบียบมีความหมายมากขึ้น Raschke และ Connors ไม่ได้พูดถึงเรื่องนี้ แต่ก็มีเหตุผลที่จะสมมติว่าการผูกขาดกับแถบแรกของวันใหม่ (ใช้กับกฎ TS อื่น ๆ ) จะชี้ไปที่ป้ายกำกับ 'morning' ของช่วงเวลา 24 ชั่วโมงโปรดระลึกถึงการตั้งค่า TS อีก 2 แบบ - การชดเชยจากขอบของชั่วโมงแรกของวัน การชดเชยควรระบุระดับของตำแหน่งการสั่งซื้อที่รอดำเนินการและระดับ StopLoss:[enum ENUM_EXIT_MODE { // List of exit methods CLOSE_ON_SL_TRAIL, // only by trailing CLOSE_ON_NEW_1ST_CLOSE, // by closing of the 1st bar of the following day CLOSE_ON_DAY_BREAK // by break-through of extremum of the position opening day }; // user settings input ENUM_APPLIED_PRICE TS_MomPin_Applied_Price = PRICE_CLOSE; // Momentum Pinball: Prices for ROC calculation input uint TS_MomPin_RSI_Period = 3; // Momentum Pinball: RSI period input double TS_MomPin_RSI_Overbought = 70; // Momentum Pinball: RSI oversold level input double TS_MomPin_RSI_Oversold = 30; // Momentum Pinball: RSI overbought level input uint TS_MomPin_Entry_Offset = 10; // Momentum Pinball: Offset of entry level from borders H1 (in points) input uint TS_MomPin_Exit_Offset = 10; // Momentum Pinball: Offset of exit level from borders H1 (in points) input ENUM_EXIT_MODE TS_MomPin_Exit_Mode = CLOSE_ON_SL_TRAIL; // Momentum Pinball: Profitable position closing method]
[
เช่นเดียวกับในเวอร์ชันก่อนหน้าเราจะไม่คำนวณทุกอย่างอีกครั้งเมื่อติ๊กแต่ละครั้งเมื่อเรียกใช้ฟังก์ชันจากโรบอต แต่เราจะเก็บข้อมูลระหว่าง ticks ในระดับที่คำนวณได้ในตัวแปรคงที่ อย่างไรก็ตามการทำงานกับฟังก์ชันนี้ในตัวบ่งชี้การซื้อขายด้วยตนเองจะมีความแตกต่างอย่างมาก และ zeroing ของตัวแปรคงที่เมื่อเรียกฟังก์ชันจากตัวบ่งชี้ควรมีให้ เพื่อแยกแยะระหว่างสายจากตัวบ่งชี้และสายจากหุ่นยนต์ให้ใช้ตัวแปร t_Time ตัวบ่งชี้จะพลิกกลับนั่นคือทำให้ค่าเป็นลบ:
ENUM_ENTRY_SIGNAL fe_Get_Entry_Signal( // Two-candle pattern analysis (D1 + H1) datetime t_Time, // current time double& d_Entry_Level, // entry level (link to the variable) double& d_SL, // StopLoss level (link to the variable) double& d_TP, // TakeProfit level (link to the variable) double& d_Range_High, // high of the range's 1st hourly bar (link to the variable) double& d_Range_Low // low of the range's 1st hourly bar (link to the variable) ) { // function body }]
[
ด้านล่างนี้หาโค้ดสำหรับรับหมายเลขอ้างอิง LBR / RSI เมื่อเรียกใช้ฟังก์ชันเป็นครั้งแรก:
static ENUM_ENTRY_SIGNAL se_Trade_Direction = ENTRY_UNKNOWN; // trading direction for today static double // variables for storing calculated levels between ticks sd_Entry_Level = 0, sd_SL = 0, sd_TP = 0, sd_Range_High = 0, sd_Range_Low = 0 ; if(t_Time < 0) { // only for call from indicator sd_Entry_Level = sd_SL = sd_TP = sd_Range_High = sd_Range_Low = 0; se_Trade_Direction = ENTRY_UNKNOWN; } // by default apply earlier saved levels of entries/exits: d_Entry_Level = sd_Entry_Level; d_SL = sd_SL; d_TP = sd_TP; d_Range_High = sd_Range_High; d_Range_Low = sd_Range_Low;]
[
หุ่นยนต์ต่อหนึ่งชั่วโมงต้องวิเคราะห์มูลค่าของตัวบ่งชี้ในแถบรายวันที่ปิดครั้งสุดท้ายและกำหนดทิศทางการซื้อขายในวันนี้ หรือควรปิดการซื้อขายหากค่า LBR / RSI อยู่ในพื้นที่ที่เป็นกลาง รหัสการเรียกค้นของค่านี้จากบัฟเฟอร์ตัวบ่งชี้และการวิเคราะห์ด้วยฟังก์ชันการบันทึกข้อมูลซึ่งอาจมีข้อผิดพลาดและลักษณะเฉพาะของการเรียกจากตัวบ่งชี้การซื้อขายด้วยตนเอง:
static int si_Indicator_Handle = INVALID_HANDLE; if(si_Indicator_Handle == INVALID_HANDLE) { // to receive indicator handle when calling the function for the first time: si_Indicator_Handle = iCustom(_Symbol, PERIOD_D1, "LBR_RSI", TS_MomPin_Applied_Price, TS_MomPin_RSI_Period, TS_MomPin_RSI_Overbought, TS_MomPin_RSI_Oversold ); if(si_Indicator_Handle == INVALID_HANDLE) { // indicator handle not received if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: error of receiving LBR_RSI indicator handle #%u", __FUNCTION__, _LastError); return(ENTRY_INTERNAL_ERROR); } }]
[
เราได้ชี้แจงทิศทางการซื้อขายที่อนุญาต งานต่อไปจะเป็นการกำหนดระดับรายการและข้อ จำกัด ในการสูญเสีย (Stop Loss) นอกจากนี้ยังเพียงพอที่จะทำสิ่งนี้ได้หนึ่งครั้งต่อ 24 ชั่วโมง - หลังจากปิดวันที่แถบแรกในระยะเวลารายชั่วโมง อย่างไรก็ตามขึ้นอยู่กับลักษณะเฉพาะของการทำงานของตัวบ่งชี้การซื้อขายด้วยตนเองเราจะทำให้ขั้นตอนการคำนวณมีความซับซ้อนขึ้นเล็กน้อย นี่เป็นเพราะความจริงที่ว่าตัวบ่งชี้ควรไม่เพียง แต่ตรวจจับระดับสัญญาณแบบเรียลไทม์ แต่ยังต้องทำเครื่องหมายในประวัติ:
static int si_Indicator_Handle = INVALID_HANDLE; if(si_Indicator_Handle == INVALID_HANDLE) { // receiving indicator handle at first function call: si_Indicator_Handle = iCustom(_Symbol, PERIOD_D1, "LBR_RSI", TS_MomPin_Applied_Price, TS_MomPin_RSI_Period, TS_MomPin_RSI_Overbought, TS_MomPin_RSI_Oversold ); if(si_Indicator_Handle == INVALID_HANDLE) { // handle not received if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: error of indicator handle receipt LBR_RSI #%u", __FUNCTION__, _LastError); return(ENTRY_INTERNAL_ERROR); } } // to find out the time of previous day daily bar: datetime ta_Bar_Time[]; if(CopyTime(_Symbol, PERIOD_D1, fabs(t_Time), 2, ta_Bar_Time) < 2) { if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: CopyTime: error #%u", __FUNCTION__, _LastError); return(ENTRY_INTERNAL_ERROR); } // previous day analysis, if this is the 1st call today: static datetime st_Prev_Day = 0; if(t_Time < 0) st_Prev_Day = 0; // only for call from indicator if(st_Prev_Day < ta_Bar_Time[0]) { // zeroing of previous day parameters: se_Trade_Direction = ENTRY_UNKNOWN; d_Entry_Level = sd_Entry_Level = d_SL = sd_SL = d_TP = sd_TP = d_Range_High = sd_Range_High = d_Range_Low = sd_Range_Low = 0; // retrieve value LBR/RSI of previous day: double da_Indicator_Value[]; if(1 > CopyBuffer(si_Indicator_Handle, 4, ta_Bar_Time[0], 1, da_Indicator_Value)) { if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: CopyBuffer: error #%u", __FUNCTION__, _LastError); return(ENTRY_INTERNAL_ERROR); } // if anything is wrong with LBR/RSI value: if(da_Indicator_Value[0] > 100. || da_Indicator_Value[0] < 0.) { if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: Indicator buffer value error (%f)", __FUNCTION__, da_Indicator_Value[0]); return(ENTRY_UNKNOWN); } st_Prev_Day = ta_Bar_Time[0]; // attempt counted // remember trading direction for today: if(da_Indicator_Value[0] > TS_MomPin_RSI_Overbought) se_Trade_Direction = ENTRY_SELL; else se_Trade_Direction = da_Indicator_Value[0] > TS_MomPin_RSI_Oversold ? ENTRY_NONE : ENTRY_BUY; // to log: if(Log_Level == LOG_LEVEL_DEBUG) PrintFormat("%s: Trading direction for %s: %s. LBR/RSI: (%.2f)", __FUNCTION__, TimeToString(ta_Bar_Time[1], TIME_DATE), StringSubstr(EnumToString(se_Trade_Direction), 6), da_Indicator_Value[0] ); }]
[
หลังจากนั้นเราควรหยุดการทำงานโดยการกลับทิศทางการซื้อขายที่ตรวจพบ:
// no signal search today if(se_Trade_Direction == ENTRY_NONE) return(ENTRY_NONE); // analysis of today’s first bar H1, unless this is already done: if(sd_Entry_Level == 0.) { // to receive data of last 24 bars H1: MqlRates oa_H1_Rates[]; int i_Price_Bars = CopyRates(_Symbol, PERIOD_H1, fabs(t_Time), 24, oa_H1_Rates); if(i_Price_Bars == WRONG_VALUE) { // handling of CopyRates function error if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: CopyRates: error #%u", __FUNCTION__, _LastError); return(ENTRY_INTERNAL_ERROR); } // among 24 bars to find the 1st bar of today and to remember High, Low: int i_Bar = i_Price_Bars; while(i_Bar-- > 0) { if(oa_H1_Rates[i_Bar].time < ta_Bar_Time[1]) break; // last bar H1 of previous day // borders of H1 1st bar range: sd_Range_High = d_Range_High = oa_H1_Rates[i_Bar].high; sd_Range_Low = d_Range_Low = oa_H1_Rates[i_Bar].low; } // H1 1st bar is not closed yet: if(i_Price_Bars - i_Bar < 3) return(ENTRY_UNKNOWN); // to calculate trading levels: // level of market entry: d_Entry_Level = _Point * TS_MomPin_Entry_Offset; // auxiliary calculations sd_Entry_Level = d_Entry_Level = se_Trade_Direction == ENTRY_SELL ? d_Range_Low - d_Entry_Level : d_Range_High + d_Entry_Level; // initial level SL: d_SL = _Point * TS_MomPin_Exit_Offset; // auxiliary calculations sd_SL = d_SL = se_Trade_Direction == ENTRY_BUY ? d_Range_Low - d_SL : d_Range_High + d_SL; }]
[
ตอนนี้ขอวิเคราะห์โปรแกรมเกี่ยวกับเงื่อนไขในการปิดสัญญาณตำแหน่ง เรามีตัวแปร 3 สายซึ่งหนึ่งในนั้น (ระดับการสูญเสียท้าย) จะถูกรับรู้แล้วในโค้ด Expert Advisor ของเวอร์ชันก่อนหน้า สองสายพันธุ์อื่น ๆ ในภาพรวมต้องการราคาและเวลาในการเข้าออกทิศทางตำแหน่งสำหรับการคำนวณ เราจะส่งต่อไปพร้อมกับเวลาปัจจุบันและวิธีการปิดที่เลือกไปยังฟังก์ชัน fe_Get_Exit_Signal:
return(se_Trade_Direction);]
[
ที่นี่เรามี 'cap' ในกรณีที่เลือก 'trailing exit' - ฟังก์ชั่นจะส่งสัญญาณกลับคืนโดยไม่มีการวิเคราะห์ใด ๆ สำหรับสองตัวเลือกอื่น ๆ การเกิดเหตุการณ์ 'เช้ามา' และ 'extremum เมื่อวานผ่าน' ถูกระบุ ตัวแปรของค่าของประเภท ENUM_EXIT_SIGNAL ที่ส่งกลับโดยฟังก์ชันจะคล้ายกับรายการค่าสัญญาณเข้าที่คล้ายคลึงกัน (ENUM_ENTRY_SIGNAL):
ENUM_EXIT_SIGNAL fe_Get_Exit_Signal( // Detection of position closing signal double d_Entry_Level, // entry level datetime t_Entry_Time, // entry time ENUM_ENTRY_SIGNAL e_Trade_Direction, // trade direction datetime t_Current_Time, // current time ENUM_EXIT_MODE e_Exit_Mode // exit mode ) { static MqlRates soa_Prev_D1_Rate[]; // data of D1 bar for previous day static int si_Price_Bars = 0; // auxiliary counter if(t_Current_Time < 0) { // to distinguish a call from indicator and a call from Expert Advisor t_Current_Time = -t_Current_Time; si_Price_Bars = 0; } double d_Curr_Entry_Level, d_SL, d_TP, d_Range_High, d_Range_Low ; if(e_Trade_Direction < 1) { // no positions, to zero everything si_Price_Bars = 0; } switch(e_Exit_Mode) { case CLOSE_ON_SL_TRAIL: // only on trail return(EXIT_NONE); case CLOSE_ON_NEW_1ST_CLOSE: // on closing of next day 1st bar if((t_Current_Time - t_Current_Time % 86400) == (t_Entry_Time - t_Current_Time % 86400) ) return(EXIT_NONE); // day of position opening not finished yet if(fe_Get_Entry_Signal(t_Current_Time, d_Curr_Entry_Level, d_SL, d_TP, d_Range_High, d_Range_Low) < ENTRY_UNKNOWN ) { if(Log_Level > LOG_LEVEL_ERR) PrintFormat("%s: 1st bar of the following day is closed", __FUNCTION__); return(EXIT_ALL); } return(EXIT_NONE); // not closed case CLOSE_ON_DAY_BREAK: // upon break-through of extremum of the position opening day if((t_Current_Time - t_Current_Time % 86400) == (t_Entry_Time - t_Current_Time % 86400) ) return(EXIT_NONE); // position opening day not finished yet if(t_Current_Time % 86400 > 36000) return(EXIT_ALL); // time out if(si_Price_Bars < 1) { si_Price_Bars = CopyRates(_Symbol, PERIOD_D1, t_Current_Time, 2, soa_Prev_D1_Rate); if(si_Price_Bars == WRONG_VALUE) { // handling of CopyRates function error if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: CopyRates: error #%u", __FUNCTION__, _LastError); return(EXIT_UNKNOWN); } if(e_Trade_Direction == ENTRY_BUY) { if(soa_Prev_D1_Rate[1].high < soa_Prev_D1_Rate[0].high) return(EXIT_NONE); // did not break-through if(Log_Level > LOG_LEVEL_ERR) PrintFormat("%s: price broke-through yesterday’s High: %s > %s", __FUNCTION__, DoubleToString(soa_Prev_D1_Rate[1].high, _Digits), DoubleToString(soa_Prev_D1_Rate[0].high, _Digits)); return(EXIT_BUY); } else { if(soa_Prev_D1_Rate[1].low > soa_Prev_D1_Rate[0].low) return(EXIT_NONE); // did not break through if(Log_Level > LOG_LEVEL_ERR) PrintFormat("%s: price broke through yesterday’s Low: %s < %s", __FUNCTION__, DoubleToString(soa_Prev_D1_Rate[1].low, _Digits), DoubleToString(soa_Prev_D1_Rate[0].low, _Digits)); return(EXIT_SELL); } } return(EXIT_NONE); // for each } return(EXIT_UNKNOWN); }]
[
enum ENUM_EXIT_SIGNAL { // The list of exit signals EXIT_UNKNOWN, // not identified EXIT_BUY, // close buys EXIT_SELL, // close sells EXIT_ALL, // close all EXIT_NONE // close nothing };]
Indicator for manual trading
โมดูลสัญญาณที่อธิบายข้างต้นควรใช้ในหุ่นยนต์เพื่อการซื้อขายอัตโนมัติ แจ้งให้เราทราบวิธีการสมัครด้านล่างนี้ ขั้นแรกให้เราสร้างเครื่องมือสำหรับการพิจารณาอย่างชัดเจนเกี่ยวกับลักษณะเฉพาะของ TS ที่แผนภูมิใน terminal นี่จะเป็นตัวบ่งชี้การใช้โมดูลสัญญาณโดยไม่มีการเปลี่ยนแปลงใด ๆ และแสดงระดับการค้าที่คำนวณได้ - อยู่ระหว่างระดับการสั่งซื้อและระดับการหยุดการขาดทุน การปิดข้อตกลงที่ทำกำไรได้ในตัวบ่งชี้นี้จะได้รับการจัดรูปแบบที่เรียบง่ายขึ้นเพียงอย่างเดียวเมื่อถึงระดับที่กำหนดไว้ล่วงหน้า (TakeProfit) ตามที่คุณจำได้ในโมดูลเราได้ตั้งอัลกอริทึมที่ซับซ้อนมากขึ้นสำหรับการตรวจจับสัญญาณออกจากการจัดการ แต่ขอปล่อยให้ใช้งานในหุ่นยนต์นอกเหนือไปจากระดับการค้าแล้วตัวบ่งชี้จะเติมแถบของชั่วโมงแรกของวันเพื่อให้ชัดเจนว่าเหตุใดจึงเป็นระดับเหล่านี้ที่ใช้ การทำเครื่องหมายดังกล่าวจะช่วยประเมินข้อดีและข้อเสียของกฎเกณฑ์ส่วนใหญ่ของกลยุทธ์ Momentum Pinball เพื่อหาสิ่งที่ไม่สามารถหาได้จากรายงานของผู้ทดสอบกลยุทธ์ การวิเคราะห์ภาพเพิ่มด้วยสถิติผู้ทดสอบจะช่วยให้กฎ TS มีประสิทธิภาพมากขึ้นในการใช้ตัวบ่งชี้สำหรับการซื้อขายด้วยตนเองตามปกติให้เพิ่มระบบการแจ้งเตือนผู้ประกอบการแบบเรียลไทม์ลงไป การแจ้งเตือนดังกล่าวจะมีทิศทางการรับสัญญาณที่แนะนำโดยโมดูลสัญญาณร่วมกับระดับตำแหน่งของคำสั่งซื้อที่รอดำเนินการและทางออกฉุกเฉิน (Stop Loss) จะมีสามวิธีในการจัดส่งหนังสือแจ้ง - หน้าต่างป๊อปอัพมาตรฐานพร้อมข้อความและเสียงสัญญาณอีเมลและการแจ้งเตือนแบบกดไปยังโทรศัพท์มือถือข้อกำหนดทั้งหมดที่จะแสดงอยู่ในรายการ ดังนั้นเราจึงสามารถดำเนินการเขียนโปรแกรมได้ เพื่อที่จะวาดแผนภูมิวัตถุทั้งหมดที่เราวางแผนไว้ตัวบ่งชี้ควรมี: หนึ่งบัฟเฟอร์ของชนิด DRAW_FILLING (กรอกช่วงของบาร์ในช่วงชั่วโมงแรกของวัน) และสามบัฟเฟอร์เพื่อแสดงระดับการค้า (ระดับรายการให้ใช้ระดับกำไร หยุดระดับการสูญเสีย) หนึ่งในนั้น (รอการจัดตำแหน่งระดับการสั่งซื้อ) ควรมีคุณลักษณะในการเปลี่ยนสี (DRAW_COLOR_LINE type) ขึ้นอยู่กับทิศทางการซื้อขายสำหรับอีกสองคนก็จะเพียงพอที่จะมี DRAW_LINE แบบสีเดียว:
[
ตอนนี้ประกาศรายการซึ่งบางส่วนไม่จำเป็นต้องใช้ในตัวบ่งชี้ (ใช้เฉพาะโดย Expert Advisor) แต่พวกเขามีส่วนร่วมในการทำงานของโมดูลสัญญาณ ตัวแปรเหล่านี้จำเป็นต้องใช้สำหรับการทำงานกับการบันทึกข้อมูลและวิธีการต่างๆในการปิดบัญชี เราจะละเว้นพวกเขาในตัวบ่งชี้ด้วย - ขอให้ฉันเตือนว่าที่นี่เรามีเลียนแบบเพียงของง่ายทำกำไรในระดับที่กำหนดไว้ล่วงหน้า (Take Profit) ตามประกาศของตัวแปรเหล่านั้นเราอาจเชื่อมต่อโมดูลภายนอก, รายการการตั้งค่าผู้ใช้และประกาศตัวแปรทั่วโลก:
#property indicator_chart_window #property indicator_buffers 6 #property indicator_plots 4 #property indicator_label1 “1st hour of a day" #property indicator_type1 DRAW_FILLING #property indicator_color1 C'255,208,234', C'179,217,255' #property indicator_width1 1 #property indicator_label2 “Entry level" #property indicator_type2 DRAW_COLOR_LINE #property indicator_style2 STYLE_DASHDOT #property indicator_color2 clrDodgerBlue, clrDeepPink #property indicator_width2 2 #property indicator_label3 "Stop Loss" #property indicator_type3 DRAW_LINE #property indicator_style3 STYLE_DASHDOTDOT #property indicator_color3 clrCrimson #property indicator_width3 1 #property indicator_label4 "Take Profit" #property indicator_type4 DRAW_LINE #property indicator_color4 clrGreen #property indicator_width4 1]
[
ฟังก์ชันการเตรียมใช้งานไม่มีอะไรน่าทึ่งที่นี่กำหนดดัชนีของบัฟเฟอร์ตัวบ่งชี้ไปยังอาร์เรย์ที่ประกาศสำหรับบัฟเฟอร์เหล่านี้ เช่นกันที่นี่ขอเปลี่ยนการตั้งค่าของผู้ใช้จากจุดเป็นราคาสัญลักษณ์ นี้ควรจะทำเพื่อลดการใช้ทรัพยากรอย่างน้อยน้อยและไม่ให้การแปลงดังกล่าวหลายพันครั้งในระหว่างการทำงานของโปรแกรมหลัก:
enum ENUM_LOG_LEVEL { // The list of logging levels LOG_LEVEL_NONE, // logging disabled LOG_LEVEL_ERR, // only info about errors LOG_LEVEL_INFO, // errors + robot comments LOG_LEVEL_DEBUG // all without exclusions }; enum ENUM_ENTRY_SIGNAL { // List of entry signals ENTRY_BUY, // buy signal ENTRY_SELL, // sell signal ENTRY_NONE, // no signal ENTRY_UNKNOWN, // status indefinite ENTRY_INTERNAL_ERROR // internal function error }; enum ENUM_EXIT_SIGNAL { // The list of exit signals EXIT_UNKNOWN, // not identified EXIT_BUY, // close buys EXIT_SELL, // close sells EXIT_ALL, // close all EXIT_NONE // close nothing }; #include <Expert\Signal\Signal_Momentum_Pinball.mqh> // signal module of ‘Momentum Pinball’ TS input uint TS_MomPin_Take_Profit = 10; // Momentum Pinball: Take Profit (in points) input bool Show_1st_H1_Bar = true; // Show day 1st hourly bar range? input bool Alert_Popup = true; // Alert: Show pop-up window? input bool Alert_Email = false; // Alert: Send e-mail? input string Alert_Email_Subj = ""; // Alert: Subjects of e-mail alert input bool Alert_Push = true; // Alert: Send push-notification? input uint Days_Limit = 7; // History layout depth (calendar days) ENUM_LOG_LEVEL Log_Level = LOG_LEVEL_DEBUG; // Logging mode double buff_1st_H1_Bar[], buff_1st_H1_Bar_Zero[], // buffers for filling day 1st hourly bar range buff_Entry[], buff_Entry_Color[], // buffers of pending order line buff_SL[], // buffer of StopLoss line buff_TP[], // buffer of TakeProfit line gd_Entry_Offset = 0, // TS_MomPin_Entry_Offset in symbol prices gd_Exit_Offset = 0 // TS_MomPin_Exit_Offset in symbol prices ;]
[
ในโค้ดตัวบ่งชี้ของบทความก่อนหน้านี้ชุดโครงสร้างของโปรแกรมบางตัวได้ถูกสร้างขึ้นการกำหนดของมันคือการเก็บข้อมูลประเภทใด ๆ ระหว่างเห็บ คุณสามารถอ่านได้ว่าทำไมมันจึงเป็นสิ่งที่จำเป็นและสร้างมาอย่างไรที่นี่ เราจะเข้าร่วมโดยไม่มีการปรับเปลี่ยนใด ๆ ในรุ่นตัวบ่งชี้นี้ออกจากชุดฟังก์ชันที่สมบูรณ์ของ "บราวนี่" จะมีเฉพาะการตั้งค่าสถานะของจุดเริ่มต้นของแถบใหม่เท่านั้น แต่ถ้าคุณต้องการทำให้ตัวบ่งชี้การซื้อขายด้วยตนเองสูงขึ้นคุณลักษณะ "Brownie" อื่น ๆ จะใช้งานได้ รหัสที่สมบูรณ์ของ go_Brownie มีอยู่ในตอนท้ายของไฟล์รหัสต้นฉบับของดัชนี (TS_Momentum_Pinball.mq5) ที่แนบมากับบทความนี้ นอกจากนี้คุณยังสามารถดูรหัสฟังก์ชันการแจ้งเตือน f_Do_Alert ได้ซึ่งยังไม่มีการแก้ไขใด ๆ หากเทียบกับตัวบ่งชี้ก่อนหน้าของชุดบทความนี้ดังนั้นจึงไม่จำเป็นต้องพิจารณาในรายละเอียด
int OnInit() { // converting points to symbol prices: gd_Entry_Offset = TS_MomPin_Entry_Offset * _Point; gd_Exit_Offset = TS_MomPin_Exit_Offset * _Point; // designation of indicator buffers: // day 1st hourly bar range rectangle SetIndexBuffer(0, buff_1st_H1_Bar, INDICATOR_DATA); PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0); SetIndexBuffer(1, buff_1st_H1_Bar_Zero, INDICATOR_DATA); PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, 0); // pending order placement line SetIndexBuffer(2, buff_Entry, INDICATOR_DATA); PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, 0); SetIndexBuffer(3, buff_Entry_Color, INDICATOR_COLOR_INDEX); // line SL SetIndexBuffer(4, buff_SL, INDICATOR_DATA); PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, 0); // line TP SetIndexBuffer(5, buff_TP, INDICATOR_DATA); PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, 0); IndicatorSetInteger(INDICATOR_DIGITS, _Digits); IndicatorSetString(INDICATOR_SHORTNAME, "Momentum Pinball"); return(INIT_SUCCEEDED); }]
ภายในตัวจัดการเหตุการณ์ที่ได้รับการติ๊กมาตรฐาน (OnCalculate) ก่อนที่จะเริ่มต้นของลูปหลักของโปรแกรมต้องมีการประกาศตัวแปรที่จำเป็น หากนี่ไม่ใช่การเรียกแรกของลูปหลักช่วงการคำนวณซ้ำควร จำกัด โดยเฉพาะแถบที่เกิดขึ้นจริงในปัจจุบันเท่านั้นสำหรับกลยุทธ์การซื้อขายนี้คือแถบเมื่อวานนี้และวันนี้ ถ้านี่เป็นลูปแรกหลังจากเริ่มต้นการล้างข้อมูลบัฟเฟอร์ตัวบ่งชี้จากข้อมูลที่เหลือควรจัด หากไม่ได้ดำเนินการพื้นที่ที่ไม่ได้ใช้งานจริงจะยังคงอยู่เมื่อเปลี่ยนช่วงเวลา นอกจากนี้การเรียกฟังก์ชันหลักควร จำกัด เพียงครั้งเดียวต่อหนึ่งบาร์ สะดวกในการใช้โครงสร้าง go_Brownie:
[
ตอนนี้ให้เราตั้งโปรแกรมวนรอบการทำงานหลัก ในตอนเริ่มต้นของการทำซ้ำแต่ละครั้งเราควรได้รับข้อมูลจากโมดูลสัญญาณประสิทธิภาพการควบคุมความพร้อมใช้งานของข้อผิดพลาดและจัดเตรียมการเปลี่ยนไปใช้การวนซ้ำในรอบถัดไปถ้าไม่มีสัญญาณใด ๆ
go_Brownie.f_Update(prev_calculated, prev_calculated); // “feed” data to Brownie datetime t_Time = TimeCurrent(); // last known server time int i_Period_Bar = 0, // auxiliary counter i_Current_TF_Bar = 0 // loop beginning bar index ; if(go_Brownie.b_First_Run) { // if this is the 1st launch i_Current_TF_Bar = rates_total — Bars(_Symbol, PERIOD_CURRENT, t_Time — t_Time % 86400 — 86400 * Days_Limit, t_Time); // clearing buffer at re-initialization: ArrayInitialize(buff_1st_H1_Bar, 0); ArrayInitialize(buff_1st_H1_Bar_Zero, 0); ArrayInitialize(buff_Entry, 0); ArrayInitialize(buff_Entry_Color, 0); ArrayInitialize(buff_TP, 0); ArrayInitialize(buff_SL, 0); } else if(!go_Brownie.b_Is_New_Bar) return(rates_total); // waiting for bar closing else { // new bar // minimum re-calculation depth - from day beginning: i_Current_TF_Bar = rates_total — Bars(_Symbol, PERIOD_CURRENT, t_Time — t_Time % 86400, t_Time); } ENUM_ENTRY_SIGNAL e_Entry_Signal = ENTRY_UNKNOWN; // entry signal double d_SL = WRONG_VALUE, // SL level d_TP = WRONG_VALUE, // TP level d_Entry_Level = WRONG_VALUE, // entry level d_Range_High = WRONG_VALUE, d_Range_Low = WRONG_VALUE // pattern's 1 st bar range borders ; datetime t_Curr_D1_Bar = 0, // current D1 bar time (pattern's 2 nd bar) t_Last_D1_Bar = 0, // time of the last bar D1, on which signal was available t_Entry_Bar = 0 // pending order placement bar time ; // making sure that initial re-calculation bar index is within allowed frames: i_Current_TF_Bar = int(fmax(0, fmin(i_Current_TF_Bar, rates_total — 1)));]
[
while(++i_Current_TF_Bar < rates_total && !IsStopped()) { // iterate over the current TF bars // receiving data from signal module: e_Entry_Signal = fe_Get_Entry_Signal(-Time[i_Current_TF_Bar], d_Entry_Level, d_SL, d_TP, d_Range_High, d_Range_Low); if(e_Entry_Signal == ENTRY_INTERNAL_ERROR) { // error of data copying from external indicator buffer // calculations and drawing should be repeated on the next tick: go_Brownie.f_Reset(); return(rates_total); } if(e_Entry_Signal > 1) continue; // no active signal on this bar]
ถ้าโมดูลตรวจพบสัญญาณบนแถบที่ต้องการและส่งคืนค่าระดับรายการโดยประมาณให้คำนวณระดับผลกำไรก่อน (Take Profit):
[
t_Curr_D1_Bar = Time[i_Current_TF_Bar] - Time[i_Current_TF_Bar] % 86400; // start of the day the bar belongs to]
[
เริ่มต้นด้วยการกรอกข้อมูลแบ็กกราวด์ของบาร์ชั่วโมงแรกของวันใช้ในการคำนวณระดับ:
t_Curr_D1_Bar = Time[i_Current_TF_Bar] - Time[i_Current_TF_Bar] % 86400; // start of the day the bar belongs to if(t_Last_D1_Bar < t_Curr_D1_Bar) { // this is 1st bar of the day, on which signal is available t_Entry_Bar = Time[i_Current_TF_Bar]; // remember the time of trading start]
[
จากนั้นวาดเส้นการจัดตำแหน่งคำสั่งซื้อที่ค้างอยู่จนกว่าจะถึงเวลาที่คำสั่งซื้อที่รอดำเนินการกลายเป็นตำแหน่งที่เปิดเช่นเมื่อราคาแตะระดับนี้:
/ Background filling 1st hour bars: if(Show_1st_H1_Bar) { i_Period_Bar = i_Current_TF_Bar; while(Time[--i_Period_Bar] >= t_Curr_D1_Bar && i_Period_Bar > 0) if(e_Entry_Signal == ENTRY_BUY) { // bullish pattern buff_1st_H1_Bar_Zero[i_Period_Bar] = d_Range_High; buff_1st_H1_Bar[i_Period_Bar] = d_Range_Low; } else { // bearish pattern buff_1st_H1_Bar[i_Period_Bar] = d_Range_High; buff_1st_H1_Bar_Zero[i_Period_Bar] = d_Range_Low; } }]
[
หากราคาไม่สามารถบรรลุระดับที่คำนวณได้ก่อนวันสิ้นเดือนให้ดำเนินการตามขั้นตอนต่อไปนี้ของลูปหลัก:
// Entry line till crossed by a bar: i_Period_Bar = i_Current_TF_Bar - 1; if(e_Entry_Signal == ENTRY_BUY) { // bullish pattern while(++i_Period_Bar < rates_total) { if(Time[i_Period_Bar] > t_Curr_D1_Bar + 86399) { // day end e_Entry_Signal = ENTRY_NONE; // pending order did not trigger break; } // extend line: buff_Entry[i_Period_Bar] = d_Entry_Level; buff_Entry_Color[i_Period_Bar] = 0; if(d_Entry_Level <= High[i_Period_Bar]) break; // entry was on this bar } } else { // bearish pattern while(++i_Period_Bar < rates_total) { if(Time[i_Period_Bar] > t_Curr_D1_Bar + 86399) { // day end e_Entry_Signal = ENTRY_NONE; // pending order did not trigger break; } // extend line: buff_Entry[i_Period_Bar] = d_Entry_Level; buff_Entry_Color[i_Period_Bar] = 1; if(d_Entry_Level >= Low[i_Period_Bar]) break; // entry was on this bar } }]
[
ถ้าวันนี้ยังไม่เสร็จสิ้นและรอการสั่งซื้อในอนาคตยังคงไม่มีกำหนดไม่มีเหตุผลใดที่จะดำเนินการต่อในลูปของโปรแกรมหลัก:
if(e_Entry_Signal == ENTRY_NONE) { // pending order did not trigger before the day end i_Current_TF_Bar = i_Period_Bar; // this day bars are not interesting to us any more continue; }]
[
หลังจากทั้งสองตัวกรองมีเพียงหนึ่งเหตุการณ์ที่เป็นไปได้เท่านั้นที่ยังคงอยู่ - กำลังรอคำสั่งที่รอดำเนินการอยู่ ลองหาแถบการดำเนินการตามใบสั่งที่รอดำเนินการอยู่และเริ่มต้นด้วยแถบนี้วาดเอากำไรและระดับการหยุดขาดทุนจนกว่าจะมีการข้ามไปหนึ่งราคาด้วยเช่นกันจนกว่าจะปิดตำแหน่ง ถ้าการเปิดและปิดตำแหน่งเกิดขึ้นบนแถบเดียวบรรทัดควรขยายโดยแถบหนึ่งไปยังอดีตเพื่อให้สามารถมองเห็นได้ในแผนภูมิ:
if(i_Period_Bar >= rates_total - 1) break; // current (not finished) day is completed]
[
หลังจากปิดตำแหน่งแล้วแถบที่เหลือของวันอาจถูกละเว้นในลูปหลักของโปรแกรม:
// order triggered, find position closing bar: i_Period_Bar = fmin(i_Period_Bar, rates_total - 1); buff_SL[i_Period_Bar] = d_SL; while(++i_Period_Bar < rates_total) { if(TS_MomPin_Exit_Mode == CLOSE_ON_SL_TRAIL) { if(Time[i_Period_Bar] >= t_Curr_D1_Bar + 86400) break; // this is the following day bar // Lines TP and SL until the bar crossing one of them: buff_SL[i_Period_Bar] = d_SL; buff_TP[i_Period_Bar] = d_TP; if(( e_Entry_Signal == ENTRY_BUY && d_SL >= Low[i_Period_Bar] ) || ( e_Entry_Signal == ENTRY_SELL && d_SL <= High[i_Period_Bar] )) { // SL exit if(buff_SL[int(fmax(0, i_Period_Bar - 1))] == 0.) { // beginning and end on a single bar, extend it by 1 bar to the past buff_SL[int(fmax(0, i_Period_Bar - 1))] = d_SL; buff_TP[int(fmax(0, i_Period_Bar - 1))] = d_TP; } break; } if(( e_Entry_Signal == ENTRY_BUY && d_TP <= High[i_Period_Bar] ) || ( e_Entry_Signal == ENTRY_SELL && d_SL >= Low[i_Period_Bar] )) { // TP exit if(buff_TP[int(fmax(0, i_Period_Bar - 1))] == 0.) { // beginning and end on a single bar, extend it by 1 bar to the past buff_SL[int(fmax(0, i_Period_Bar - 1))] = d_SL; buff_TP[int(fmax(0, i_Period_Bar - 1))] = d_TP; } break; } } }]
[
ที่นี่รหัสห่วงหลักเสร็จสิ้น ขณะนี้ควรมีการแจ้งเตือนหากมีการตรวจพบสัญญาณบนแถบปัจจุบัน:
i_Period_Bar = i_Current_TF_Bar; t_Curr_D1_Bar = Time[i_Period_Bar] - Time[i_Period_Bar] % 86400; while( ++i_Period_Bar < rates_total && t_Curr_D1_Bar == Time[i_Period_Bar] - Time[i_Period_Bar] % 86400 ) i_Current_TF_Bar = i_Period_Bar;]
[
รหัสตัวบ่งชี้ที่มีอยู่ทั้งหมดมีอยู่ในไฟล์ TS_Momentum_Pinball.mq5 ที่แนบมาด้านล่าง
i_Period_Bar = rates_total - 1; // current bar if(Alert_Popup + Alert_Email + Alert_Push == 0) return(rates_total); // all disabled if(t_Entry_Bar != Time[i_Period_Bar]) return(rates_total); // no signal on this bar // message wording: string s_Message = StringFormat("ТС Momentum Pinball: needed %s @ %s, SL: %s", e_Entry_Signal == ENTRY_BUY ? "BuyStop" : "SellStop", DoubleToString(d_Entry_Level, _Digits), DoubleToString(d_SL, _Digits) ); // alert: f_Do_Alert(s_Message, Alert_Popup, false, Alert_Email, Alert_Push, Alert_Email_Subj);]
Expert Advisor for testing the Momentum Pinball TS
คุณสมบัติของที่ปรึกษาผู้เชี่ยวชาญขั้นพื้นฐานควรจะขยายออกไปบ้างเมื่อเตรียมการทดสอบกลยุทธ์การซื้อขายอื่นจากหนังสือ Raschke Connors คุณสามารถค้นหาซอร์สโค้ดที่ใช้เป็นพื้นฐานของรุ่นนี้ร่วมกับคำอธิบายโดยละเอียดในบทความก่อนหน้านี้ ที่นี่เราจะไม่พูดซ้ำตัวเองศึกษาเฉพาะการเปลี่ยนแปลงที่สำคัญและอาหารเสริมและมีอยู่สองอย่างส่วนเสริมแรก - รายการสัญญาณทางออกซึ่งไม่สามารถใช้ได้กับหุ่นยนต์การค้าเวอร์ชันก่อนหน้า นอกจากนี้สถานะ ENTRY_INTERNAL_ERROR ถูกเพิ่มลงในรายการสัญญาณเข้า รายการเหล่านี้มีลำดับเลขไม่แตกต่างจากรายการ enum เดียวกันในตัวบ่งชี้ที่ศึกษาข้างต้น ในรหัสหุ่นยนต์เราวางไว้ก่อนสตริงการเชื่อมต่อของคลาสการซื้อขายไลบรารีมาตรฐาน ในไฟล์ Street_Smarts_Bot_MomPin.mq5 ของสิ่งที่แนบมากับบทความนี่เป็นสตริง 24..32การเปลี่ยนแปลงครั้งที่สองเชื่อมโยงกับข้อเท็จจริงที่ว่าโมดูลสัญญาณนี้มีสัญญาณการปิดตำแหน่งเช่นกัน ลองเพิ่มบล็อครหัสที่เกี่ยวข้องเพื่อทำงานร่วมกับสัญญาณนี้ ในเวอร์ชันหุ่นยนต์ก่อนหน้านี้มีโอเปอเรเตอร์เงื่อนไข 'if' สำหรับตรวจสอบว่าตำแหน่งที่มีอยู่นั้นใหม่หรือไม่ (สตริง 139); ตรวจสอบจะใช้ในการคำนวณและวางระดับเริ่มต้น StopLoss ในรุ่นนี้ให้เพิ่มไปยังตัวดำเนินการ 'if' ผ่านทาง 'else' อื่นที่เป็นบล็อครหัสที่เกี่ยวข้องสำหรับการเรียกโมดูลสัญญาณ หากผลการเรียกร้องดังกล่าวต้องการผู้เชี่ยวชาญที่ปรึกษาควรปิดตำแหน่ง:
[
ในรหัสที่มาของหุ่นยนต์นี่คือสตริง 171..186
} else { // not new position // conditions for position closing ready? ENUM_EXIT_SIGNAL e_Exit_Signal = fe_Get_Exit_Signal(d_Entry_Level, datetime(PositionGetInteger(POSITION_TIME)), e_Entry_Signal, TimeCurrent(), TS_MomPin_Exit_Mode); if(( e_Exit_Signal == EXIT_BUY && e_Entry_Signal == ENTRY_BUY ) || ( e_Exit_Signal == EXIT_SELL && e_Entry_Signal == ENTRY_SELL ) || e_Exit_Signal == EXIT_ALL ) { // it must be closed CTrade o_Trade; o_Trade.LogLevel(LOG_LEVEL_ERRORS); o_Trade.PositionClose(_Symbol); return; } }]
มีการเปลี่ยนแปลงบางอย่างในโค้ดของฟังก์ชันที่ควบคุมความพอเพียงของระยะทางการค้าได้ fb_Is_Acceptable_Distance (strings 424..434)
Strategy backtesting
เราได้สร้างเครื่องมือ (ตัวบ่งชี้และผู้เชี่ยวชาญที่ปรึกษา) สำหรับการศึกษาระบบการซื้อขายซึ่งเป็นที่รู้จักกันดีเนื่องจากหนังสือของ L. Raschke และ L. Connors วัตถุประสงค์หลักของ backtesting ผู้เชี่ยวชาญที่ปรึกษาคือการตรวจสอบความมีชีวิตของหุ่นยนต์การค้าซึ่งเป็นหนึ่งในเครื่องมือเหล่านั้น ดังนั้นฉันไม่ได้เพิ่มประสิทธิภาพพารามิเตอร์จึงมีการทดสอบด้วยการตั้งค่าเริ่มต้น
ผลลัพธ์ที่ได้จากการทำงานทั้งหมดจะมีอยู่ในไฟล์แนบ ที่นี่จะมีการจัดแผนภูมิการเปลี่ยนแปลงความสมดุลเท่านั้น เป็นเพียงภาพประกอบของวัตถุประสงค์ที่สอง (โดยนัย) วัตถุประสงค์ของการทดสอบ - หยาบ (โดยไม่มีการเพิ่มประสิทธิภาพพารามิเตอร์) การประเมินผลการปฏิบัติงานของ TS ภายใต้สภาวะตลาดที่ทันสมัย ขอให้ผมนึกถึงว่าผู้เขียนแสดงกลยุทธ์ตามแผนภูมิจากช่วงปลายศตวรรษที่แล้ว
ผังความผันแปรของยอดคงเหลือในการทดสอบจากผู้เชี่ยวชาญของ Advisor ตั้งแต่ต้นปี 2014 ในราคาของเซิร์ฟเวอร์สาธิต MetaQuotes สัญลักษณ์ - EURJPY, ระยะเวลา - H1:
Similar chart for EURUSD symbol, same timeframe and same testing period:
เมื่อทดสอบโดยไม่ต้องแก้ไขค่าของโลหะ (XAUUSD) ในช่วงเวลาเดียวกันและในช่วงเวลาเดียวกันแผนภูมิความผันแปรจะมีลักษณะดังนี้:
Conclusion
กฎสำหรับระบบการซื้อขายข้อพิพาทโมเมนตัมที่ระบุไว้ใน Street Smarts: ความน่าจะเป็นสูงกลยุทธ์การซื้อขายระยะสั้นจะดำเนินการไปยังรหัสของตัวบ่งชี้และที่ปรึกษาผู้เชี่ยวชาญ แต่คำอธิบายไม่ละเอียดเท่าที่ควรและมีตัวแปรมากกว่าหนึ่งรูปแบบสำหรับกฎการติดตามและปิดตำแหน่ง ดังนั้นสำหรับผู้ที่ต้องการศึกษาลักษณะเฉพาะของระบบการซื้อขายในรายละเอียดมีช่องทางที่ค่อนข้างกว้างสำหรับการเลือกพารามิเตอร์และอัลกอริธึมที่เหมาะสมสำหรับกิจกรรมหุ่นยนต์ รหัสที่สร้างขึ้นจะช่วยให้สามารถทำได้ นอกจากนี้หวังว่ารหัสแหล่งที่มาจะเป็นประโยชน์ในการเรียนรู้การเขียนโปรแกรมเชิงวัตถุ
รหัสแหล่งที่มาไฟล์ที่รวบรวมและไลบรารีใน MQL5.zip จะอยู่ในแคตตาล็อกที่เกี่ยวข้อง การกำหนดแต่ละรายการ:
# File name Type Description 1 LBR_RSI.mq5 indicator Indicator which consolidated ROC and RSI. Used to determine trade direction (or its disabled status) of a starting day 2 TS_Momentum_Pinball.mq5 indicator Indicator for manual trading by this TS. Displays calculated levels of entries and exits, highlights the first hour range, on which basis calculations are performed 3 Signal_Momentum_Pinball.mqh library Library of functions, structures and user settings. Used by indicator and Expert Advisor 4 Street_Smarts_Bot_MomPin.mq5 Expert Advisor Expert Advisor for automated trading by this TS
[Download EA##download##]
ความคิดเห็น
แสดงความคิดเห็น