
独自のトレーリングストップ注文を作成する方法
はじめに
この論説の主題を始める前に、i に点をつけ、 t に横線を引くとよいと思う。もう一度「ポジション」と「オーダー」という用語の定義をする。
- ポジション - はトレードの責務、すなわち、いくらかの金融商品の買いあるいは売りの契約である。一つの商品にポジションは一つだけある。
- オーダー - は金融商品を買う、あるいは売るためのブローカーのための指示である。数種類のタイプのオーダーがある:市場と保留、さらに停止(stop)オーダーである。()close
図 1. ポジションとオーダー
この論説はポジションのためのトレーリングストップロスレベルに焦点を当てている。保留オーダーにこの操作は意味がない、というのはオーダーの価格に直接移れるからである。そしてポジション (あるいはその部分) になれば、この材料は手元にある。
トレードポジションはポジションダイアログボックスで「終了(Close)」ボタンを押すだけで終了できるだけではない。
図 2. ポジション対話ボックスで「終了」ボタンを用いての終了。<br0>1 - ポジション関連メニューで開始、 2 - 「ポジション終了」ボタンを選ぶ
3 - 「終了」ボタンをクリックする。
さらに、ポジションは価格があらかじめ定めた利得のレベル (利得取り:Take Profit) 、あるいは損失のレベル (損失停止:Stop Loss) に到達したとき自動的に終了できる。「終了」ボタンを用いてポジションを終了するのと違って、「損失停止」と「利得取り」による終了は端末から行うのではなく (トレーダーあるいはエキスパートによる)、ブローカーによって行われる。こうして、終了のポジションは完全に接続と電力供給に関わらず保証される。これにより、トレーダーがストップロスの使用を実用的に義務的な要素とするのである。
トレーダーが行うべき行動は - ストップロスレベルを設定するようにブローカーに命令を与えることである。言い換えれば、ポジションにストップロスを設定しなければならない (あるいはそのレベルでポジションを開始する)。ストップロスの設定は端末にて"Modify"のメニューコマンドを用いて行われる。ポジションのリストの中にあるポジションを右クリックで選択し、さらに「修正あるいは尺女」を選択する。次にポジションの対話ボックスで損失停止の必要なレベルを記入し、"修正"をクリックする。
図 3. ポジションのストップロスのレベルを設定する。1 - ポジション関連のメニューを開ける、 2 - "Modify or Delete"をクリックする, 3 - 値を設定する, 4 - "Modify"をクリックする。
ポジションのストップロスレベルは価格チャート上でその開始レベルと共に表示される(図 4)。
図 4. 損失停止のポジションそのレベルは左端にslとラベルのついた赤の点線でマークされている。
ポジションのストップロスをインストールするだけでなく、その値を周期的に変更することができる。例えば、価格が利益の出る方向に変化するときにそれを引き上げることができ、可能性ある損失を減少させることができる。このように保護レベルを引き上げるのはトレーリングストップ(Trailing Stop)として知られる。
多くの継続停止の変形があり、単に価格が所定の範囲に入った後で損失停止を引き上げることができる。ストップロスを直ちに始めなくても良い、しかしポジションがある利得のレベルに達したなら直ちに損益分離点のレベルに移動する。この変形は標準的なものでMetaTrader 5 のクライアントターミナルに組み込まれている。標準のストップロスを使うならポジションを右クリックし、「Trailing Stop」を選択する。
図 5. ターミナルの標準のストップロスを有効化する1 - ポジション関連のメニューを開き, 2 - "Trailing Stop"をクリック、 3 - 値を選ぶ (値を設定)。
値(カスタム)を設定する命令はその関連メニューの一番下に あり、そして画像には出ていない。
さらに直接の価格を監視するには、ストップロスをレクニカルインジケーターにもとづいて行える。例えば、短期の価格変化には反応しないようにできる移動平均に基づいて、Ichimokuインジケーター 、あるいはさらにこの目的に設計されたものではないが放物線 (Parabolic) SAR (停止と逆転)に基づいて。 図 6 参照
図 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 変数に)、そして sl と possl の変数の値と比較する。 もし新しい Stop Loss id の値が現在の値より良ければ - ポジションを修正する。
修正の前に MqlTradeRequestストラクチャーの sl と tp フィールド を埋める。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. トレーリングストップなしのエキスパートのテスト結果
図 8. トレーリングストップありのエキスパートのテスト結果
トレーリングストップを用いた効果は明白である。
My_First_EA_SARTrailing.mq5 ファイルはこの論説に添付されている。
4. NRTRのためのトレーリングストップサブクラス
NRTR インジケーター (Nick Rypock 継続逆転) その名前と見え方(図 9)はその上に継続停止を作成しようとする関心を作る。
図 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) とトレーリングストップなしのエキスパートの作業 (図 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 をスタートした後のチャート上のボタンとインジケーター
対応するポジションを開始した後フォローするにはそのボタンを押すだけである。
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





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索