多通貨エキスパートアドバイザーの開発(第5回):可変ポジションサイズ
はじめに
前回は、再起動後にEAの状態を復元する機能を説明しました。端末の再起動、EAでのチャート時間枠の変更、より新しいバージョンのEAの起動など理由は何であっても、状態を復元することで、EAはゼロから作業を開始することなく、すでに建てられているポジションを失うことなく、処理を継続することができます。
しかし、建てたポジションのサイズは、テスト期間全体を通じて、戦略の各インスタンスで同じままでした。そのサイズはEAの起動時に設定されていました。EA操作の結果、取引口座の残高が増加した場合、リスクを増加させることなく、ポジションサイズを増加させることができるでしょう。これを活用するのが合理的なので、可変ポジションサイズの使用を実装し始めましょう。
概念
まず第一に、複数の取引戦略のインスタンス間で最適なコラボレーションを実現するという共通の目標に基づいた概念に合意する必要があります。
固定戦略サイズ(固定ロット):取引戦略のすべてのポジションのサイズを計算するために使用される一定のサイズ。最も単純なケースでは、建てられたポジションはすべて、この値に等しいサイズを持つことができます。シリーズの2つ目以降のポジションのサイズを増減させる何らかのトリックを使用する場合、固定サイズはシリーズの最初のポジションのサイズを設定することができ、それ以降のポジションはそのサイズとすでに建てられているポジションの数に基づいて計算されます。これはあまり優れた技術とは思われません。
正規化戦略残高(Fitted Balance):全テスト期間中のドローダウンが、選択した固定戦略サイズの初期残高の10%に達するが、それを超えない初期残高。なぜ10%なのでしょうか。この数字はまだそれほど大きなドローダウンではありません。心理的には受け入れられそうで、素早く大雑把に計算するには都合がよいことがわかったからです。一般的には、1%、5%、50%など、どのような値でも取ることができます。これはただの正規化パラメータです。
正規化取引戦略:固定戦略サイズと 正規化戦略残高が選択される取引戦略。したがって、選択したテスト期間にこのような戦略を起動すると、正規化戦略残高の約10%の最大ドローダウン値が得られるはずです。
取引戦略を手に入れたら、それを正規化取引戦略に変えるには、次のようにします。
- 固定戦略サイズを選択する(例:0.01)
- テスト期間(開始日と終了日)を選択する
- 選択したテスト期間において、初期残高を大きくして戦略を起動し、エクイティ別の最大絶対ドローダウンの値を見る
- 正規化戦略残高値を求め、エクイティによる最大絶対ドローダウンを10倍する
次の例を考えてみましょう。固定戦略サイズ0.01で、最大絶対ドローダウンがUSD 440であったとします。この値を最初の残高のちょうど10%にしたい場合は、USD 440を0.10で割ります(または、10を掛けます)。
USD 440 / 0.10 = USD 440 * 10 = USD 4400
この2つの値(0.01と4400)を取引戦略のインスタンスを作成するためのパラメータに設定すると、正規化取引戦略が得られます。
さて、正規化取引戦略の場合、株式による相対的なドローダウンの最大値を10%に維持しながら、どのような残高値に対してもポジションの大きさを計算することができます。そのためには、現在の合計残高 (Total Balance)と正規化残高(Fitted Balanceの比率に比例して、建玉の大きさを変更すれば十分です。
CurrentLot = FixedLot * (TotalBalance / FittedBalance)
例えば、上記の例で使用された0.01と4400の値について、残高がUSD 10,000の場合、ポジションのサイズは基準値に基づいて計算される必要があります。
CurrentLot = 0.01 * (10,000 / 4400) = 0.0227
正確にこのサイズを開くことはできないでしょう。0.02に四捨五入しなければならないので、この場合のドローダウンは、テストでは10%よりわずかに少ないかもしれません。0.03に切り上げれば、ドローダウンは10%をわずかに超えるかもしれません。残高が増えれば、丸め誤差は減ります。
戦略に固定ポジションサイズという概念を導入したのであれば、戦略のポジションサイズを管理するオプションを戦略自体に委ねることができます。したがって、取引戦略のさまざまなインスタンスを組み合わせたEAのレベルで、資金管理戦略のための3つの可能なオプションを実装するだけです。
- 固定サイズまたは資金管理戦略の不在:戦略で指定された固定サイズは、取引口座の残高に関係なく適用されます。この戦略は、戦略の別のインスタンスをテストする際に使用され、正規化された戦略残高を決定します。
- 指定された固定残高に対して一定のサイズ:開始時に、戦略の正規化残高と固定サイズに基づいて、戦略の固定残高に比例するサイズが計算されます。このサイズはテスト期間を通じて使用されます。この戦略を使用して、指定された最大絶対ドローダウンを条件として、テスト期間全体を通じてファンドの成長カーブの均一性(直線性)をチェックします。
- 現在残高を表す可変サイズ:ポジションを建てるごとに、戦略の正規化残高と固定サイズに基づいて、現在の口座残高に比例してサイズが決定されます。この戦略は、最大相対ドローダウンの期待値を提供する実作業に使用されます。
これら3つのオプションの使用例を挙げてみましょう。EAを1コピーの戦略とし、USD 100,000の大きな開始残高を設定し、オープンポジションのサイズを0.01ロットに固定して、2018年から2022年の期間のテストを開始してみましょう。次のような結果が出ました。
図1:サイズ固定、残高USD 100,000での結果
見てわかるように、このテスト期間中、株式の最大絶対ドローダウンは約USD 153で、これは口座残高の約0.15%に相当します。より正確には、初期口座残高に関する相対的なドローダウンを評価する方が正しです。しかし、初期残高と最終残高の差は小さい(初期残高の約1%)ので、0.15%のドローダウンは、テスト期間中のどの時点で発生しても、精度よくUSD 150の絶対値に相当します。
最大絶対ドローダウンが初期残高の10%になるように、初期残高をどの程度に設定できるかを計算してみましょう。
FittedBalance =MaxDrawdown / 10% = 153 / 0.10 = 153 * 10 = USD 1530
計算を確認してみましょう。
図2:サイズを固定し、残高をUSD 1530とした場合の結果
株式の絶対的なドローダウンはUSD 153と同額ですが、相対的なドローダウンは10%ではなく、わずか7.2%であることがわかります。これは、口座残高が初期値からいくらか増加し、USD 153の価値がすでに現在残高の10%未満であったときに最大のドローダウンが発生したことを意味するだけであり、正常です。
では、2つ目の選択肢(与えられた固定残高に対して一定のサイズ)を確認してみましょう。初期残高をUSD 100,000と多めに設定し、その10分の1、つまりUSD 10,000しか使用できないようにします。これは現在残高の値であり、全テスト期間を通じて変わりません。このような条件下では、建てられたポジションの大きさは次のようになるはずです。
CurrentBalance = TotalBalance * 0.1 = 10,000
CurrentLot = FixedLot * (CurrentBalance / FittedBalance) = 0.01 * (10,000 / 1530) = 0.0653
動作中、この値はロット変更ステップの倍数に丸められます。次のような結果が出ました。
図3:USD 100,000のうちUSD 10,000を固定残高とした場合の結果
ご覧の通り、絶対ドローダウンはUSD 1016であり、十分な精度をもってすれば、この戦略に割り当てたUSD 10,000の10%に相当します。しかし、これは残高全体のわずか1%に過ぎません。
最後に、3つ目のオプションである現在残高の可変サイズを見てみましょう。初期残高をUSD 10,000に設定し、全額使用できるようにします。こうなります。
図4:現在残高を可変サイズにした結果
ここでは、最大絶対ドローダウンはすでに初期残高の10%を超えていますが、相対ドローダウンは引き続き許容範囲内の10%にとどまっていることがわかります。これで正規化された取引戦略が得られ、Fitted Balance= 1530となり、所定のドローダウン10%を確保するためのポジションのサイズを簡単に計算できるようになりました。
ポジションサイズの計算
次のような資金管理の選択肢を考える場合、次のような見解が成り立ちます。
- もし戦略のコピーつついて話しているのであれば、可変ロットのオプションは有用でしょうか。有用でなかったようです。必要なのは最初のオプションだけです。2番目と3番目は、パフォーマンスを示すために2、3回使用することができるが、その後は必要ないでしょう。
- すでに複数の取引戦略を組み合わせたEAを使用している場合、固定ロットでの取引は有用でしょうか。有用でないようです。この場合、テスト段階では2番目のオプションが役に立つかもしれず、3番目のオプションが主に使用されることになるでしょう。
これにより、次のような結論が導き出されます。仮想注文は常に、固定戦略サイズパラメータから計算された固定サイズを持ちます。ここで取り上げる戦略では、最小ロットを固定サイズとして使用すれば十分です。これはほとんどの商品では0.01に等しくなります。
ワークフローに基づいて、受信者オブジェクトまたは銘柄受信者は、実際のポジションサイズに再計算されなければならないことが判明しました。そのためには、戦略から、より正確には仮想注文から、この残高の10%のドローダウンを保証する正規化された残高の値を受け取らなければなりません。
しかし、ドローダウンをより小さく、あるいは大きくしたい場合はどうするのでしょうか。そのためには、予想される最大ドローダウンを10%という値と比較して何倍変化させたいかに比例して、建てたポジションのサイズを何らかの方法で変更すれば十分です。
その1つが、経常収支のどの部分をEAが利用できるかを示す加重乗数を明示的に導入する手法です。
割当残高(現在残高)は、取引用にこのEAに割り当てられた合計口座残高の一部です。
残高倍率(Depo Part) - 総口座残高に対する配分戦略残高の比率。
DepoPart = CurrentBalance / TotalBalance
そして、初期ポジションのサイズは次のように計算できます。
CurrentLot = FixedLot * (CurrentBalance / FittedBalance)
CurrentLot = FixedLot * (DepoPart * TotalBalance / FittedBalance)
ここで、実装に非常に役立つ重要なコメントをすることができます。戦略インスタンスを作成した後、Total Balanceを再計算すると、現在の戦略残高ではなく、合計残高がポジションサイズ計算式に使用されます。
FittedBalance = FittedBalance / DepoPart
CurrentLot = FixedLot * (TotalBalance / FittedBalance)
正規化残高の再計算は、EAを初期化する際に一度だけおこないます。それ以降はDepo Partの倍率は必要ありません。
複数の戦略を組み合わせる
これまでの議論は、1つのEAで1つの戦略のインスタンスを使用する場合を想定したものです。ここで、標準化された複数の戦略を、テスト期間全体を通じてドローダウンが10%(または事前に指定した別の値)を超えない1つのEAにまとめたい場合、何をする必要があるか考えてみましょう。当面は、指定されたドローダウン値がちょうど10%であると考えます。
戦略の組み合わせで起こりうる最悪のケースに注目すれば、これはすべての戦略が同時に最大ドローダウン10%を達成することです。この場合、各戦略のポジションサイズをインスタンス数に比例して小さくしなければなりません。例えば、戦略を3つ組み合わせるとすれば、ポジションのサイズを3倍にする必要があります。
これは、戦略に割り当てられた残高を所定の回数減らすか、戦略の正規化された残高を所定の回数増やすことで達成できます。2番目のオプションを使用します。
グループ内の戦略の数をStrategiesCountで表すと、正規化残高の再計算式は次のようになります。
FittedBalance = StrategiesCount * FittedBalance
ただし、戦略インスタンスが可能な限り互いに異なるように選択される場合、戦略インスタンスの数が増えるにつれて、このような最悪のケースの可能性は大幅に減少します。この場合、ドローダウンは異なるタイミングで発生し、同時には発生しません。これはテスト中に見ることができます。ここで別のスケールファクタ(Scale)を導入します。これはデフォルトでは1に等しいが、必要であれば、戦略の正規化残高を減らしてポジションのサイズを大きくするために大きくすることができます。
FittedBalance = StrategiesCount * FittedBalance
FittedBalance = FittedBalance / Scale
Scale乗数を選択することで、テスト期間全体を通じて、戦略グループが指定されたドローダウンを提供することを再度確認することができます。この場合、正規化された戦略のグループが得られます。
正規化された戦略のグループ:正規化取引戦略のグループで、グループが一緒に機能したときに最大ドローダウンが10%以下になるようにスケールファクタが選択されています。
そして、正規化された戦略のグループをいくつか作ったなら、正規化された戦略を組み合わせるときに使用されるのと同じ原理に従って、それらをすべて新しい正規化されたグループに再び組み合わせることができます。言い換えれば、すべてのグループのすべての戦略が同時に機能したときに、最大ドローダウンが10%を超えないように、グループの倍率を選ぶべきです。この統一プロセスは、いくつでも続けることができます。この場合、各戦略の初期正規化残高は、単純に各レベルの関連性におけるグループ内の戦略またはグループの数を乗じ、各レベルのスケールファクタで割ります。
FittedBalance = StrategiesCount1 * FittedBalance
FittedBalance = StrategiesCount2 * FittedBalance
...
FittedBalance = FittedBalance / Scale1
FittedBalance = FittedBalance / Scale2
...
すると、各戦略の正規化残高を再計算する最終式は次のようになります。
FittedBalance = (StrategiesCount1 * StrategiesCount1 * ... ) * FittedBalance / (Scale1 * Scale2 * ... )
最後に、組み合わせの最高レベルにある戦略の正規化グループを、10%ではなく別の指定ドローダウンを持つグループに変換する可能性があるため、ポジションサイズの計算式に最後のスケーリングDepo Part乗数を適用します。
CurrentLot = FixedLot * (DepoPart * TotalBalance / FittedBalance)
実装する新しいクラスが2つあります。最初のクラスCVirtualStrategyGroupは、戦略をグループにまとめる際に、戦略の正規化された残高を再計算する役割を担います。2つ目のクラスのCMoneyは、戦略の固定サイズ、正規化された残高、および配分された残高に基づいて、実際のオープニングボリュームを計算する責任を負います。
取引戦略グループクラス
このクラスは、戦略のグループまたは戦略グループのグループを表すオブジェクトを作成するために使用されます。どちらの場合も、グループ作成時に、単一のScale()メソッドを呼び出すことで、スケールファクタが適用されます。
//+------------------------------------------------------------------+ //| Class of trading strategies group(s) | //+------------------------------------------------------------------+ class CVirtualStrategyGroup { protected: void Scale(double p_scale); // Scale normalized balance public: CVirtualStrategyGroup(CVirtualStrategy *&p_strategies[], double p_scale = 1); // Constructor for a group of strategies CVirtualStrategyGroup(CVirtualStrategyGroup *&p_groups[], double p_scale = 1); // Constructor for a group of strategy groups CVirtualStrategy *m_strategies[]; // Array of strategies CVirtualStrategyGroup *m_groups[]; // Array of strategy groups };
コンストラクタは、パラメータとしてスケールファクタと、戦略へのポインタの配列か戦略グループへのポインタの配列を受け取ります。出来上がった配列は、作成されたオブジェクトの対応するプロパティにコピーされ、Scale()メソッドが各配列要素に適用されます。戦略オブジェクトの戦略クラスにこのメソッドを追加する必要があります。
//+------------------------------------------------------------------+ //| Constructor for strategy groups | //+------------------------------------------------------------------+ CVirtualStrategyGroup::CVirtualStrategyGroup( CVirtualStrategy *&p_strategies[], double p_scale ) { ArrayCopy(m_strategies, p_strategies); Scale(p_scale / ArraySize(m_strategies)); } //+------------------------------------------------------------------+ //| Constructor for a group of strategy groups | //+------------------------------------------------------------------+ CVirtualStrategyGroup::CVirtualStrategyGroup( CVirtualStrategyGroup *&p_groups[], double p_scale ) { ArrayCopy(m_groups, p_groups); Scale(p_scale / ArraySize(m_groups)); } //+------------------------------------------------------------------+ //| Scale normalized balance | //+------------------------------------------------------------------+ void CVirtualStrategyGroup::Scale(double p_scale) { FOREACH(m_groups, m_groups[i].Scale(p_scale)); FOREACH(m_strategies, m_strategies[i].Scale(p_scale)); }
現在のフォルダのVirtualStrategyGroup.mqhファイルにコードを保存します。
仮想戦略クラスに必要な追加をしましょう。戦略の正規化残高と固定サイズを保存するために、2つの新しいクラスプロパティを追加する必要があります。これらはインストールされるべきなので、以前は不要だったコンストラクタが必要になりました。FittedBalance() publicメソッドは、戦略正規化残高の値を返すだけで、Scale()メソッドは指定した乗数でスケーリングします。
//+------------------------------------------------------------------+ //| Class of a trading strategy with virtual positions | //+------------------------------------------------------------------+ class CVirtualStrategy : public CStrategy { protected: ... double m_fittedBalance; // Strategy normalized balance double m_fixedLot; // Strategy fixed size ... public: CVirtualStrategy(double p_fittedBalance = 0, double p_fixedLot = 0.01); // Constructor ... double FittedBalance() { // Strategy normalized balance return m_fittedBalance; } void Scale(double p_scale) { // Scale normalized balance m_fittedBalance /= p_scale; } };
このコードを現在のフォルダのVirtualStrategy.mqhファイルに保存します。
また、CSimpleVolumesStrategyクラスを少し変更する必要があります。戦略正規化残高のコンストラクタに追加パラメータを実装し、仮想ポジションのサイズを設定するパラメータを削除すべきです。これは常に同じで、最小ロットの0.01と等しくなります。
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CSimpleVolumesStrategy::CSimpleVolumesStrategy( string p_symbol, ENUM_TIMEFRAMES p_timeframe, int p_signalPeriod, double p_signalDeviation, double p_signaAddlDeviation, int p_openDistance, double p_stopLevel, double p_takeLevel, int p_ordersExpiration, int p_maxCountOfOrders, double p_fittedBalance = 0) : // Initialization list CVirtualStrategy(p_fittedBalance, 0.01), m_symbol(p_symbol), m_timeframe(p_timeframe), m_signalPeriod(p_signalPeriod), m_signalDeviation(p_signalDeviation), m_signaAddlDeviation(p_signaAddlDeviation), m_openDistance(p_openDistance), m_stopLevel(p_stopLevel), m_takeLevel(p_takeLevel), m_ordersExpiration(p_ordersExpiration), m_maxCountOfOrders(p_maxCountOfOrders) { ... }
現在のフォルダのSimpleVolumesStrategy.mqhファイルに変更を保存します。
戦略のグループ、つまり新しいCVirtualStrategyGroupクラスのインスタンスをEAオブジェクトに追加する機能が必要です。そこで、EAクラスにオーバーロードされたAdd()メソッドを実装しましょう。このメソッドがそれを担います。
//+------------------------------------------------------------------+ //| Class of the EA handling virtual positions (orders) | //+------------------------------------------------------------------+ class CVirtualAdvisor : public CAdvisor { ... public: ... virtual void Add(CVirtualStrategyGroup &p_group); // Method for adding a group of strategies ... }; //+------------------------------------------------------------------+ //| Method for adding a group of strategies | //+------------------------------------------------------------------+ void CVirtualAdvisor::Add(CVirtualStrategyGroup &p_group) { FOREACH(p_group.m_groups, { CVirtualAdvisor::Add(p_group.m_groups[i]); delete p_group.m_groups[i]; }); FOREACH(p_group.m_strategies, CAdvisor::Add(p_group.m_strategies[i])); }
戦略グループはEAに追加された後は不要になるので、このメソッドではダイナミックメモリ領域から即座に削除します。VirtualAdvisor.mqhファイルに加えられた変更を現在のフォルダに保存します。
資金管理クラス
このクラスは、3つの可能な資金管理戦略オプションに従って、仮想ポジションの実際のサイズを決定する責任を負います。
クラスオブジェクトは一意でなければなりません。つまり、シングルトンデザインパターンを使用するか、最終的に実装されたように、クラスにstaticフィールドと、どのオブジェクトからもアクセス可能なメソッドだけを含めることができます。
このクラスのメインメソッドは、Volume()仮想ポジション(注文)の実サイズを決定するメソッドです。さらに2つのメソッドで、取引口座残高のどの部分が取引に関与するかを決定する2つのパラメータの値を設定できます。
//+------------------------------------------------------------------+ //| Basic money management class | //+------------------------------------------------------------------+ class CMoney { static double s_depoPart; // Used part of the total balance static double s_fixedBalance; // Total balance used public: CMoney() = delete; // Disable the constructor static double Volume(CVirtualOrder *p_order); // Determine the real size of the virtual position static void DepoPart(double p_depoPart) { s_depoPart = p_depoPart; } static void FixedBalance(double p_fixedBalance) { s_fixedBalance = p_fixedBalance; } }; double CMoney::s_depoPart = 1.0; double CMoney::s_fixedBalance = 0; //+------------------------------------------------------------------+ //| Determine the real size of the virtual position | //+------------------------------------------------------------------+ double CMoney::Volume(CVirtualOrder *p_order) { // Request the normalized strategy balance for the virtual position double fittedBalance = p_order.FittedBalance(); // If it is 0, then the real volume is equal to the virtual one if(fittedBalance == 0.0) { return p_order.Volume(); } // Otherwise, find the value of the total balance for trading double totalBalance = s_fixedBalance > 0 ? s_fixedBalance : AccountInfoDouble(ACCOUNT_BALANCE); // Return the calculated real volume based on the virtual one return p_order.Volume() * totalBalance * s_depoPart / fittedBalance ; } //+------------------------------------------------------------------+
このコードを現在のフォルダのMoney.mqhファイルに保存します。
テストEA
テスト用のEAファイルに変更を加えてみましょう。SimpleVolumesExpertSingle.mq5ファイルでは、EA初期化関数の戦略コンストラクタのパラメータリストからポジションサイズのパラメータを削除するだけです。
int OnInit() { // Create an EA handling virtual positions expert = new CVirtualAdvisor(magic_, "SimpleVolumesSingle"); expert.Add(new CSimpleVolumesStrategy( symbol_, timeframe_, fixedLot_, signalPeriod_, signalDeviation_, signaAddlDeviation_, openDistance_, stopLevel_, takeLevel_, ordersExpiration_, maxCountOfOrders_) ); // Add one strategy instance return(INIT_SUCCEEDED); }
ここでは、EAを使用して個々の戦略インスタンスのパラメータの新しい良い組み合わせを探すことはしません。先ほど見つけた組み合わせを使用するためです。しかし必要であれば、EAを最適化する準備は整っています。
SimpleVolumesExpert.mq5ファイルにもっと重要な追加をしてみましょう。主に、追加されたクラスの能力をデモンストレーションするために必要なので、これを最終的なコードと考えるべきではありません。
まず最初に、取引戦略のインスタンスをグループ化するさまざまな方法を表す列挙を作成します。
enum ENUM_VA_GROUP { VAG_EURGBP, // Only EURGBP (3 items) VAG_EURUSD, // Only EURUSD (3 items) VAG_GBPUSD, // Only GBPUSD (3 items) VAG_EURGBPUSD_9, // EUR-GBP-USD (9 items) VAG_EURGBPUSD_3_3_3 // EUR-GBP-USD (3+3+3 items) };
最初の3つの値は、1つの銘柄(EURGBP、EURUSD、GBPUSD)に対して3つの取引戦略を使用することに対応します。4番目の値は、9つの戦略インスタンスすべてを1つのグループで使用することに対応します。5番目の値は、3つの正規化されたグループの使用に対応し、特定の銘柄の取引戦略の3つのコピーを含むことになります。
入力パラメータのリストを少し広げてみましょう。
//+------------------------------------------------------------------+ //| Inputs | //+------------------------------------------------------------------+ input group "::: Strategy groups" input ENUM_VA_GROUP group_ = VAG_EURGBP; // - Strategy group input group "::: Money management" input double expectedDrawdown_ = 10; // - Maximum risk (%) input double fixedBalance_ = 0; // - Used deposit (0 - use all) in the account currency input double scale_ = 1.0; // - Group scaling multiplier input group "::: Other parameters" input ulong magic_ = 27183; // - Magic
EA初期化関数で、最大許容ドローダウン10%の正規化を考慮した資金管理パラメータを設定し、戦略のコピーを9つ作成し、選択したグループ分けに従って配置し、EAに追加します。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Set parameters in the money management class CMoney::DepoPart(expectedDrawdown_ / 10.0); CMoney::FixedBalance(fixedBalance_); // Create an EA handling virtual positions expert = new CVirtualAdvisor(magic_, "SimpleVolumes_" + EnumToString(group_)); // Create and fill the array of all strategy instances CVirtualStrategy *strategies[] = { new CSimpleVolumesStrategy("EURGBP", PERIOD_H1, 13, 0.3, 1.0, 0, 10500, 465, 1000, 3, 1600), new CSimpleVolumesStrategy("EURGBP", PERIOD_H1, 17, 1.7, 0.5, 0, 16500, 220, 1000, 3, 900), new CSimpleVolumesStrategy("EURGBP", PERIOD_H1, 51, 0.5, 1.1, 0, 19500, 370, 22000, 3, 1600), new CSimpleVolumesStrategy("EURUSD", PERIOD_H1, 24, 0.1, 0.3, 0, 7500, 2400, 24000, 3, 2300), new CSimpleVolumesStrategy("EURUSD", PERIOD_H1, 18, 0.2, 0.4, 0, 19500, 1480, 6000, 3, 2000), new CSimpleVolumesStrategy("EURUSD", PERIOD_H1, 128, 0.7, 0.3, 0, 3000, 170, 42000, 3, 2200), new CSimpleVolumesStrategy("GBPUSD", PERIOD_H1, 80, 1.1, 0.2, 0, 6000, 1190, 1000, 3, 2500), new CSimpleVolumesStrategy("GBPUSD", PERIOD_H1, 128, 2.0, 0.9, 0, 2000, 1170, 1000, 3, 900), new CSimpleVolumesStrategy("GBPUSD", PERIOD_H1, 13, 1.5, 0.8, 0, 2500, 1375, 1000, 3, 1400), }; // Create arrays of pointers to strategies, one symbol at a time, from the available strategies CVirtualStrategy *strategiesEG[] = {strategies[0], strategies[1], strategies[2]}; CVirtualStrategy *strategiesEU[] = {strategies[3], strategies[4], strategies[5]}; CVirtualStrategy *strategiesGU[] = {strategies[6], strategies[7], strategies[8]}; // Create and add selected groups of strategies to the EA switch(group_) { case VAG_EURGBP: { expert.Add(CVirtualStrategyGroup(strategiesEG, scale_)); FOREACH(strategiesEU, delete strategiesEU[i]); FOREACH(strategiesGU, delete strategiesGU[i]); break; } case VAG_EURUSD: { expert.Add(CVirtualStrategyGroup(strategiesEU, scale_)); FOREACH(strategiesEG, delete strategiesEG[i]); FOREACH(strategiesGU, delete strategiesGU[i]); break; } case VAG_GBPUSD: { expert.Add(CVirtualStrategyGroup(strategiesGU, scale_)); FOREACH(strategiesEU, delete strategiesEU[i]); FOREACH(strategiesEG, delete strategiesEG[i]); break; } case VAG_EURGBPUSD_9: { expert.Add(CVirtualStrategyGroup(strategies, scale_)); break; } case VAG_EURGBPUSD_3_3_3: { // Create a group of three strategy groups CVirtualStrategyGroup *groups[] = { new CVirtualStrategyGroup(strategiesEG, 1.25), new CVirtualStrategyGroup(strategiesEU, 2.24), new CVirtualStrategyGroup(strategiesGU, 2.64) }; expert.Add(CVirtualStrategyGroup(groups, scale_)); break; } default: return(INIT_FAILED); } // Load the previous state if available expert.Load(); return(INIT_SUCCEEDED); }
現在のフォルダのSimpleVolumesExpert.mq5ファイルに加えられた変更を保存します。
検証
最初のグループ、つまりEURGBP銘柄で動作する戦略の3つのコピーをテストしてみましょう。次のような結果が出ました。
図5:3つの戦略によるEURGBPの結果、Scale=1
見てわかるように、正規化戦略の個々のインスタンスでは、最大相対ドローダウンは10%ではなく8%でした。つまり、ポジションサイズを少し大きくできます。10%のドローダウンを達成するために、Scale = 10% / 8% = 1.25とします。
図6:3つの戦略によるEURGBPの結果、Scale=1.25
ドローダウンは約10%になりました。同様の操作をおこなって、第2グループと第3グループのスケーリング乗数を選択してみましょう。次のような結果が出ました。
図7:3つの戦略によるEURUSDの結果、Scale=2.24
図8:3つの戦略によるGBPUSDの結果、Scale=2.64
コードで選択したスケーリング乗数の値を使用して、3つの正規化された戦略グループの正規化グループを作成します。
// Create a group of three strategy groups CVirtualStrategyGroup *groups[] = { new CVirtualStrategyGroup(strategiesEG, 1.25), new CVirtualStrategyGroup(strategiesEU, 2.24), new CVirtualStrategyGroup(strategiesGU, 2.64) };
では、第4グループのスケーリング倍率を選択しましょう。9つのインスタンスを1つのグループにまとめると、以下のような結果になります。
図9:EURGBP、EURUSD、GBPUSD(計9戦略)の結果、Scale=1
これにより、スケールファクタを3.3に上げても、相対的なドローダウンを10%以内に抑えることができます。
図10:EURGBP、EURUSD、GBPUSDの結果(合計9戦略)、スケール=3.3
最後に、最も興味深いことがあります。同じ9つの正規化戦略を別の方法で組み合わせてみましょう。個々の銘柄について3つの戦略のグループを別々に正規化し、その結果できた3つの正規化グループを1つのグループにまとめます。それは以下のようになります。
図11:EURGBP、EURUSD、GBPUSD(3+3+3戦略)の結果、Scale=1
最終的な残高は、同じScale=1の第4グループよりも大きくなりましたが、ドローダウンも大きくなりました。3%ではなく4.57%です。第5グループを10%のドローダウンにして、最終結果を比較してみましょう。
図12:EURGBP、EURUSD、GBPUSD(3+3+3戦略)の結果、Scale=2.18
さて、戦略をグループ化するための5番目のオプションが、相対的な最大ドローダウンを10%以内に維持しながら、はるかに良い結果を与えることは明らかです。選択されたテスト期間中、利益は4番目のグループ分けオプションに比べて2倍以上になりました。
最後に、5番目のグループ分けオプションの残高増加の直線性を見てみましょう。これにより、テスト期間を通じて、EAのパフォーマンスが他の内部期間よりも明らかに悪い内部期間があるかどうかを評価することができます。これをおこなうには、パラメータFixedBalanceを10,000に設定し、EA が常にこの口座残高のみを使用してポジションサイズを計算するようにします。
図13:EURGBP、EURUSD、GBPUSD(3+3+3戦略)の結果、FixedBalance=10000、Scale=2.18
テストグラフでは、残高の伸びがゼロに近い内部期間を緑の四角で示しました。期間は1ヶ月から6ヶ月です。まあ、目指すものがあるということです。このような期間に対処する最も簡単な方法は、より多様化することです。異なる銘柄や時間枠で機能する取引戦略をより多く使用することです。
株式の最大ドローダウンは絶対額でUSD 995、つまり取引に使用したUSD 10,000の残高のわずか10%程度でした。これにより、導入された資金管理システムが正しく動作することが確認されました。
結論
これで、異なる初期残高値を持つ取引口座でEAを実行し、取引戦略の異なるインスタンスに対して残高がどのように分配されるかを制御できるようになりました。残高が増え、より多くのポジションを持つものもあるでしょうし、残高が減り、ポジションのサイズも小さくなるものもあるでしょう。一般的に、テストの助けを借りて、事前に選択した最大許容ドローダウンに適合するパラメータを選択することができます。
注目すべきは、ドローダウンが守られているかどうかはテストを通してしかわからないということです。最適化のために使用されていない期間にEAを起動した場合、その遵守が維持されるかどうかは保証できません。上向きにも、(奇妙なことに)下向きにも変化します。したがってここでは、テストで得られた結果をどの程度信用し、どのように利用するかについて、誰もが独自の判断を下す必要があります。
このプロジェクトの開発を続けるつもりです。ご精読ありがとうございました。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/14336
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索