English Русский 中文 Español Deutsch Português 한국어 Français Italiano Türkçe
独自のトレーリングストップ注文を作成する方法

独自のトレーリングストップ注文を作成する方法

MetaTrader 5インディケータ | 18 9月 2015, 18:31
4 949 0
Dmitry Fedoseev
Dmitry Fedoseev

はじめに

この論説の主題を始める前に、i に点をつけ、 t に横線を引くとよいと思う。もう一度「ポジション」と「オーダー」という用語の定義をする。

  • ポジション - はトレードの責務、すなわち、いくらかの金融商品の買いあるいは売りの契約である。一つの商品にポジションは一つだけある。
  • オーダー - は金融商品を買う、あるいは売るためのブローカーのための指示である。数種類のタイプのオーダーがある:市場と保留、さらに停止(stop)オーダーである。()close

図 1. ポジションとオーダー

図 1. ポジションとオーダー

この論説はポジションのためのトレーリングストップロスレベルに焦点を当てている。保留オーダーにこの操作は意味がない、というのはオーダーの価格に直接移れるからである。そしてポジション (あるいはその部分) になれば、この材料は手元にある。

トレードポジションはポジションダイアログボックスで「終了(Close)」ボタンを押すだけで終了できるだけではない。

図 2. ポジション対話ボックスで「終了」ボタンを用いての終了。

図 2. ポジション対話ボックスで「終了」ボタンを用いての終了。<br0>1 - ポジション関連メニューで開始、 2 - 「ポジション終了」ボタンを選ぶ
3 - 「終了」ボタンをクリックする。

さらに、ポジションは価格があらかじめ定めた利得のレベル (利得取り:Take Profit) 、あるいは損失のレベル (損失停止:Stop Loss) に到達したとき自動的に終了できる。「終了」ボタンを用いてポジションを終了するのと違って、「損失停止」と「利得取り」による終了は端末から行うのではなく (トレーダーあるいはエキスパートによる)、ブローカーによって行われる。こうして、終了のポジションは完全に接続と電力供給に関わらず保証される。これにより、トレーダーがストップロスの使用を実用的に義務的な要素とするのである。

トレーダーが行うべき行動は - ストップロスレベルを設定するようにブローカーに命令を与えることである。言い換えれば、ポジションにストップロスを設定しなければならない (あるいはそのレベルでポジションを開始する)。ストップロスの設定は端末にて"Modify"のメニューコマンドを用いて行われる。ポジションのリストの中にあるポジションを右クリックで選択し、さらに「修正あるいは尺女」を選択する。次にポジションの対話ボックスで損失停止の必要なレベルを記入し、"修正"をクリックする。

図 3. ポジションのストップロスのレベルを設定する。

図 3. ポジションのストップロスのレベルを設定する。1 - ポジション関連のメニューを開ける、 2 - "Modify or Delete"をクリックする, 3 - 値を設定する, 4 - "Modify"をクリックする。

ポジションのストップロスレベルは価格チャート上でその開始レベルと共に表示される(図 4)。

図 4. 損失停止のポジションそのレベルは左端にslとラベルのついた赤の点線でマークされている。

図 4. 損失停止のポジションそのレベルは左端にslとラベルのついた赤の点線でマークされている。

ポジションのストップロスをインストールするだけでなく、その値を周期的に変更することができる。例えば、価格が利益の出る方向に変化するときにそれを引き上げることができ、可能性ある損失を減少させることができる。このように保護レベルを引き上げるのはトレーリングストップ(Trailing Stop)として知られる。

多くの継続停止の変形があり、単に価格が所定の範囲に入った後で損失停止を引き上げることができる。ストップロスを直ちに始めなくても良い、しかしポジションがある利得のレベルに達したなら直ちに損益分離点のレベルに移動する。この変形は標準的なものでMetaTrader 5 のクライアントターミナルに組み込まれている。標準のストップロスを使うならポジションを右クリックし、「Trailing Stop」を選択する。

図 5. 端末の標準の継続停止を有効化する

図 5. ターミナルの標準のストップロスを有効化する1 - ポジション関連のメニューを開き, 2 - "Trailing Stop"をクリック、 3 - 値を選ぶ (値を設定)。

値(カスタム)を設定する命令はその関連メニューの一番下に あり、そして画像には出ていない。

さらに直接の価格を監視するには、ストップロスをレクニカルインジケーターにもとづいて行える。例えば、短期の価格変化には反応しないようにできる移動平均に基づいて、Ichimokuインジケーター 、あるいはさらにこの目的に設計されたものではないが放物線 (Parabolic) SAR (停止と逆転)に基づいて。 図 6 参照

図 6. 放物線 SAR インジケーター

図 6. 放物線 SAR インジケーター

MQL4 手続きプログラミングにおいてストップロスは通常分離したファンクションであったり、あるいは他のファンクションに統合されていた。例えば、 MetaTrader 4に含まれていたMACDサンプルエキスパートでは、継続停止ファンクションはオーダーの市場終了のファンクションに統合されている。

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);
                 }
              }
           }
        }
     }
  }

Object-Oriented LanguageのMQL5 はエキスパートの設計にさらに多くの可能性を与える。これは汎用的で複数の関数を持つクラス を作成し、それは後ほど素早く簡単に大半のエキスパートにも統合できる。この記事ではこのようなクラスを開発する。


1. ストップロスの基礎クラスを作成する

上に述べたように、膨大な数のトレーリングストップがあるが、それらは全て共通の仕様で作成されている。

  • ポジションのタイプ (方向) を決定する
  • 現在のポジションのストップロスのレベルを決定する。
  • 新しいストップロスレベルを計算する
  • 現ストップロスレベルを変える必要性をチェック
  • ポジションのストップロスのレベルを修正する

ストップロスの種類はストップロスレベルの値を決定するだけである。そのため、トレーリングストップの機能は基礎クラスに含まれる。機能的には、トレーリングストップのタイプに依存して、サブクラスが作成される。これらサブクラスのメソッドを適用するのは、基礎クラスの仮想メソッド を通じて行われる。

テクニカルインジケーターを使う計画なので、その安定な動きを確実にするため定期的な装備を提供することが要求される。この目的のため タイマーを使用する。トレーリングストップのオン・オフの切り替えを行い、 (クラスを自動トレードシステムの部分として使うとき)、そのオン・オフにはgraphical object -ボタンを使う (クラスを補助エキスパートの部分として使うとき)。 これらの機能的要求に従って基礎クラスは次の一連のメソッドを持つ:

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() - はエキスパートの作業を終了し、タイマーを停止させるのに使う。
  • On() - はトレーリングストップを可能にしそしてボタンを押された状態に設定する (ボタンを使う場合)。
  • Off() - はトレーリングストップを無効化しボタンを押されていない状態に設定する (ボタンを使う場合)。
  • DoStoploss() - はポジションの損失停止レベルを制御する中心のメソッドである。
  • EventHandle() - は チャートイベントの処理に使い、 特にボタンの位置によって、押されたボタンに対応し、トレーリングストップのオン・オフの切り替えを行うのに使う。
  • Deinit() - はエキスパートがその作業を終えた際に呼ばれ、インジケーターのハンドルの開放を確実にする。
  • Refresh() - はインジケーターの値を更新するのに使う。このメソッドはインジケーターの現在の値をストップロスの値を計算する前に決定するのに必要である。また、このメソッドは独立して使われる - これはタイマーによって周期的に呼ばれ、インジケーターを使える状態に保つ。
  • SetParameters() - このメソッドを呼ぶとインジケーターのパラメーターを受け入れ、インジケーターは指定のパラメーターを供給される。
  • Trend() - インジケーターで示されるトレンドを得るメソッド。もしインジケーターが上昇方向を示すと、1 の値を戻し、下降方向なら -1 を戻す。
  • BuyStoploss() to SellStoploss() メソッドは売りと買いのポジションにインジケーターで計算された新しいストップロスの値を戻す。

1.1. Init() メソッド

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() メソッド はエキスパートの共通 タイマー をスタートする。

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

タイマーを使うとき、インジケーターに周期的に働きかけるため、Refresh()メソッドをOnTimer()機能に追加しなくてはいけない。

1.3. StopTimer() メソッド

StopTimer() メソッド はエキスパートの タイマー をストップする。

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

エキスパートが処理を終えると、このメソッドがタイマーを使った場合には停止させる。このメソッドはクラスの Deinit() メソッドを走らせるときに呼ばれる。

1.4. On() メソッド

On() メソッドはトレーリングストップをオンにする。オンにするのは変数 m_onoff にtrue値を割り当てて行う。 もしボタンがクラスの初期化にて使用されるようセットされると、トレーリングストップがオンに切り替えられる。

//--- 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() メソッドがトレーリングストップをオフに切り替える。オフにするには変数 m_onoff にfalse値を割り当てて行う。もしボタンがクラスの初期化に使用されるようセットされると、オフに切り替わる。

//--- 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() メソッドはエキスパートが作業を終えたときに呼ばれる。このメソッドはタイマーを止め、インジケーターのハンドルを開放し、もし使われた場合にはボタンを消去する。

//--- 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() 仮想メソッドは後ほどトレーリングストップサブクラスを作成するときに説明する。  さてベースクラスのメソッドを考えよう - the 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() メソッドを用いて、必要な Stop Loss の値を得る(sl 変数に)。次に、Stop Loss がセットできる許容されるレベルを決定する。 もし計算された値が近ければ許容され、 sl変数の値を合わせる。そして現在のStop Lossポジションの値を得 (possl 変数に)、そして slpossl の変数の値と比較する。 もし新しい Stop Loss id の値が現在の値より良ければ - ポジションを修正する。

修正の前に MqlTradeRequestストラクチャーの sltp フィールド を埋める。m_request.sl 変数が要求された Stop Lossの値で割り当てられ、 m_request.tp 変数は - Take Profit の既存の値を割り当てられる(変わらない)。残りのフィールドは Init() メソッドが実行されるときに埋められる。ストラクチャーを埋めた後 OrderSend() ファンクションが呼ばれる。

この処理を終えると m_result.retcode 変数の値がチェックされる。もしその値が TRADE_RETCODE_DONEと等しくなければ、 OrderSend() ファンクションの要請によって、ある理由により処理を実行することができない。同時にエラーの数とメソッドの完了がメッセージのログに記録される。OrderSend() function が成功裏に終了すると、バーの時刻を呼び出し、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);
  }

買いと売りポジションのコードの違いに注意する。売りのポジションには、売りポジションがAsk 価格に近いため、SellStoploss() から戻る値はスプレッドの値だけ増加する。従って買いの Stop Loss 最小レベルのカウントダウンは Bid 価格から行われ、売りに対しては - ASk 価格からとなる。

これでトレリングストップの基本クラスの作成を終了した。サブクラスの作成に進もう。

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() function)ファンクションに添付される。

// 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() メソッドは新しい価格を取得 、そしてインジケーターの値を更新する。クラスの"protected" 部分には価格の値の pricebuf 配列 とインジケーターの値のための indbuf 配列がある。どちらの配列も一つを持ち - それはバーを形成するあるいは形成した1つのみの価格の値とインジケーター の値 (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() メソッド

放物線 SAR インジケーターは1本のラインのみを持つので、どちらのメソッドも同一である。それらは 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]);
  };

これでトレーリングストップの準備ができた。それはただ1つのクラスしか持たないが、しかし既に使われている可能性がある。それをSample_TrailingStop.mqhの名前で \MQL5\Include フォルダーの別のインクルードファイルに保存する(ファイルはこの論説に添付されている)。

3. Parabolicのトレーリングストップをエキスパートに加える

ParabolicのトレーリングストップをStep-By-Step Guide to writing an Expert Advisor in MQL5 for Beginners の記事を参考に My_First_EAのようなトレリングストップをエキスパートに加えよう。

3.1. MetaEditorの中のMy_First_EA エキスパート を開き、それを My_First_EA_SARTrailing として保存しよう。

3.2. トレーリングストップファイルをインクルード。このエキスパートのコード上の部分に (できれば外部変数の宣言の前に) そのラインを加える。

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

3.3. 外部変数が CParabolicStop クラスのインスタンスを作成した後 Trailing と名付ける。

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. エキスパートのコード OnTimer() ファンクションを見つける。OnTimer() ファンクションは My_First_EA では用いない、 そこでそれを加え、そしてその中に Refresh() ファンクションの呼び出しを加える。

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

3.6. OnTick() ファンクションの先頭で DoStoploss() メソッドの呼び出しを加える。

3.7. エキスパートをコンパイルし、それを試してみる。エキスパートの結果は図 7 に (継続停止なしで) そして図 8 に (継続停止ありで) on Figure 8 (with trailing stop)に示されている。

図 7. 継続停止なしのエキスパートのテスト結果

図 7. トレーリングストップなしのエキスパートのテスト結果

図 8. 継続停止ありのエキスパートのテスト結果

図 8. トレーリングストップありのエキスパートのテスト結果

トレーリングストップを用いた効果は明白である。

My_First_EA_SARTrailing.mq5 ファイルはこの論説に添付されている。

4. NRTRのためのトレーリングストップサブクラス

NRTR インジケーター (Nick Rypock 継続逆転) その名前と見え方(図 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 インジケーターの2つのバッファーをコピーする - 支持ラインバッファーと抵抗ラインバッファー。

// 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);
  }

"保護された"セクションでは、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. NRTR のトレーリングストップをエキスパートに加える

Parabolic に対するトレーリングストップと同様に NRTR に対する My_First_EA トレーリングストップをエキスパートに加える。

5.1. MetaEditorの中のMy_First_EA_SAR継続エキスパート を開き、それを My_First_EA_NRTR 継続として保存する。

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. エキスパートをコンパイルし、それを試してみる。

図 10. NRTR用のトレーリングストップありのエキスパートのテスト結果

図 10. NRTR用のトレーリングストップありのエキスパートのテスト結果

あるトレーリングストップ戦略付きによるエキスパートアドバイザーの作業結果 (図 10) とトレーリングストップなしのエキスパートの作業 (図 7) と比べるとほとんど変化がない。Parabolic でのトレーリングストップの使用はこのエキスパートに対してより効果的であると証明された。一定の数のトレーリングストップの武器庫 (道具箱) がエキスパートを開発するのに非常に有益であると結論できる - 実験をし、もっとも適切なタイプのトレーリングストップを選択することができる。

My_First_EA_NRTRTrailing.mq5 ファイルはこの記事に添付されている。

6. エキスパート-アシスタント

トレーリングストップの基本クラスが作成されたとき、トレーリングストップをボタンでオン・オフする制御をするように意図されていた。種々のシンボルに対して異なったタイプのトレーリングストップでフォローするためのエキスパート-アシスタントを作成しよう。エキスパートはポジションを開始しない、しかし開始したものをフォローするだけである。

6.1. MetaEditor に Sample_TrailingStop と名付けた新しいエキスパートを作成する。

6.2.Sample_TrailingStop.mqh file をインクルードする。

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

インジケーターの外部パラメーターを宣言する。

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. エキスパートが処理を行うシンボルの配列を宣言する。

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

6.5. クラスをロードする配列を宣言する。

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

6.6. OnInit() ファンクションでクラスをロードする配列のサイズを シンボルの配列のサイズに従って再調整する。

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() メソッドを呼ぶときにはボタンの座標を計算する。左側に放物線用のトレーリングストップの電源ボタンがあり、右側には - NRTR用がある。

6.8. 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() ファンクションの中で全てのクラスのインスタンスをディイニシャライゼーション(deinitialization)をし、それらを消去する。

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

コンパイルし、エキスパートをチャートに添付する。インジケーターとボタンがチャート上に現れる (図 11) - エキスパートアドバイザーの出番である。

図 11. Sample_TrailingStop をスタートした後のチャート上のボタンとインジケーター

図 11. Sample_TrailingStop をスタートした後のチャート上のボタンとインジケーター

対応するポジションを開始した後フォローするにはそのボタンを押すだけである。

Sample_TrailingStop.mq5 ファイルはこの論説に添付されている。

結論

自動トレーディングシステムを作成したときにCTrailingStop クラスを用いたオーダーを見直そう。

1. Sample_TrailingStop.mqh fileをインクルードする。

2. 使用したトレーリングストップのインジケーターパラメーターで外部変数を宣言する。

3. クラスインスタンスを作成する。

4. OnInit()ファンクションからInit()、SetParameters()、 StartTimer() と On() メソッド の呼び出しを追加する 。

5.OnTimer()ファンクションからRefresh() メソッドの呼び出しを追加する。

6. OnTick()ファンクションからDoStopLoss() メソッドの呼び出しを追加する。

7. OnDeinit()ファンクションからDeinit() メソッドの呼び出しを追加する。


7ステップ、5分以内で、エキスパートアドバイザーにトレーリングストップファンクションを作れた。

MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/134

他のインジケーターをベースにインジケーターを作成する 他のインジケーターをベースにインジケーターを作成する
MQL5ではインジケーターをゼロから、あるいは他に顧客の端末に組み込まれていたり、カスタムのものだったりで既に存在するものからなど、どちらからでも作成することができる。 そしてまたここで2つの方法がある - あるインジケーターに新しい計算やグラフィックスタイルを加えることでを改良する方法、あるいは顧客の端末に組み込みあるいはカスタムのものを iCustom() あるいは IndicatorCreate() ファンクション経由で使う方法である。
トレードロボットのプロトタイプ トレードロボットのプロトタイプ
この論説ではトレーディングシステムのアルゴリズムと要素の作成の原理を要約しシステム化する。この論説はエキスパートアルゴリズムの設計について考察する。トレーディングシステムCExpertAdvisor クラスのある例について考察する。これはトレーディングシステムを素早く簡単に開発するのに使える。
トレードシステムの評価 - 参入、退出と取引における一般の有効性 トレードシステムの評価 - 参入、退出と取引における一般の有効性
トレードシステムの有効性と利益性を決定できる多数の尺度がある。しかし、トレーダーは常にどのシステムでも試したいと考えている。この記事はどのようにして有効性の尺度に基づいた統計が MetaTrader 5 のプラットフォームに使えるかを教えるものである。 これは取引による統計の解釈を、S.V.Bulashev(ブラシェフ)による著作"Statistika dlya traderov"(トレーダーのための統計) の記述に矛盾しないものに変換するクラスを含んでいる。また最適化のためのカスタムファンクションの例も含んでいる。
MQL5における 20のトレードシグナル MQL5における 20のトレードシグナル
この記事ではトレードシステムが有効であるために必要なトレードシグナルをいかに受け取るかを教える。ここでは20のトレードシグナルを作る例を個別のカスタムファンクションとして与えるので、エキスパートアドバイザーを開発する間にも利用することができる。便宜上、この論説で使われた全てのファンクションは一つのmqhインクルードファイルにまとめられているので将来エキスパートアドバイザーに接続することは容易である。