履歴によるフィルター

17 2月 2016, 11:15
Andrey Khatimlianskii
0
392

はじめに


フィルターには異なる種類のものがあります。インディケータの値、市場の不安定性、時間、平日、といったものです。すべて損失トレードをふるいにかけるために使用されるものです。Expert Advisor にそういったフィルターを追加するのはきわめてたやすいことです。開始ブロックの前に条件を1つ追加すればいいだけです。

ですが、フィルターとしてExpert Advisor 履歴を使いたいと思えば、どうすればよいのでしょうか?数多くのトレードがうまくいってトレードシステムのスイッチを切ったら、のちに履歴を取得することはありません。またそのため分析対象となるものはなにもないのです。この問題を解決するために必要なことは Expert Advisor に仮想トレードを教えるのです。すなわち、トレードの開始、変更、終了を実際にトレード処理なしでシミュレーションするのです。

本稿で取り上げるのはこれです。

実験的戦略


われわれのシステムを導入するにあたり、 Expert Advisor の CrossMACD_DeLuxe.mq4 に少し手を加えます。
  • 各ポジションのオープン/変更/クローズのところで、仮想ポジションの配列に変更を書き入れるのです。それが以下です。
  • 仮想ポジションのストップロスとテイクプロフィット処理の追跡を追加。
  • フィルター基準-実トレードが開始されないようにする条件、の追加。
できる限り詳細に EA の変更手順をすべて説明していくようにいたします。説明が面白くなければ、既成の Expert Advisor をダウンロードし、『ゲームはろうそくの価値がないか?』のパートに行くことができます。

仮想ポジションの会計


というわけで、ポジションをオープンするシグナルが表示されます。パラメータであるストップロスとテイクプロフィットが計算され、関数 OrderSend() を呼び出す準備がすべてととのいました。この瞬間まさに仮想トレードを開始するのです。ただ適切な変数に必要なパラメータをすべて保存するだけです。

void OpenBuy()
  {
    int _GetLastError = 0;
    double _OpenPriceLevel, _StopLossLevel, _TakeProfitLevel;
    _OpenPriceLevel = NormalizeDouble(Ask, Digits);
 
    if(StopLoss > 0)
        _StopLossLevel = NormalizeDouble(_OpenPriceLevel -
                                     StopLoss*Point, Digits);
    else
        _StopLossLevel = 0.0;
    if(TakeProfit > 0)
        _TakeProfitLevel = NormalizeDouble(_OpenPriceLevel +
                                   TakeProfit*Point, Digits);
    else
        _TakeProfitLevel = 0.0;
 
    //---- open the virtual position
    virtOrderSend(OP_BUY, _OpenPriceLevel, _StopLossLevel,
                  _TakeProfitLevel);
 
    if(OrderSend(Symbol(), OP_BUY, 0.1, _OpenPriceLevel, 3,
       _StopLossLevel, _TakeProfitLevel, "CrossMACD",
       _MagicNumber, 0, Green) < 0)
      {
        _GetLastError = GetLastError();
        Alert("Error OrderSend № ", _GetLastError);
        return(-1);
      }
  }
 
//---- Save parameters of the opened position in main variables
void virtualOrderSend(int type, double openprice, double stoploss,
                      double takeprofit)
  {
    virtTicket = 1;
    virtType = type;
    virtOpenPrice = openprice;
    virtStopLoss = stoploss;
    virtTakeProfit = takeprofit;
  }

使う変数はたった5個なのがお分かりですね。

int       virtTicket     = 0;   
// determines, if there is an open virtual position
int       virtType       = 0;   // position type
double    virtOpenPrice  = 0.0; // position opening price
double    virtStopLoss   = 0.0; // position StopLoss
double    virtTakeProfit = 0.0; // position TakeProfit

われわれのタスク救済のためにそれ以上のキャラクターは必要ありません。この例の機能性を広げたいとお思いなら、変数の必要な量を追加するだけです。

ポジションのクローズや変更を追跡するにはもっとやることがあります。ポジションをオープンする制御ブロックをコピーします。これは Expert Advisor にあり、オーダー特性を仮想に変更します。

int start()
  {
    // skipped...
 
    //+------------------------------------------------------------------+
    //| Control block of "virtual" positions                             |
    //+------------------------------------------------------------------+
    if(virtTicket > 0)
      {
        //---- if BUY-position is open,
        if(virtType == OP_BUY)
          {
            //---- if MACD crossed 0-line downwards,
            if(NormalizeDouble(MACD_1 + CloseLuft*Point*0.1,
               Digits + 1) <= 0.0)
              {
                //---- close position
                virtOrderClose(Bid);
              }
            //---- if the signal did not change, accompany the position by 
            //     TrailingStop
            else
              {
                if(TrailingStop > 0)
                  {
                    if(NormalizeDouble(Bid - virtOpenPrice,
                       Digits ) > 0.0)
                      {
                        if(NormalizeDouble( Bid - TrailingStop*Point -
                           virtStopLoss, Digits) > 0.0 || virtStopLoss < Point)
                        {
                          virtStopLoss = Bid - TrailingStop*Point;
                        }
                      }
                  }
              }
          }
        //---- if SELL position is open
        if(virtType == OP_SELL)
          {
            //---- if MACD crossed 0-line upwards,
            if(NormalizeDouble(MACD_1 - CloseLuft*Point*0.1,
               Digits + 1 ) >= 0.0)
              {
                //---- close the position
                virtOrderClose(Ask);
              }
            //---- if the signal did not change, accompany the position by 
            //     TrailingStop
            else
              {
                if ( TrailingStop > 0 )
                  {
                    if(NormalizeDouble( virtOpenPrice - Ask,
                       Digits ) > 0.0 )
                      {
                        if(NormalizeDouble( virtStopLoss - ( Ask +
                           TrailingStop*Point ), Digits ) > 0.0 ||
                           virtStopLoss <= Point )
                          {
                            virtStopLoss = Ask + TrailingStop*Point;
                          }
                      }
                  }
              }
          }
      }
    // skipped...
    return(0);
  }
 

//---- virtual position closing function
void virtOrderClose(double closeprice)
  {
    //---- Save the parameters of the closed position in the array
    ArrayResize(virtClosedOrders, virtClosedOrdersCount + 1);
 
    virtClosedOrders[virtClosedOrdersCount][0] = virtType;
    virtClosedOrders[virtClosedOrdersCount][1] = virtOpenPrice;
    virtClosedOrders[virtClosedOrdersCount][2] = virtStopLoss;
    virtClosedOrders[virtClosedOrdersCount][3] = virtTakeProfit;
    virtClosedOrders[virtClosedOrdersCount][4] = closeprice;
 
    virtClosedOrdersCount ++;
 
    //---- clear variables
    virtTicket = 0;
    virtType = 0;
    virtOpenPrice = 0.0;
    virtStopLoss = 0.0;
    virtTakeProfit = 0.0;
  }

ごらんのように、変更は変数 virtStopLoss への新しい値割りの単純な代入です。そして終了はたいへんむつかしいものです。オーダークローズの特性すべてが配列に保存されます。のちに、仮想履歴全体がそこに保存されることとなります。そこからクローズされたポジションに関する情報を取り、新規ポジションオープンの判断をするのです。

ここでストップロスとテイクプロフィットについてクローズするポジションを処理する必要があります。このためには作成済みの制御ブロックに文字列をいくつか追加します。

if(virtType == OP_BUY)
  {
    //---- check, whether SL was not activated 
    if(virtStopLoss > 0.0 && NormalizeDouble(virtStopLoss - Bid, 
       Digits ) >= 0.0)
      {
        virtOrderClose(virtStopLoss);
      }
    //---- check, whether TPL was not activated
    if(virtTakeProfit > 0.0 && NormalizeDouble( Bid - virtTakeProfit, 
       Digits ) >= 0.0)
      {
        virtOrderClose(virtTakeProfit);
      }
  }

ここでわれわれの仮想履歴は準備ずみでフィルター基準を追加することができます。

何が『良く』何が『悪い』のでしょうか?


特定の条件が実装されたあと、ポジションオープンを禁止する必要があります。ですがどの条件を選ぶべきでしょうか?続く複数の敗北トレード、ストップロスのアクティブ化、または複数の直近トレードの平均減益。たしかに答えはむつかしいです。バリアントにはどれもメリットとデメリットがあります。

条件それぞれの効率を確認するには、3件すべてをコード化し履歴上で検証するのです。

extern int TradeFiltrVariant = 0;
 
//---- Function of checking the necessity of the real trading
bool virtCheckCondition()
  {
    int pos, check_pos = 2;
    double last_profit = 0.0, pre_last_profit = 0.0;
    
    //---- depending on the value of TradeFiltrVariant:
    switch(TradeFiltrVariant)
      {
        //---- 1: prohibit real trading, if 2 last deals are losing
        case 1:
          {
            //---- if the virtual history contains enough orders,
            if(virtClosedOrdersCount >= check_pos)
              {
                for(pos = 1; pos check_pos; pos ++)
                  {
                    //---- if the deal is profitable, return true
                    if((virtClosedOrders[virtClosedOrdersCount-pos][0] == 0 &&
                        virtClosedOrders[virtClosedOrdersCount-pos][4] -
                        virtClosedOrders[virtClosedOrdersCount-pos][1] >= 0.0) ||
                        (virtClosedOrders[virtClosedOrdersCount-pos][0] == 1 &&
                        virtClosedOrders[virtClosedOrdersCount-pos][1] -
                        virtClosedOrders[virtClosedOrdersCount-pos][4] >= 0.0))
                      {
                        return(true);
                      }
                    }
              }
            return(false);
          }
        //---- 2: prohibit real trading if the last position was closed 
        //        by StopLoss
        case 2:
          {
            //---- if the virtual history contains enough orders,
            if(virtClosedOrdersCount > 0)
              {
                //---- if the closing price of the last order is equal to StopLoss,
                if(virtClosedOrders[virtClosedOrdersCount-1][2] -
                   virtClosedOrders[virtClosedOrdersCount-1][4] < Point &&
                   virtClosedOrders[virtClosedOrdersCount-1][4] -
                   virtClosedOrders[virtClosedOrdersCount-1][2] < Point)
                  {
                    return(false);
                  }
              }
            return(true);
          }
        //---- 3: prohibit real trading, if the profit of the last position  
        //----    is lower than that of the last but one position (or loss is higher)
        case 3:
          {
            if(virtClosedOrdersCount >= 2)
              {
                if(virtClosedOrders[virtClosedOrdersCount-1][0] == 0)
                  {
                    last_profit =  virtClosedOrders[virtClosedOrdersCount-1][4] -
                                   virtClosedOrders[virtClosedOrdersCount-1][1];
                  }
                else
                  {
                    last_profit =  virtClosedOrders[virtClosedOrdersCount-1][1] -
                                   virtClosedOrders[virtClosedOrdersCount-1][4];
                  }
                if(virtClosedOrders[virtClosedOrdersCount-2][0] == 0)
                  {
                    pre_last_profit = virtClosedOrders[virtClosedOrdersCount-2][4] -
                                      virtClosedOrders[virtClosedOrdersCount-2][1];
                  }
                else
                  {
                    pre_last_profit = virtClosedOrders[virtClosedOrdersCount-2][1] -
                                      virtClosedOrders[virtClosedOrdersCount-2][4];
                  }
 
                if(pre_last_profit - last_profit > 0.0)
                  {
                    return(false);
                  }
              }
            return(true);
          }
        //---- by default the filter is off, i.e. positions will be always opened in reality
        default: return(true);
      }
    return(true);
  }
 
void OpenBuy()
  {
    int _GetLastError = 0;
    double _OpenPriceLevel, _StopLossLevel, _TakeProfitLevel;
    _OpenPriceLevel = NormalizeDouble(Ask, Digits);
 
    if(StopLoss > 0)
      {
        _StopLossLevel = NormalizeDouble(_OpenPriceLevel - StopLoss*Point, Digits);
      }
    else
      {
        _StopLossLevel = 0.0;
      }
 
    if(TakeProfit > 0)
      {
        _TakeProfitLevel = NormalizeDouble(_OpenPriceLevel + TakeProfit*Point, Digits);
      }
    else
      {
        _TakeProfitLevel = 0.0;
      }
 
    //---- open a virtual position
    virtOrderSend(OP_BUY, _OpenPriceLevel, _StopLossLevel, _TakeProfitLevel);
 
    //---- if virtual positions filter prohibits trading, exit
    if(virtCheckCondition() == false)
      {
        return(0);
      }
 
    if(OrderSend( Symbol(), OP_BUY, 0.1, _OpenPriceLevel, 3, _StopLossLevel,
          _TakeProfitLevel, "CrossMACD", _MagicNumber, 0, Green) < 0 )
      {
        _GetLastError = GetLastError();
        Alert("Error OrderSend № ", _GetLastError);
        return(-1);
      }
  }

ごらんのように、ここでは外部変数 TradeFiltrVariant を取得しました。それはフィルター基準の選択を行います。

extern int TradeFiltrVariant = 0;
//---- 0: filter is off, i.e. position is always opened in reality
//---- 1: prohibit real trading, if 2 last positions are losing
//---- 2: prohibit real trading, if the last position closed by StopLoss
//---- 3: prohibit real trading, if the profit of the last position is lower, 
//----    than that of the last but one psition (or loss is higher)

ここでは、異なるフィルターを持つ Expert Advisor を検証し、結果を比較します

ゲームはろうそく足の価値がないのでしょうか?


検証のために私は以下のパラメータを選択しました。
シンボル:GBPUSD
期間:H4, 01.01.2005 ~ 01.01.2006
モデル化モード:全ティック(モデル化クオリティー 90%、 HistoryCenter によるクオート)

EA パラメータ
ストップロス:50
テイクプロフィット:0(無効)
トレーリングストップ:0(無効)
FastEMAPeriod:12
SlowEMAPeriod:26
OpenLuft:10
CloseLuft:0

次のテーブルには使用フィルターの結果への依存を示しています。

TradeFiltrVariantトータル収益/損失
トータルトレード
収益性のあるトレード
敗北トレード
0 1678.75
41 9 (22%)
32 (78%)
1 105.65 20
2 (10%)
18 (90%)
2 -550.20 11 0 (0%)
11 (100%)
3 1225.13 28 7 (25%)
21 (75%)









このようにフィルターの有用性についての理論は証明されていませんでした。そのうえ、フィルターを用いたトレード結果はフィルターなしのトレードよりも悪いものでした。唯一の例外は3番目のバリアントです。収益性あるトレード割合が高い(22%に対して25%)のですが、トータル収益は3つのバリアント全部の中で一番低いものです。

それでは何が悪いのでしょうか?おそらく、フィルター基準が悪かったのです。3つのフィルターすべてを反対のフィルターに変えます。すなわち以下の場合、実トレードを終了するのです。
  • 直近のトレード2つに収益性があります。
  • 直近のポジションは収益を上げています(テイクプロフィットが無効なので、ストップロスの類似隊は取得しません)。
  • 直近ポジションの収益は1ポジション以外は最後のものよりも高くなっています。
Expert Advisor を切り捨てないために、TradeFiltrVariant の値を4、5、6 の さらに 3 つだけ追加します。

//---- 4: prohibit real trading, if two last trades are profitable
//---- 5: prohibit real trading, if the last position is profitable
//---- 6: prohibit real trading, if the profit of the last position is higher, 
//----    than that of the last but one position (or loss is lower)
 
    //---- 4: prohibit real trading, if two last trades are profitable
    case 4:
      {
        if(virtClosedOrdersCount >= check_pos)
          {
            for(pos = 1; pos check_pos; pos ++)
              {
                //---- if the trade is losing, return true
                if((virtClosedOrders[virtClosedOrdersCount-pos][0] == 0 &&
                   virtClosedOrders[virtClosedOrdersCount-pos][1] -
                   virtClosedOrders[virtClosedOrdersCount-pos][4] > 0.0) ||
                   (virtClosedOrders[virtClosedOrdersCount-pos][0] == 1 &&
                   virtClosedOrders[virtClosedOrdersCount-pos][4] -
                   virtClosedOrders[virtClosedOrdersCount-pos][1] > 0.0))
                  {
                    return(true);
                  }
              }
          }
        return(false);
      }
    //---- 5: prohibit real trading, if the last position is profitable
    case 5:
      {
        if(virtClosedOrdersCount >= 1)
          {
            if(virtClosedOrders[virtClosedOrdersCount-1][0] == 0)
              {
                last_profit =  virtClosedOrders[virtClosedOrdersCount-1][4] -
                               virtClosedOrders[virtClosedOrdersCount-1][1];
              }
            else
              {
                last_profit =  virtClosedOrders[virtClosedOrdersCount-1][1] -
                               virtClosedOrders[virtClosedOrdersCount-1][4];
              }
 
            if(last_profit > 0.0)
              {
                return(false);
              }
          }
        return(true);
      }
    //---- 6: prohibit real trading, if the profit of the last position is higher, 
    //----    than that of the last but one position (or loss is lower)
    case 6:
      {
        if(virtClosedOrdersCount >= 2)
          {
            if(virtClosedOrders[virtClosedOrdersCount-1][0] == 0)
              {
                last_profit =  virtClosedOrders[virtClosedOrdersCount-1][4] -
                               virtClosedOrders[virtClosedOrdersCount-1][1];
              }
            else
              {
                last_profit =  virtClosedOrders[virtClosedOrdersCount-1][1] -
                               virtClosedOrders[virtClosedOrdersCount-1][4];
              }
            if(virtClosedOrders[virtClosedOrdersCount-2][0] == 0)
              {
                pre_last_profit = virtClosedOrders[virtClosedOrdersCount-2][4] -
                                  virtClosedOrders[virtClosedOrdersCount-2][1];
              }
            else
              {
                pre_last_profit = virtClosedOrders[virtClosedOrdersCount-2][1] -
                                  virtClosedOrders[virtClosedOrdersCount-2][4];
              }
 
            if(last_profit - pre_last_profit > 0.0)
              {
                return(false);
              }
          }
        return(true);
      }

ここで新規の3つのバリアントを検証し、それらをテーブルに追加します。

AdaptVariantトータル収益/損失
トータルトレード
収益性のあるトレード
敗北トレード
0 1678.75
41 9 (22%)
32 (78%)
1 105.65 20 2 (10%)
18 (90%)
2 -550.20 11 0 (0%)
11 (100%)
3 1225.13 28 7 (25%)
21 (75%)
4
1779.24
39 9 (23%) 30 (77%)
5
2178.95
31
9 (29%)
22 (71%)
6
602.32
24
5 (21%)
19 (79%)














6番目のバリアントはトレードの半分をふるい落としました。収益のあるもの、損失を出したものの両方です。4番目は損失を出したトレードを2件除外し、トータル収益を $ 100.49 増やしました。.

ベストなバリアントは収益を出すトレードの後のトレードを禁止するもので、それは損失を出したトレードや収益を出さないトレードを100件ふるい落としました。

そういうわけで、希望はあります。シンプルで一般的な戦略の改善が可能なのです。

おわりに


私見ですが、そういったフィルターは実際のシステム改善には十分ではありません。もっと踏み込んだ研究により新しい結論が導かれる必要があります。

本稿で説明したフィルターは複雑で戦略によってまったく変わる可能性があります。効率は直接、収益性あるポジションと敗者ポジションの交換により決まります。

本稿はフィルターのみに限定してお話ししています。ただそれがいっそうの発展への勢いを与えることを願っています。




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

添付されたファイル |
テスターにおける再クオートのモデル化と Expert Advisor 安定性解析 テスターにおける再クオートのモデル化と Expert Advisor 安定性解析
再クオートは多くの Expert Advisors にとって災難です。とりわけトレードへのエンター/エグジット条件の感度が高い場合は。本稿では再クオートについての EA 安定性を確認する方法を提供します。
サポート/レジスタンスレベルの表示 サポート/レジスタンスレベルの表示
本稿では MetaTrader 4 プログラムにおけるサポート/レジスタンスレベルの検出および表示について取り上げます。その便利で万能のインディケータはシンプルなアルゴリズムを基にしています。本稿ではまた、一つのワークスペース内の異なるタイムフレームからもたらされる結果を表示する、シンプルなインディケータの作成という有用なテーマにも取り組みます。
3Dグラフ-市場分析のプロのツール 3Dグラフ-市場分析のプロのツール
本稿では、3D グラフ作成用の簡単なライブラリとそのグラフを Microsoft Excel でののちの閲覧を書きます。準備には標準の MQL4 オプションを使い、*.csv ファイルにデータをエクスポートします。
サポート/レジスタンスレベルを描く方法 サポート/レジスタンスレベルを描く方法
本稿はサポート/レジスタンスレベルを検出するシンプルなスクリプトを作成する手順について説明します。対象は初心者です。よって手順の各段階の詳細説明を確認することができます。ただ、スクリプトはひじょうにシンプルでも、本稿は上級トレーダーや MetaTrader 4 プラットフォーム利用者にとっても有用なことでしょう。ここには表形式へのデータエクスポート例、テーブルの Microsoft Excel へのインポート、より詳細な分析のためのチャートプロットの例が入っています。