
あらゆるタイプのトレーリングストップを開発してEAに接続する方法
内容
はじめに
前回の記事に引き続き、今回はトレーリングストップに関する内容をさらに深めます。本稿では、さまざまなストップロスポジションのトレーリングストップを簡単に実装できるトレーリングストップクラスについて考察します。このクラスを用いることで、現在の価格からのストップの移動や、指標値に基づくストップロスレベルの設定など、柔軟なアルゴリズムを作成することが可能になります。この記事を通じて、ストップロス位置を自動で調整するアルゴリズムを作成し、それをEA(エキスパートアドバイザー)に接続する方法を理解できるでしょう。加えて、トレーリングストップの使用がより明確で簡便になることを目指します。
トレーリングストップの操作アルゴリズムを簡単に考えてみましょう。それぞれのトレーリングストップには3つの使用条件があることに同意しましょう。
- トレーリング開始:トレーリング終了が発動されるポジションの利益ポイント数
- トレーリングステップ:ストップロスを次に移動するために価格が利益方向に動くべきポイント数
- トレーリング距離:現在価格からストップロスが配置されるべき距離
これら3つのパラメータは、どのようなトレーリングストップにも適用可能です。設定に応じて必要な場合のみ使用し、不要な場合は省略できます。あるいは、他の値で置き換えることも可能です。例えば、「トレーリング距離」の代わりに、ストップロスを特定の指標値に設定することも可能です。この場合、ストップは指標が示す価格そのものではなく、指標が指示した価格からポイント単位の距離を離れた場所に設定されます。
一般的に、上にリストした3つのパラメータは様々なトレーリングストップで最も使用されるものであり、トレーリングストップクラスを作成する際にはこれらを考慮します。
ストップロス位置を適切な価格に移動する際には、いくつかのチェックが必要です。
- ストップロス価格が現在価格に近すぎないこと:ストップロス価格は、銘柄のStopLevel (SYMBOL_TRADE_STOPS_LEVEL)値による最小シフトポイントを超える必要があります。これは、現在の終値からの最小ポイント以上離れてストップ注文を設定しなければならないことを意味します。
- ストップロス価格が、すでに設定されている価格と同一でないこと。また、ロングポジションでは現在のストップロスより高く、ショートポジションでは低く設定されること。
- トレーリングストップアルゴリズムがパラメータに従っているかを確認すること。
これらは基本的なチェックです。すべてのトレーリングストップアルゴリズムの動作は同じです。ストップを設定するために必要な価格が入力され、必要なすべての制限がチェックされ、すべてのチェックに合格した場合、ストップポジションは指定されたレベルに移動します。
シンプルなトレーリングストップの仕組みは次の通りです。
- トレーリング開始:指定された利益ポイントに達するとトレーリングが開始されます(パラメータを使用しない場合はゼロ)。
- トレーリングステップ:何ポイント利益が出たらストップレベルを引き上げるかを指定します。
- トレーリング距離:現行価格からストップロスの位置までの距離を設定します(パラメータを使用しない場合はゼロ)。
- さらに、すべての銘柄の位置を任意のマジックナンバーでトレーリングする必要がある場合は、トレーリングする銘柄とマジックナンバーを設定するか、それぞれNULLと-1を設定することができます。
ポジションの利益が指定されたポイント数に達すると、トレーリングストップが開始され、ストップは現在の価格から一定の距離に設定されます。その後、価格がさらに指定されたポイント数(トレーリングステップ)だけポジションの利益方向に動くと、ストップも指定された距離を保ちながら再度調整され、価格に追随します。このプロセスは、価格が逆方向に動くまで続きます。価格が反転すると、ストップはその時点で固定され、価格がそのレベルに達すると、ストップロスによりポジションが利益確定されます。このように、トレーリングストップは、価格が反転した際にポジションを決済して利益を確保すると同時に、価格がポジションに有利な方向に動く間は、ストップを価格に追随させて徐々に調整します。これにより、利益を保護しながら、さらなる価格上昇に対応することができます。
「トレーリング距離」パラメータの代わりに、指標から得られる価格を使用することも可能です。これは、指標トレーリングです。例えば、前のローソク足のHighやLowなどの値を指定して、指標に基づいたトレーリングストップをおこなうことができます。これは、バー価格によるトレーリングです。ただし、基本的なトレーリングストップアルゴリズム自体は変更されません。
したがって、どのようなトレーリングストップアルゴリズムを作成する際にも、まず基本的なトレーリングストップを構築し、必要な価格をストップロスの位置に渡して、すべての必要なチェックを行った上でストップロスをそのレベルに移動させる必要があります。
前回の記事では、シンプルなトレーリングストップやさまざまな指標に基づいたトレーリングストップ機能について説明しました。この方法を使えば、トレーリングストップをEA(エキスパートアドバイザー)に接続し、現在の銘柄のポジションに対してトレーリングストップをおこなうことが可能です。しかし、これには限界があります。EAで指標を作成し、それぞれの指標に対してトレーリングハンドルをトレーリング関数に送信する必要があります。また、トレーリング関数は、現在の銘柄のポジションにのみ適用されます。
トレーリングストップクラスを使用することで、異なる設定で複数のトレーリングストップクラスのインスタンスを作成でき、作成されたすべてのトレーリングストップは、プログラマーが指定した特定のアルゴリズムに従って1つのEA内で同時に動作させることが可能です。つまり、各パラメータセットごとに独自のアルゴリズムで動作するカスタムトレーリングストップを作成できるのです。このように作成されたトレーリングストップは、特定の条件下でEA内で起動され、ストップロスポジションをトレーリングする複雑なアルゴリズムを実現します。
たとえ異なる銘柄に対して同じトレーリングストップを2つ作成したとしても、それぞれの銘柄に対して別々にトレーリングが作成され、各トレーリングストップは作成時に指定された銘柄のデータに基づいて動作します。これにより、プログラムでのトレーリングの使用が大幅に簡素化されます。トレーリングストップは必要なパラメータを指定して作成し、EAハンドラでそれを呼び出すだけで機能します。公平を期すために、各タイプのトレーリングストップは端末内の既存のポジションの独自の検索を開始するため、このアプローチがすべてのアルゴリズムにとって最適とは限らないことに注意が必要です。真に汎用的なアルゴリズムを作成し、すべてのトレーリングに同じ端末ポジションリストを使用する場合、その設計・開発コストが需要に見合わない可能性があります。この点については本記事の範囲を超える話題です。
要するに、すべての必要なチェックをおこないながら、ポジションのストップロスを指定された価格に移動させるシンプルなトレーリングストップクラスをまず作成します。
前回の記事では、このシンプルで基本的なトレーリングストップをTrailingStopByValue()と呼びましたが、ここではSimpleTrailingと呼ぶことにします。これはカスタムプログラムで活用できます。
前回の記事と同様に、すべてのトレーリングストップクラスを1つのファイルにまとめ、このファイルをEAに接続するだけで済みます。一般的に、インクルードファイルは\MQL5\Include\フォルダやそのサブフォルダに保存されます。
基本トレーリングストップクラス
\MQL5\Include\端末フォルダに、新しいTrailings\サブフォルダを作成し、Trailings.mqhという新しいクラスファイルを配置します。
作成されたTrailings.mqhで、クラス名はCSimpleTrailingとし、CObject標準ライブラリの基本オブジェクトクラスを基本クラスにします。
MQLウィザードで作業を完了すると、 \MQL5\Include\Trailings\Trailings.mqhに以下のクラステンプレートができます。
//+------------------------------------------------------------------+ //| Trailings.mqh | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" class CSimpleTrailing : public CObject { private: public: CSimpleTrailing(); ~CSimpleTrailing(); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CSimpleTrailing::CSimpleTrailing() { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CSimpleTrailing::~CSimpleTrailing() { } //+------------------------------------------------------------------+
作成したファイルに標準ライブラリ基本オブジェクトのファイルをインクルードします。
//+------------------------------------------------------------------+ //| Trailings.mqh | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #include <Object.mqh> //+------------------------------------------------------------------+ //| Class of the position StopLoss simple trailing | //+------------------------------------------------------------------+ class CSimpleTrailing : public CObject
なぜ作成したすべてのトレーリングストップクラスを標準ライブラリの基本オブジェクトから継承する必要があるのでしょうか。
これにより、これらのクラスから後続のコレクションを簡単に作成し、リストに配置したり、リスト内で必要なオブジェクトを検索したりすることが可能になります。つまり、もしEAファイル内で単に将来的なクラスのオブジェクトをインスタンスとして使いたいだけであれば、CObjectを継承する必要はありません。しかし、これらのクラスから本格的なコレクションを構築し、MQL5標準ライブラリのあらゆる機能を活用したい場合には、これらのクラスをCObject基本クラスから派生させることが推奨されます。
CObjectクラスはMQL5標準ライブラリの基盤となるクラスであり、すべての子孫クラスに対して、リンクリストの要素として機能するための基本的な機能を提供します。
さらに、多くの仮想メソッドが定義されており、子孫クラスで実装することが可能です。
クラスのprivateセクションで3つのメソッドを宣言します。
class CSimpleTrailing : public CObject { private: //--- check the criteria for modifying the StopLoss position and return the flag bool CheckCriterion(ENUM_POSITION_TYPE pos_type, double pos_open, double pos_sl, double value_sl, MqlTick &tick); //--- modify StopLoss of a position by its ticket bool ModifySL(const ulong ticket, const double stop_loss); //--- return StopLevel in points int StopLevel(void); protected:
CheckCriterion()は、ストップロスポジションの変更に必要なすべてのチェックに合格したかどうかのフラグを返します。
ModifySL()は、ポジションのストップロスを指定された値で変更します。
StopLevel()は、銘柄のStopLevelプロパティの値を返します。
前回の記事では、これらのメソッドはすべて別々の関数でした。これらは基となるトレーリングストップクラスの一部として機能するようになり、そこから他のトレーリングのクラスを継承することになります。
クラスのprotectedセクションで、クラスが機能するために必要な変数をすべて宣言します。
protected: string m_symbol; // trading symbol long m_magic; // EA ID double m_point; // Symbol Point int m_digits; // Symbol digits int m_offset; // stop distance from price int m_trail_start; // profit in points for launching trailing uint m_trail_step; // trailing step uint m_spread_mlt; // spread multiplier for returning StopLevel value bool m_active; // trailing activity flag //--- calculate and return the StopLoss level of the selected position virtual double GetStopLossValue(ENUM_POSITION_TYPE pos_type, MqlTick &tick); public:
ここでは、「スプレッド倍率」変数についての説明が必要です。銘柄のStopLevel値は、ストップロスを設定できない価格からの最低距離を示します。サーバーによって設定されているStopLevelがゼロであっても、これはストップレベルがないことを意味するわけではありません。実際には、ストップレベルは変動しており、通常はスプレッドの2倍、または3倍の値になります。これらの値はサーバーの設定によって異なるため、プログラムではこの倍率を任意で設定できるようにしています。たとえば、StopLevelにダブルスプレッドが適用される場合、m_spread_mlt変数は「2」に設定され、トリプルスプレッドなら「3」と設定されます。デフォルトでは「2」に設定されています。
GetStopLossValue()仮想メソッドは、ポジションの損切り価格を計算し返すメソッドです。異なるタイプのトレーリングストップでは、ストップロスレベルの計算方法が異なるため、各トレーリングストップクラスでこのメソッドをオーバーライドして独自の計算ロジックを実装する必要があります。このメソッドが仮想メソッドとして宣言されている理由は、継承されたトレーリングストップクラスがそれぞれ異なるストップロスの計算方法を持つ可能性があるためです。
クラスのpublicセクションには、protectedセクションで宣言された変数に値を設定・取得するメソッド、およびクラスのコンストラクタとデストラクタを記述します。
public: //--- set trailing parameters void SetSymbol(const string symbol) { this.m_symbol = (symbol==NULL || symbol=="" ? ::Symbol() : symbol); this.m_point =::SymbolInfoDouble(this.m_symbol, SYMBOL_POINT); this.m_digits = (int)::SymbolInfoInteger(this.m_symbol, SYMBOL_DIGITS); } void SetMagicNumber(const long magic) { this.m_magic = magic; } void SetStopLossOffset(const int offset) { this.m_offset = offset; } void SetTrailingStart(const int start) { this.m_trail_start = start; } void SetTrailingStep(const uint step) { this.m_trail_step = step; } void SetSpreadMultiplier(const uint value) { this.m_spread_mlt = value; } void SetActive(const bool flag) { this.m_active = flag; } //--- return trailing parameters string Symbol(void) const { return this.m_symbol; } long MagicNumber(void) const { return this.m_magic; } int StopLossOffset(void) const { return this.m_offset; } int TrailingStart(void) const { return this.m_trail_start; } uint TrailingStep(void) const { return this.m_trail_step; } uint SpreadMultiplier(void) const { return this.m_spread_mlt; } bool IsActive(void) const { return this.m_active; } //--- launch trailing with StopLoss offset from the price bool Run(void); //--- constructors CSimpleTrailing() : m_symbol(::Symbol()), m_point(::Point()), m_digits(::Digits()), m_magic(-1), m_trail_start(0), m_trail_step(0), m_offset(0), m_spread_mlt(2) {} CSimpleTrailing(const string symbol, const long magic, const int trailing_start, const uint trailing_step, const int offset); //--- destructor ~CSimpleTrailing() {} };
このクラスは2つのコンストラクタを持ちます。
- 最初の(デフォルトの)コンストラクタは、現在の銘柄を使用し、マジックナンバーは常に「-1」に設定されます。これは、例外なく現在の銘柄に対するすべてのポジションを対象とすることを意味しています。さらに、トレーリングストップに関するパラメータはすべてゼロに設定されます。
- 2つ目のコンストラクタは、パラメータを受け取る形式で実装され、銘柄名、ポジションのマジックナンバー、および3つのトレーリングパラメータ(スタート、ステップ、シフト)がコンストラクタに渡されます。これにより、ユーザーは特定の銘柄とポジションに対して柔軟にトレーリングストップの動作を設定でき、各パラメータを個別に指定して運用することが可能になります。
パラメトリックコンストラクタの実装を考えてみましょう。
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CSimpleTrailing::CSimpleTrailing(const string symbol, const long magic, const int trail_start, const uint trail_step, const int offset) : m_spread_mlt(2) { this.SetSymbol(symbol); this.m_magic = magic; this.m_trail_start= trail_start; this.m_trail_step = trail_step; this.m_offset = offset; }
初期化文字列がすべての変数に現在の銘柄値を設定し、トレーリングのパラメータにゼロ値を設定するデフォルトのコンストラクタとは異なり、ここではコンストラクタは対応する変数に設定された値を取得します。SetSymbol()メソッドは、一度に複数の変数に値を設定します。
void SetSymbol(const string symbol) { this.m_symbol = (symbol==NULL || symbol=="" ? ::Symbol() : symbol); this.m_point =::SymbolInfoDouble(this.m_symbol, SYMBOL_POINT); this.m_digits = (int)::SymbolInfoInteger(this.m_symbol, SYMBOL_DIGITS); }
どちらのコンストラクタでも、スプレッドの倍率は2に設定されます。必要であれば、SetSpreadMultiplier()メソッドで変更できます。
以下は、選択されたポジションのストップロスレベルを計算して返す仮想メソッドです。
//+------------------------------------------------------------------+ //| Calculate and return the StopLoss level of the selected position | //+------------------------------------------------------------------+ double CSimpleTrailing::GetStopLossValue(ENUM_POSITION_TYPE pos_type, MqlTick &tick) { //--- calculate and return the StopLoss level depending on the position type switch(pos_type) { case POSITION_TYPE_BUY : return(tick.bid - this.m_offset * this.m_point); case POSITION_TYPE_SELL : return(tick.ask + this.m_offset * this.m_point); default : return 0; } }
これはシンプルなトレーリングストップであるため、ポジションのストップロスは常に現在価格から指定された距離に保たれます。
計算されたポジションのストップロス値はメソッドから返され、CheckCriterion()メソッドで検証されます。
//+------------------------------------------------------------------+ //|Check the StopLoss modification criteria and return a flag | //+------------------------------------------------------------------+ bool CSimpleTrailing::CheckCriterion(ENUM_POSITION_TYPE pos_type, double pos_open, double pos_sl, double value_sl, MqlTick &tick) { //--- if the stop position and the stop level for modification are equal or a zero StopLoss is passed, return 'false' if(::NormalizeDouble(pos_sl - value_sl, this.m_digits) == 0 || value_sl==0) return false; //--- trailing variables double trailing_step = this.m_trail_step * this.m_point; // convert the trailing step into price double stop_level = this.StopLevel() * this.m_point; // convert the symbol StopLevel into price int pos_profit_pt = 0; // position profit in points //--- depending on the type of position, check the conditions for modifying StopLoss switch(pos_type) { //--- long position case POSITION_TYPE_BUY : pos_profit_pt = int((tick.bid - pos_open) / this.m_point); // calculate the position profit in points if(tick.bid - stop_level > value_sl // if the StopLoss level is lower than the price with the StopLevel level set down from it (the distance according to StopLevel is maintained) && pos_sl + trailing_step < value_sl // if the StopLoss level exceeds the trailing step set upwards from the current position StopLoss && (this.m_trail_start == 0 || pos_profit_pt > this.m_trail_start) // if we trail at any profit or position profit in points exceeds the trailing start, return 'true' ) return true; break; //--- short position case POSITION_TYPE_SELL : pos_profit_pt = int((pos_open - tick.ask) / this.m_point); // calculate the position profit in points if(tick.ask + stop_level < value_sl // if the StopLoss level is higher than the price with the StopLevel level set upwards from it (the distance according to StopLevel is maintained) && (pos_sl - trailing_step > value_sl || pos_sl == 0) // if the StopLoss level is below the trailing step set downwards from the current StopLoss or a position has no StopLoss && (this.m_trail_start == 0 || pos_profit_pt > this.m_trail_start) // if we trail at any profit or position profit in points exceeds the trailing start, return 'true' ) return true; break; //--- return 'false' by default default: break; } //--- conditions are not met - return 'false' return false; }
メソッドに渡されたストップロス値がすべてのフィルタの基準を満たす場合、メソッドはtrueを返し、ポジションのストップロスはModifySL() メソッドを使用して変更されます。
//+------------------------------------------------------------------+ //| Modify StopLoss of a position by ticket | //+------------------------------------------------------------------+ bool CSimpleTrailing::ModifySL(const ulong ticket, const double stop_loss) { //--- if the EA stop flag is set, report this in the journal and return 'false' if(::IsStopped()) { Print("The Expert Advisor is stopped, trading is disabled"); return false; } //--- if failed to select a position by ticket, report this in the journal and return 'false' ::ResetLastError(); if(!::PositionSelectByTicket(ticket)) { ::PrintFormat("%s: Failed to select position by ticket number %I64u. Error %d", __FUNCTION__, ticket, ::GetLastError()); return false; } //--- declare the structures of the trade request and the request result MqlTradeRequest request= {}; MqlTradeResult result = {}; //--- fill in the request structure request.action = TRADE_ACTION_SLTP; request.symbol = ::PositionGetString(POSITION_SYMBOL); request.magic = ::PositionGetInteger(POSITION_MAGIC); request.tp = ::PositionGetDouble(POSITION_TP); request.position = ticket; request.sl = ::NormalizeDouble(stop_loss, this.m_digits); //--- if the trade operation could not be sent, report this to the journal and return 'false' if(!::OrderSend(request, result)) { PrintFormat("%s: OrderSend() failed to modify position #%I64u. Error %d",__FUNCTION__, ticket, ::GetLastError()); return false; } //--- request to change StopLoss position successfully sent return true; }
ストップレベルを取得するにはStopLevel()メソッドを使用します。
//+------------------------------------------------------------------+ //| Return StopLevel in points | //+------------------------------------------------------------------+ int CSimpleTrailing::StopLevel(void) { int spread = (int)::SymbolInfoInteger(this.m_symbol, SYMBOL_SPREAD); int stop_level = (int)::SymbolInfoInteger(this.m_symbol, SYMBOL_TRADE_STOPS_LEVEL); return int(stop_level == 0 ? spread * this.m_spread_mlt : stop_level); }
ストップレベルは一定の値ではなく、動的に変化する可能性があるため、メソッドを呼び出すたびに、銘柄のプロパティから必要な値を要求します。銘柄のStopLevelが0に設定されている場合、銘柄のスプレッドに2を掛けた値を使用します。これはデフォルト値です。
取り上げた3つのメソッドは、前回の記事ですべて別々の関数として実装済みです。これらのメソッドはクラスのprivateセクションに隠され、外部からアクセスされることなく機能を実行します。
主なトレーリングストップメソッドはRun()メソッドであり、ポジションを選択し、そのストップレベルを計算された値に移動させるサイクルを開始します。
//+------------------------------------------------------------------+ //| Launch simple trailing with StopLoss offset from the price | //+------------------------------------------------------------------+ bool CSimpleTrailing::Run(void) { //--- if disabled, leave if(!this.m_active) return false; //--- trailing variables MqlTick tick = {}; // price structure bool res = true; // result of modification of all positions //--- check the correctness of the data by symbol if(this.m_point==0) { //--- let's try to get the data again ::ResetLastError(); this.SetSymbol(this.m_symbol); if(this.m_point==0) { ::PrintFormat("%s: Correct data was not received for the %s symbol. Error %d",__FUNCTION__, this.m_symbol, ::GetLastError()); return false; } } //--- in a loop by the total number of open positions int total =::PositionsTotal(); for(int i = total - 1; i >= 0; i--) { //--- get the ticket of the next position ulong pos_ticket =::PositionGetTicket(i); if(pos_ticket == 0) continue; //--- get the symbol and position magic string pos_symbol = ::PositionGetString(POSITION_SYMBOL); long pos_magic = ::PositionGetInteger(POSITION_MAGIC); //--- if the position does not match the filter by symbol and magic number, leave if((this.m_magic != -1 && pos_magic != this.m_magic) || (pos_symbol != this.m_symbol)) continue; //--- if failed to get the prices, move on if(!::SymbolInfoTick(this.m_symbol, tick)) continue; //--- get the position type, its opening price and StopLoss level ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)::PositionGetInteger(POSITION_TYPE); double pos_open =::PositionGetDouble(POSITION_PRICE_OPEN); double pos_sl =::PositionGetDouble(POSITION_SL); //--- get the calculated StopLoss level double value_sl = this.GetStopLossValue(pos_type, tick); //--- if StopLoss modification conditions are suitable, modify the position stop level and add the result to the res variable if(this.CheckCriterion(pos_type, pos_open, pos_sl, value_sl, tick)) res &=this.ModifySL(pos_ticket, value_sl); } //--- at the end of the loop, return the result of modifying each position that matches the "symbol/magic" filter return res; }
このメソッドは、端末のアクティブポジションのリストを調べ、銘柄とマジックナンバーで並び替え、各ポジションのストップを現在の価格から計算された距離に移動させます。各ポジションのストップロスの修正結果はres変数に追加されます。すべてのポジションのストップレベルの変更が終了した時点で、銘柄とマジックフィルターに一致するすべてのポジションのストップレベルが正常に変更された場合はtrueを設定し、少なくとも1つのポジションが正常に変更されなかった場合はfalseを設定します。これは、外部からのストップの変更をさらにコントロールするためにおこなわれます。
では、このクラスをEAに接続して、その機能をテストしてみましょう。
テストをおこなうには、\MQL5\Experts\Examples\端末フォルダにあるMoving Average.mq5を使用します。このファイルをMovingAverageWithSimpleTrail.mq5として保存します。
EAにトレーリングストップクラスファイルをインクルードし、設定に追加パラメータを入力し、シンプルなトレーリングストップクラスのインスタンスを作成します。
//+------------------------------------------------------------------+ //| MovingAverageWithSimpleTrail.mq5 | //| Copyright 2000-2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2000-2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #include <Trade\Trade.mqh> #include <Trailings\Trailings.mqh> input group " - Moving Averages Expert Parameters -" input double MaximumRisk = 0.02; // Maximum Risk in percentage input double DecreaseFactor = 3; // Descrease factor input int MovingPeriod = 12; // Moving Average period input int MovingShift = 6; // Moving Average shift input group " - Simple Trailing Parameters -" input int InpTrailingStart = 10; // Trailing start input int InpTrailingStep = 20; // Trailing step in points input int InpTrailingOffset = 30; // Trailing offset in points input bool InpUseTrailing = true; // Use Trailing Stop //--- int ExtHandle=0; bool ExtHedging=false; CTrade ExtTrade; CSimpleTrailing ExtTrailing; // simple trailing class instance #define MA_MAGIC 1234501 //+------------------------------------------------------------------+
OnInit()ハンドラで、トレーリングストップのパラメータを設定します。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(void) { //--- prepare trade class to control positions if hedging mode is active ExtHedging=((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING); ExtTrade.SetExpertMagicNumber(MA_MAGIC); ExtTrade.SetMarginMode(); ExtTrade.SetTypeFillingBySymbol(Symbol()); //--- Moving Average indicator ExtHandle=iMA(_Symbol,_Period,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE); if(ExtHandle==INVALID_HANDLE) { printf("Error creating MA indicator"); return(INIT_FAILED); } //--- set trailing parameters ExtTrailing.SetActive(InpUseTrailing); ExtTrailing.SetSymbol(Symbol()); ExtTrailing.SetMagicNumber(MA_MAGIC); ExtTrailing.SetTrailingStart(InpTrailingStart); ExtTrailing.SetTrailingStep(InpTrailingStep); ExtTrailing.SetStopLossOffset(InpTrailingOffset); //--- ok return(INIT_SUCCEEDED); }
OnTick()ハンドラで、トレーリングストップを開始します。
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(void) { //--- if(SelectPosition()) CheckForClose(); else CheckForOpen(); //--- launch trailing ExtTrailing.Run(); }
EAファイルに必要な追加入力はこれだけです。コンパイルして、ストラテジーテスターでEURUSD M15で実行してみましょう。実行遅延のないすべてのティックを使用します。
パラメータは以下の通り(トレーリングストップは無効)です。
トレーリングストップを無効にしてテストを実行し、結果を見てみましょう。
次にトレーリングストップを使用します。
まったく同じテストをおこないます。
テスト結果は以下の通りです。
結果は少し良くなりました。これらの設定では、トレーリングストップが統計値をわずかに向上させることは明らかです。
次に、シンプルなトレーリングストップをもとに、他のトレーリングストップを作ることができます。
パラボリックSARやクライアント端末に表示されるトレンドフォロー指標のリストからの様々な移動平均線を含むいくつかの指標のトレーリングストップクラスを実装してみましょう。
指標別トレーリングストップクラス
同じファイルにコードを書き続けます。指標に基づくトレーリングストップの場合、必要な指標を作成する必要があります。そのためには、データシリーズを構築するために使用する銘柄と時間枠を指定し、さらに指標にアクセスするために、作成した指標のハンドルを変数に保存する必要があります。指標からデータを受け取る際は、ハンドルによって指定された指標に依存することはありません。データは、ハンドルを使用して指定した指標からCopyBuffer()メソッドによって取得されます。
指標からデータを取得するためのすべてのアクションは、指標ベースのトレーリングストップクラスに設定する必要があります。この場合、異なる指標で動作する各トレーリングクラスには、これらのアクションをすべて設定することになります。これは可能ですが、最適な方法ではありません。したがって、このクラスでは、ハンドルを使って指標からデータを取得するメソッドを実装し、各指標に対して同じ変数(銘柄、時間枠、データ時系列インデックス、指標ハンドル)を指定します。トレーリングストップクラス自体は、基本クラスから派生したものになります。その後、クラス編成をおこないます。基本クラスは、ハンドルによる指標データへのアクセスを特徴としており、継承されたクラスは、必要な指標の作成とそのデータの処理を含みます。
Trailings.mqhファイルにコードを書き続けます。指標に基づくトレーリングストップの基本クラスを実装してみましょう。
//+------------------------------------------------------------------+ //| Base class of indicator-based trailings | //+------------------------------------------------------------------+ class CTrailingByInd : public CSimpleTrailing { protected: ENUM_TIMEFRAMES m_timeframe; // indicator timeframe int m_handle; // indicator handle uint m_data_index; // indicator data bar string m_timeframe_descr; // timeframe description //--- return indicator data double GetDataInd(void) const { return this.GetDataInd(this.m_data_index); } //--- calculate and return the StopLoss level of the selected position virtual double GetStopLossValue(ENUM_POSITION_TYPE pos_type, MqlTick &tick); public: //--- set parameters void SetTimeframe(const ENUM_TIMEFRAMES timeframe) { this.m_timeframe=(timeframe==PERIOD_CURRENT ? ::Period() : timeframe); this.m_timeframe_descr=::StringSubstr(::EnumToString(this.m_timeframe), 7); } void SetDataIndex(const uint index) { this.m_data_index=index; } //--- return parameters ENUM_TIMEFRAMES Timeframe(void) const { return this.m_timeframe; } uint DataIndex(void) const { return this.m_data_index; } string TimeframeDescription(void) const { return this.m_timeframe_descr; } //--- return indicator data from the specified timeseries index double GetDataInd(const int index) const; //--- constructors CTrailingByInd(void) : CSimpleTrailing(::Symbol(), -1, 0, 0, 0), m_timeframe(::Period()), m_handle(INVALID_HANDLE), m_data_index(1), m_timeframe_descr(::StringSubstr(::EnumToString(::Period()), 7)) {} CTrailingByInd(const string symbol, const ENUM_TIMEFRAMES timeframe, const long magic, const int trail_start, const uint trail_step, const int trail_offset) : CSimpleTrailing(symbol, magic, trail_start, trail_step, trail_offset), m_data_index(1), m_handle(INVALID_HANDLE) { this.SetTimeframe(timeframe); } //--- destructor ~CTrailingByInd(void) { if(this.m_handle!=INVALID_HANDLE) { ::IndicatorRelease(this.m_handle); this.m_handle=INVALID_HANDLE; } } };
このクラスはシンプルなトレーリングストップのクラスから継承されているため、このクラスのすべての機能が利用可能であり、ハンドルによって指標データにアクセスするためのメソッドも用意されています。
クラスのprotectedセクションでは、クラス変数が宣言され、ハンドルを使用して指標からデータを受け取るメソッドと、親クラスの同名のメソッドをオーバーライドしてストップロスレベルを計算する仮想メソッドが宣言されています。
クラスのpublicセクションには、指標のプロパティを設定したり取得したりするメソッド、コンストラクタ、デストラクタが含まれています。
デフォルトのコンストラクタの初期化文字列では、マジックナンバーの値-1(すべてのポジション)と、トレーリングのゼロパラメータが親クラスに渡されます。指標の計算には、現在のチャートの時間枠が使用されます。
パラメトリックコンストラクタでは、指標を計算するためのチャートの銘柄と周期、およびすべてのトレーリングストップパラメータが、コンストラクタの仮パラメータとして渡されます。
クラスのデストラクタでは、指標の計算部分が解放され、ハンドルを格納する変数にはINVALID_HANDLEの値が設定されます。
以下は、指定された時系列インデックスから指標データを返すメソッドです。
//+------------------------------------------------------------------+ //| Return indicator data from the specified timeseries index | //+------------------------------------------------------------------+ double CTrailingByInd::GetDataInd(const int index) const { //--- if the handle is invalid, report this and return "empty value" if(this.m_handle==INVALID_HANDLE) { ::PrintFormat("%s: Error. Invalid handle",__FUNCTION__); return EMPTY_VALUE; } //--- get the value of the indicator buffer by the specified index double array[1]; ::ResetLastError(); if(::CopyBuffer(this.m_handle, 0, index, 1, array)!=1) { ::PrintFormat("%s: CopyBuffer() failed. Error %d", __FUNCTION__, ::GetLastError()); return EMPTY_VALUE; } //--- return the received value return array[0]; }
CopyBuffer()を使用して、指定したインデックスで単一の指標バーの値を取得します。正常に受信した場合、その値を返しますが、エラーが発生した場合は操作ログにメッセージを表示し、空の値(EMPTY_VALUE)を返します。
CopyBuffer()関数は、ハンドルによってどの指標からのデータでも動作するため、このメソッドは普遍的であり、各クラスの指標に基づくトレーリングストップで変更することなく使用できます。
以下は、選択されたポジションのストップロスレベルを計算して返す仮想メソッドです。
//+------------------------------------------------------------------+ //| Calculate and return the StopLoss level of the selected position | //+------------------------------------------------------------------+ double CTrailingByInd::GetStopLossValue(ENUM_POSITION_TYPE pos_type, MqlTick &tick) { //--- get the indicator value as a level for StopLoss double data=this.GetDataInd(); //--- calculate and return the StopLoss level depending on the position type switch(pos_type) { case POSITION_TYPE_BUY : return(data!=EMPTY_VALUE ? data - this.m_offset * this.m_point : tick.bid); case POSITION_TYPE_SELL : return(data!=EMPTY_VALUE ? data + this.m_offset * this.m_point : tick.ask); default : return 0; } }
このメソッドでは、親クラスにおいて単純に価格からオフセットされたストップロスレベルを計算します。このメソッドの同じクラスでは、指標からデータを受け取り、それをストップロスレベルとして渡す必要があります。指標からデータを受信していない場合、このメソッドは現在の価格を返します。StopLevelレベルでは、現在の価格でストップレベルを設定することができないため、ポジションのストップロスを設定することができません。
その結果、このクラスはハンドルによる指標データへのアクセスと、指標値によるストップロスポジションのレベル計算を実装し、すべてのメソッドが子クラスで利用できるようになります。
次に、作成したクラスに基づいて、指標ベースのトレーリングストップクラスを実装できます。
パラボリックSARのトレーリングストップクラス
引き続き、同じファイル(\MQL5\Include\Trailings\Trailings.mqh)にコードを書きます。
指標ベースのトレーリングストップの他のすべてのクラスと同様に、パラボリックSAR トレーリングストップクラスは指標ベースのトレーリングストップの基本クラスから派生させる必要があります。
//+------------------------------------------------------------------+ //| Parabolic SAR position StopLoss trailing class | //+------------------------------------------------------------------+ class CTrailingBySAR : public CTrailingByInd { private: double m_sar_step; // Parabolic SAR Step parameter double m_sar_max; // Parabolic SAR Maximum parameter public: //--- set Parabolic SAR parameters void SetSARStep(const double step) { this.m_sar_step=(step<0.0001 ? 0.0001 : step); } void SetSARMaximum(const double max) { this.m_sar_max =(max <0.0001 ? 0.0001 : max); } //--- return Parabolic SAR parameters double SARStep(void) const { return this.m_sar_step; } double SARMaximum(void) const { return this.m_sar_max; } //--- create Parabolic SAR indicator and return the result bool Initialize(const string symbol, const ENUM_TIMEFRAMES timeframe, const double sar_step, const double sar_maximum); //--- constructors CTrailingBySAR() : CTrailingByInd(::Symbol(), ::Period(), -1, 0, 0, 0) { this.Initialize(this.m_symbol, this.m_timeframe, 0.02, 0.2); } CTrailingBySAR(const string symbol, const ENUM_TIMEFRAMES timeframe, const long magic, const double sar_step, const double sar_maximum, const int trail_start, const uint trail_step, const int trail_offset); //--- destructor ~CTrailingBySAR(){} };
クラスのprivateセクションで、パラボリックSAR指標パラメータの値を格納する変数を宣言します。
publicセクションには、指標プロパティの値を設定したり返したりするメソッドが含まれています。指標を作成するメソッドと、デフォルトとパラメトリックの2つのコンストラクタを宣言しました。
コンストラクタのデフォルトでは、指標は現在の銘柄とチャート期間に作成され、トレーリングストップのパラメータ値はゼロに設定されます。
パラメトリックコンストラクタでは、すべてのパラメータがコンストラクタの入力に渡され、コンストラクタに渡されたパラメータに基づいて指標が作成されます。
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CTrailingBySAR::CTrailingBySAR(const string symbol, const ENUM_TIMEFRAMES timeframe, const long magic, const double sar_step, const double sar_maximum, const int trail_start, const uint trail_step, const int trail_offset) : CTrailingByInd(symbol, timeframe, magic, trail_start, trail_step, trail_offset) { this.Initialize(symbol, timeframe, sar_step, sar_maximum); }
Initialize()メソッドは、パラボリックSAR指標を作成し、その作成結果を返します。
//+------------------------------------------------------------------+ //| create Parabolic SAR indicator and return the result | //+------------------------------------------------------------------+ bool CTrailingBySAR::Initialize(const string symbol, const ENUM_TIMEFRAMES timeframe, const double sar_step, const double sar_maximum) { this.SetSymbol(symbol); this.SetTimeframe(timeframe); this.SetSARStep(sar_step); this.SetSARMaximum(sar_maximum); ::ResetLastError(); this.m_handle=::iSAR(this.m_symbol, this.m_timeframe, this.m_sar_step, this.m_sar_max); if(this.m_handle==INVALID_HANDLE) { ::PrintFormat("Failed to create iSAR(%s, %s, %.3f, %.2f) handle. Error %d", this.m_symbol, this.TimeframeDescription(), this.m_sar_step, this.m_sar_max, ::GetLastError()); } return(this.m_handle!=INVALID_HANDLE); }
作成したパラボリックSARベースのトレーリングストップクラスをテストしてみましょう。
テストをおこなうために、標準配信のEA \MQL5\Experts\Advisors\ExpertMACD.mq5を使用します。このファイルをExpertMACDWithTrailingBySAR.mq5として保存し、必要なコード文字列を追加します。
トレーリングストップクラスを含むファイルをインクルードし、新しい入力を追加し、トレーリングストップクラスのインスタンスを宣言します。
//+------------------------------------------------------------------+ //| ExpertMACD.mq5 | //| Copyright 2000-2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2000-2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| Include | //+------------------------------------------------------------------+ #include <Expert\Expert.mqh> #include <Expert\Signal\SignalMACD.mqh> #include <Expert\Trailing\TrailingNone.mqh> #include <Expert\Money\MoneyNone.mqh> #include <Trailings\Trailings.mqh> //+------------------------------------------------------------------+ //| Inputs | //+------------------------------------------------------------------+ //--- inputs for expert input group " - ExpertMACD Parameters -" input string Inp_Expert_Title ="ExpertMACD"; int Expert_MagicNumber =10981; bool Expert_EveryTick =false; //--- inputs for signal input int Inp_Signal_MACD_PeriodFast =12; input int Inp_Signal_MACD_PeriodSlow =24; input int Inp_Signal_MACD_PeriodSignal=9; input int Inp_Signal_MACD_TakeProfit =50; input int Inp_Signal_MACD_StopLoss =20; input group " - Trailing By SAR Parameters -" input ENUM_TIMEFRAMES InpTimeframeSAR = PERIOD_CURRENT; // Parabolic SAR Timeframe input double InpStepSAR = 0.02; // Parabolic SAR Step input double InpMaximumSAR = 0.2; // Parabolic SAR Maximum input int InpTrailingStart = 0; // Trailing Start input uint InpTrailingStep = 0; // Trailing Step input int InpTrailingOffset = 0; // Trailing Offset input bool InpUseTrailing = true; // Use Trailing Stop //+------------------------------------------------------------------+ //| Global expert object | //+------------------------------------------------------------------+ CExpert ExtExpert; CTrailingBySAR ExtTrailing;
OnInit()ハンドラで、パラボリックベースのトレーリングストップを初期化します。
//+------------------------------------------------------------------+ //| Initialization function of the expert | //+------------------------------------------------------------------+ int OnInit(void) { //--- Initializing trailing if(ExtTrailing.Initialize(NULL,PERIOD_CURRENT,InpStepSAR,InpMaximumSAR)) { ExtTrailing.SetMagicNumber(Expert_MagicNumber); ExtTrailing.SetTrailingStart(InpTrailingStart); ExtTrailing.SetTrailingStep(InpTrailingStep); ExtTrailing.SetStopLossOffset(InpTrailingOffset); ExtTrailing.SetActive(InpUseTrailing); } //--- Initializing expert
OnTick()ハンドラで、トレーリングストップを有効にします。
//+------------------------------------------------------------------+ //| Function-event handler "tick" | //+------------------------------------------------------------------+ void OnTick(void) { ExtExpert.OnTick(); ExtTrailing.Run(); }
EAをコンパイルし、デフォルトのパラメータでテスターで実行してみましょう。
ご覧のように、ポジションのストップレベルは、パラボリックSARの最初のバーの値によって正しくトレーリングされています。
それでは、クライアント端末に表示されるさまざまな移動平均のトレーリングストップクラスを作成してみましょう。
移動平均のトレーリングストップクラス
これらのクラスは、入力のセットと、Initialize()メソッドがそのクラスに対応する指標を作成するという点でのみ、パラボリック SAR トレーリングストップクラスと異なります。
これに基づき、適応型移動平均(Adaptive Moving Average)ベースのトレーリングストップクラスのみを検討します。
//+------------------------------------------------------------------+ //| Adaptive Moving Average position StopLoss trailing class | //+------------------------------------------------------------------+ class CTrailingByAMA : public CTrailingByInd { private: int m_period; // Period AMA parameter int m_fast_ema; // Fast EMA Period parameter int m_slow_ema; // Slow EMA Period parameter int m_shift; // Shift AMA parameter ENUM_APPLIED_PRICE m_price; // Applied Price AMA parameter public: //--- set AMA parameters void SetPeriod(const uint period) { this.m_period=int(period<1 ? 9 : period); } void SetFastEMAPeriod(const uint period) { this.m_fast_ema=int(period<1 ? 2 : period); } void SetSlowEMAPeriod(const uint period) { this.m_slow_ema=int(period<1 ? 30 : period); } void SetShift(const int shift) { this.m_shift = shift; } void SetPrice(const ENUM_APPLIED_PRICE price) { this.m_price = price; } //--- return AMA parameters int Period(void) const { return this.m_period; } int FastEMAPeriod(void) const { return this.m_fast_ema; } int SlowEMAPeriod(void) const { return this.m_slow_ema; } int Shift(void) const { return this.m_shift; } ENUM_APPLIED_PRICE Price(void) const { return this.m_price; } //--- create AMA indicator and return the result bool Initialize(const string symbol, const ENUM_TIMEFRAMES timeframe, const int period, const int fast_ema, const int slow_ema, const int shift, const ENUM_APPLIED_PRICE price); //--- constructors CTrailingByAMA() : CTrailingByInd(::Symbol(), ::Period(), -1, 0, 0, 0) { this.Initialize(this.m_symbol, this.m_timeframe, 9, 2, 30, 0, PRICE_CLOSE); } CTrailingByAMA(const string symbol, const ENUM_TIMEFRAMES timeframe, const long magic, const int period, const int fast_ema, const int slow_ema, const int shift, const ENUM_APPLIED_PRICE price, const int trail_start, const uint trail_step, const int trail_offset); //--- destructor ~CTrailingByAMA(){} }; //+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CTrailingByAMA::CTrailingByAMA(const string symbol, const ENUM_TIMEFRAMES timeframe, const long magic, const int period, const int fast_ema, const int slow_ema, const int shift, const ENUM_APPLIED_PRICE price, const int trail_start, const uint trail_step, const int trail_offset) : CTrailingByInd(symbol, timeframe, magic, trail_start, trail_step, trail_offset) { this.Initialize(symbol, timeframe, period, fast_ema, slow_ema, shift, price); } //+------------------------------------------------------------------+ //| create AMA indicator and return the result | //+------------------------------------------------------------------+ bool CTrailingByAMA::Initialize(const string symbol, const ENUM_TIMEFRAMES timeframe, const int period, const int fast_ema, const int slow_ema, const int shift, const ENUM_APPLIED_PRICE price) { this.SetSymbol(symbol); this.SetTimeframe(timeframe); this.SetPeriod(period); this.SetFastEMAPeriod(fast_ema); this.SetSlowEMAPeriod(slow_ema); this.SetShift(shift); this.SetPrice(price); ::ResetLastError(); this.m_handle=::iAMA(this.m_symbol, this.m_timeframe, this.m_period, this.m_fast_ema, this.m_slow_ema, this.m_shift, this.m_price); if(this.m_handle==INVALID_HANDLE) { ::PrintFormat("Failed to create iAMA(%s, %s, %d, %d, %d, %s) handle. Error %d", this.m_symbol, this.TimeframeDescription(), this.m_period, this.m_fast_ema, this.m_slow_ema, ::StringSubstr(::EnumToString(this.m_price),6), ::GetLastError()); } return(this.m_handle!=INVALID_HANDLE); }
このクラスは、前述のパラボリックSARに基づいたトレーリングストップクラスとまったく同じです。各MAに基づいたトレーリングストップクラスは、指標パラメータを格納するための独自の変数セットと、これらの変数に対応する値を設定したり返したりするためのメソッドを備えていなければなりません。MAに基づいたトレーリングストップクラスはすべて同じです。以下に添付したTrailings.mqhファイルにあるコードを見て学習することができます。また、以下に添付するExpertMACDWithTrailingByMA.mq5ファイルからテストEAを起動して、MAに基づいたトレーリングストップクラスをテストすることができます。
端末に提供される標準的な指標に基づいて、ポジションストップロスを配置するためのレベルを示すのに適したシンプルなトレーリングストップと他のトレーリングストップのクラスを作成しました。
しかし、紹介したクラスを使って作成できるトレーリングストップの型はこれだけではありません。ロングポジションとショートポジションで別々に指定された特定の価格レベルにポジションのストップレベルを移動させるトレーリングストップもあります。例えば、High/Lowローソク足に基づくトレーリングストップや、フラクタル指標に基づくトレーリングストップなどがその例です。
このようなトレーリングストップを実装するためには、ポジションの種類ごとにストップロスを設定するレベルを個別に指定できるようにする必要があります。そのためのクラスを作成してみましょう。
指定したストップロスレベルのトレーリングストップ
指定された値に基づいてトレーリングストップを作成するためには、指標のパラメータの代わりに変数を定義し、ロングおよびショートポジションのストップロス値を設定する必要があります。指標からデータを取得するのではなく、これらの変数を使用して指定された値からストップロスポジションの値を計算します。この値は、トレーリングのRun()メソッドを呼び出す際に、制御プログラムから直接設定されます。
次のクラスを実装しましょう。
//+------------------------------------------------------------------+ //| Trailing class based on a specified value | //+------------------------------------------------------------------+ class CTrailingByValue : public CSimpleTrailing { protected: double m_value_sl_long; // StopLoss level for long positions double m_value_sl_short; // StopLoss level for short positions //--- calculate and return the StopLoss level of the selected position virtual double GetStopLossValue(ENUM_POSITION_TYPE pos_type, MqlTick &tick); public: //--- return StopLoss level for (2) long and (2) short positions double StopLossValueLong(void) const { return this.m_value_sl_long; } double StopLossValueShort(void) const { return this.m_value_sl_short; } //--- launch trailing with the specified StopLoss offset from the price bool Run(const double value_sl_long, double value_sl_short); //--- constructors CTrailingByValue(void) : CSimpleTrailing(::Symbol(), -1, 0, 0, 0), m_value_sl_long(0), m_value_sl_short(0) {} CTrailingByValue(const string symbol, const long magic, const int trail_start, const uint trail_step, const int trail_offset) : CSimpleTrailing(symbol, magic, trail_start, trail_step, trail_offset), m_value_sl_long(0), m_value_sl_short(0) {} //--- destructor ~CTrailingByValue(void){} };
このクラスは、指標ベースのトレーリングストップのすべてのクラスとほぼ同様ですが、指標を作成する必要がないため、Initialize()メソッドは含まれていません。
選択したポジションのストップロスレベルを計算して返す仮想メソッドでは、ストップロスの値がロングポジションとショートポジションのストップレベルから計算されます。
//+------------------------------------------------------------------+ //| Calculate and return the StopLoss level of the selected position | //+------------------------------------------------------------------+ double CTrailingByValue::GetStopLossValue(ENUM_POSITION_TYPE pos_type, MqlTick &tick) { //--- calculate and return the StopLoss level depending on the position type switch(pos_type) { case POSITION_TYPE_BUY : return(this.m_value_sl_long - this.m_offset * this.m_point); case POSITION_TYPE_SELL : return(this.m_value_sl_short + this.m_offset * this.m_point); default : return 0; } }
ロングポジションとショートポジションのストップロス値は、Run() メソッドの仮パラメータを使用してクラスに渡されます。
//+------------------------------------------------------------------+ //| Launch trailing with the specified StopLoss offset from the price| //+------------------------------------------------------------------+ bool CTrailingByValue::Run(const double value_sl_long, double value_sl_short) { this.m_value_sl_long =value_sl_long; this.m_value_sl_short=value_sl_short; return CSimpleTrailing::Run(); }
まず、仮パラメータで渡された値がクラスのメンバ変数に設定され、それから親クラスのRun()メソッドが呼ばれます。再定義されたGetStopLossValue()仮想メソッドが親クラスから呼び出され、ポジションのストップレベルはその中で計算された値に設定されます。
トレーリングストップをEAに接続する
指標ベースのトレーリングストップをテストする際に、トレーリングストップをEAに接続する方法については既に説明しました。次に、渡された値、すなわちローソク足のHighとLowに基づいて、どのようにトレーリングストップを接続し、起動するかを考えてみましょう。
標準で含まれているEA\MQL5\Experts\Advisors\ExpertMACD.mq5にトレーリングストップを含めます。このファイルをExpertMACDWithTrailingByValue.mq5として保存し、必要な改良を加えます。
EAにトレーリングストップクラスを含むファイルをインクルードし、トレーリングセットアップ入力を追加し、値ベースのトレーリングストップクラスインスタンスを宣言します。
//+------------------------------------------------------------------+ //| ExpertMACD.mq5 | //| Copyright 2000-2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2000-2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| Include | //+------------------------------------------------------------------+ #include <Expert\Expert.mqh> #include <Expert\Signal\SignalMACD.mqh> #include <Expert\Trailing\TrailingNone.mqh> #include <Expert\Money\MoneyNone.mqh> #include <Trailings\Trailings.mqh> //+------------------------------------------------------------------+ //| Inputs | //+------------------------------------------------------------------+ //--- inputs for expert input group " - ExpertMACD Parameters -" input string Inp_Expert_Title ="ExpertMACD"; int Expert_MagicNumber =10981; bool Expert_EveryTick =false; //--- inputs for signal input int Inp_Signal_MACD_PeriodFast =12; input int Inp_Signal_MACD_PeriodSlow =24; input int Inp_Signal_MACD_PeriodSignal=9; input int Inp_Signal_MACD_TakeProfit =50; input int Inp_Signal_MACD_StopLoss =20; input group " - Trailing By Value Parameters -" input ENUM_TIMEFRAMES InpTimeframe = PERIOD_CURRENT; // Data Rates Timeframe input uint InpDataRatesIndex = 2; // Data Rates Index for StopLoss input uint InpTrailingStep = 0; // Trailing Step input int InpTrailingStart = 0; // Trailing Start input int InpTrailingOffset = 0; // Trailing Offset input bool InpUseTrailing = true; // Use Trailing Stop //+------------------------------------------------------------------+ //| Global expert object | //+------------------------------------------------------------------+ CExpert ExtExpert; CTrailingByValue ExtTrailing;
データレートタイムフレーム inputは、チャートの期間であり、ショートポジションのストップレベルの高値とロングポジションのストップレベルの安値がそこから取られます。
ストップロス用のデータレートインデックスは、データレートの時間枠期間を持つチャート上のバーインデックスであり、そこからストップロスを設定するために高値と安値を取得します。
EAのOnInit()ハンドラで、トレーリングストップのパラメータを初期化します。
//+------------------------------------------------------------------+ //| Initialization function of the expert | //+------------------------------------------------------------------+ int OnInit(void) { //--- Initializing trailing ExtTrailing.SetMagicNumber(Expert_MagicNumber); ExtTrailing.SetTrailingStart(InpTrailingStart); ExtTrailing.SetTrailingStep(InpTrailingStep); ExtTrailing.SetStopLossOffset(InpTrailingOffset); ExtTrailing.SetActive(InpUseTrailing); //--- Initializing expert
マジックナンバーがEAのものと一致するポジションのストップレベルのみがトレーリングされます。言い換えれば、EAによって建てられたポジションのみをトレーリングします。
EAのOnTick()ハンドラで、input InpDataRatesIndexで指定されたインデックスでバーデータを取得し、 ロング ポジションとショートポジションの損切り価格(バーのHighとLow)を指定しながらトレーリングストップを開始します。
//+------------------------------------------------------------------+ //| Function-event handler "tick" | //+------------------------------------------------------------------+ void OnTick(void) { ExtExpert.OnTick(); MqlRates rates[1]={}; if(CopyRates(ExtTrailing.Symbol(),InpTimeframe,InpDataRatesIndex,1,rates)) ExtTrailing.Run(rates[0].low,rates[0].high); }
EAにトレーリングストップを接続するために必要なことはこれだけです。すでにお気づきかもしれませんが、異なるトレーリングストップをEAに接続する際の違いは、異なるトレーリングタイプのインスタンスを宣言することだけです。それ以外のトレーリング接続の手順はほとんど同じであり、特に疑問や疑念を抱く必要はありません。
EAをコンパイルし、デフォルト設定のビジュアルテスターモードで実行してみましょう。
ストップポジションが、時系列2にインデックスを持つローソク足の高値と安値にどのように設定されているかを確認できます。
このようなトレーリングが取引システムでどの程度効果的かは、独自のテストが必要です。
さらに、この記事で紹介されているクラスを使用すれば、誰でも自分のアルゴリズムに基づいてカスタムトレーリングストップを作成できます。ここには、ほとんど無限の研究の余地があります。
結論
この記事では、あらゆるEAに簡単にトレーリングストップを組み込むことができる様々なトレーリングストップのクラスを作成しました。作成されたクラスは、カスタムアルゴリズムを使用してトレーリングストップを実装するための優れたツールセットでもあります。
例では、クラスオブジェクトを扱う方法の一つとして、トレーリングクラスオブジェクトのインスタンスを作成する方法のみを説明しました。この方法により、必要なトレーリングのタイプや、そのパラメータを事前にプログラムで決定することが可能になります。各トレーリングに対して、1つのオブジェクトがグローバルエリアに作成されます。このアプローチはシンプルで明快ですが、プログラム実行中に「new」オブジェクト生成演算子を使用してトレーリングのオブジェクトを動的に生成する場合、リンクされたオブジェクトリストを作成するために標準ライブラリが提供する機能を利用する方が良いでしょう。これらの目的のために、すべてのトレーリングクラスはCObject標準ライブラリの基本オブジェクトから派生しています。このアプローチについては、今回のトピックの範囲外なので、コメントで議論することができます。
この記事で紹介されているすべてのクラスは、開発に「そのまま」使用することもできますし、ニーズやタスクに合わせて変更することも可能です。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/14862





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