
クロスプラットフォームEA: カスタムストップ、ブレイクイーブン、トレーリング
目次
イントロダクション
前の記事、「クロスプラットフォームEA: ストップ」では、CStop クラスを使用して、クロスプラットフォームEAの特定のトレードに対して ストップロス およびテイクプロフィットレベルを設定するメソッドが示されています。 このレベルはピップまたはポイントで表現することができます。 しかし、現実世界のEAでは、常にそれが最良ではありません。 ストップロス とテイプロフィットは、多くの場合、エントリー価格からの距離に基づいて計算する必要はありません。 むしろ、インスタンスでは、ストップロス および/またはテイプロフィットレベルは、通常、他の計算の結果として、チャート価格で表現されます。 この記事では、CStop クラスを使用して、チャート面で ストップロス およびテイプロフィットレベルを指定する方法について説明します。 また、この記事で紹介するライブラリを使用して、ストップレベルを変更するメソッドについても説明します。 また、カスタムストップの実装メソッドに似た新しいクラス CTrail も導入されています。 ただし、カスタムストップとは異なり、CTrail は主に時間の経過とともにストップレベルを定義するために使用されます。
目次
前回の記事では、クロスプラットフォームEAで pip または point ベースの ストップロス とテイプロフィットをどのように実装できるかが示されています。 しかし、常にすべての EAで適切ではありません。 ストップロス とテイプロフィットレベルの動的計算を必要とする戦略があります。 通常、時系列データなどのデータに基づいて (OHLC) だけでなく、技術的なインジケーターからの出力です。
動的に計算された ストップロス とテイプロフィットのレベルは、その2つのメソッド、StopLossCustom と TakeProfitCustom を介して CStop で設定することができます。 次のコードスニペットは、2つのメソッドを示しています。
boolCStopBase:: StopLossCustom (void) { return m_stoploss==0; } boolCStopBase:: StopLossCustom (void) { return m_takeprofit==0; }
プログラマは、CStop の任意のインスタンスにストップロス またはテイプロフィットレベルを達成するために、メソッドを拡張することができます。
上記の2つのメソッドは、実際にクラスでオーバーロードされたメソッドです。 Boolean型の値を返す代替メソッドを次に示します。
boolCStopBase:: StopLossCustom (void) { return m_stoploss==0; } boolCStopBase:: StopLossCustom (void) { return m_takeprofit==0; }
どちらの関数も、特定のクラスメンバ (m_stoploss または m_takeprofit) の値が0の場合、true を返します。 その目的は後に説明します。
CStop は、次のガイドラインを使用してストップレベルを計算します。 ストップレベルは、メンバ m_stoploss と m_takeprofit として CStop で表される ストップロス またはテイプロフィットレベルにすることができます。 以下の手順では、ストップロス レベル (m_stoploss) のみを処理していると仮定します。
- m_stoploss がゼロの場合は、ストップロス の計算に StopLossCustom を使用します。
- m_stoploss がゼロでない場合は、m_stoploss を使用して、エントリー価格に対するトレードの実際の ストップロス を計算します。
同じガイドラインは、メソッド TakeProfitCustom とクラスメンバ m_takeprofit を使用してテイプロフィットを計算するために使用されます。
4つのメソッドの実際の使用法は、2つのメインエリアにあります。 メインストップレベルの場合、メソッド呼び出しは、オーダーマネージャ自体 (COrderManager) 内にあります。 その他のストップについては、各オーダーインスタンス (コーダー) 内で行います。
メインストップのレベルが最初のトレードリクエストと共にブローカーに送られ、従って、EAは最初のトレードがあった後、トレードリクエストを処理し始める情報を必要とします。 MetaTrader4とMetaTrader5ヘッジモードでブローカーベースのストップについては true です。 これらのモードでは、ストップロス およびテイクプロフィットレベルの情報を OrderSend (MQL4、 MQL5) 関数に含める必要があり、これらのストップレベルは、関連するメイン取引にのみ適用されます (グローバルではありません)。
オーダーマネージャーの TradeOpen メソッド内で、CStop の2つのメソッドへの呼び出しを見つけることができます。 MQL4 バージョンのコードを次に示します。
bool COrderManager::TradeOpen(const string symbol,ENUM_ORDER_TYPE type,double price,bool in_points=true) { int trades_total = TradesTotal(); int orders_total = OrdersTotal(); m_symbol=m_symbol_man.Get(symbol); if(!CheckPointer(m_symbol)) return false; if(!IsPositionAllowed(type)) return true; if(m_max_orders>orders_total && (m_max_trades>trades_total || m_max_trades<=0)) { ENUM_ORDER_TYPE ordertype=type; if(in_points) price=PriceCalculate(type); double sl=0,tp=0; if(CheckPointer(m_main_stop)==POINTER_DYNAMIC) { sl = m_main_stop.StopLossCustom()?m_main_stop.StopLossCustom(symbol,type,price):m_main_stop.StopLossCalculate(symbol,type,price); tp = m_main_stop.TakeProfitCustom()?m_main_stop.TakeProfitCustom(symbol,type,price):m_main_stop.TakeProfitCalculate(symbol,type,price); } double lotsize=LotSizeCalculate(price,type,sl); if(CheckPointer(m_main_stop)==POINTER_DYNAMIC) { if (!m_main_stop.Broker()) { sl = 0; tp = 0; } } int ticket=(int)SendOrder(type,lotsize,price,sl,tp); if(ticket>0) { if(OrderSelect(ticket,SELECT_BY_TICKET)) { COrder *order = m_orders.NewOrder(OrderTicket(),OrderSymbol(),OrderMagicNumber(), (ENUM_ORDER_TYPE)::OrderType(),::OrderLots(),::OrderOpenPrice()); if (CheckPointer(order)) { LatestOrder(GetPointer(order)); return true; } } } } return false; }
以下はMQL5用のコードです。
bool COrderManager::TradeOpen(const string symbol,ENUM_ORDER_TYPE type,double price,bool in_points=true) { bool ret=false; double lotsize=0.0; int trades_total =TradesTotal(); int orders_total = OrdersTotal(); m_symbol=m_symbol_man.Get(symbol); if(!IsPositionAllowed(type)) return true; if(m_max_orders>orders_total && (m_max_trades>trades_total || m_max_trades<=0)) { if(in_points) price=PriceCalculate(type); double sl=0.0,tp=0.0; if(CheckPointer(m_main_stop)==POINTER_DYNAMIC) { sl = m_main_stop.StopLossCustom()?m_main_stop.StopLossCustom(symbol,type,price):m_main_stop.StopLossCalculate(symbol,type,price); tp = m_main_stop.TakeProfitCustom()?m_main_stop.TakeProfitCustom(symbol,type,price):m_main_stop.TakeProfitCalculate(symbol,type,price); } lotsize=LotSizeCalculate(price,type,m_main_stop==NULL?0:m_main_stop.StopLossCalculate(symbol,type,price)); if(CheckPointer(m_main_stop)==POINTER_DYNAMIC) { if (!m_main_stop.Broker() || !IsHedging()) { sl = 0; tp = 0; } } ret=SendOrder(type,lotsize,price,sl,tp); if(ret) { COrder *order = m_orders.NewOrder((int)m_trade.ResultOrder(),m_trade.RequestSymbol(),(int)m_trade.RequestMagic(), m_trade.RequestType(),m_trade.ResultVolume(),m_trade.ResultPrice()); if (CheckPointer(order)) { LatestOrder(GetPointer(order)); return true; } } } return ret; }
TradeOpen は分割実装ですが、2つのバージョンのメソッドがメインのストップレベルを計算するメソッドに関して、共通の分母を見つけることができます。 まず、メインストップレベルから ストップロス とテイプロフィット (それぞれ sl と tp として表されます) を計算します。 情報がサイズ (資金管理) の計算に必要とされるかもしれないので、ストップロス とテイプロフィットが実際の最初のトレードリクエストで必要とされるかに応じて行われます。
サイズが計算された後、特定のケースで情報がゼロにリセットされることが重要です。 MQL4 では、メインストップがブローカーベースのストップでない場合、変数 sl と tp はゼロに設定されます。
if(CheckPointer(m_main_stop)==POINTER_DYNAMIC) { if (!m_main_stop.Broker()) { sl = 0; tp = 0; } }
MQL5 では、ストップロス とテイプロフィットの使用を回避します。 したがって、ストップがブローカーベースのストップでない場合、またはプラットフォームがネットモードである場合、変数はゼロにリセットされます。
if(CheckPointer(m_main_stop)==POINTER_DYNAMIC) { if (!m_main_stop.Broker() || !IsHedging()) { sl = 0; tp = 0; } }
前の記事 (「クロスプラットフォームEA: ストップ」を参照) で説明したように、EAはブローカーベースのストップを、ネットモードで予約オーダーストップに変換するため、ストップロス とテイプロフィットを送信する必要がなくなります。
複数の ストップロス とテイプロフィットのレベルを持つトレードに、メイントレードが正常に処理されたとすぐに処理されます。 トレードはメイントレードが実際に相場に入っていなければストップレベルが無意味であるので、最初に入らなければなりません。 コーダーのメソッド CreateStops を介して利用可能なコーダーインスタンスの初期化内に他のストップレベルの生成を行います。(COrderBase から、MQL4 と MQL5 の実装を共有するためです):
void COrderBase::CreateStops(CStops *stops) { if(!CheckPointer(stops)) return; if(stops.Total()>0) { for(int i=0;i<stops.Total();i++) { CStop *stop=stops.At(i); if(CheckPointer(stop)==POINTER_INVALID) continue; m_order_stops.NewOrderStop(GetPointer(this),stop); } } }
上記のコードに示されているように、EAによって検出された CStop のインスタンスごとにCOrderStop 生成されます。 ただし、追加のストップレベルは、メイントレードが正常に相場に参入した後にのみ作成されます。
カスタムストップ
CStop には CTradeManager の独立したインスタンスがあり、したがって CExpertTradeX (MQL5 標準ライブラリからの CExpertTrade の拡張) があります。 このインスタンスは、メインのトレードのエントリを処理するために排他的に使用されるオーダーマネージャ (COrderManager) で見つかったインスタンスから独立しています。 したがって、ストップレベルの変更は COrderManager ではなく、CStop 自体のインスタンスによって管理されます。 ただし、ストップレベルの変更はオーダーごとに実行されるため、ストップレベルを変更するための呼び出しは、トレードを表すコーダーインスタンス自体がトレードに派生する必要があります。
ストップレベルの監視は、次のコードを示すコーダーの CheckStops メソッドから始まります。
void COrderBase::CheckStops(void) { m_order_stops.Check(m_volume); }
ここでは、COrderStops のインスタンスであるクラスメンバの1つの Check メソッドを呼び出します。 前の記事のように、COrderStops は COrderStop のインスタンスへのポインタのコンテナです。 一方、COrderStop は、実際のインスタンスにおける CStop の表現です。
さて、COrderStops のチェックメソッドを調べてみましょう。 このメソッドは、次のコードスニペットに示されています。
COrderStopsBase::Check(double &volume) { if(!Active()) return; for(int i=0;i<Total();i++) { COrderStop *order_stop=(COrderStop *)At(i); if(CheckPointer(order_stop)) { if(order_stop.IsClosed()) continue; order_stop.CheckTrailing(); order_stop.Update(); order_stop.Check(volume); if(!CheckNewTicket(order_stop)) return; } } }
このコードからわかるように、COrderStop の特定のインスタンスに対して少なくとも5つの関数を実行します。
- COrderStop の特定のインスタンスが既に閉じているかどうかをチェックします (IsClosed メソッド)。
- トレーリングストップインスタンスに基づいてストップレベルが更新されます (CheckTrailing メソッドがある場合)。
- ストップレベルを更新します (更新メソッド)
- 相場が特定のストップレベルに当ったかどうかチェックする (チェックメソッド)
- 新しいチケット ID がトレードを表すかどうかをチェックします。
これらのタスクのうち、2番目のタスクは、ストップロス またはテイプロフィットのトレーリングストップに関連するものです。 最初のタスクは、ストップレベルでさらにアクションが必要かどうかを確認するためにのみ使用されます (ストップレベルが閉じられている場合、EAはそれ以上チェックする必要はありません)。 3番目のメソッドは、ストップレベルがEAの外部から変更された場合に使用します (仮想ストップのストップ線のドラッグなど)。 4番目のタスクは、ストップレベル (トレーリングストップによって更新されるかどうか) が相場によってヒットされているかどうかを確認するために使用されます。 5番目のタスクは、MetaTrader4 でのみ使用されますが、このプラットフォームではEAが、部分的に閉じたときにそのチケット ID を変更することになります。 トレードの進行の明確なフットプリントを維持するので、MetaTrader5は、この場合には、より簡単な実装になります。
COrderStop のインスタンスは、CStop によって定義されたトレードのストップレベルを表します。 したがって、最終的にこのクラスオブジェクトのインスタンスの変更になります。 次のコードは、CheckTrailing メソッドを示しています。
boolCOrderStopBase:: CheckTrailing (void) { if(!CheckPointer(m_stop) || m_order.IsClosed() || m_order.IsSuspended() || m_stoploss_closed & & m_takeprofit_closed) return false; double stoploss=0,takeprofit=0; string symbol=m_order.Symbol(); ENUM_ORDER_TYPE type=m_order.OrderType(); double price=m_order.Price(); double sl = StopLoss(); double tp = TakeProfit(); if(!m_stoploss_closed) stoploss=m_stop.CheckTrailing(symbol,type,price,sl,TRAIL_TARGET_STOPLOSS); if(!m_takeprofit_closed) takeprofit=m_stop.CheckTrailing(symbol,type,price,tp,TRAIL_TARGET_TAKEPROFIT); if(!IsStopLossValid(stoploss)) stoploss=0; if(!IsTakeProfitValid(takeprofit)) takeprofit=0; return Modify(stoploss,takeprofit); }このコードからは、ストップレベルが既に決済しているときに、特定のストップレベルを変更しないという一般的なガイドラインに従っていることがわかります。 オーダーストップレベルが適切である場合、CStop インスタンスのメソッド CheckTrailing の呼び出しに移ります。 さて、 CStop の CheckTrailing メソッドを検討してみましょう:
double CStopBase::CheckTrailing(const string symbol,const ENUM_ORDER_TYPE type,const double entry_price,const double price,const ENUM_TRAIL_TARGET mode) { if(!CheckPointer(m_trails)) return 0; return m_trails.Check(symbol,type,entry_price,price,mode); }
ここでは、CStop はクラスメンバ m_trails のいずれかのチェックメソッドを呼び出します。 m_trails は、トレーリングまたは トレーリングストップ オブジェクトへのポインタのコンテナです。 このメソッドのコードを次に示します。
double CTrailsBase::Check(const string symbol,const ENUM_ORDER_TYPE type,const double entry_price,const double price,const ENUM_TRAIL_TARGET mode) { if(!Active()) return 0; double val=0.0,ret=0.0; for(int i=0;i<Total();i++) { CTrail *trail=At(i); if(!CheckPointer(trail)) continue; if(!trail.Active()) continue; int trail_target=trail.TrailTarget(); if(mode!=trail_target) continue; val=trail.Check(symbol,type,entry_price,price,mode); if((type==ORDER_TYPE_BUY && trail_target==TRAIL_TARGET_STOPLOSS) || (type==ORDER_TYPE_SELL && trail_target==TRAIL_TARGET_TAKEPROFIT)) { if(val>ret || ret==0.0) ret=val; } else if((type==ORDER_TYPE_SELL && trail_target==TRAIL_TARGET_STOPLOSS) || (type==ORDER_TYPE_BUY && trail_target==TRAIL_TARGET_TAKEPROFIT)) { if(val<ret || ret==0.0) ret=val; } } return ret; }
この時点では、CTrails コンテナが CTrail の独自のインスタンスを繰り返し処理し、最終的な値を返すことを理解するのに十分です。 この最終的な値は、選択したシンボルのチャート価格によって、 double型になります。 これは、トレーリングストップが成功した後の ストップロス またはテイプロフィットの新しい値です。 ストップレベルの変更の実際の呼び出しが行われるこのメソッドからなので、COrderStop の CheckTrailing メソッドに戻りましょう:
boolCOrderStopBase:: CheckTrailing (void) { if(!CheckPointer(m_stop) || m_order.IsClosed() || m_order.IsSuspended() || m_stoploss_closed & & m_takeprofit_closed) return false; double stoploss=0,takeprofit=0; string symbol=m_order.Symbol(); ENUM_ORDER_TYPE type=m_order.OrderType(); double price=m_order.Price(); double sl = StopLoss(); double tp = TakeProfit(); if(!m_stoploss_closed) stoploss=m_stop.CheckTrailing(symbol,type,price,sl,TRAIL_TARGET_STOPLOSS); if(!m_takeprofit_closed) takeprofit=m_stop.CheckTrailing(symbol,type,price,tp,TRAIL_TARGET_TAKEPROFIT); if(!IsStopLossValid(stoploss)) stoploss=0; if(!IsTakeProfitValid(takeprofit)) takeprofit=0; return Modify(stoploss,takeprofit); }
このメソッドの戻り値は、COrderStop の Modify メソッドによる ストップレベルの変更の結果でブール型です (成功した場合は true を返します)。 しかし、変更のリクエストを送信する前に、ストップロス とテイプロフィットが有効であるかどうかをチェックし、メソッド IsStopLossValid と IsTakeProfitValid を使用します。 値が有効でない場合は、ゼロにリセットされます。
bool COrderStopBase::IsStopLossValid(const double stoploss) const { return stoploss!=StopLoss(); } bool COrderStopBase::IsTakeProfitValid(const double takeprofit) const { return takeprofit!=TakeProfit(); }
上記のコードでは、ストップロス とテイプロフィットの両方について、値が現在の値と等しくないようにしてください。
ストップロス とテイプロフィットの評価後、COrderStop の修正メソッドが呼び出され、以下のようになります。
bool COrderStopBase::Modify(const double stoploss,const double takeprofit) { bool stoploss_modified=false,takeprofit_modified=false; if(stoploss>0 && takeprofit>0) { if(ModifyStops(stoploss,takeprofit)) { stoploss_modified=true; takeprofit_modified=true; } } else if(stoploss>0 && takeprofit==0) stoploss_modified=ModifyStopLoss(stoploss); else if(takeprofit>0 && stoploss==0) takeprofit_modified=ModifyTakeProfit(takeprofit); return stoploss_modified || takeprofit_modified; }
この時点で、ストップロス とテイプロフィットの値に応じて、3種類の操作が実行されます。 通常、変更は、ブローカーベースのストップの場合と同様に、1つのアクションで実行できますが、常にそうではありません。 予約オーダーに基づいてストップをし、仮想ストップを個別に ストップロス とテイプロフィットにそれぞれ処理する必要があります。 両方の値を変更するコードは、次のメソッドにあります。
bool COrderStopBase::ModifyStops(const double stoploss,const double takeprofit) { return ModifyStopLoss(stoploss) && ModifyTakeProfit(takeprofit); }
メソッド ModifyStops は、他の2つのメソッドを呼び出します。 分割実装は、(1) 使用するコンパイラの種類 (MQL4 または MQL5) と (2) ストップの種類 (ブローカーベース、保留中、または仮想) の2つの要素に基づいて、この時点で開始されます。 ストップがブローカー基づかせていたストップなら、メイントレードをは変更するトレードリクエストに起因します。 ストップが予約オーダーに基づいている場合、EAは、対象となる予約オーダーのエントリ価格を移動する必要があります。 ストップが仮想ストップの場合、EAは、ストップレベルに関する内部データを更新するだけで済みます。
COrderStop には、クラスメンバの1つとしてトレードオブジェクト (またはへのポインタ) がないため、独自のストップレベルを変更することは本質的には不可能です。 独自のストップレベルを変更するには、CStop インスタンスに依存させる必要があります。 したがって、ストップレベルを変更すると、最終的には CStop で特定のメソッドを呼び出すことになります。
ブレイクイーブン
ブレイクイーブンは、収益がコストに等しいポイントです。 トレードでは、利益はトレードの実際の利益で、コストはスプレッドや手数料です。 これは利益/損失が0になるポイントです。 マーケットメイカー、または手数料がないブローカーの場合は、損益ポイントは、通常、トレードのエントリ価格です。
EAでは、トレードは、EAが正常にそのエントリの価格に等しいトレード ストップロス を設定している場合でもブレイクイーブンになります。 この状態に達すると、トレードのリスクはゼロになります。 但し、トレードはまだ利益の決済シグナルを、通常残すことができます。
EAは、少なくとも2つの制限によって制限されます:
- トレード ストップロス のダイナミクス
- ブローカーの最小距離
トレードの ストップロス はロングポジションのとき下に常にあり、ショートポジションの場合、上にあるべきです。 この制限により、EAは、トレードが未実現利益の状態にある場合にのみ、ストップロス をブレイクイーブンポイントに移動することができます。
ブローカーの最小距離の要件はまだトレードの最初のエントリにだけではなく、トレードの ストップロス を変更するときに適用されます。 このように、相場に参入した途端に、損益地点にトレードの ストップロス を設定することはできないことが多いです。 EAが損益ポイントのストップロス を動かす前に、相場は有利な方向に動かなければならない。
この制限を考えると、損益関数の設計では、少なくとも2つの要素を考慮する必要があります。
- アクティベーション価格
- アクティベーション時の新しい ストップロス
アクティベーション価格、またはトリガ価格は、相場が損益ポイントにトレードの ストップロス を移動するために到達しなければならない価格です。 このアクティベート化の価格はトレードによって異なった価格があると期待されるので、トレードごとに評価されなければならない。
アクティベーションの際に、EAは、損益ポイントにトレードの ストップロス を移動することを余儀なくされます。 通常、問題のトレードのエントリ価格ですが、常に同じではありません。 あるブローカーは手数料を要求し、損益ポイントはロングポジションの場合、上のどこかにあり、ショートポジションの場合下でどこかになります。
これらの値は、通常、現在の相場価格やトレードのエントリー価格など、別の値を参照して計算されます。
次は上記の損益の計算の図を示しています。 このフローチャートに基づいて、3つの値のアクティベーション、非アクティブ化、および新しいストップレベルが事前に計算されます。 現在の価格レベルが初期段階 (ストップロス ~ 損益) に必要な最安値レベル以上の場合は、計算された新しいストップレベルがトレードの新しいストップレベルとして使用されます。 そうでない場合、出力はゼロになります。 次の手順では、新しいストップレベルが現在のストップレベル内にあるかどうかを確認し、前の条件が満たされた場合は常に true を返し、計算されたストップレベルを最終出力として返す必要があります。
トレーリングストップ
トレーリングストップ は、損益の特殊なケースと見なすことができます。 損益は通常1回だけ適用されますが、トレーリングストップ は任意の回数適用できます。 アクティブ時に、トレーリングは通常、トレードの残りの存続期間を通じて適用されます。 トレーリングストップの設計では、少なくとも3つの要素を考慮する必要があります。
- アクティベーション価格
- アクティベーション時の新しい ストップロス
- トレーリングの頻度
トレーリングストップ 関数は、最初の2つの変数を損益で共有します。 しかし、損益とは異なり、トレーリングストップ のアクティベート価格は、トレードが未実現損失の状態で可能です。 トレードの ストップロス は、トレードが負けている間に変更することができますので、同様に ストップロス をトレールすることも可能です。 これが起こるとき、トレードの有効な ストップロス はまだブリクするための前提条件をまだ満たしておらず、損失に起因します。
3番目の要因は、ストップロス、または "ステップ" のトレーリングストップの頻度です。 ストップロス の最初のトレーリングの後に、現在の ストップロス レベルの距離 (ポイントまたは pip) が次の位置に決定されます。 これはアクティベーション価格である最初の変数にかなり似ています。 ただし、アクティベーション価格とは異なり、この3番目の変数から生じる計算された価格は、トレーリングの各段階で変化します。
トレーリングストップの4番目の要因は、非アクティブの価格です。 このポイントに達すると、EAは、ストップロス のトレーリングストップをします。 また、トレードごとに評価されます。
次の図は、トレードの ストップロス をトレーリングするためのフローチャートを示しています。 前の図(損益) とは大きく異なります。 価格がトレーリングの初期段階より大きい場合は、ストップレベルがすでに初期段階を超えている可能性が高くなります。 それ以外の場合でも、現在の新しいストップレベルの値として、トレーリングの初期段階を使用します。 ストップレベルがまだアクティベーション価格を超えていない場合、損益を計算するための手順の残りに従います (非アクティブ化価格は損益に適用されません)。
トレーリングテイクプロフィット
トレーリングまたはトレーリングストップは、ストップロス のトレーリングストップを参照してください。 この場合、価格がテイプロフィットレベルに近づくにつれてテイプロフィットが調整されます (ストップロス のトレーリングストップの反対)。 理論的には、テイプロフィットがヒットすることはありません。 しかし、このような突然の価格のスパイクやギャップの価格の動きなど、特定の状況では、可能性があります。
テイプロフィットのトレーリングストップに意味があるのは、この条件です。
- アクティベーション価格は現在のテイプロフィットレベル以内である必要があります
- 次の価格は、トレーリングの次の段階をトリガするには、現在のテイプロフィットレベル以内にする必要があります
- 理想的には、トレーリングの頻度を低くする必要があります。
アクティベーション価格が現在のテイプロフィットレベルを超えている場合、相場はアクティベーション価格の前にテイプロフィットレベルを最初にヒットします。 テイプロフィットがヒットしたとして、トレードはテイプロフィットレベルの任意のトレーリングストップの前にも開始することができます。 ロングポジションに対して、アクティベート化の価格はテイプロフィットのレベルより少しであるべきです。 ショートポジションの場合、アクティベーション価格はテイプロフィットレベルより大きくする必要があります。 2番目の条件についても同様ですが、最初のアクティベーション後にさらに適用されます。
理想的には、トレーリングの頻度を低くする必要があります。 つまり、1つのテイプロフィットレベル間の距離 (ステップ) は、十分な幅である必要があります。 より頻繁にテイプロフィットのトレーリングを変えれば変えるほど、テイプロフィットがヒットする可能性は下がります。これはテイクプロフィットが現在の価格から離れたところに配置されるためです。
実装
CTrail の基本クラスとして機能する CTrailBase は、以下のコードに示されています。
class CTrailBase : public CObject { protected: bool m_active; ENUM_TRAIL_TARGET m_target; double m_trail; double m_start; double m_end; double m_step; CSymbolManager *m_symbol_man; CSymbolInfo *m_symbol; CEventAggregator *m_event_man; CObject *m_container; public: CTrailBase(void); ~CTrailBase(void); virtual int Type(void) const {return CLASS_TYPE_TRAIL;} //---初期化 virtual bool Init(CSymbolManager*,CEventAggregator*); virtual CObject *GetContainer(void); virtual void SetContainer(CObject*); virtual bool Validate(void) const; //--- getters and setters bool Active(void) const; void Active(const bool); double End(void) const; void End(const double); void Set(const double,const double,const double,const double); double Start(void) const; void Start(const double); double Step(void) const; void Step(const double); double Trail(void) const; void Trail(const double); int TrailTarget(void) const; void TrailTarget(const ENUM_TRAIL_TARGET); //--- checking virtual double Check(const string,const ENUM_ORDER_TYPE,const double,const double,const ENUM_TRAIL_TARGET); protected: //---価格計算 virtual double ActivationPrice(const ENUM_ORDER_TYPE,const double); virtual double DeactivationPrice(const ENUM_ORDER_TYPE,const double); virtual double Price(const ENUM_ORDER_TYPE); virtual bool Refresh(const string); };
Set メソッドを使用すると、CTrails のインスタンスの設定を構成できます。 通常のクラスコンストラクタと同じように動作します。 必要に応じて、このメソッドを呼び出すカスタムコンストラクタを宣言することもできます。
void CTrailBase::Set(const double trail,const double st,const double step=1,const double end=0) { m_trail=trail; m_start=st; m_end=end; m_step=step; }
CTrail は、その計算の相場データに依存しています。 したがって、クラスメンバの1つとしてシンボルマネージャー (CSymbolManager) のインスタンスがあります。 計算を実行する前に、シンボルを更新する必要があります。
bool CTrailBase::Refresh(const string symbol) { if(!CheckPointer(m_symbol) || StringCompare(m_symbol.Name(),symbol)!=0) m_symbol=m_symbol_man.Get(symbol); return CheckPointer(m_symbol); }
アクティベーション価格は、CTrail インスタンスによって ストップロス またはテイプロフィットの初期移動をトリガする価格です。 クラスは、チャート価格の面でアクティベーション価格を計算する必要があります。 同じメソッドは他の価格の計算で使用されます:
double CTrailBase::ActivationPrice(const ENUM_ORDER_TYPE type,const double entry_price) { if(type==ORDER_TYPE_BUY) return entry_price+m_start*m_symbol.Point(); else if(type==ORDER_TYPE_SELL) return entry_price-m_start*m_symbol.Point(); return 0; }
非アクティブ化価格の計算も同じルーチンに従いますが、今回は m_end クラスメンバを使用します。
double CTrailBase::DeactivationPrice(const ENUM_ORDER_TYPE type,const double entry_price) { if(type==ORDER_TYPE_BUY) return m_end==0?0:entry_price+m_end*m_symbol.Point(); else if(type==ORDER_TYPE_SELL) return m_end==0?0:entry_price-m_end*m_symbol.Point(); return 0; }
非アクティブ化価格としてゼロの値を使用すると、関数が無効になります。 メイントレードが決済するまで、トレーリングは適用されます。
Price メソッドは、条件が評価時に ストップロス の移動またはトレーリングを許可する場合に、ストップロス またはテイプロフィットの新しい値を計算します。
double CTrailBase::Price(const ENUM_ORDER_TYPE type) { if(type==ORDER_TYPE_BUY) { if(m_target==TRAIL_TARGET_STOPLOSS) return m_symbol.Bid()-m_trail*m_symbol.Point(); else if(m_target==TRAIL_TARGET_TAKEPROFIT) return m_symbol.Ask()+m_trail*m_symbol.Point(); } else if(type==ORDER_TYPE_SELL) { if(m_target==TRAIL_TARGET_STOPLOSS) return m_symbol.Ask()+m_trail*m_symbol.Point(); else if(m_target==TRAIL_TARGET_TAKEPROFIT) return m_symbol.Bid()-m_trail*m_symbol.Point(); } return 0; }
ここで、Checkメソッドについて説明しましょう。 CTrail の特定のインスタンスは、ストップロス またはテイプロフィットのいずれかをたどることができます。 したがって、ちょうど ストップロス またはトレードのテイプロフィットを変更することを目指しているかどうかを指定する CTrail のコンテナが必要です。 これは列挙体 ENUM_TRAIL_TARGET によって実現されます。 この列挙体の宣言は、MQLx\Common\Enum\ENUM_TRAIL_TARGET.mqh. にあります。 そのコードを以下に示します。
enum ENUM_TRAIL_TARGET
{
TRAIL_TARGET_STOPLOSS,
TRAIL_TARGET_TAKEPROFIT
};
クラスの Check メソッドを次のコードに示します。 これまでに説明した他のメソッドとは異なり、このメソッドはパブリックメソッドです。 トレーリングストップ レベルの更新をチェックする必要がある場合に呼び出されるメソッドです。
double CTrailBase::Check(const string symbol,const ENUM_ORDER_TYPE type,const double entry_price,const double price,const ENUM_TRAIL_TARGET mode) { if(!Active()) return 0; if(!Refresh(symbol)) return 0; if(m_start==0 || m_trail==0) return 0; double next_stop=0.0,activation=0.0,deactivation=0.0,new_price=0.0,point=m_symbol.Point(); activation=ActivationPrice(type,entry_price); deactivation=DeactivationPrice(type,entry_price); new_price=Price(type); if(type==ORDER_TYPE_BUY) { if (m_target==TRAIL_TARGET_STOPLOSS) { if(m_step>0 && (activation==0.0 || price>=activation-m_trail*point) && (new_price>price+m_step*point)) next_stop=new_price; else next_stop=activation-m_trail*point; if(next_stop<price) next_stop=price; if((deactivation==0) || (deactivation>0 && next_stop>=deactivation && next_stop>0.0)) if(next_stop<=new_price) return next_stop; } else if (m_target==TRAIL_TARGET_TAKEPROFIT) { if(m_step>0 && ( activation==0.0 || price>=activation) && (new_price>price+m_step*point)) next_stop=new_price; else next_stop=activation+m_trail*point; if(next_stop<price) next_stop=price; if((deactivation==0) || (deactivation>0 && next_stop<=deactivation && next_stop>0.0)) if(next_stop>=new_price) return next_stop; } } if(type==ORDER_TYPE_SELL) { if (m_target==TRAIL_TARGET_STOPLOSS) { if(m_step>0 && (activation==0.0 || price<=activation+m_trail*point) && (new_price<price-m_step*point)) next_stop=new_price; else next_stop=activation+m_trail*point; if(next_stop>price) next_stop=price; if((deactivation==0) || (deactivation>0 && next_stop<=deactivation && next_stop>0.0)) if(next_stop>=new_price) return next_stop; } else if (m_target==TRAIL_TARGET_TAKEPROFIT) { if(m_step>0 && (activation==0.0 || price<=activation) && (new_price<price-m_step*point)) next_stop=new_price; else next_stop=activation-m_trail*point; if(next_stop>price) next_stop=price; if((deactivation==0) || (deactivation>0 && next_stop<=deactivation && next_stop>0.0)) if(next_stop<=new_price) return next_stop; } } return 0; }
ここから、計算が ストップロス とテイプロフィットのトレーリングストップの間で異なっていることがわかります。 ストップロス 値については、トレーリングの各段階で相場に近づくようにしたいと思います。 テイプロフィット値については、反対のことをします。-特定の距離 (ポイントまたはピップ) で、現在の相場価格からトレーリングの各段階で価格が離れるようにしてください。
CTrails (コンテナ)
CTrail には、CTrails という名前のコンテナもあります。 その定義を次のコードに示します。
class CTrailsBase : public CArrayObj { protected: bool m_active; CEventAggregator *m_event_man; CStop *m_stop; public: CTrailsBase(void); ~CTrailsBase(void); virtual int Type(void) const {return CLASS_TYPE_TRAILS;} //---初期化 virtual bool Init(CSymbolManager*,CEventAggregator*); virtual CStop *GetContainer(void); virtual void SetContainer(CStop*stop); virtual bool Validate(void) const; //--- getters and setters bool Active(void) const; void Active(const bool activate); //--- checking virtual double Check(const string,const ENUM_ORDER_TYPE,const double,const double,const ENUM_TRAIL_TARGET); };
このコンテナは、CStop と、参照する CTrail オブジェクトの間でインターフェイスする必要があります。 また、独自のCheckメソッドがあります:
double CTrailsBase::Check(const string symbol,const ENUM_ORDER_TYPE type,const double entry_price,const double price,const ENUM_TRAIL_TARGET mode) { if(!Active()) return 0; double val=0.0,ret=0.0; for(int i=0;i<Total();i++) { CTrail *trail=At(i); if(!CheckPointer(trail)) continue; if(!trail.Active()) continue; int trail_target=trail.TrailTarget(); if(mode!=trail_target) continue; val=trail.Check(symbol,type,entry_price,price,mode); if((type==ORDER_TYPE_BUY && trail_target==TRAIL_TARGET_STOPLOSS) || (type==ORDER_TYPE_SELL && trail_target==TRAIL_TARGET_TAKEPROFIT)) { if(val>ret || ret==0.0) ret=val; } else if((type==ORDER_TYPE_SELL && trail_target==TRAIL_TARGET_STOPLOSS) || (type==ORDER_TYPE_BUY && trail_target==TRAIL_TARGET_TAKEPROFIT)) { if(val<ret || ret==0.0) ret=val; } } return ret; }
CTrails は、この記事シリーズで使用されている他のコンテナと同様に、CObject とその継承のインスタンスの複数のポインタを格納するように設計された CArrayObj の孫です。 CTrails は、CTrail の複数のインスタンスを格納できます。 CTrail のインスタンスへの複数のポインタが CTrails に存在する場合、その check メソッドが呼び出されると、すべての CTrail インスタンスの check メソッドを呼び出します。 ただし、現在の相場価格に最も近い値のみが最終出力として返されます。 この動作は、CTrails を拡張することによって変更できます。
クラスCTrail と CTrails は、純粋な計算が削減されています。 つまり、すべてのメソッドは、その言語固有の実装ではなく、基本クラス (CTrailBase) にコード化されます。 Trailingstop (CStop) のステータスを確認するために呼び出されると、それはストップの新しい値を取得し、CStop はそれに応じて取引のストップレベルを変更します。
拡張 CTrail
CTrail クラスは、トレーリングストップ およびブレイクイーブンに対してのみ適用されません。 時間をかけてトレードのストップレベルのほぼすべての進化を定義するために使用することができます。 このプロセスは、この記事で説明したようにカスタムのストップが実装されるメソッドと非常によく似ています。 ただし、トレードが相場に参入した後にストップレベルを変更するために、CTrail のメソッドを拡張することによって変更が適用されます。
例
例 #1: カスタムストップ
この記事の最初の例では、EAがエントリーするトレードに対してカスタム ストップロス とテイプロフィットを使用しています。 このEAを作成するために、前の記事 (「クロスプラットフォームEA: ストップ」を参照) から3番目のEAを拡張します。 カスタムストップレベルは、前のロウソクの高値と安値に基づいて計算されます。 追加のセーフガードとして、最小ストップレベルの条件を追加します: 計算された ストップロス またはテイプロフィットが200ポイント未満でエントリー価格に近い場合、代わりにエントリー価格から200ポイントに ストップロス またはテイプロフィットを設定します。 トレードリクエストがブローカーによって受け入れられることを保障する手助けになります。
まず、CStop に孫クラスを宣言します。 この例では、その孫 CCustomStop を呼び出します。 その定義を以下に示します。
class CCustomStop : public CStop { public: CCustomStop(const string); ~CCustomStop(void); virtual double StopLossCustom(const string,const ENUM_ORDER_TYPE,const double); virtual double TakeProfitCustom(const string,const ENUM_ORDER_TYPE,const double); };
ここでは、StopLossCustom と TakeProfitCustom のメソッドを拡張する予定です。 次のコードは、StopLossCustom メソッドを示しています。
double CCustomStop::StopLossCustom(const string symbol,const ENUM_ORDER_TYPE type,const double price) { double array[1]; double val=0; if(type==ORDER_TYPE_BUY) { if(CopyHigh(symbol,PERIOD_CURRENT,1,1,array)) { val=array[0]; } } else if(type==ORDER_TYPE_SELL) { if(CopyLow(symbol,PERIOD_CURRENT,1,1,array)) { val=array[0]; } } if(val>0) { double distance=MathAbs(price-val)/m_symbol.Point(); if(distance<200) { if(type==ORDER_TYPE_BUY) val = price+200*m_symbol.Point(); else if(type==ORDER_TYPE_SELL) val=price-200*m_symbol.Point(); } } return val; }
まず、CopyLow と CopyHigh の関数を使用して、トレードのタイプ (売買) に応じて ストップロス を計算します。 初期値を取得した後、ポイントのエントリ価格からの距離の絶対的な値を取得します。 距離が最小に満たない場合は、代わりに最小に距離を設定します。
TakeProfitCustom メソッドは、次に示すように、同様のプロセスも引き受けます。
double CCustomStop::TakeProfitCustom(const string symbol,const ENUM_ORDER_TYPE type,const double price) { double array[1]; double val=0; m_symbol=m_symbol_man.Get(symbol); if(!CheckPointer(m_symbol)) return 0; if(type==ORDER_TYPE_BUY) { if(CopyLow(symbol,PERIOD_CURRENT,1,1,array)) { val=array[0]; } } else if(type==ORDER_TYPE_SELL) { if(CopyHigh(symbol,PERIOD_CURRENT,1,1,array)) { val=array[0]; } } if(val>0) { double distance=MathAbs(price-val)/m_symbol.Point(); if(distance<200) { if(type==ORDER_TYPE_BUY) val = price-200*m_symbol.Point(); else if(type==ORDER_TYPE_SELL) val=price+200*m_symbol.Point(); } } return val; }
CStop のクラスメンバは、ストップロス とテイプロフィットの割り当てられた値をポイント (m_stoploss と m_takeprofit のぞれ) に対して担当し、デフォルトでは0に初期化されます。 したがって、次の行には、以下のようにコメントを入れる必要があります。
//main.StopLoss(stop_loss); //main.TakeProfit(take_profit);
ストップレベルの ストップロス とテイプロフィットの値 (ポイント単位) が0に設定されている場合、そのインスタンスは、CStop のカスタム計算を使用します。
例 #2: 損益
ブレイクイーブン関数については、前の記事 (例 #1 と同じ) から3番目の例のメインヘッダーファイルも変更します。 しかし今回は、クラスオブジェクトを拡張するつもりはありません。 むしろ、CTrail のインスタンスだけでなく、CTrails のインスタンスも宣言します。
CTrails *trails = new CTrails(); CTrail *trail = new CTrail(); trail.Set(breakeven_value,breakeven_start,0); trails.Add(trail); main.Add(trails);
最後のラインは必須です。 CTrails のインスタンスを CStop の既存のインスタンスに追加する必要があります。 このようにして、動作は CStop のその特定のインスタンスに適用されます。
CTrail の Set メソッドの3番目の引数はステップです。 デフォルト値は 1 (1 ポイント) です。 ブレイクイーブン関数を使用しているので、0の値を与えます。
上記のコードは、アクティベーション価格からの ストップロス (ポイント単位) の新しい距離である breakeven_value と、エントリー価格からのアクティベート価格 (ポイント単位) である変数 breakeven_start を必要とします。 次の2つのインプットパラメータを宣言します。
input int breakeven_value = 200; input int breakeven_start = 200;
この設定では、相場が少なくとも200ポイント移動するとすぐに、ストップロス は、アクティベーション価格から200ポイント移動されます。 200-200 = 0 ので、新しい計算された ストップロス は、トレード自体のエントリ価格になります。
例 #3: トレーリングストップ
さて、 EA の代わりに トレーリングストップ 関数の実装に移動してみましょう。 この例は、前の例と非常によく似ています。 前の例では、次のコードを挿入することを思い出してください。
CTrails *trails = new CTrails(); CTrail *trail = new CTrail(); trail.Set(breakeven_value,breakeven_start,0); trails.Add(trail); main.Add(trails);
トレーリングストップ を使用する場合、このプロセスは異なります。
CTrails *trails = new CTrails(); CTrail *trail = new CTrail(); trail.Set(trail_value,trail_start,trail_step); trails.Add(trail); main.Add(trails);
今回は、CTrail の Set メソッドは3つのパラメータを使用します。 最初の2つは、前の例と同じです。 3番目のパラメーターは、アクティブ化後のトレーリングのステップです。 前の例では、このメソッドパラメーターは既定値の0を受け取り、これは最初のアクティベーション後に追加のトレーリングが行われないことを意味します。 次に、EA のインプットパラメータ trail_value、trail_start、および trail_step を宣言します。
例 #4: カスタムトレーリング
この例では、カスタムトレーリングフィーチャーを実装してみましょう。 この記事の最初の例で使用したEAを拡張します。 最初の例のEAでは、前のローソクの値のハイローに基づいて ストップロス とテイプロフィットを設定するカスタムストップを宣言したことを思い出してください。 この例では、エントリと ストップロス とテイクプロフィットを設定するだけではありません。 また、前のロウソクの高値または安値を使用して相場価格をトレールするためにストップロス をしたいと思います。 安全対策として、トレーリングステージごとに最小 ストップロス を実装しています。
このEAを作成するには、まず CTrail を拡張します。 クラスの子孫を CCustomTrail として名づけます。 その定義を以下に示します。
class CCustomTrail : public CTrail { public: CCustomTrail(void); ~CCustomTrail(void); virtual double Check(const string,const ENUM_ORDER_TYPE,const double,const double,const ENUM_TRAIL_TARGET); };
ここで、Checkメソッドを拡張してみましょう。 トレールターゲットをチェックしないことは、この場合問題ないことに注意してください。 ストップロス のトレーリングストップにあり、 CTrail のデフォルトのトレールターゲットです:
double CCustomTrail::Check(const string symbol,const ENUM_ORDER_TYPE type,const double entry_price,const double price,const ENUM_TRAIL_TARGET mode) { if(!Active()) return 0; if(!Refresh(symbol)) return 0; double array[1]; double val=0; if(type==ORDER_TYPE_BUY) { if(CopyLow(symbol,PERIOD_CURRENT,1,1,array)) { val=array[0]; } } else if(type==ORDER_TYPE_SELL) { if(CopyHigh(symbol,PERIOD_CURRENT,1,1,array)) { val=array[0]; } } if(val>0) { double distance=MathAbs(price-val)/m_symbol.Point(); if(distance<200) { if(type==ORDER_TYPE_BUY) val = m_symbol.Ask()-200*m_symbol.Point(); else if(type==ORDER_TYPE_SELL) val=m_symbol.Bid()+200*m_symbol.Point(); } } if((type==ORDER_TYPE_BUY && val<=price+10*m_symbol.Point()) || (type==ORDER_TYPE_SELL && val>=price-10*m_symbol.Point())) val = 0; return val; }ご覧の通り、計算はCCustomStop のメソッドに表示されるものと似ています。 また、コードの後半部分では、提案された新しい ストップロス が前の10ポイント (1 ピップ) になるように、戻り値をチェックして追加します。 最近の高値または安値の値に基づいて、トレードの ストップロス が浮遊するのを防ぐためです。 上下の高/安値の値に基づいて行くよりも、新しいストップロスのレベルは、常に (ロングポジションの高い、ショートポジションに低い) に置き換えるように設定します。
結論
この記事では、クロスプラットフォームEAでカスタムストップレベルをどのように実現できるかを示しました。 ストップロス とテイプロフィットのレベルをピップとポイントの観点から設定するのではなく、提示されたメソッドは、上記のレベルを表すことができるメソッドを紹介しています。 この記事はまた、ストップロス とテイプロフィットのレベルが時間をかけて変更することができるメソッドを示しています。
記事で使用されるプログラム
# |
名前 |
タイプ |
詳細 |
---|---|---|---|
1. |
breakeven_ha_ma.mqh |
ヘッダーファイル |
最初の例で使用されるメインヘッダーファイル |
2. |
breakeven_ha_ma.mq4 | EA |
最初の例の MQL4 バージョンで使用されているメインソースファイル |
3. | breakeven_ha_ma.mq5 | EA | 最初の例の MQL5 バージョンで使用されているメインソースファイル |
4. | trail_ha_ma.mqh | ヘッダーファイル |
2番目の例で使用されるメインヘッダーファイル |
5. | trail_ha_ma.mq4 | EA |
2番目の例の MQL4 バージョンで使用されているメインソースファイル |
6. | trail_ha_ma.mq5 | EA |
2番目の例の MQL5 バージョンで使用されているメインソースファイル |
7. | custom_stop_ha_ma.mqh | ヘッダーファイル | 3番目の例で使用されるメインヘッダーファイル |
8. | custom_stop_ha_ma.mq4 | EA | 3番目の例の MQL4 バージョンで使用されているメインソースファイル |
9. | custom_stop_ha_ma.mq5 | EA | 3番目の例の MQL5 バージョンで使用されるメインソースファイル |
10. | custom_trail_ha_ma.mqh |
ヘッダーファイル | 4番目の例で使用されるメインヘッダーファイル |
11. | custom_trail_ha_ma.mq4 | EA | 4番目の例の MQL4 バージョンで使用されているメインソースファイル |
12. |
custom_trail_ha_ma.mq5 | EA | 4番目の例の MQL5 バージョンで使用されているメインソースファイル |
この記事で紹介されているクラスファイル
# | 名前 |
タイプ |
詳細 |
---|---|---|---|
1. |
MQLx\Base\Stop\StopBase.mqh | ヘッダーファイル | CStop (基本クラス) |
2. |
MQLx\MQL4\Stop\Stop.mqh | ヘッダーファイル | CStop (MQL4 版) |
3. |
MQLx\MQL5\Stop\Stop.mqh | ヘッダーファイル | CStop (MQL5 バージョン) |
4. | MQLx\Base\Trail\TrailBase.mqh | ヘッダーファイル | CTrail (基本クラス) |
5. | MQLx\MQL4\Trail\Trail.mqh | ヘッダーファイル | CTrail (MQL4 版) |
6. | MQLx\MQL5\Trail\Trail.mqh | ヘッダーファイル | CTrail (MQL5 バージョン) |
7. | MQLx\Base\Trail\TrailsBase.mqh | ヘッダーファイル | CTrails (基本クラス) |
8. | MQLx\MQL4\Trail\Trails.mqh | ヘッダーファイル | CTrails (MQL4 版) |
9. | MQLx\MQL5\Trail\Trails.mqh | ヘッダーファイル | CTrails (MQL5 バージョン) |
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/3621





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