กลยุทธ์ Momentum Pinball trading

 

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 สำหรับรายการซื้อมีสูตรดังนี

  1. ในวันที่ D1 มูลค่าของ LBR / RSI ของวันที่ปิดล่าสุดควรอยู่ในพื้นที่ที่ขายเกิน - ต่ำกว่า 30
  2. หลังจากปิดบาร์รายชั่วโมงแรกของวันใหม่แล้วให้วางคำสั่งซื้อที่รอดำเนินการเกินกว่าขีดสูงสุดดังกล่าว
  3. หลังจากที่เรียกใช้คำสั่งซื้อที่ค้างอยู่ให้วางการสูญเสียการหยุดพักไปที่ระดับต่ำสุดของแถบรายชั่วโมงแรก
  4. ถ้าตำแหน่งถูกปิดด้วยความสูญเสียให้วางคำสั่งซื้อที่รอดำเนินการอีกครั้งไว้ที่ระดับเดียวกัน
  5. ถ้าในตอนท้ายของวันตำแหน่งยังคงทำกำไรได้ทิ้งไว้ในวันถัดไป ในวันซื้อขายวันที่สองต้องปิดสถานะดังกล่าว
    การแสดงกฎการเข้าเมืองด้วยความช่วยเหลือของสองตัวชี้วัดที่อธิบายไว้ด้านล่างมีลักษณะดังนี้: 
    - LBR / RSI ในระยะเวลารายวันอยู่ในพื้นที่ขายมาก (ดู 30 ตุลาคม, 2017)

    - ตัวบ่งชี้ TS_Momentum_Pinball ในช่วงเวลาที่ไม่ได้ระบุ (จาก M1 ถึง D1) จะแสดงระดับการซื้อขายและช่วงราคาของช่วงเวลาแรกของวันซึ่งจะคำนวณจากระดับเหล่านี้:


    กฎสำหรับการออกจากตลาดไม่ได้ระบุไว้อย่างชัดเจนในหนังสือ: ผู้เขียนพูดเกี่ยวกับการใช้ต่อท้ายเกี่ยวกับการปิดในเช้าวันรุ่งขึ้นและเกี่ยวกับออกจากที่สูงขึ้นแล้ววันซื้อขายวันแรกสูง
    กฎสำหรับรายการขายมีความคล้ายคลึงกัน - LBR / RSI ควรอยู่ในพื้นที่ที่ซื้อเกิน (สูงกว่า 70) การสั่งซื้อที่ค้างอยู่ควรวางไว้ที่ระดับต่ำสุดของแถบรายชั่วโมงแรก

    LBR/RSI indicator

    แน่นอนการคำนวณทั้งหมดที่จำเป็นสำหรับการรับสัญญาณอาจทำได้ในโมดูลสัญญาณมาก แต่นอกเหนือจากการซื้อขายอัตโนมัติในแผนของบทความนี้ให้การซื้อขายด้วยตนเองเช่นกัน การมีตัวบ่งชี้แยกต่างหาก LBR / RSI พร้อมกับเน้นพื้นที่ที่ซื้อเกินหรือซื้อเกินกำหนดจะเป็นประโยชน์เพื่อความสะดวกในการระบุรูปแบบเวอร์ชันด้วยตนเอง และเพื่อเพิ่มประสิทธิภาพความพยายามของเราเราจะไม่ใช้โปรแกรมการประมาณค่า LBR / RSI หลายรุ่นในหลายรูปแบบ ('buffer' สำหรับตัวบ่งชี้และ 'bufferless' สำหรับหุ่นยนต์) เชื่อมต่อไฟแสดงสถานะภายนอกเข้ากับโมดูลสัญญาณผ่านฟังก์ชัน iCustom มาตรฐาน ตัวบ่งชี้นี้จะไม่ทำการประเมินโดยใช้ทรัพยากรมากและไม่จำเป็นต้องตั้งคำถามในแต่ละจุดที่ TS ระบุค่าของตัวบ่งชี้ที่แถบรายวันที่ปิดไว้ เราไม่สนใจเกี่ยวกับการเปลี่ยนแปลงค่าปัจจุบันอย่างต่อเนื่อง ดังนั้นจึงไม่มีอุปสรรคสำคัญสำหรับการแก้ปัญหาดังกล่าว

    ที่นี่รวมเอาอัลกอริทึมการคำนวณ ROC และ RSI ซึ่งจะวาดเส้นโค้งออสซิลเลเตอร์ที่เป็นผลลัพธ์ เพื่อที่จะตรวจจับค่าที่ต้องการได้อย่างง่ายดายให้เพิ่มการเติมพื้นที่ที่ซื้อจนเกินไปและขายในหลายสี สำหรับการทำเช่นนี้เราต้องใช้ 5 บัฟเฟอร์เพื่อแสดงและอีก 4 ตัวสำหรับการคำนวณเสริม

    เพิ่มการตั้งค่ามาตรฐาน (ระยะเวลา RSI และค่าขอบของสองพื้นที่) ด้วยกฎอื่นที่ไม่ได้มาจากกฎของระบบการซื้อขายต้นฉบับ สำหรับการคำนวณคุณจะสามารถใช้ราคาปิดบาร์ได้ทุกวัน แต่ยังมีค่ามัธยฐานข้อมูลเพิ่มเติมโดยทั่วไปราคาปกติหรือถัวเฉลี่ย อันที่จริงแล้วสำหรับการทดลองของเขาผู้ใช้สามารถเลือกได้ระหว่างเจ็ดตัวแปรที่จัดหาโดย ENUM_APPLIED_PRICE

    การประกาศบัฟเฟอร์กล่องข้อความของผู้ใช้และบล็อกการเตรียมใช้งานจะมีลักษณะดังนี้:
       
    [
    #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);
      }]
    ในตัวจัดการเหตุการณ์มาตรฐาน OnCalculate จัดเรียงลูปสองอันแยกกัน: กลุ่มแรกเตรียมอาร์เรย์ข้อมูล ROC ค่าที่สองจะคำนวณค่า oscillator จากข้อมูลอาร์เรย์นี้

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


    [
    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;
        }]
    ลูปที่สอง (การคำนวณ RSI) ไม่มีลักษณะเฉพาะอย่างยิ่งมันเกือบจะทำซ้ำขั้นตอนของออสซิลเลเตอร์มาตรฐานของประเภทนี้:

    [
    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;
    }]
    ลองตั้งชื่อตัวบ่งชี้ LBR_RSI.mq5 แล้ววางลงในโฟลเดอร์ตัวบ่งชี้มาตรฐานของแคตตาล็อกข้อมูลเทอร์มินัล เป็นชื่อที่จะระบุในฟังก์ชัน iCustom ของโมดูลสัญญาณดังนั้นคุณจึงไม่ควรเปลี่ยน

    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]
    ฟังก์ชั่นโมดูลหลัก fe_Get_Entry_Signal จะรวมเป็นหนึ่งเดียวกับฟังก์ชันโมดูลสัญญาณของกลยุทธ์การซื้อขายก่อนหน้านี้จากหนังสือ Raschke and Connors รวมถึงโมดูลแบบอะนาล็อกที่ตามมาของ TS อื่น ๆ ที่อธิบายไว้ในแหล่งนี้ ซึ่งหมายความว่าฟังก์ชันควรมีแพ็คเกจดังกล่าวของพารามิเตอร์ที่ส่งผ่านไปเชื่อมโยงไปยังตัวแปรและชนิดเดียวกันของค่าที่ส่งคืน:

    [
    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
      }]
    เช่นเดียวกับในเวอร์ชันก่อนหน้าเราจะไม่คำนวณทุกอย่างอีกครั้งเมื่อติ๊กแต่ละครั้งเมื่อเรียกใช้ฟังก์ชันจากโรบอต แต่เราจะเก็บข้อมูลระหว่าง ticks ในระดับที่คำนวณได้ในตัวแปรคงที่ อย่างไรก็ตามการทำงานกับฟังก์ชันนี้ในตัวบ่งชี้การซื้อขายด้วยตนเองจะมีความแตกต่างอย่างมาก และ zeroing ของตัวแปรคงที่เมื่อเรียกฟังก์ชันจากตัวบ่งชี้ควรมีให้ เพื่อแยกแยะระหว่างสายจากตัวบ่งชี้และสายจากหุ่นยนต์ให้ใช้ตัวแปร t_Time ตัวบ่งชี้จะพลิกกลับนั่นคือทำให้ค่าเป็นลบ:

    [
    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);
      }
      }]
    หุ่นยนต์ต่อหนึ่งชั่วโมงต้องวิเคราะห์มูลค่าของตัวบ่งชี้ในแถบรายวันที่ปิดครั้งสุดท้ายและกำหนดทิศทางการซื้อขายในวันนี้ หรือควรปิดการซื้อขายหากค่า LBR / RSI อยู่ในพื้นที่ที่เป็นกลาง รหัสการเรียกค้นของค่านี้จากบัฟเฟอร์ตัวบ่งชี้และการวิเคราะห์ด้วยฟังก์ชันการบันทึกข้อมูลซึ่งอาจมีข้อผิดพลาดและลักษณะเฉพาะของการเรียกจากตัวบ่งชี้การซื้อขายด้วยตนเอง:

    [
    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]
      );
      }]
    เราได้ชี้แจงทิศทางการซื้อขายที่อนุญาต งานต่อไปจะเป็นการกำหนดระดับรายการและข้อ จำกัด ในการสูญเสีย (Stop Loss) นอกจากนี้ยังเพียงพอที่จะทำสิ่งนี้ได้หนึ่งครั้งต่อ 24 ชั่วโมง - หลังจากปิดวันที่แถบแรกในระยะเวลารายชั่วโมง อย่างไรก็ตามขึ้นอยู่กับลักษณะเฉพาะของการทำงานของตัวบ่งชี้การซื้อขายด้วยตนเองเราจะทำให้ขั้นตอนการคำนวณมีความซับซ้อนขึ้นเล็กน้อย นี่เป็นเพราะความจริงที่ว่าตัวบ่งชี้ควรไม่เพียง แต่ตรวจจับระดับสัญญาณแบบเรียลไทม์ แต่ยังต้องทำเครื่องหมายในประวัติ:

    [
    // 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;
      }]
    หลังจากนั้นเราควรหยุดการทำงานโดยการกลับทิศทางการซื้อขายที่ตรวจพบ:

    [
    return(se_Trade_Direction);]
    ตอนนี้ขอวิเคราะห์โปรแกรมเกี่ยวกับเงื่อนไขในการปิดสัญญาณตำแหน่ง เรามีตัวแปร 3 สายซึ่งหนึ่งในนั้น (ระดับการสูญเสียท้าย) จะถูกรับรู้แล้วในโค้ด Expert Advisor ของเวอร์ชันก่อนหน้า สองสายพันธุ์อื่น ๆ ในภาพรวมต้องการราคาและเวลาในการเข้าออกทิศทางตำแหน่งสำหรับการคำนวณ เราจะส่งต่อไปพร้อมกับเวลาปัจจุบันและวิธีการปิดที่เลือกไปยังฟังก์ชัน fe_Get_Exit_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);
      }]
    ที่นี่เรามี 'cap' ในกรณีที่เลือก 'trailing exit' - ฟังก์ชั่นจะส่งสัญญาณกลับคืนโดยไม่มีการวิเคราะห์ใด ๆ สำหรับสองตัวเลือกอื่น ๆ การเกิดเหตุการณ์ 'เช้ามา' และ 'extremum เมื่อวานผ่าน' ถูกระบุ ตัวแปรของค่าของประเภท ENUM_EXIT_SIGNAL ที่ส่งกลับโดยฟังก์ชันจะคล้ายกับรายการค่าสัญญาณเข้าที่คล้ายคลึงกัน (ENUM_ENTRY_SIGNAL):

    [
    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 แบบสีเดียว:

    [
    #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]
    ตอนนี้ประกาศรายการซึ่งบางส่วนไม่จำเป็นต้องใช้ในตัวบ่งชี้ (ใช้เฉพาะโดย Expert Advisor) แต่พวกเขามีส่วนร่วมในการทำงานของโมดูลสัญญาณ ตัวแปรเหล่านี้จำเป็นต้องใช้สำหรับการทำงานกับการบันทึกข้อมูลและวิธีการต่างๆในการปิดบัญชี เราจะละเว้นพวกเขาในตัวบ่งชี้ด้วย - ขอให้ฉันเตือนว่าที่นี่เรามีเลียนแบบเพียงของง่ายทำกำไรในระดับที่กำหนดไว้ล่วงหน้า (Take Profit) ตามประกาศของตัวแปรเหล่านั้นเราอาจเชื่อมต่อโมดูลภายนอก, รายการการตั้งค่าผู้ใช้และประกาศตัวแปรทั่วโลก:

    [
    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
      ;]
    ฟังก์ชันการเตรียมใช้งานไม่มีอะไรน่าทึ่งที่นี่กำหนดดัชนีของบัฟเฟอร์ตัวบ่งชี้ไปยังอาร์เรย์ที่ประกาศสำหรับบัฟเฟอร์เหล่านี้ เช่นกันที่นี่ขอเปลี่ยนการตั้งค่าของผู้ใช้จากจุดเป็นราคาสัญลักษณ์ นี้ควรจะทำเพื่อลดการใช้ทรัพยากรอย่างน้อยน้อยและไม่ให้การแปลงดังกล่าวหลายพันครั้งในระหว่างการทำงานของโปรแกรมหลัก:

    [
    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);
      }]
    ในโค้ดตัวบ่งชี้ของบทความก่อนหน้านี้ชุดโครงสร้างของโปรแกรมบางตัวได้ถูกสร้างขึ้นการกำหนดของมันคือการเก็บข้อมูลประเภทใด ๆ ระหว่างเห็บ คุณสามารถอ่านได้ว่าทำไมมันจึงเป็นสิ่งที่จำเป็นและสร้างมาอย่างไรที่นี่ เราจะเข้าร่วมโดยไม่มีการปรับเปลี่ยนใด ๆ ในรุ่นตัวบ่งชี้นี้ออกจากชุดฟังก์ชันที่สมบูรณ์ของ "บราวนี่" จะมีเฉพาะการตั้งค่าสถานะของจุดเริ่มต้นของแถบใหม่เท่านั้น แต่ถ้าคุณต้องการทำให้ตัวบ่งชี้การซื้อขายด้วยตนเองสูงขึ้นคุณลักษณะ "Brownie" อื่น ๆ จะใช้งานได้ รหัสที่สมบูรณ์ของ go_Brownie มีอยู่ในตอนท้ายของไฟล์รหัสต้นฉบับของดัชนี (TS_Momentum_Pinball.mq5) ที่แนบมากับบทความนี้ นอกจากนี้คุณยังสามารถดูรหัสฟังก์ชันการแจ้งเตือน f_Do_Alert ได้ซึ่งยังไม่มีการแก้ไขใด ๆ หากเทียบกับตัวบ่งชี้ก่อนหน้าของชุดบทความนี้ดังนั้นจึงไม่จำเป็นต้องพิจารณาในรายละเอียด

    ภายในตัวจัดการเหตุการณ์ที่ได้รับการติ๊กมาตรฐาน (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 % 8640086400 * 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;]
    ที่นี่รหัสห่วงหลักเสร็จสิ้น ขณะนี้ควรมีการแจ้งเตือนหากมีการตรวจพบสัญญาณบนแถบปัจจุบัน:

    [
    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);]
    รหัสตัวบ่งชี้ที่มีอยู่ทั้งหมดมีอยู่ในไฟล์ TS_Momentum_Pinball.mq5 ที่แนบมาด้านล่าง

    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' อื่นที่เป็นบล็อครหัสที่เกี่ยวข้องสำหรับการเรียกโมดูลสัญญาณ หากผลการเรียกร้องดังกล่าวต้องการผู้เชี่ยวชาญที่ปรึกษาควรปิดตำแหน่ง:

    [
    } 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;
      }
      }]
    ในรหัสที่มาของหุ่นยนต์นี่คือสตริง 171..186

    มีการเปลี่ยนแปลงบางอย่างในโค้ดของฟังก์ชันที่ควบคุมความพอเพียงของระยะทางการค้าได้ 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##] 

ความคิดเห็น

เรื่องราวที่น่าสนใจ