English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
자신만의 추적 손절매 만드는 법

자신만의 추적 손절매 만드는 법

MetaTrader 5지표 | 18 8월 2021, 09:32
296 0
Dmitry Fedoseev
Dmitry Fedoseev

소개

글 주제로 시작하기 전에 i에 점을 찍고 t를 교차하는 것이 좋습니다. 다시 한 번 "포지션"와 "주문"이라는 용어를 정의합니다.

  • 포지션 - 거래 의무, 즉 금융 상품에 대한 매수 또는 매도 계약 수입니다. 하나의 인스트루먼트당 하나의 포지션만 있을 수 있습니다.
  • 주문 - 브로커가 금융 상품을 사고파는 것에 대한 지시입니다. 몇 가지 유형의 주문이 있습니다: 시장 및 보류, 그리고 손절매 주문(손절매 및 이익실현).

그림 1. 포지션 및 주문.

그림 1. 포지션 및 주문.

이 글은 포지션에 대한 후행 손절매 수준에 중점을 둡니다. 보류 중인 주문의 경우 주문 가격으로 직접 이동할 수 있으므로 이 작업이 의미가 없습니다. 그리고 그것이 포지션(또는 그 부분)으로 변할 때, 이 자료가 유용할 것입니다.

거래 포지션은 포지션 대화 상자에서 "닫기" 버튼을 눌러 닫을 수 있을 뿐만 아니라(그림 2).

그림 2. 포지션 대화 상자에서 "닫기" 버튼을 사용하여 포지션을 닫습니다.

그림 2. 포지션 대화 상자에서 "닫기" 버튼을 사용하여 포지션을 닫습니다. 1 - 포지션 컨텍스트 메뉴 열기, 2 - '포지션 닫기' 선택
3 - "닫기" 버튼을 클릭합니다.

또한 가격이 일정 수준의 이익 실현(Take Profit) 또는 손절매(Stop Loss)에 도달하면 포지션이 자동으로 닫힐 수 있습니다. "닫기" 버튼을 사용하는 클로징 포지션과 달리 손절매 및 이익실현에 의한 클로징은 터미널(트레이더 (trader) 또는 expert에 의한)이 아닌 브로커에 의해 이루어집니다. 따라서 연결 여부 및 전원 공급 여부에 관계없이 닫힘 포지션이 완전히 보장됩니다. 이것은 트레이더의 작업에서 손절매의 사용을 실질적으로 필수 요소로 만듭니다.

트레이더가 해야 할 유일한 조치는 중개인에게 보호 정지 수준을 설정하도록 명령하는 것입니다. 즉, 포지션(또는 이 수준이 설정된 오픈 포지션)에 손절매를 설정해야 합니다. 손절매 설정은 터미널의 "수정" 컨텍스트 메뉴 명령을 사용하여 수행됩니다. 포지션 목록에서 포지션을 선택하고 마우스 오른쪽 버튼을 클릭한 다음 "수정 또는 삭제"를 선택합니다. 그런 다음 포지션 대화 상자에서 손절매의 필요한 수준을 입력하고 "수정"을 클릭해야 합니다(그림 3).

그림 3. 포지션의 손절매 수준 설정.

그림 3. 포지션의 손절매 수준 설정. 1 - 포지션 컨텍스트 메뉴 열기, 2 - "수정 또는 삭제" 클릭, 3 - 값 설정, 4 - "수정" 클릭. 

포지션의 손절매 수준은 시가 수준과 함께 가격 차트에 표시됩니다(그림 4).

그림 4. 손절매가 있는 포지션. 수준은 왼쪽 가장자리에 sl이라고 표시된 빨간색 점선으로 표시됩니다.

그림 4. 손절매가 있는 포지션. 수준은 왼쪽 가장자리에 sl이라고 표시된 빨간색 점선으로 표시됩니다.

포지션에 대한 손절매 (Stop Loss)를 설치할 수 있을 뿐만 아니라 주기적으로 값을 변경할 수도 있습니다. 예를 들어 가격이 수익성 방향으로 변할 때 끌어올려 가능한 손실을 줄일 수 있습니다. 이러한 보호 수준을 당기는 것을 추적 손절매(trailing stop)라고 합니다.

추적 손절매에는 변형이 많이 있습니다. 주어진 거리에서 가격 후 손절매를 간단히 풀 수 있습니다. 손절매를 즉시 이동할 수는 없지만 포지션이 특정 수익성에 도달하면 즉시 손익분기점 수준으로 이동합니다. 이 변종은 표준이며 MetaTrader 5 클라이언트 터미널에 내장되어 있습니다. 표준 추적 손절매를 사용하려면 포지션을 마우스 오른쪽 버튼으로 클릭하고 "추적 손절매"를 선택합니다(그림 5).

그림 5. 터미널에서 표준 추적 손절매를 활성화합니다.

그림 5. 터미널에서 표준 추적 손절매를 활성화합니다. 1 - 포지션 컨텍스트 메뉴 열기, 2 - "추적 손절매" 클릭, 3 - 값 선택(또는 값 설정).
값을 설정하는 명령(Custom)
은 컨텍스트 메뉴 하단에 있으며 이미지에는 표시되지 않습니다.

직접적인 가격 모니터링 외에도 추적 손절매는 일부 기술 지표를 기반으로 작동할 수 있습니다. 예를 들어, Ichimoku 지표를 기반으로 하거나 더 적절한 것을 기반으로 하는 이동 평균을 기반으로 하여 단기 가격 변동에 반응하지 않도록 합니다. 그리고 초기에 이 목적을 위해 설계되지 않은 표시기 Parabolic SAR(Stop And Reverse)에서 조차도 마찬가지 입니다. 그림 6을 참조하십시오.  

그림 6. 포물선 SAR 표시기.

그림 6. 포물선 SAR 표시기. 

MQL4 절차 프로그래밍에서 추적 손절매는 일반적으로 별도의 함수로 생성되거나 다른 함수에 통합되었습니다. 예를 들어, MetaTrader 4에 포함된 MACD 샘플 expert에서 추적 손절매 기능은 주문의 시장 마감 기능과 통합됩니다.

for(cnt=0;cnt<total;cnt++)
  {
   OrderSelect(cnt,SELECT_BY_POS,MODE_TRADES);
   if(OrderType()<=OP_SELL &&         // check for opened position 
      OrderSymbol()==Symbol())        // check for symbol
     {
      if(OrderType()==OP_BUY)         // long position is opened
        {
         // should it be closed?
         if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious && 
            MacdCurrent>(MACDCloseLevel*Point))
           {
            OrderClose(OrderTicket(),OrderLots(),Bid,3,Violet); // close position
            return(0); // exit
           }
         // check for trailing stop
         if(TrailingStop>0) 
           {
             
            if(Bid-OrderOpenPrice()>Point*TrailingStop)
              {
               if(OrderStopLoss()<Bid-Point*TrailingStop)
                 {
                  OrderModify(OrderTicket(),OrderOpenPrice(),Bid-Point*TrailingStop,OrderTakeProfit(),0,Green);
                  return(0);
                 }
              }
           }
        }
      else // go to short position
        {
         // should it be closed?
         if(MacdCurrent<0 && MacdCurrent>SignalCurrent && 
            MacdPrevious<SignalPrevious && MathAbs(MacdCurrent)>(MACDCloseLevel*Point))
           {
            OrderClose(OrderTicket(),OrderLots(),Ask,3,Violet); // close position
            return(0); // exit
           }
         // check for trailing stop
         if(TrailingStop>0) 
           {
             
            if((OrderOpenPrice()-Ask)>(Point*TrailingStop))
              {
               if((OrderStopLoss()>(Ask+Point*TrailingStop)) || (OrderStopLoss()==0))
                 {
                  OrderModify(OrderTicket(),OrderOpenPrice(),Ask+Point*TrailingStop,OrderTakeProfit(),0,Red);
                  return(0);
                 }
              }
           }
        }
     }
  }

객체 지향 언어로서의 MQL5는 expert 설계에 있어 훨씬 더 많은 가능성을 제공합니다. 이를 통해 나중에 거의 모든 expert에게 빠르고 쉽게 통합할 수 있는 다목적 및 다기능 클래스를 만들 수 있습니다. 이 글에서 우리는 그러한 클래스를 개발할 것입니다.


1. 추적 손절매의 기본 클래스 생성

위에서 언급했듯이 추적 손절매가 엄청나게 많지만 모두 공통된 기능적 측면이 있습니다.

  • 포지션의 종류(방향) 결정
  • 현재 포지션의 손절매 수준 결정
  • 새로운 손절매 수준 계산
  • 현재 손절매 수준을 변경할 필요가 있는지 확인
  • 수정 손절매 포지션 수준

추적 손절매의 유형은 계산된 손절매 수준의 값만 결정합니다. 따라서 추적 손절매의 기본 기능은 기본 클래스에 포함될 것입니다. 기능의 경우 추적 손절매 유형에 따라 하위 클래스가 생성됩니다. 이러한 하위 클래스의 메소드에 적용하는 것은 기본 클래스의 가상 메소드를 통해 이루어집니다.

기술적 지표를 사용할 계획이므로 안정적인 작동을 보장하기 위해 정기적인 장치를 제공해야 합니다. 이를 위해 타이머를 사용합니다. 우리는 또한 추적 손절매를 켜고 끄고(클래스를 기계 거래 시스템의 일부로 사용할 때), 그래픽 개체 - 버튼을 사용하여 켜고 끌 계획입니다(클래스를 보조 expert 시스템의 일부로 사용할 때). 이러한 기능적 요구 사항에 따라 기본 클래스에는 다음과 같은 메소드 집합이 있습니다.

class CTrailingStop
  {
protected:
public:
   void CTrailingStop(){};
   void ~CTrailingStop(){};
   void Init(){};                   // Initialization of class
   bool StartTimer(){};             // Start timer
   void StopTimer(){};              // Stop timer
   void On(){};                     // Turn on trailing stop
   void Off(){};                    // Turn off trailing stop
   bool DoStoploss(){};             // Main method of controlling level of Stop Loss position
   void EventHandle(){};            // Method of processing chart events (pressing button to turn on trailing stop)
   void Deinit(){};                 // Deinitialization
   virtual bool Refresh(){};        // Refresh indicator
   virtual void Setparameters(){};  // Setting parameters and loading indicator
   virtual int Trend(){};           // Trend shown by indicator
   virtual double BuyStoploss(){};  // Stop Loss value for the Buy position
   virtual double SellStoploss(){}; // Stop Loss value for the Sell position
  };

Init() 메소드를 호출할 때 사용된 추적 손절매의 유형에 의존하지 않는 일반 매개변수를 수락합니다. 이 방법은 추적 손절매 모드를 설정하고 일부 시장 매개변수로 변수를 준비합니다.

  • StartTimer() - 표시기에 주기적으로 주소를 지정하고 터미널 캐시에 강제로 유지하는 데 필요한 타이머를 시작하는 데 사용됩니다.
  • Stoptimer() - expert의 작업 종료 시 타이머를 중지하는 데 사용됩니다.
  • On() - 추적 손절매를 활성화하고 버튼을 누름 모드로 설정합니다(버튼이 사용되는 경우).
  • Off() - 추적 손절매를 비활성화하고 버튼을 누름 모드로 설정합니다(버튼이 사용되는 경우).
  • DoStoploss() - 손절매 포지션의 수준을 제어하는 ​​주요 방법
  • EventHandle() - 차트 이벤트를 처리하는 데 사용되며, 특히 버튼 포지션에 따라 버튼을 누르고 추적 손절매를 켜거나 끄는 것에 응답합니다.
  • Deinit() - expert가 작업을 완료하면 실행되고 표시기 핸들이 해제되도록 합니다.
  • Refresh() - 표시기 값을 새로 고칩니다. 이 방법은 손절매 값을 계산하기 전에 표시기의 현재 값을 결정하는 데 필요합니다. 또한 이 방법은 독립적으로 사용됩니다. 표시기를 작동 상태로 유지하기 위해 타이머에 의해 주기적으로 호출됩니다.
  • SetParameters() - 이 메소드를 호출하면 표시기 매개변수를 수락하면 표시기가 지정된 매개변수와 함께 로드됩니다.
  • Trend() - 지표로 표시되는 추세를 찾는 방법입니다. 표시기가 위쪽 방향을 나타내면 값 1을 반환하고 아래쪽이면 -1을 반환합니다.
  • BuyStoploss() 및 SellStoploss() 메소드는 지표에 의해 계산된 매수 및 매도 포지션에 대한 손절매의 새로운 값을 반환합니다. 

1.1. 초기화() 메소드

Init() 메소드는 클래스의 인스턴스를 생성한 후 호출되는 첫 번째 메소드입니다. 기호, 시간 프레임, 추적 손절매 모드(틱 또는 바 기준)와 관계없이 일반 매개변수를 허용하여 차트에 표시기를 첨부하거나 생성하지 않고 버튼을 생성하거나 생성하지 않습니다. 그런 다음 버튼 속성을 수락합니다: 버튼의 X 좌표, 버튼의 Y 좌표, 버튼 색상, 버튼 캡션 색상.

추가 작업에 필요한 매개변수는 클래스 변수에 저장됩니다. 또한 Init() 메소드가 작동할 때 추적 손절매에 필요한 주요 변하지 않는 시장 매개변수인 쉼표 뒤의 자릿수와 포인트 값을 결정합니다. 마지막으로 추적 손절매 유형에 따라 버튼의 이름과 캡션이 형성됩니다. 버튼을 사용하도록 설정하면 생성됩니다. 

"보호된" 섹션에서 필요한 모든 변수를 선언해 보겠습니다.

protected:
string m_symbol;             // symbol
ENUM_TIMEFRAMES m_timeframe; // timeframe
bool m_eachtick;             // work on each tick
bool m_indicator;            // show indicator on chart
bool m_button;               // show "turn on/turn off" button
int m_button_x;              // x coordinate of button
int m_button_y;              // y coordinate of button
color m_bgcolor;             // button color
color m_txtcolor;            // button caption color
int m_shift;                 // bar shift
bool m_onoff;                // turned on/turned off
int m_handle;                // indicator handle
datetime m_lasttime;         // time of trailing stop last execution
MqlTradeRequest m_request;   // trade request structure
MqlTradeResult m_result;     // structure of trade request result
int m_digits;                // number of digits after comma for price
double m_point;              // value of point
string m_objname;            // button name
string m_typename;           // name of trailing stop type
string m_caption;            // button caption

이제 Init() 메소드 자체를 작성해 보겠습니다.

//--- Trailing stop initialization method
void Init(string             symbol,
          ENUM_TIMEFRAMES timeframe,
          bool   eachtick  =   true,
          bool   indicator =  false,
          bool   button    =  false,
          int    button_x  =      5,
          int    button_y  =     15,
          color  bgcolor   = Silver,
          color  txtcolor  =   Blue)
  {
//--- set parameters
   m_symbol    = symbol;    // symbol
   m_timeframe = timeframe; // timeframe
   m_eachtick  = eachtick;  // true - work on each tick, false - false - work once per bar 
//--- set bar, from which indicator value is used
   if(eachtick)
     {
      m_shift=0; // created bar in per tick mode
     }
   else
     {
      m_shift=1; // created bar in per bar mode
     }
   m_indicator = indicator; // true - attach indicator to chart
   m_button    = button;    // true - create button to turn on/turn off trailing stop
   m_button_x  = button_x;  // x coordinate of button
   m_button_y  = button_y;  // y coordinate of button
   m_bgcolor   = bgcolor;   // button color
   m_txtcolor  = txtcolor;  // button caption color 
//--- get unchanged market history 
   m_digits=(int)SymbolInfoInteger(m_symbol,SYMBOL_DIGITS); // number of digits after comma for price
   m_point=SymbolInfoDouble(m_symbol,SYMBOL_POINT);         // value of point 
//--- creating button name and button caption
   m_objname="CTrailingStop_"+m_typename+"_"+symbol;        // button name
   m_caption=symbol+" "+m_typename+" Trailing";             // button caption 
//--- filling the trade request structure
   m_request.symbol=m_symbol;                               // preparing trade request structure, setting symbol
   m_request.action=TRADE_ACTION_SLTP;                      // preparing trade request structure, setting type of trade action
//--- creating button
   if(m_button)
     {
      ObjectCreate(0,m_objname,OBJ_BUTTON,0,0,0);                 // creating
      ObjectSetInteger(0,m_objname,OBJPROP_XDISTANCE,m_button_x); // setting x coordinate
      ObjectSetInteger(0,m_objname,OBJPROP_YDISTANCE,m_button_y); // setting y coordinate
      ObjectSetInteger(0,m_objname,OBJPROP_BGCOLOR,m_bgcolor);    // setting background color
      ObjectSetInteger(0,m_objname,OBJPROP_COLOR,m_txtcolor);     // setting caption color
      ObjectSetInteger(0,m_objname,OBJPROP_XSIZE,120);            // setting width
      ObjectSetInteger(0,m_objname,OBJPROP_YSIZE,15);             // setting height
      ObjectSetInteger(0,m_objname,OBJPROP_FONTSIZE,7);           // setting font size
      ObjectSetString(0,m_objname,OBJPROP_TEXT,m_caption);        // setting button caption 
      ObjectSetInteger(0,m_objname,OBJPROP_STATE,false);          // setting button state, turned off by default
      ObjectSetInteger(0,m_objname,OBJPROP_SELECTABLE,false);     // user can't select and move button, only click it
      ChartRedraw();                                              // chart redraw 
     }
//--- setting state of trailing stop
   m_onoff=false;                                                 // state of trailing stop - turned on/turned off, turned off by default 
  };

버튼 이름과 캡션을 생성할 때 어떤 값으로도 초기화되지 않은 m_typename 변수가 사용되는 것을 볼 수 있습니다. 값을 할당하는 것은 하위 클래스 생성자에서 수행됩니다. 따라서 다른 추적 손절매 방법을 사용할 때 사용된 추적 손절매 유형에 따라 다른 값을 갖게 됩니다. 

1.2. StartTimer() 메소드

StartTimer() 메소드는 expert의 공통 타이머를 시작합니다.  

//--- Start timer
bool StartTimer()
  {
   return(EventSetTimer(1));
  };

타이머를 사용할 때 OnTimer() 함수에 Refresh() 메소드 호출을 추가하여 주기적으로 인디케이터에 호소해야 합니다.

1.3. StopTimer() 메소드

StartTimer() 메소드는 expert의 타이머를 중지합니다.  

//--- Stop timer
void StopTimer()
  {
   EventKillTimer();
  };

expert가 작업을 마치면 이 방법을 사용했다면 타이머를 중지합니다. 이 메소드는 클래스의 Deinit() 메소드를 실행할 때 호출됩니다.  

1.4. On() 메소드

On() 메소드는 추적 손절매를 켭니다. 켜기는 값이 true인 변수 m_onoff를 할당하여 수행됩니다. 버튼이 클래스 초기화에 사용되도록 설정되어 있으면 눌립니다. 

//--- Turn on trailing stop
void On()
  {
   m_onoff=true; 
   if(m_button)
     { // if button is used, it is "pressed"
      if(!ObjectGetInteger(0,m_objname,OBJPROP_STATE))
        {
         ObjectSetInteger(0,m_objname,OBJPROP_STATE,true);
        }
     }
  }

1.5. Off() 메소드

Off() 메소드는 추적 손절매를 끕니다. 끄기는 값이 false인 변수 m_onoff를 할당하여 수행됩니다. 버튼이 클래스 초기화에 사용되도록 설정되어 있으면 눌려집니다. 

//--- Turn off trailing stop
void Off()
  {
   m_onoff=false;
   if(m_button)
     { // if button is used, it is "depressed"
      if(ObjectGetInteger(0,m_objname,OBJPROP_STATE))
        {
         ObjectSetInteger(0,m_objname,OBJPROP_STATE,false);
        }
     }
  }

1.6. EventHandle() 메소드

EventHandle() 메소드는 OnChartEvent() 함수에서 호출되며 따라서 OnChartEvent() 함수에 전달된 모든 매개변수를 수락합니다.

//--- Method of tracking button state - turned on/turned off
void EventHandle(const int id,const long  &lparam,const double &dparam,const string &sparam)
  {
   if(id==CHARTEVENT_OBJECT_CLICK && sparam==m_objname)
     { // there is an event with button
      if(ObjectGetInteger(0,m_objname,OBJPROP_STATE))
        { // check button state
         On(); // turn on
        }
      else
        {
         Off(); // turn off
        }
     }
  }

CHARTEVENT_OBJECT_CLICK 이벤트가 발생하고 이 이벤트가 m_objname 이름(이벤트가 발생한 객체 이름은 sparam 변수), 버튼의 상태에 따라 On() 또는 Off() 메소드가 실행됩니다.

1.7. Deinit() 메소드

Deinit() 메소드는 expert가 작업을 완료하면 호출되어야 합니다. 이 메소드는 타이머를 중지하고 표시기 핸들을 해제하며 사용된 경우 버튼을 삭제합니다. 

//--- Method of deinitialization
void Deinit()
  {
   StopTimer();                  // stop timer
   IndicatorRelease(m_handle);   // release indicator handle
   if(m_button)
     {
      ObjectDelete(0,m_objname); // delete button
      ChartRedraw();             // chart redraw
     }
  }

Refresh(), SetParameters(), Trend(), BuyStoploss() 및 SellStoploss() 가상 메소드는 추적 손절매 하위 클래스를 만들 때 나중에 설명합니다. 이제 기본 클래스의 주요 메소드인 DoStoploss() 메소드를 살펴보겠습니다.

1.8. DoStoploss() 메소드

DoStoploss() 메소드는 각 틱에서 OnTick() 함수에서 호출되어야 하는 주요 작업 메소드입니다. m_onoff 변수의 값이 false(추적 손절매 해제)인 경우 메소드는 즉시 작업을 완료합니다.  

if(!m_onoff)
  {
   return(true);// if trailing stop is turned off
  }

또한, 추적 손절매가 바 단위 모드에서 작동하는 경우 시간을 확인하여 바가 생성된 시간과 기능이 마지막으로 성공한 시간을 비교합니다. 시간이 일치하면 메소드가 작업을 완료합니다.

datetime tm[1];
// get the time of last bar in per bar mode 
if(!m_eachtick)
  { 
   // if unable to copy time, finish method, repeat on next tick 
   if(CopyTime(m_symbol,m_timeframe,0,1,tm)==-1)
     {
      return(false); 
     }
   // if the bar time is equal to time of method's last execution - finish method
   if(tm[0]==m_lasttime)
     { 
      return(true);
     }
  }

추적 손절매가 켜져 있고 시간 확인이 통과되면 메소드의 주요 부분이 실행됩니다. 표시기 값이 새로 고쳐집니다(Refresh() 메소드가 호출됨).

if(!Refresh())
  { // get indicator values
   return(false);
  }

그런 다음 Trend() 메소드에서 반환된 값에 따라 매수 또는 매도 포지션에 대한 추적 정지가 실행됩니다.

// depending on trend, shown by indicator, do various actions
switch (Trend())
  {
   // Up trend
   case 1: 
      // code of trailing stop for the buy position
      break;
   // Down trend
   case -1: 
      // code of trailing stop for the sell position
      break;
  }

구매 포지션의 예에 대한 작업을 고려하십시오.

if(PositionSelect(m_symbol,1000))
  {   //--- select position. if succeeded, then position exists
   if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
     {//--- if position is buy

      //--- get Stop Loss value for the buy position
      sl=BuyStoploss(); 
      //--- find out allowed level of Stop Loss placement for the buy position
      double minimal=SymbolInfoDouble(m_symbol,SYMBOL_BID)-m_point*SymbolInfoInteger(m_symbol,SYMBOL_TRADE_STOPS_LEVEL);
      //--- value normalizing
      sl=NormalizeDouble(sl,m_digits); 
      //--- value normalizing
      minimal=NormalizeDouble(minimal,m_digits); 
      //--- if unable to place Stop Loss on level, obtained from indicator, 
      //    this Stop Loss will be placed on closest possible level
      sl=MathMin(sl,minimal); 
      //--- value of Stop Loss position
      double possl=PositionGetDouble(POSITION_SL); 
      //--- value normalizing
      possl=NormalizeDouble(possl,m_digits); 
      if(sl>possl)
        {//--- if new value of Stop Loss if bigger than current value of Stop Loss, 
         //    an attempt to move Stop Loss on a new level will be made
         //--- filling request structure
         m_request.sl=sl; 
         //--- filling request structure
         m_request.tp=PositionGetDouble(POSITION_TP); 
         //--- request
         OrderSend(m_request,m_result); 
         if(m_result.retcode!=TRADE_RETCODE_DONE)
           {//--- check request result
            //--- log error message
            printf("Unable to move Stop Loss of position %s, error #%I64u",m_symbol,m_result.retcode); 
            //--- unable to move Stop Loss, finishing
            return(false); 
           }
        }
     }
  }

포지션을 선택할 수 있는 경우 유형이 확인됩니다. 포지션 유형이 추세에 해당하면 BuyStoploss() 메소드를 사용하여 손절매의 필요한 값을 얻습니다(sl 변수로). 다음으로 손절매를 설정할 수 있는 허용 수준을 결정합니다. 계산된 수준이 허용된 것보다 가까우면 sl 변수의 값을 조정하십시오. 그런 다음 손절매 포지션의 현재 값(possl 변수로)을 가져오고 slpossl 변수의 값을 비교합니다. Stop Loss id의 새로운 값이 현재 값보다 좋은 경우 - 포지션을 수정합니다.

수정하기 전에 Mqltraderequest 구조의 sltp 필드를 작성하십시오. m_request.sl 변수는 손절매의 필수 값인 m_request.tp 변수와 함께 할당되며 기존의 이익실현 값과 함께 할당됩니다(변경되지 않은 상태로 두십시오). 나머지 필드는 Init() 메소드가 실행될 때 채워집니다. 구조를 채운 후 OrderSend() 함수가 호출됩니다.

작업이 끝나면 m_result.retcode 변수의 값을 확인합니다. 값이 TRADE_RETCODE_DONE과 같지 않으면 어떤 이유로 OrderSend() 함수에서 요청한 작업을 수행할 수 없습니다. 동시에 로그 메시지에 오류 수와 메소드 완료가 실행됩니다. OrderSend() 함수가 성공적으로 종료되면 DoStoploss() 메소드의 마지막 작업이 이루어진 바의 시간을 기억하십시오. 오류가 발생하면 바 단위 모드에서도 다음 틱에서 메소드를 다시 시도합니다. 작업을 성공적으로 완료하는 한 시도는 계속됩니다.

다음은 DoStopLoss() 메소드의 전체 코드입니다.

bool DoStoploss()
  {
//--- if trailing stop is turned off
   if(!m_onoff)
     {
      return(true);
     }
   datetime tm[1];
//--- get the time of last bar in per bar mode
   if(!m_eachtick)
     {
      //--- if unable to copy time, finish method, repeat on next tick 
      if(CopyTime(m_symbol,m_timeframe,0,1,tm)==-1)
        {
         return(false);
        }
      //--- if the bar time is equal to time of method's last execution - finish method
      if(tm[0]==m_lasttime)
        {
         return(true);
        }
     }
//--- get indicator values
   if(!Refresh())
     {
      return(false);
     }
   double sl;
//--- depending on trend, shown by indicator, do various actions
   switch(Trend())
     {
      //--- Up trend
      case 1:
         //--- select position. if succeeded, then position exists
         if(PositionSelect(m_symbol))
           {
            //--- if position is buy
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
              {
               //--- get Stop Loss value for the buy position
               sl=BuyStoploss();
               //--- find out allowed level of Stop Loss placement for the buy position
               double minimal=SymbolInfoDouble(m_symbol,SYMBOL_BID)-m_point*SymbolInfoInteger(m_symbol,SYMBOL_TRADE_STOPS_LEVEL);
               //--- value normalizing
               sl=NormalizeDouble(sl,m_digits);
               //--- value normalizing
               minimal=NormalizeDouble(minimal,m_digits);
               //--- if unable to place Stop Loss on level, obtained from indicator, 
               //    this Stop Loss will be placed on closest possible level
               sl=MathMin(sl,minimal);
               //--- value of Stop Loss position
               double possl=PositionGetDouble(POSITION_SL);
               //--- value normalizing
               possl=NormalizeDouble(possl,m_digits);
               //--- if new value of Stop Loss if bigger than current value of Stop Loss, 
               //    an attempt to move Stop Loss on a new level will be made
               if(sl>possl)
                 {
                  //--- filling request structure
                  m_request.sl=sl;
                  //--- filling request structure
                  m_request.tp=PositionGetDouble(POSITION_TP);
                  //--- request
                  OrderSend(m_request,m_result);
                  //--- check request result
                  if(m_result.retcode!=TRADE_RETCODE_DONE)
                    {
                     //--- log error message
                     printf("Unable to move Stop Loss of position %s, error #%I64u",m_symbol,m_result.retcode);
                     //--- unable to move Stop Loss, finishing
                     return(false);
                    }
                 }
              }
           }
         break;
         //--- Down trend
      case -1:
         if(PositionSelect(m_symbol))
           {
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
              {
               sl=SellStoploss();
               //--- adding spread, since Sell is closing by the Ask price
               sl+=(SymbolInfoDouble(m_symbol,SYMBOL_ASK)-SymbolInfoDouble(m_symbol,SYMBOL_BID));
               double minimal=SymbolInfoDouble(m_symbol,SYMBOL_ASK)+m_point*SymbolInfoInteger(m_symbol,SYMBOL_TRADE_STOPS_LEVEL);
               sl=NormalizeDouble(sl,m_digits);
               minimal=NormalizeDouble(minimal,m_digits);
               sl=MathMax(sl,minimal);
               double possl=PositionGetDouble(POSITION_SL);
               possl=NormalizeDouble(possl,m_digits);
               if(sl<possl || possl==0)
                 {
                  m_request.sl=sl;
                  m_request.tp=PositionGetDouble(POSITION_TP);
                  OrderSend(m_request,m_result);
                  if(m_result.retcode!=TRADE_RETCODE_DONE)
                    {
                     printf("Unable to move Stop Loss of position %s, error #%I64u",m_symbol,m_result.retcode);
                     return(false);
                    }
                 }
              }
           }
         break;
     }
//--- remember the time of method's last execution
   m_lasttime=tm[0];
   return(true);
  }

구매 및 판매 포지션에 대한 코드의 차이점에 유의하십시오. 매도 포지션의 경우 SellStoploss()가 반환하는 값은 매도 포지션이 매도가로 마감되기 때문에 스프레드 값만큼 증가합니다. 따라서 매수를 위한 손절매 최소 수준의 카운트다운은 매도의 경우 매도인의 매도호가부터 매도인의 매수호가부터 이루어집니다.

지금은 추적 손절매 기본 클래스 생성을 완료했습니다. 서브클래스 생성을 진행해 보겠습니다. 

2. 포물선 SAR 표시기에 대한 추적 손절매 하위 분류

CTrailingStop 클래스의 가상 메소드는 이미 하위 클래스의 내용을 알려줍니다 - SetParameters(), Refresh(), Trend(), BuyStoploss(), SellStoploss() 메소드 및 추적 손절매의 이름을 설정하는 클래스 생성자. 클래스 이름은 CParabolicStop으로 지정됩니다. 이 클래스는 CTrailingStop의 하위 클래스이므로 선언에서 언급됩니다.

class CParabolicStop: public CTrailingStop

이 선언으로 CParabolicStop 클래스의 가상 메소드를 호출하면 기본 클래스의 상속된 메소드가 실행됩니다.  

하위 클래스의 모든 메소드를 자세히 살펴보겠습니다.

2.1. CParabolicStop() 메소드

이 메소드는 클래스 자체와 이름이 같으며 이 메소드를 생성자라고 합니다. 클래스가 로드될 때 클래스의 다른 메소드보다 먼저 자동으로 실행됩니다. CParabolicStop() 메소드에서 추적 손절매의 이름은 m_typename 변수에 할당됩니다. 이 변수는 버튼 이름과 캡션을 만드는 데 사용됩니다(기본 클래스의 Init() 메소드에서).

void CParabolicStop()
  {
   m_typename="SAR"; // setting name of trailing stop type
  };

2.2. SetParameters() 메소드

SetParameters() 메소드를 호출할 때 표시기 매개변수를 수락하고 표시기는 이러한 매개변수와 함께 로드됩니다. m_indicator 매개변수가 기본 클래스의 Init() 메소드로 설정되면 표시기가 차트에 첨부됩니다(ChartIndicatorAdd() 함수).

// Method of setting parameters and loading the indicator
bool SetParameters(double sarstep=0.02,double sarmaximum=0.2)
  {
   m_handle=iSAR(m_symbol,m_timeframe,sarstep,sarmaximum); // loading indicator
   if(m_handle==-1)
     {
      return(false); // if unable to load indicator, method returns false
     }
   if(m_indicator)
     {
      ChartIndicatorAdd(0,0,m_handle); // attach indicator to chart
     }
    
   return(true);
  }

2.3. Refresh() 메소드

Refresh() 메소드는 새 가격을 가져오고 표시기 값을 새로 고칩니다. 클래스의 "보호된" 섹션에는 가격 값을 위한 pricebuf 배열과 표시기 값을 위한 indbuf 배열이 있습니다. 두 배열 모두 하나의 요소 크기를 갖습니다. 형성 또는 형성 바에서 하나의 가격 값과 하나의 표시기 값이어야 합니다(기본 클래스 초기화 시 설정되는 m_shift 매개변수에 따라 다름).

// Method of getting indicator values
bool Refresh()
  {
   if(CopyBuffer(m_handle,0,m_shift,1,indbuf)==-1)
     {
      return(false); // if unable to copy value to array, return false
     }
    
   if(CopyClose(m_symbol,m_timeframe,m_shift,1,pricebuf)==-1)
     {
      return(false); // if unable to copy value to array, return false
     }    
   return(true); 
  }

2.4. Trend() 메소드

Trend() 메소드는 표시선을 기준으로 가격 포지션을 확인합니다. 가격이 선 위에 있으면 상승 추세이며 메소드는 값 1을 반환합니다. 가격이 표시선 아래에 있으면 하락 추세이며 메소드는 값 -1을 반환합니다. 가격이 지표선과 같은 경우(드물지만 가능한 경우)를 제외하지 않습니다. 이 경우 값 0이 반환됩니다.  

// Method of finding trend
int Trend()
  {
   if(pricebuf[0]>indbuf[0])
     { // price is higher than indicator line, up trend
      return(1);
     }
   if(pricebuf[0]<indbuf[0])
     { // price is lower than indicator line, down trend
      return(-1);
     }    
   return(0);
  }

2.5. BuyStoploss() 및 SellStoploss() 메소드

Parabolic SAR 표시기에는 한 줄만 있기 때문에 두 방법 모두 동일합니다. Refresh() 메소드에서 얻은 값을 반환합니다.

// Method of finding out Stop Loss level for buy
virtual double BuyStoploss()
  {
   return(indbuf[0]);
  };
// Method of finding out Stop Loss level for sell
virtual double SellStoploss()
  {
   return(indbuf[0]);
  };

지금은 추적 손절매가 준비되었습니다. 아직 클래스가 하나뿐이지만 이미 사용할 수 있습니다. Sample_TrailingStop.mqh 이름 아래의 .\MQL5\Include 폴더에 별도의 포함 파일로 저장합니다(파일은 글에 첨부됨). 

3. expert에 포물선의 추적 손절매 추가

초보자를 위한 MQL5의 Expert Advisor 작성에 대한 단계별 가이드 글의 My_First_EA와 같은 일부 expert에게 추적 손절매를 추가해 보겠습니다.

3.1. MetaEditor에서 My_First_EA expert를 열고 My_First_EA_SARTrailing으로 저장합니다.

3.2. 추적 손절매 파일을 포함합니다. expert 코드의 상단 부분(바람직하게는 외부 변수 선언 전에)에 다음 줄을 추가합니다. 

#include <Sample_TrailingStop.mqh> // include Trailing Stop class

3.3. 외부 변수가 Trailing이라는 CParabolicStop 클래스의 인스턴스를 만든 후.

CParabolicStop Trailing; // create class instance 

 3.4. OnInit() 함수에서 클래스를 초기화하고 해당 매개변수를 설정합니다. 먼저 표시기 매개변수를 사용하여 외부 변수를 선언합니다.

input double TrailingSARStep=0.02;
input double TrailingSARMaximum=0.2;

그런 다음 OnInit() 함수에 코드를 추가합니다.

Trailing.Init(_Symbol,PERIOD_CURRENT,true,true,false); // Initialize (set basic parameters)
if(!trailing.setparameters(TrailingSARStep,TrailingSARMaximum))
  { // Set parameters of used trailing stop type
   Alert("trailing error");
   return(-1);
  } 
Trailing.StartTimer(); // Start timer
Trailing.On();         // Turn on

3.5. expert 코드에서 OnTimer() 함수를 찾습니다. OnTimer() 함수는 My_First_EA에서 사용되지 않으므로 추가하고 여기에 Refresh() 호출을 추가합니다. 

void OnTimer()
  {
   Trailing.Refresh();
  }

3.6. OnTick() 함수의 맨 위에 DoStoploss() 메소드 호출을 추가합니다.

3.7. expert를 컴파일하고 테스트하십시오. expert의 테스트 결과는 그림 7(추적 손절매 없음)과 그림 8(추적 손절매 포함)에 나와 있습니다.

그림 7. 추적 손절매가 없는 expert의 테스트 결과.

그림 7. 추적 손절매가 없는 expert의 테스트 결과.   

그림 8. 추적 손절매가 있는 expert의 테스트 결과.

그림 8. 추적 손절매가 있는 expert의 테스트 결과. 

추적 손절매 사용의 효과는 분명합니다.

글에 My_First_EA_SARTrailing.mq5 파일이 첨부되어 있습니다.

4. NRTR에 대한 추적 손절매 하위 클래스

이름과 모양에 따른 NRTR 표시기(Nick Rypock Trailing Reverse)(그림 9)는 그 위에 추적 손절매를 생성하려는 시도를 하게 만듭니다.

그림 9. NRTR 표시기.

그림 9. NRTR 표시기.

지표는 기준선(지지선 또는 저항선)과 목표선을 그립니다. 가격이 목표선을 초과하면 기준선이 가격 이동 방향으로 이동하고 가격의 작은 변동은 무시됩니다. 가격이 기준선과 교차할 때 - 추세의 변화로 간주하여 가격에 대한 기준선과 목표선의 포지션을 ​​변경합니다. 상승 추세에서 지지선과 목표선은 파란색으로, 하락 추세에서는 빨간색으로 칠해집니다.

이제 NRTR 표시기에 대해 다른 추적 손절매를 만듭니다.

CNRTRStop 기본 클래스에 포함된 다른 클래스 CNRTRStop을 선언합니다. 

class CNRTRStop: public CTrailingStop

NRTR 하위 클래스에 대한 추적 손절매는 생성자를 제외하고 Parabolic에 대한 추적 손절매와 정확히 동일한 메소드 집합을 갖습니다. 이제 CNRTRstop()으로 이름이 지정됩니다.  

4.1. CNRTRStop() 메소드

이제 클래스 생성자에서 m_typename 변수에 사용된 표시기에 따라 NRTR 값이 할당됩니다.

void CNRTRStop()
  {
   m_typename="NRTR"; // setting name of trailing stop type
  };

4.2. SetParameters() 메소드

SetParameters() 메소드를 호출할 때 표시기 매개변수를 수락하고 로드됩니다. 그런 다음 추적 손절매의 기본 매개변수에 따라 차트에 표시기가 첨부됩니다.

// Method of setting parameters and loading the indicator
bool SetParameters(int period,double k)
  {
   m_handle=iCustom(m_symbol,m_timeframe,"NRTR",period,k); // loading indicator
   if(m_handle==-1)
     { // if unable to load indicator, method returns false
      return(false); 
     }
   if(m_indicator)
     {
       
      ChartIndicatorAdd(0,0,m_handle); // attach indicator to chart
     }
   return(true);
  }

4.3. Refresh() 메소드

Refresh() 메소드는 NRTR 표시기의 두 버퍼(지원 라인 버퍼와 저항 라인 버퍼)를 복사합니다.  

 // Method of getting indicator values
bool Refresh()
  {
   if(CopyBuffer(m_handle,0,m_shift,1,sup)==-1)
     {
      return(false); // if unable to copy value to array, return false
     }
    
   if(CopyBuffer(m_handle,1,m_shift,1,res)==-1)
     {
      return(false); // if unable to copy value to array, return false
     }
    
   return(true);
  }

클래스의 "protected" 섹션에는 double sup[] 및 double res[]라는 두 개의 선언된 배열이 있습니다.

protected:
double sup[1]; // value of support level
double res[1]; // value of resistance level

4.4. Trend() 메소드

Trend() 메소드는 현재 존재하는 라인을 확인합니다. 지지선이라면 지표가 상승 추세를 보여주고 있음을 의미합니다. 메소드 자체는 값 1을 반환합니다. 저항선인 경우 메소드는 -1을 반환합니다. 

// Method of finding trend
int Trend()
  {
   if(sup[0]!=0)
     { // there is support line, then it is up trend
      return(1);
     }
   if(res[0]!=0)
     { // there is resistance line, then it is down trend
      return(-1);
     }
    
   return(0);
  }

4.5. BuyStoploss() 메소드

BuyStoploss() 메소드는 지지선의 값을 반환합니다. 

// Method of finding out Stop Loss level for buy
double BuyStoploss()
  {
   return(sup[0]);
  }

4.6. SellStoploss() 메소드

SellStoploss() 메소드는 저항선의 값을 반환합니다.  

// Method of finding out Stop Loss level for sell
double SellStoploss()
  {
   return(res[0]);
  }

이제 추적 손절매 클래스가 완료되었습니다. 

5. Expert에 NRTR에 대한 추적 손절매 추가

Parabolic에 대한 추적 손절매와 마찬가지로 NRTR에 대한 My_First_EA 추적 손절매를 Expert에 추가해 보겠습니다.

5.1. MetaEditor에서 My_First_EA_SARTrailing expert를 열고 My_First_EA_NRTRTrailing으로 저장합니다.

5.2. Parabolic에 대한 추적 손절매의 외부 매개변수를 NRTR에 대한 추적 손절매 매개변수로 교체합니다.

input int TrailingNRTRPeriod = 40;
input double TrailingNRTRK   =  2;

5.3. CParabolicStop 클래스의 인스턴스를 만드는 대신 CNRTRStop 클래스의 인스턴스를 만듭니다. 코드는 외부 변수 뒤에 있습니다. 

CNRTRStop Trailing; // create class instance 

5.4. OnInit() 함수에서 SetParameters() 메소드 호출의 매개 변수를 NRTR 매개 변수로 바꿉니다.

Trailing.SetParameters(TrailingNRTRPeriod,TrailingNRTRK)

5.5. expert를 컴파일하고 테스트하십시오. 

그림 10. NRTR에 대한 추적 손절매가 있는 expert의 테스트 결과.

그림 10. NRTR에 대한 추적 손절매가 있는 expert의 테스트 결과.

추적 손절매 전략이 있는 Expert Advisor(그림 10)의 작업 결과는 그렇지 않은 expert의 작업(그림 7)과 비교하여 거의 변경되지 않았습니다. Parabolic에 대한 추적 손절매를 사용하는 것이 이 expert에게 더 효과적인 것으로 판명되었습니다. 특정 수의 추적 손절매 장치는 expert를 개발할 때 실험을 수행하고 가장 적합한 추적 손절매 유형을 선택하는 데 매우 유용할 수 있다고 결론지을 수 있습니다.  

글에 My_First_EA_NRTRTrailing.mq5 파일이 첨부되어 있습니다.

6. expert 비서

추적 손절매의 기본 클래스를 만들 때 버튼을 통해 추적 손절매 켜기/끄기를 제어하기 위한 것입니다. 다양한 유형의 추적 손절매가 있는 다양한 기호의 포지션을 ​​추적하는 expert 조수를 만들어 보겠습니다. expert는 포지션을 열지 않고 열린 포지션만 따를 것입니다.

6.1. MetaEditor에서 Sample_TrailingStop이라는 새 expert를 만듭니다.

6.2. Sample_TrailingStop.mqh 파일을 포함합니다. 

#include <Sample_TrailingStop.mqh> // include Trailing Stop class

6.3. 지표에 대한 외부 매개변수를 선언합니다.

input double SARStep=0.02;     // Step of Parabolic
input double SARMaximum=0.02;  // Maximum of Parabolic
input int NRTRPeriod=40;       // NRTR period
input double NRTRK=2;          // NRTR factor

6.4. expert가 작업할 수 있는 기호 배열을 선언합니다.

string Symbols[]={"EURUSD","GBPUSD","USDCHF","USDJPY"};

6.5. 클래스를 로드할 배열을 선언합니다.

CParabolicStop *SARTrailing[];
CNRTRStop *NRTRTrailing[];

6.6. OnInit() 함수에서 Symbols 배열의 크기에 따라 클래스를 로드하도록 배열의 크기를 조정합니다. 

ArrayResize(SARTrailing,ArraySize(Symbols));  // resize according to number of used symbols
ArrayResize(NRTRTrailing,ArraySize(Symbols)); // resize according to number of used symbols 

6.7. 배열 로드 클래스 인스턴스의 각 요소에 대해 루프에서.

for(int i=0;i<ArraySize(Symbols);i++)
  { // for all symbols
   SARTrailing[i]=new CParabolicStop(); // create CParabolicStop class instance
   SARTrailing[i].Init(Symbols[i],PERIOD_CURRENT,false,true,true,5,15+i*17,Silver,Blue);    // initialization of CParabolicStop class instance 
   if(!SARTrailing[i].SetParameters(SARStep,SARMaximum))
     { // setting parameters of CParabolicStop class instance 
      Alert("trailing error");
      return(-1);
     }
   SARTrailing[i].StartTimer();         // start timer
//----
   NRTRTrailing[i]=new CNRTRStop();     // create CNRTRStop class instance
   NRTRTrailing[i].Init(Symbols[i],PERIOD_CURRENT,false,true,true,127,15+i*17,Silver,Blue); // initialization of CNRTRStop class instance
   if(!NRTRTrailing[i].SetParameters(NRTRPeriod,NRTRK))
     { // setting parameters of CNRTRcStop class instance 
      Alert("trailing error");
      return(-1);
     }
   NRTRTrailing[i].StartTimer();        // start timer 
  }

참고: Init() 메소드를 호출할 때 버튼 좌표가 계산됩니다. 왼쪽에는 Parabolic의 경우 추적 손절매의 전원 버튼이 있고 오른쪽에는 NRTR의 경우 전원 버튼이 있습니다.  

6.8. Int OnTick() 함수는 추적 손절매의 각 인스턴스에 대해 DoStoploss() 메소드 호출을 추가합니다.

for(int i=0;i<ArraySize(Symbols);i++)
  {
   SARTrailing[i].DoStoploss();
   NRTRTrailing[i].DoStoploss();
  }

6.9. 차트 이벤트 처리를 추가합니다. 

void OnChartEvent(const int         id,
                  const long   &lparam,
                  const double &dparam,
                  const string &sparam 
                  )
  {
   for(int i=0;i<ArraySize(Symbols);i++)
     {
      SARTrailing[i].EventHandle(id,lparam,dparam,sparam);
      NRTRTrailing[i].EventHandle(id,lparam,dparam,sparam);
     }
    
  }

6.10. Deinit() 함수에서 모든 클래스 인스턴스를 초기화 해제하고 삭제합니다.

for(int i=0;i<ArraySize(Symbols);i++)
  {
   SARTrailing[i].Deinit(); 
   NRTRTrailing[i].Deinit();
   delete(SARTrailing[i]);  // delete object
   delete(NRTRTrailing[i]); // delete object
  }

편집하고 차트에 expert를 첨부합니다. 차트에 표시기와 버튼이 나타납니다(그림 11) - Expert Advisor가 작동할 준비가 되었습니다.  

그림 11. Sample_TrailingStop을 시작한 후 차트의 버튼 및 표시기.

그림 11. Sample_TrailingStop을 시작한 후 차트의 버튼 및 표시기. 

열렸을 때 해당 포지션을 따라가려면 버튼을 누르기만 하면 됩니다.

Sample_TrailingStop.mq5 파일이 글에 첨부되어 있습니다.

결론

기계 거래 시스템을 만들 때 CTrailingStop 클래스를 사용하는 순서를 살펴보겠습니다.

1. Sample_TrailingStop.mqh 파일을 포함합니다.

2. 사용된 추적 손절매의 표시 매개변수와 함께 외부 변수를 선언합니다.

3. 클래스 인스턴스를 생성합니다.

4. OnInit() 함수에서 Init(), SetParameters(), StartTimer() 및 On() 메소드 호출을 추가합니다.

5. OnTimer() 함수에서 Refresh() 메소드 호출을 추가합니다.

6. OnTick() 함수에서 DoStopLoss() 메소드 호출을 추가합니다.

7. OnDeinit() 함수에서 Deinit() 메소드 호출을 추가합니다.


7단계, 5분 미만, Expert Advisor는 추적 손절매 기능이 있습니다!

MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/134

MQL5에서 다중 색상 표시기 만들기 MQL5에서 다중 색상 표시기 만들기
이 글에서는 다중 색상 표시기를 생성하거나 기존 표시기를 다중 색상으로 변환하는 방법을 고려할 것입니다. MQL5를 사용하면 정보를 편리한 형식으로 표현할 수 있습니다. 이제 지표가 있는 수십 개의 차트를 보고 RSI 또는 스토캐스틱 수준의 분석을 수행할 필요가 없습니다. 지표의 값에 따라 다른 색상으로 캔들을 페인트하는 것이 좋습니다.
Expert Advisor의 한계 및 검증 Expert Advisor의 한계 및 검증
월요일에 이 기호를 거래할 수 있습니까? 포지션을 열 수 있는 충분한 자금이 있습니까? 손절매가 발동되면 손실이 얼마나 됩니까? 보류 중인 주문 수를 제한하는 방법은 무엇입니까? 거래 작업이 현재 바에서 실행되었습니까 아니면 이전 바에서 실행되었습니까? 거래 로봇이 이러한 종류의 검증을 수행할 수 없다면 모든 거래 전략이 패배할 수 있습니다. 이 문서는 모든 Expert Advisor에서 유용한 검증의 예를 보여줍니다.
MQL5에서 추세를 찾는 여러 방법 MQL5에서 추세를 찾는 여러 방법
모든 트레이더는 주어진 시간에 추세를 정확하게 감지하는 기회를 많이 얻게 됩니다. 아마 이거야 말로 모두가 찾고 있는 성배 (Holy Grail)일 것입니다. 이 글에서는 추세를 감지하는 몇 가지 방법을 고려해보겠습니다. 더 정확하게 말하면 MQL5를 통해 추세를 감지하는 몇 가지 고전적인 방법을 프로그래밍하는 방법입니다.
하나의 지표를 다른 지표에 적용하기 하나의 지표를 다른 지표에 적용하기
OnCalculate() 함수 호출의 간단한 형식을 사용하는 지시자를 작성할 때, 가격 데이터뿐만 아니라 다른 지시자의 데이터로도 지시자를 계산할 수 있다는 사실을 놓칠 수 있습니다 (내장형이든 맞춤형이든 상관없이). 다른 지표의 데이터에 대한 올바른 적용을 위해 지표를 개선하고 싶습니까? 이 글에서는 그러한 수정에 필요한 모든 단계를 검토할 것입니다.