トレードシステムの評価 - 参入、退出と取引における一般の有効性

Mykola Demko | 18 9月, 2015

はじめに

トレードシステムの有効性を決定する多数の尺度がある;そして、トレーダーは気に入ったものを選んでいる。この記事は、S.V.Bulashev(ブラシェフ)による著作"Statistika dlya traderov"(トレーダーのための統計) に書かれているアプローチについて教えるものである。残念なことに、この本のコピー版は非常に少なく、そして長い間再版もされなかった;しかし、その電子版が多くのウェブサイトで入手可能である。


まえがき

この本は 2003 年に出版されたことを述べておく。そしてそれは MQL-II プログラム言語つきのMetaTrader 3 の頃であり、その当時進歩の途上にあった。そのため、トレード条件自体も最新のMetaTrader 5 クライアントターミナルと比較してその変化を辿ることができる。この本の著者は何世代にも渡るトレーダーの教祖になったことも知って頂きたい (この領域における世代交代が速いことを考慮すると)。しかし時は止まることが無い;この本に書かれた原理はいまだに適用できるが、そのアプローチは修正されるべきである。

S.V. ブラシェフは彼の本を、まず、当時の現実のトレード状況に基づいて書いた。このことが、この著者による統計の記述を変形なしには使えない理由である。明確にするため、当時のトレーディングを思い出そう:スポット市場でのマージン取引は投機的な利益を得るため、通貨を買うとしばらくしてそれを売ることになることを意味した。

これは基本であり、思い起こす価値があるものである。この解釈は「トレーダーのための統計」の本が書かれたときのものである。1ロットの各取引は同じ量の逆取引で終了(Close)しなければならなかった。しかし、(2005年に) そのような統計の使用は再構成が必要だった。その理由は取引の部分的終了がMetaTrader 4で可能となったからである。そのため、ブラシェフの記述した統計を使うためには、解釈のシステムを強化する必要がある。特に解釈は開始(Open)によってではなく終了の事実でされなければならない。

さらに次の5年間に状況は顕著に変わった。今や使い慣れた用語「オーダー」はどこにあるだろうか?完全に使われなくなってしまったようだ。このフォーラムでの質問の傾向を考慮して、MetaTrader 5での解釈システムをそのまま紹介することが良いだろう。

そのため、今日、古典的な用語「オーダー」はもはや無い。今日のオーダーは、ブローカーのサーバーへの取引要求であり、それはトレーダーあるいはMTS によって行われるトレードポジションの開始あるいは変更である。今ではそれはポジションであり;その意味を理解するためマージン取引を述べた。実際のところ、マージン取引がお金を借りる際実行され、ポジションはそのお金が存在する限り存在する。

借り手のアカウントをポジションを終了して清算し、そして損益を確定した結果後すぐにポジションは消滅する。ちなみに、この事実はなぜポジションの逆転がそれを終了させないのかの理由を説明している。実際、借金は残り、あなたがそれを買うためあるいは売るために借りたとしてもそこに違いは無い。取引は実行したオーダーの歴史である。

ここでトレーディングの機能について話そう。現在のところ、MetaTrader 5には、ポジショントレードを部分的に終了することも既存のものを増加させることもできる。こうして、ある額のポジションを開始すると同額で終了する解釈の古典的システムは、過去のものとなった。しかしMetaTrader 5に保存された情報から回復することは本当に不可能なのか?そこで、まず、解釈を再構成していく。


参入(Entering)の有効性

多くの人々がそのトレーディングを効果的に行いたいと望むのは確実である、しかし、どう(形式化)すればよいのか?取引は価格により成り立つ道だと仮定するなら、その道に2つの極端な点がある:すなわち観察部分での最小と最大価格。(買うときには)誰もができる限り最小値に近い市場に入ろうと殺到する。これはどのトレーディングでも中心的規則と考えてもよい:低い価格で買い、高い価格で売るというものだ。

参入の有効性はどれだけ最小に近く買うかである。言い換えると、参入の有効性は最大と参入の価格との距離の比である。なぜ最小から最大までの距離を測るのか?有効性を最小で参入すると1、最大で参入すると 0に等しくする必要がある。

これが最小と参入自体の間の距離でなく、残りの距離で比を求めるのかの理由である。売りの状況は買いの状況が反映されると指摘しなければならない。

参入ポジションの有効性はあるトレードの間、如何に MTS が参入の価格に相対的に有利な利益を実現したかを示すものである。次の式で計算される。

for long positions
enter_efficiency=(max_price_trade-enter_price)/(max_price_trade-min_price_trade);

for short positions
enter_efficiency=(enter_price-min_price_trade)/(max_price_trade-min_price_trade);

参入の有効性は 0 からの範囲の値を持ち得る。 


退出の有効性

退出の状況は同様である。

退出ポジションの有効性はあるトレードの間、如何に MTS が退出の価格に相対的に有利な利益を実現したかを示すものである。次の式で計算される。

for lone positions
exit_efficiency=(exit_price - min_price_trade)/(max_price_trade - min_price_trade);

for short positions
exit_efficiency=(max_price_trade - exit_price)/(max_price_trade - min_price_trade);

退出の有効性は 0 から 1までの範囲内の値を取り得る。


トレードの有効性

全体として、トレードの有効性は参入と退出の両方で決定される。それはトレード中の参入と退出の最大距離(すなわち最小と最大の間の差)に対する比として計算される。 従って、トレードの有効性は2通りに計算される - トレードについての基本情報を直接用いた方法。あるいは前に評価した参入と退出の結果を用いた方法である(区間をずらして)。

トレードの有効性はあるトレードの間に如何によく MTS が全体の利益を実現したかを示すものである。それは次の式で計算される:

for long positions
trade_efficiency=(exit_price-enter_price)/(max_price_trade-min_price_trade);

for short positions
trade_efficiency=(enter_price-exit_price)/(max_price_trade-min_price_trade);

general formula
trade_efficiency=enter_efficiency+exit_efficiency-1;

トレードの有効性は -1 to 1の範囲内の値をとりえる。
トレードの有効性は 0.2 以上でなければならない。
有効性の解析は視覚的にシステムを強化する方向をを示す、なぜならポジションの参入と退出の信号の質を互いから分離して評価することが可能だからである。


解釈の変換

まず最初に、混乱を避けるため、解釈の対象の名前を明確にする必要がある。同じ用語 - オーダー, 取引(deal), ポジション がMetaTrader 5において用いられ、ブラシェフによると、それらを分離する必要がある。私の論説においては、ブラチェフの解釈の対象である「トレード」の名前を用いる、 すなわちトレードは取引である;彼はまた「オーダー」の用語をそれに用い、このような文脈ではこれらの用語は同じものである。ブラチェフは未完の取引をポジションと呼び、我々は未終了のトレードと呼ぶ。

ここでこれら3つの用語は全て簡単に1つの単語「トレード」に合わせられるのが分かる。我々はMetaTrader 5での解釈の名前の付け替えはしない、そしてこれらの3つの用語はクライアントターミナルの開発者によって設計されたのと同様の解釈に留める。結果として我々が使うのは4単語となる - ポジション、取引、オーダー そして トレードの4つである。

オーダーはポジションの開始/変更のサーバーへのコマンドであり、それは統計に直接には関与しない、それは直接には取引とはならないので (理由はオーダーを送ることは必ずしも対応する特定の額と価格の取引に対応する実行とはならないから)、統計をオーダーではなく取引で集めるのは正しい。

同じポジションの解釈の例を考察しよう(上記の説明を明確にするため):

interpretation in МТ-5
deal[ 0 ]  in      0.1   sell   1.22218   2010.06.14 13:33
deal[ 1 ]  in/out  0.2   buy    1.22261   2010.06.14 13:36
deal[ 2 ]  in      0.1   buy    1.22337   2010.06.14 13:39
deal[ 3 ]  out     0.2   sell   1.22310   2010.06.14 13:41
ブラチェフによる解釈
trade[ 0 ]  in 0.1  sell 1.22218 2010.06.14 13:33   out 1.22261 2010.06.14 13:36
trade[ 1 ]  in 0.1  buy  1.22261 2010.06.14 13:36   out 1.22310 2010.06.14 13:41
trade[ 2 ]  in 0.1  buy  1.22337 2010.06.14 13:39   out 1.22310 2010.06.14 13:41


ここでこれらの取扱いが行われる方法を説明しよう。Deal[ 0 ] はポジションを開始し、それを新しい取引の始まりとする。

trade[ 0 ]  in 0.1  sell 1.22218 2010.06.14 13:33

次にポジションの逆転が来る;これは前のトレードが全て閉じられるべきであることを意味する。従って、逆転に関する情報 deal[ 1 ] は終了と開始の両方でそして新トレードの開始で考慮される。イン/アウト取引の前に全ての閉じられていないトレードが一旦閉じられると、新しいトレードを開始(open)する必要がある。すなわち選択した終了の取引の 価格時刻 の情報のみを使う、これはトレードの開始で タイプ が追加で使われるのと異なる。ここで新しい解釈で現れるまで使われていなかった用語を明確にしておく必要がある - それは 取引の方向である。前に "方向" と言うことで買いや売りを意味した、「タイプ」と言う用語も同じ意味を持っていた。これ以降 タイプ と方向は異なる用語である。

タイプ は買いあるいは売りであり、それにたいして 方向 はポジションの参入あるいは退出である。これがなぜポジションが常に イン 方向の取引で開始し、 アウト の取引で終了するのかの理由である。しかし方向はポジションの開始と終了のみに限られない。. この用語はまたポジションの額の増加 (もし「イン」取引がリストの最初に無い場合) とポジションの部分的終了 (「アウト」取引がリストの最後でない場合)も含む。部分終了が可能になったので、ポジションの逆転も導入するのは当然であり、現在のポジションより大きな額の反対取引が行われたとき逆転が起こる、これは イン/アウト 取引である。

そこで前に開始したトレードを終了した (ポジションを逆にするため):

trade[ 0 ]  in 0.1  sell 1.22218 2010.06.14 13:33   out 1.22261 2010.06.14 13:36

残った額は 0.1 ロットであり、それは新しいトレードを開始するのに使われる。

trade[ 1 ]  in 0.1  buy  1.22261 2010.06.14 13:36

そしてイン 方向でdeal[ 2 ] が来てもう一つのトレードを開く:

trade[ 2 ]  in 0.1  buy  1.22337 2010.06.14 13:39

最後に、ポジションを終了する取引 - deal[ 3 ] がまだ終了していない全てのポジションのトレードを終了する。

trade[ 1 ]  in 0.1  buy  1.22261 2010.06.14 13:36   out 1.22310 2010.06.14 13:41
trade[ 2 ]  in 0.1  buy  1.22337 2010.06.14 13:39   out 1.22310 2010.06.14 13:41

上に述べた解釈はブラチェフが用いた解釈の要点を示し - 各開始トレードは一定の 参入(entry) ポイント とある 退出(exit)ポイント を持ち タイプを持つ。この解釈のシステムはあるニュアンス - 部分終了 - を考慮していない。よく見てみると、トレードの数は イン の取引 (イン/アウトを考慮) の数に等しいのが分かる 。このケースでは、 イン 取引で解釈する価値があるが、部分終了でより多くの アウト の取引がある だろう(インとアウトの取引の数が等しい状況もあるかもしれないが、それらは額においてお互いに対応しない)。

全ての アウト 取引を処理するには、 アウトの取引で解釈しなければならない。そしてこの矛盾は取引を別々に処理するならば(最初に - 全ての イン、そして全ての アウト 取引 ,あるいは逆順に)、解決できるようには見えない。しかし取引を順に処理し、各々に特別な処理を適用するならば、矛盾は無い。

ここに例を示す、ここでは アウト 取引の数が イン 取引の数より多い (説明付き):

МТ-5での解釈
deal[ 0 ]  in      0.3   sell      1.22133   2010.06.15 08:00
deal[ 1 ]  out     0.2   buy       1.22145   2010.06.15 08:01
deal[ 2 ]  in/out  0.4   buy       1.22145   2010.06.15 08:02
deal[ 3 ]  in/out  0.4   sell      1.22122   2010.06.15 08:03
deal[ 4 ]  out     0.1   buy       1.2206    2010.06.15 08:06
ブラチェフによる解釈
trade[ 0 ]  in 0.1  sell 1.22218 2010.06.14 13:33   out 1.22261 2010.06.14 13:36
trade[ 1 ]  in 0.1  buy  1.22261 2010.06.14 13:36   out 1.22310 2010.06.14 13:41
trade[ 2 ]  in 0.1  buy  1.22337 2010.06.14 13:39   out 1.22310 2010.06.14 13:41    

終了取引が開始取引より後に現れ、しかし全数ではなく、その部分のみという状況がある (0.3 ロットが開始され 0.2が終了する)。そのような状況をどう扱うか?もし各取引が同じ額で終了するなら、状況は一つの取引での数トレードの開始と考えられる。そこで、それらは同じポイントの開始と異なったポイントの終了を持つ (各トレードの額は終了額で決定されるのは明らか) 例えば、処理のために deal[ 0 ] を選び、トレードを開始する:

trade[ 0 ]  in 0.3  sell 1.22133 2010.06.15 08:00

そして deal[ 1 ] を選び、オープンなトレードを終了し、終了の間に終了額が十分でないことに気が付く。前の開始トレードのコピーを作り、そして「額(Volume)」のパラメーターに額の不足分を指定する。その後で最初のトレード を取引の額で終了する(すなわち最初のトレードの開始で指定した額を終了額に変える):

trade[ 0 ]  in 0.2  sell 1.22133 2010.06.15 08:00   out 1.22145 2010.06.15 08:01   
trade[ 1 ]  in 0.1  sell 1.22133 2010.06.15 08:00

そうした変換は、トレーダーがこのトレードでなく、他のものを終了したいかもしれないので、トレーダーにとって適当でないと見えるかもしれない。とにかく、システムの評価は正しい変換の結果傷つかない。唯一つトレーダーのMetaTrader 4で損失無くトレードするという確信が傷つくかもしれない;再計算をするこのシステムはあらゆる妄想を起すかもしれない。

ブラチェフの本に書かれた統計的解釈のシステムは感情を持たず、参入、退出両方のレートのポジションからの決定を正直に評価することを可能にする。解釈の変換の可能性(あるものから他のものへデータを失うことなく) はMetaTrader 4のために開発されたMTSは MetaTrader 5 の解釈システムのために作り直せないということが間違っていることを証明する。解釈の変換で唯一失うものは別のオーダー (MetaTrader 4) に属する額かもしれない。しかし事実、もはやオーダーが (この用語の古い意味での) 計数されないとすると、それはトレーダーの主観的な見積りに過ぎない。


解釈の変換のためのコード

ここでコードの全ての部分をもっと詳細に見ていこう。変換に備えるのにOOP(オブジェクト指向プログラミング)の インヘリタンス(相続)の機能を必要とする。これが、慣れていない人に MQL5 User Guide を開けて理論を学ぶことを勧める理由である。まず最初に取引の解釈の 構造を説明する( これらの値を直接 MQL5 の標準ファンクションを用いて得るとコードを加速できるかもしれないが、それは読みづらく混乱するかも知れない)。

//+------------------------------------------------------------------+
//| structure of deal                                                |
//+------------------------------------------------------------------+
struct S_Stat_Deals
  {
public:
   ulong             DTicket;         // ticket of deal   
   ENUM_DEAL_TYPE     deals_type;      // type of deal
   ENUM_DEAL_ENTRY    deals_entry;     // direction of deal 
   double            deals_volume;    // volume of deal    
   double            deals_price;     // price of opening of deal   
   datetime          deals_date;      // time of opening of deal  

                     S_Stat_Deals(){};
                    ~S_Stat_Deals(){};
  };

この構造は取引の全ての核の部分の詳細を含んでいるが、派生された詳細は必要なら計算できるため含んでいない。開発者は既にブラチェフの多くのメソッドを戦略の試験に使っているため、カスタムなメソッドを補足するだけである。そのようなメソッドを全体的なトレードの有効性と開始と終了の有効性のために組み込むことにしよう。

これらの値を得るため、開始と終了の時刻、トレード期間中の最小と最大価格等の主要情報の解釈の組み込みを必要とする。そのような主要情報を持っていれば、多くの派生的情報も得ることができる。以下に説明するトレードの構造に注目いただきたい、これは核となる構造であり、全ての解釈の変換はこれに基づいている。

//+------------------------------------------------------------------+
//| structure of trade                                               |
//+------------------------------------------------------------------+
struct S_Stat_Trades
  {
public:
   ulong             OTicket;         // ticket of opening deal
   ulong             CTicket;         // ticket of closing deal     
   ENUM_DEAL_TYPE     trade_type;     // type of trade
   double            trade_volume;    // volume of trade
   double            max_price_trade; // maximum price of trade
   double            min_price_trade; // minimum price of trade
   double            enter_price;     // price of opening of trade
   datetime          enter_date;      // time of opening of trade
   double            exit_price;      // price of closing of trade/s22>
   datetime          exit_date;       // time of closing of trade

   double            enter_efficiency;// effectiveness of entering
   double            exit_efficiency; // effectiveness of exiting
   double            trade_efficiency;// effectiveness of trade

                     S_Stat_Trades(){};
                    ~S_Stat_Trades(){};
  };

ここで2つの核となる構造を作成したので、新しいクラス C_Pos を定義することができ、それが解釈の変換をする。最初に、取引とトレードの解釈の構造へのポインターを定義しよう。相続されたファンクションでその情報が必要なので、それを publicと宣言し; 多くの取引とトレードがあるため、配列を構造へのポインターとして用いる。そのため、情報は構造化され、どの場所からでも使うことができる。

さて来歴を分離したポジションに分割しそしてポジション内の全ての変換を完全なトレードのサイクルとして実行する。これを行うため、 ポジションの属性 の解釈のための変数を宣言する(ポジションのid、ポジションのシンボル、取引の数、 トレードの数)。

//+------------------------------------------------------------------+
//| class for transforming deals into trades                         |
//+------------------------------------------------------------------+
class C_Pos
  {
public:
   S_Stat_Deals      m_deals_stats[];  // structure of deals
   S_Stat_Trades     m_trades_stats[]; // structure of trades
   long              pos_id;          // id of position
   string            symbol;          // symbol of position
   int               count_deals;     // number of deals
   int               count_trades;    // number of trades
   int               trades_ends;     // number of closed trades
   int               DIGITS;          // accuracy of minimum volume by the symbols of position  
                     C_Pos()
     {
      count_deals=0;
      count_trades=0;
      trades_ends=0;
     };
                    ~C_Pos(){};
   void              OnHistory();         // creation of history of position
   void              OnHistoryTransform();// transformation of position history into the new system of interpretation
   void              efficiency();        // calculation of effectiveness by Bulachev's method

private:
   void              open_pos(int c);
   void              copy_pos(int x);
   void              close_pos(int i,int c);
   double            nd(double v){return(NormalizeDouble(v,DIGITS));};// normalization to minimum volume
   void              DigitMinLots(); // accuracy of minimum volume
   double            iHighest(string          symbol_name,// symbol name
                              ENUM_TIMEFRAMES  timeframe,  // period
                              datetime         start_time, // start date
                              datetime         stop_time   // end date
                              );
   double            iLowest(string          symbol_name,// symbol name
                             ENUM_TIMEFRAMES  timeframe,  // period
                             datetime         start_time, // start date
                             datetime         stop_time   // end date
                             );
  };

このクラスはポジションを処理する3つのパブリックなメソッドを持っている。

OnHistory() はポジションの来歴を作成する:
//+------------------------------------------------------------------+
//| filling the structures of history deals                          |
//+------------------------------------------------------------------+
void C_Pos::OnHistory()
  {
   ArrayResize(m_deals_stats,count_deals);
   for(int i=0;i<count_deals;i++)
     {
      m_deals_stats[i].DTicket=HistoryDealGetTicket(i);
      m_deals_stats[i].deals_type=(ENUM_DEAL_TYPE)HistoryDealGetInteger(m_deals_stats[i].DTicket,DEAL_TYPE);   // type of deal
      m_deals_stats[i].deals_entry=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(m_deals_stats[i].DTicket,DEAL_ENTRY);// direction of deal
      m_deals_stats[i].deals_volume=HistoryDealGetDouble(m_deals_stats[i].DTicket,DEAL_VOLUME);              // volume of deal
      m_deals_stats[i].deals_price=HistoryDealGetDouble(m_deals_stats[i].DTicket,DEAL_PRICE);                // price of opening
      m_deals_stats[i].deals_date=(datetime)HistoryDealGetInteger(m_deals_stats[i].DTicket,DEAL_TIME);        // time of opening
     }
  };

各取引に対してこのメソッドは構造のコピーを作成し、それを取引についての情報で埋める。これがまさに以前にそれなしでできると言ったときに意味したものであるが、これでより便利になるのだ (時間をマイクロ秒の単位で短縮したいならこれらの構造を等号のサインの右に置く行で置き換えることができる)。

OnHistoryTransform() はポジションの履歴を新しい解釈のシステムに変換する:

必要なときこのメソッドを至るところで使うとよい;新しいオブジェクトの解釈が現れるたびに、メモリーの割り当てをする。必要なメモリーの量の正確な情報がないときには、概略値で割り当てを行う必要がある。いずれにしても、各ステップで全配列を再割り当てするより経済的である。

次に、取引が インイン/アウト, アウト.であれば、3つのフィルターを用いてポジションの全ての取引をフィルターするループが来る: 特定の行動が各変形に対して組まれる。フィルターはシーケンシャルで、入れ子になっている。あるフィルターがfalseを戻すと、この場合にのみ次のフィルターをチェックする。このような構成は、必要のない行動がカットされるためリソースの面で経済的である。コードを読みやすくするため、クラスで private と宣言されているファンクションに多くの行動が取られる。とにかく、これらのファンクションは開発の間 public であるが、コードの他の部分で必要がないことが分かっている、そのためそれらは再度 private と宣言されている。これがOOPにおいて如何に簡単にデータ scope を扱えるかである。

そこで イン のフィルターで新しい取引が実行され (the open_pos() ファンクション)、 これがポインターの配列の大きさを1増やし取引の構造を対応するトレードの構造のフィールドにコピーする理由である。さらに、取引の構造は価格と時間にに2倍のフィールドを持つため、取引が始められたとき開始のフィールドだけが埋められ、未完成とカウントされる;これを count_tradestrades_endsの差で理解できる。これは最初カウンターにゼロの値を持つということである。取引が現れるとすぐに count_trades カウンターが1加えられ、取引が終了すると trades_ends counter is increasカウンターが1加えられる。こうして count_tradestrades_ends は任意の時にいくつのトレードが終了していないのかを教えることができる。

open_pos() ファンクションは簡単で、取引を開始し対応するカウンターを進めるだけであるが、他のファンクションはそう簡単ではない。 取引がイン タイプでないなら、それは イン/アウト あるいは アウトである。2つのケースで、まず、簡単に実行できるものを選ぶ (これは本質的なことではないが、実行の難しさの順でチェックを作成した)。

イン/アウト フィルターを処理するファンクションはオープンポジションを未終了取引で集計する (どの取引が終了していないかを count_tradestrades_endsの間の差を使って、いかにして知るかを既に述べた)。従って、与えられた取引で終了した額の総計を計算する (そして残りの額は現在の取引のタイプで再度開始される)。ここでこの取引は イン/アウト 方向でありその額は前に開始されているポジションの総額を越えないことを意味するので注意する必要がある。それがポジションと イン/アウト の取引の間の差を計算するのに 利にかなっている理由であり、再度開始すべき新トレードの額を知ることができる。

取引が アウト 方向であると、全てはさらに複雑になる。まず、ポジションの最後の取引は常に アウト 方向であり、例外を作らなければならない - もしそれが最後の取引であれば持っている全てを終了しなければならない。そうでないとき (通り引きが最後のものではない)、 2つの変形が可能である。取引が イン/アウトではなく アウト なので、変形は:最初の変形では額が開始時と全く同じであり、開始の取引の額は終了の取引の額と等しい;第2の変形ではそれらの額は同じではない。

最初の変形は終了で処理される。第2の変形は複雑で、さらに2つの変形があり得る:額がより大きい場合と額が開始のものより小さい場合。額がより大きい場合、次の取引を終了の額が開始の額に等しいかより少ないとなるよう終了する。もし額が次の全取引を終了する のに十分でないなら(額が少ない)、 部分的終了を意味する。ここで取引を新しい額 (前の操作の後残ったもの)で終了する必要がある が、 その前に失くした額でトレードのコピーを作る。そしてもちろん、カウンターを忘れてはいけない。

取引では、取引の再開始後、既に後ほどの部分的終了の取引の待ちがあるという状況もありえる。混乱を避けるため、終了の時系列を保つためこれらの全てを1つずらすべきである。

//+------------------------------------------------------------------+
//| transformation of deals into trades (engine classes)             |
//+------------------------------------------------------------------+
void C_Pos::OnHistoryTransform()
  {
   DigitMinLots();// fill the DIGITS value
   count_trades=0;trades_ends=0;
   ArrayResize(m_trades_stats,count_trades,count_deals);
   for(int c=0;c<count_deals;c++)
     {
      if(m_deals_stats[c].deals_entry==DEAL_ENTRY_IN)
        {
         open_pos(c);
        }
      else// else in
        {
         double POS=0;
         for(int i=trades_ends;i<count_trades;i++)POS+=m_trades_stats[i].trade_volume;

         if(m_deals_stats[c].deals_entry==DEAL_ENTRY_INOUT)
           {
            for(int i=trades_ends;i<count_trades;i++)close_pos(i,c);
            trades_ends=count_trades;
            open_pos(c);
            m_trades_stats[count_trades-1].trade_volume=m_deals_stats[c].deals_volume-POS;
           }
         else// else in/out
           {
            if(m_deals_stats[c].deals_entry==DEAL_ENTRY_OUT)
              {
               if(c==count_deals-1)// if it's the last deal
                 {
                  for(int i=trades_ends;i<count_trades;i++)close_pos(i,c);
                  trades_ends=count_trades-1;
                 }
               else// if it's not the last deal
                 {
                  double out_vol=nd(m_deals_stats[c].deals_volume);
                  while(nd(out_vol)>0)
                    {
                     if(nd(out_vol)>=nd(m_trades_stats[trades_ends].trade_volume))
                       {
                        close_pos(trades_ends,c);
                        out_vol-=nd(m_trades_stats[trades_ends].trade_volume);
                        trades_ends++;
                       }
                     else// if the remainder of closed position is less than the next trade
                       {
                        // move all trades forward by one
                        count_trades++;
                        ArrayResize(m_trades_stats,count_trades);
                        for(int x=count_trades-1;x>trades_ends;x--)copy_pos(x);
                        // open a copy with the volume equal to difference of the current position and the remainder
                        m_trades_stats[trades_ends+1].trade_volume=nd(m_trades_stats[trades_ends].trade_volume-out_vol);
                        // close the current trade with new volume, which is equal to remainder
                        close_pos(trades_ends,c);
                        m_trades_stats[trades_ends].trade_volume=nd(out_vol);
                        out_vol=0;
                        trades_ends++;
                       }
                    }// while(out_vol>0)
                 }// if it's not the last deal
              }// if out
           }// else in/out
        }// else in
     }
  };


有効性の計算

解釈のシステムが変換されると、ブラチェフの方法論による取引の有効性を評価することができる。そのような評価に必要なファンクション は efficiency() メソッドにあり、 取引のストラクチャーを計算されたデータで埋めるのもそこで行われる。参入(Entering)と退出(Exit) の有効性は 0 から 1までで測られ、取引全体に対しては -1 から 1 までで測られる。

//+------------------------------------------------------------------+
//| calculation of effectiveness                                     |
//+------------------------------------------------------------------+
void C_Pos::efficiency()
  {
   for(int i=0;i<count_trades;i++)
     {
      m_trades_stats[i].max_price_trade=iHighest(symbol,PERIOD_M1,m_trades_stats[i].enter_date,m_trades_stats[i].exit_date); // maximal price of trade
      m_trades_stats[i].min_price_trade=iLowest(symbol,PERIOD_M1,m_trades_stats[i].enter_date,m_trades_stats[i].exit_date);  // minimal price of trade
      double minimax=0;
      minimax=m_trades_stats[i].max_price_trade-m_trades_stats[i].min_price_trade;// difference between maximum and minimum
      if(minimax!=0)minimax=1.0/minimax;
      if(m_trades_stats[i].trade_type==DEAL_TYPE_BUY)
        {
         //Effectiveness of entering a position
         m_trades_stats[i].enter_efficiency=(m_trades_stats[i].max_price_trade-m_trades_stats[i].enter_price)*minimax;
         //Effectiveness of exiting from a position
         m_trades_stats[i].exit_efficiency=(m_trades_stats[i].exit_price-m_trades_stats[i].min_price_trade)*minimax;
         //Effectiveness of trade
         m_trades_stats[i].trade_efficiency=(m_trades_stats[i].exit_price-m_trades_stats[i].enter_price)*minimax;
        }
      else
        {
         if(m_trades_stats[i].trade_type==DEAL_TYPE_SELL)
           {
            //Effectiveness of entering a position
            m_trades_stats[i].enter_efficiency=(m_trades_stats[i].enter_price-m_trades_stats[i].min_price_trade)*minimax;
            //Effectiveness of exiting from a position
            m_trades_stats[i].exit_efficiency=(m_trades_stats[i].max_price_trade-m_trades_stats[i].exit_price)*minimax;
            //Effectiveness of trade
            m_trades_stats[i].trade_efficiency=(m_trades_stats[i].enter_price-m_trades_stats[i].exit_price)*minimax;
           }
        }
     }
  }

このメソッドは2つのプライベートメソッド iHighest() と iLowest() を用いる - それらは似ているが唯一の違いは要求されたデータとサーチファンクションが fmin あるいは fmaxである点である。

//+------------------------------------------------------------------+
//| searching maximum within the period start_time --> stop_time     |
//+------------------------------------------------------------------+
double C_Pos::iHighest(string           symbol_name,// symbols name
                       ENUM_TIMEFRAMES  timeframe,  // period
                       datetime         start_time, // start date
                       datetime         stop_time   // end date
                       )
  {
   double  buf[];
   datetime  start_t=(start_time/60)*60;// normalization of time of opening
   datetime  stop_t=(stop_time/60+1)*60;// normaliztion of time of closing  
   int period=CopyHigh(symbol_name,timeframe,start_t,stop_t,buf);
   double res=buf[0];
   for(int i=1;i<period;i++)
      res=fmax(res,buf[i]);
   return(res);
  }

そのメソッドは2つの指定された日付の間の期間の最大値をサーチする。日付はファンクションに start_timestop_time パラメーターとしてファンクションに渡される。取引の日付はファンクションに渡され、そしてトレードの要求は1分のバーの中間でも来るかもしれないので、デートを最近接バーの値に正規化するのはそのファンクションの中で行われる。同じことが iLowest()ファンクションでも行われる。開発したメソッド efficiency() で、ポジションについての全機能を持つが、ポジションそのものの取り扱いはない。これを全ての前のメソッドが使える新しいクラスを決めて扱うことにしよう; 言い換えれば、それをC_Posデリバティブとして宣言する。


デリバティブ・クラス (エンジンクラス)

class C_PosStat:public C_Pos

統計情報を考慮するために、その新しいクラスに与えられる構造を作成する。

//+------------------------------------------------------------------+
//| structure of effectiveness                                       |
//+------------------------------------------------------------------+
struct S_efficiency

  {
   double            enter_efficiency; // effectiveness of entering
   double            exit_efficiency;  // effectiveness of exiting
   double            trade_efficiency; // effectiveness of trade
                     S_efficiency()
     {
      enter_efficiency=0;
      exit_efficiency=0;
      trade_efficiency=0;
     };
                    ~S_efficiency(){};
  };


そしてクラスそのものの本体:

//+------------------------------------------------------------------+
//| class of statistics of trade in whole                            |
//+------------------------------------------------------------------+
class C_PosStat:public C_Pos
  {
public:
   int               PosTotal;         // number of positions in history
   C_Pos             pos[];            // array of pointers to positions
   int               All_count_trades; // total number of trades in history
   S_efficiency      trade[];          // array of pointers to the structure of effectiveness of entering, exiting and trades
   S_efficiency      avg;              // pointer to the structure of average value of effectiveness of entering, exiting and trades
   S_efficiency      stdev;            // pointer to the structure of standard deviation from
                                       // average value of effectiveness of entering, exiting and trades

                     C_PosStat(){PosTotal=0;};
                    ~C_PosStat(){};
   void              OnPosStat();                         // engine classes
   void              OnTradesStat();                      // gathering information about trades into the common array
   
   // functions of writing information to a file
   void              WriteFileDeals(string folder="deals");
   void              WriteFileTrades(string folder="trades");
   void              WriteFileTrades_all(string folder="trades_all");
   void              WriteFileDealsHTML(string folder="deals");
   void              WriteFileDealsHTML2(string folder="deals");
   void              WriteFileTradesHTML(string folder="trades");
   void              WriteFileTradesHTML2(string folder="trades");
   string            enum_translit(ENUM_DEAL_ENTRY x,bool latin=true);// transformation of enumeration into string
   string            enum_translit(ENUM_DEAL_TYPE x,bool latin=true); 
                                                              // transformation of enumeration into string (overloaded)
private:   

   S_efficiency      AVG(int count);                                        // arithmetical mean
   S_efficiency      STDEV(const S_efficiency &mo,int count);               // standard deviation
   S_efficiency      add(const S_efficiency &a,const S_efficiency &b);      //add
   S_efficiency      take(const S_efficiency &a,const S_efficiency &b);     //subtract
   S_efficiency      multiply(const S_efficiency &a,const S_efficiency &b); //multiply
   S_efficiency      divided(const S_efficiency &a,double b);               //divide
   S_efficiency      square_root(const S_efficiency &a);                    //square root
   string            Head_style(string title);
  };  

このクラスを終わりから始めに逆の方向で解析することを勧める。全ては取引とトレードの表をファイルに書くことで終わる。ファンクションのある行がこの目的のために書かれる (名前からお互いの目的を理解できる)。 ファンクションは取引とトレードについてのcvs レポート、また2つのタイプの html レポートを作成する (それらは見た目が異なるだけで、同じ内容を持っている)。

void              WriteFileDeals();      // writing csv report on deals
      void              WriteFileTrades();     // writing csv report on trade
      void              WriteFileTrades_all(); // writing summary csv report of fitness functions
      void              WriteFileDealsHTML2(); // writing html report on deals, 1 variant
      void              WriteFileTradesHTML2();// writing html report on trades, 2 variant

enum_translit() ファンクションは enumerations の値を string タイプにしてログファイルに書き込む。private セクションは S_efficiency 構造の数個のファンクションを含んでいる。全てのファンクションは言語の不利な点を補い、構造にまさに算術操作を行う。これらのメソッドの組み込みに対する見解は多様なので、異なった方法でも実現できる。私は構造のフィールドに対する算術操作のメソッドとしてそれらを実現した。構造の各フィールドを個別のメソッドを使って処理する方がよいと言う人もいるかもしれない。総括すると、プログラマーの数だけの見解があるといえるだろう。将来このような操作を組み込みのメソッドを使って行う可能性がある。

AVG() メソッド は渡された配列の算術平均値を計算するものであるが、分布の全体像を示していない、これがもう一つの標準偏差を計算するメソッド STDEV() とともに提供されている理由である。OnTradesStat() ファンクションは有効性 (前に OnPosStat() で計算された)の値を得、それらを統計的メソッドで処理する。最後に、 クラスの主ファンクション OnPosStat().である。

このファンクションは詳細に検討されなければならない。それは2つの部分で構成されていて、簡単に分割できる。第1の部分は全てのポジションとその id の所有をサーチし、一時的な配列 id_pos に保存する。ステップごと: 利用できる来歴の全体を選択し、取引の数を計算し、取引処理のサイクルを走らせる。サイクル: 取引のタイプがバランスすれば、それを飛ばす (始めの取引を解釈する必要はない)、そうでなければポジションの id を変数に保存しサーチを実行する。もし同じ id が既にベースに存在すれば ( id_po 配列)、 次の取引に行き、そうでなければ id をベースに書く。このような方法で、全ての取引を処理すると配列が既存のポジションの id とポジションの数で埋められる。

long  id_pos[];// auxiliary array for creating the history of positions
   if(HistorySelect(0,TimeCurrent()))
     {
      int HTD=HistoryDealsTotal();
      ArrayResize(id_pos,PosTotal,HTD);
      for(int i=0;i<HTD;i++)
        {
         ulong DTicket=(ulong)HistoryDealGetTicket(i);
         if((ENUM_DEAL_TYPE)HistoryDealGetInteger(DTicket,DEAL_TYPE)==DEAL_TYPE_BALANCE)
            continue;// if it's a balance deal, skip it
         long id=HistoryDealGetInteger(DTicket,DEAL_POSITION_ID);
         bool present=false; // initial state, there's no such position           
         for(int j=0;j<PosTotal;j++)
           { if(id==id_pos[j]){ present=true; break; } }// if such position already exists break

         if(!present)// write id as a new position appears
           {
            PosTotal++;
            ArrayResize(id_pos,PosTotal);
            id_pos[PosTotal-1]=id;
           }
        }
     }
   ArrayResize(pos,PosTotal);

第2の部分で、前に基本クラス C_Pos で説明した全てのメソッドを実現する。それはポジションを総なめにして対応するポジションを処理するメソッドを走らせる。そのメソッドの説明は以下のコード中に与えられる。

for(int p=0;p<PosTotal;p++)
     {
      if(HistorySelectByPosition(id_pos[p]))// select position
        {
         pos[p].pos_id=id_pos[p]; // assigned id of position to the corresponding field of the class C_Pos
         pos[p].count_deals=HistoryDealsTotal();// assign the number of deal in position to the field of the class C_Pos
         pos[p].symbol=HistoryDealGetString(HistoryDealGetTicket(0),DEAL_SYMBOL);// the same actions with symbol
         pos[p].OnHistory();          // start filling the structure sd with the history of position
         pos[p].OnHistoryTransform(); // transformation of interpretation, filling the structure st.
         pos[p].efficiency();         // calculation of the effectiveness of obtained data
         All_count_trades+=pos[p].count_trades;// save the number of trades for displaying the total number
        }
     }


クラスの呼び出し方法

これで、クラス全体を検討した。呼び出しの例を与えることが残されている。構成の可能性を保つため、1つのファンクションにこの呼び出しを明示的に宣言しなかった。さらに、必要に応じてクラスを強化することができ、新しいデータの統計処理のメソッドを組み込むことができる。ここにスクリプトからクラスのメソッドを呼び出す例がある:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
#include <Bulaschev_Statistic.mqh> void OnStart()
  {
   C_PosStat  start;
   start.OnPosStat();
   start.OnTradesStat();
   start.WriteFileDeals();
   start.WriteFileTrades();
   start.WriteFileTrades_all();
   start.WriteFileDealsHTML2();
   start.WriteFileTradesHTML2();
   Print("cko tr ef=" ,start.stdev.trade_efficiency);
   Print("mo  tr ef=" ,start.avg.trade_efficiency);
   Print("cko out ef=",start.stdev.exit_efficiency);
   Print("mo  out ef=",start.avg.exit_efficiency);
   Print("cko in ef=" ,start.stdev.enter_efficiency);
   Print("mo  in ef=" ,start.avg.enter_efficiency);
  }

このスクリプトは、データを Files\OnHistory ディレクトリーにあるファイルに書き込むファンクションの量に応じて、5つのレポートファイルを作成する。次の主ファンクションがここに示される - OnPosStat() と OnTradesStat()、それらは必要な全てのメソッドを呼び出すのに使われる。 スクリプトは全体のトレードの有効性として得られた値を印刷して終わる。それらの値は遺伝的最適化のために使われる。

最適化の間各々のレポートを書く必要はないので、エキスパートアドバイザーのクラスの呼び出しは少し異なって見える。まず、スクリプトとは違って、エキスパートアドバイザーはテスターで稼働させることができる。戦略テスターで稼働させるにはその特殊性を持たなくてはならない。最適化されたとき、OnTester() ファンクションにアクセスをし、 OnDeinit() ファンクションの実行の前にそれを実行する。こうして、変換の主メソッドの呼び出しは分離される。エキスパートアドバイザーのパラメーターの合わせのファンクションの改変の便宜のため、一覧表をクラスの一部としてでなくグローバルに宣言した。この一覧表はクラス C_PosStat のメソッドと同じシートにある。

//+------------------------------------------------------------------+
//| enumeration of fitness functions                                 |
//+------------------------------------------------------------------+
enum Enum_Efficiency
  {
   avg_enter_eff,
   stdev_enter_eff,
   avg_exit_eff,
   stdev_exit_eff,
   avg_trade_eff,
   stdev_trade_eff
  };

これはエキスパートアドバイザーのヘディングに追加されるべきものである。

#include <Bulaschev_Statistic.mqh>
input Enum_Efficiency result=0;// Fitness function


ここで switch オペレーターを使って必要なパラメーターの受け渡しを記述するだけである。

//+------------------------------------------------------------------+
//| Expert optimization function                                     |
//+------------------------------------------------------------------+
double OnTester()
  {
   start.OnPosStat();
   start.OnTradesStat();
   double res;
   switch(result)
     {
      case 0: res=start.avg.enter_efficiency;    break;
      case 1: res=-start.stdev.enter_efficiency; break;
      case 2: res=start.avg.exit_efficiency;     break;
      case 3: res=-start.stdev.exit_efficiency;  break;
      case 4: res=start.avg.trade_efficiency;    break;
      case 5: res=-start.stdev.trade_efficiency; break;
      default : res=0; break;
     }  
   return(res);
  }

OnTester() ファンクションがカスタムファンクションの最大化に使われている事実に注目していただきたい。 カスタムファンクションの最小値を見つける必要があれば、ファンクション自体に-1 を掛けて逆転させるのがよい。標準偏差の付いたこの例のように、偏差が小さいほどトレードの有効性の間の差は小さくなり、トレードの安定性は高くなる。これが標準偏差が最小であるべき理由である。これで、クラスメソッドの呼び出しを扱ったので、レポートをファイルに書くことを検討しよう。

前に、レポートを作成するクラスメソッドを説明した。ここで、どこでいつそれらを呼ぶべきかを見よう。レポートはエキスパートアドバイザーが単一の実行を立ち上げたときにのみ作成されるべきである。そうでないと、エキスパートアドバイザーは最適化モードでファイルを作成し;すなわち1つのファイルでなく、たくさんのファイルを作成することになる (もし異なる名前が毎回渡されると) あるいは1つ、ただし全てのランに同じ名前としその最後のもの、これは消される情報にリソースを無駄使いするので全く意味がない。

とにかく、最適化中はレポートを作成するべきではない。もし、多数の異なった名前を持ったファイルを得たなら、多分その大半を開けることもないだろう。第2の変形は直ちに消去される情報を得るためにリソースを無駄使いする。

これが最高の変形はフィルターを作ることである (Optimization[disabled] モードでレポートを始める)。こうして、HDD は見ることのないレポートで書き散らされることはない。さらに、最適化のスピードは増加する (ファイル操作が最も遅い操作であることは明確である); さらに、素早く必要なパラメーターを保ってそのレポートを得ることが可能である。実際、OnTester あるいは OnDeinit ファンクションの中でどこにフィルターを置いても問題はない。重要なのは、これがレポートを作成するクラスメソッドであり、変換を実行する主メソッドの後で呼び出されるべきである。私はフィルターコードを一杯にしないためにフィルターを OnDeinit() に置いた。

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(!(bool)MQL5InfoInteger(MQL5_OPTIMIZATION))
     {
      start.WriteFileDeals();      // writing csv report on deals
      start.WriteFileTrades();     // writing csv report on trades
      start.WriteFileTrades_all(); // writing summary csv report on fitness functions
      start.WriteFileDealsHTML2(); // writing html report on deals
      start.WriteFileTradesHTML2();// writing html report on trades
     }
  }
//+------------------------------------------------------------------+

メソッドを呼び出す順序は重要ではない。レポート作るために必要な全ては、OnPosStat と OnTradesStat メソッドの中に準備されている。また、レポートを書くために全てのメソッドを呼び出すか、そのいくつかだけかは問題とならず、それらの操作は個別であり;既にクラスに保存された情報の解釈である。


戦略テスターのチェック

戦略テスターの一回の稼働による結果は以下である。

移動平均統計のトレードレポート
# Ticket type volume Open Close Price Efficiency
open close price time price time max min enter exit trade
pos[0] id 2 EURUSD
0 2 3 buy 0.1 1.37203 2010.03.15 13:00:00 1.37169 2010.03.15 14:00:00 1.37236 1.37063 0.19075 0.61272 -0.19653
pos[1] id 4 EURUSD
1 4 5 sell 0.1 1.35188 2010.03.23 08:00:00 1.35243 2010.03.23 10:00:00 1.35292 1.35025 0.61049 0.18352 -0.20599
pos[2] id 6 EURUSD
2 6 7 sell 0.1 1.35050 2010.03.23 12:00:00 1.35343 2010.03.23 16:00:00 1.35600 1.34755 0.34911 0.30414 -0.34675
pos[3] id 8 EURUSD
3 8 9 sell 0.1 1.35167 2010.03.23 18:00:00 1.33343 2010.03.26 05:00:00 1.35240 1.32671 0.97158 0.73842 0.71000
pos[4] id 10 EURUSD
4 10 11 sell 0.1 1.34436 2010.03.30 16:00:00 1.33616 2010.04.08 23:00:00 1.35904 1.32821 0.52384 0.74213 0.26597
pos[5] id 12 EURUSD
5 12 13 buy 0.1 1.35881 2010.04.13 08:00:00 1.35936 2010.04.15 10:00:00 1.36780 1.35463 0.68261 0.35915 0.04176
pos[6] id 14 EURUSD
6 14 15 sell 0.1 1.34735 2010.04.20 04:00:00 1.34807 2010.04.20 10:00:00 1.34890 1.34492 0.61055 0.20854 -0.18090
pos[7] id 16 EURUSD
7 16 17 sell 0.1 1.34432 2010.04.20 18:00:00 1.33619 2010.04.23 17:00:00 1.34491 1.32016 0.97616 0.35232 0.32848
pos[8] id 18 EURUSD
8 18 19 sell 0.1 1.33472 2010.04.27 10:00:00 1.32174 2010.04.29 05:00:00 1.33677 1.31141 0.91916 0.59267 0.51183
pos[9] id 20 EURUSD
9 20 21 sell 0.1 1.32237 2010.05.03 04:00:00 1.27336 2010.05.07 20:00:00 1.32525 1.25270 0.96030 0.71523 0.67553

 

Effectiveness Report
Fitness Func Average Value Standard Deviation
Enter 0.68 0.26
Exit 0.48 0.21
Trades 0.16 0.37

 

そしてバランスのグラフは:

チャート上でカスタムファンクションの最適化は、取引額がより大きなパラメーターを選ぼうとしているわけでなく、長期間の取引でほぼ同じ利益を持つもの、すなわち分散の大きくないもの、を選んでいるのが明確に見てとれる。

Moving Averagesのコードはポジションの増額の機能あるいはその部分終了を含んでいないので、変換の結果は上記に述べたものに近いとは見えない。下に、特にテストコードのために開始したアカウントでスクリプトを立ち上げたもう一つの結果が見られる。

pos[286] id 1019514 EURUSD
944 1092288 1092289 buy 0.1 1.26733 2010.07.08 21:14:49 1.26719 2010.07.08 21:14:57 1.26752 1.26703 0.38776 0.32653 -0.28571
pos[287] id 1019544 EURUSD
945 1092317 1092322 sell 0.2 1.26761 2010.07.08 21:21:14 1.26767 2010.07.08 21:22:29 1.26781 1.26749 0.37500 0.43750 -0.18750
946 1092317 1092330 sell 0.2 1.26761 2010.07.08 21:21:14 1.26792 2010.07.08 21:24:05 1.26782 1.26749 0.36364 -0.30303 -0.93939
947 1092319 1092330 sell 0.3 1.26761 2010.07.08 21:21:37 1.26792 2010.07.08 21:24:05 1.26782 1.26749 0.36364 -0.30303 -0.93939
pos[288] id 1019623 EURUSD
948 1092394 1092406 buy 0.1 1.26832 2010.07.08 21:36:43 1.26843 2010.07.08 21:37:38 1.26882 1.26813 0.72464 0.43478 0.15942
pos[289] id 1019641 EURUSD
949 1092413 1092417 buy 0.1 1.26847 2010.07.08 21:38:19 1.26852 2010.07.08 21:38:51 1.26910 1.26829 0.77778 0.28395 0.06173
950 1092417 1092433 sell 0.1 1.26852 2010.07.08 21:38:51 1.26922 2010.07.08 21:39:58 1.26916 1.26829 0.26437 -0.06897 -0.80460
pos[290] id 1150923 EURUSD
951 1226007 1226046 buy 0.2 1.31653 2010.08.05 16:06:20 1.31682 2010.08.05 16:10:53 1.31706 1.31611 0.55789 0.74737 0.30526
952 1226024 1226046 buy 0.3 1.31632 2010.08.05 16:08:31 1.31682 2010.08.05 16:10:53 1.31706 1.31611 0.77895 0.74737 0.52632
953 1226046 1226066 sell 0.1 1.31682 2010.08.05 16:10:53 1.31756 2010.08.05 16:12:49 1.31750 1.31647 0.33981 -0.05825 -0.71845
954 1226046 1226078 sell 0.2 1.31682 2010.08.05 16:10:53 1.31744 2010.08.05 16:15:16 1.31750 1.31647 0.33981 0.05825 -0.60194
pos[291] id 1155527 EURUSD
955 1230640 1232744 sell 0.1 1.31671 2010.08.06 13:52:11 1.32923 2010.08.06 17:39:50 1.33327 1.31648 0.01370 0.24062 -0.74568
956 1231369 1232744 sell 0.1 1.32584 2010.08.06 14:54:53 1.32923 2010.08.06 17:39:50 1.33327 1.32518 0.08158 0.49938 -0.41904
957 1231455 1232744 sell 0.1 1.32732 2010.08.06 14:58:13 1.32923 2010.08.06 17:39:50 1.33327 1.32539 0.24492 0.51269 -0.24239
958 1231476 1232744 sell 0.1 1.32685 2010.08.06 14:59:47 1.32923 2010.08.06 17:39:50 1.33327 1.32539 0.18528 0.51269 -0.30203
959 1231484 1232744 sell 0.2 1.32686 2010.08.06 15:00:20 1.32923 2010.08.06 17:39:50 1.33327 1.32539 0.18655 0.51269 -0.30076
960 1231926 1232744 sell 0.4 1.33009 2010.08.06 15:57:32 1.32923 2010.08.06 17:39:50 1.33327 1.32806 0.38964 0.77543 0.16507
961 1232591 1232748 sell 0.4 1.33123 2010.08.06 17:11:29 1.32850 2010.08.06 17:40:40 1.33129 1.32806 0.98142 0.86378 0.84520
962 1232591 1232754 sell 0.4 1.33123 2010.08.06 17:11:29 1.32829 2010.08.06 17:42:14 1.33129 1.32796 0.98198 0.90090 0.88288
963 1232591 1232757 sell 0.2 1.33123 2010.08.06 17:11:29 1.32839 2010.08.06 17:43:15 1.33129 1.32796 0.98198 0.87087 0.85285
pos[292] id 1167490 EURUSD
964 1242941 1243332 sell 0.1 1.31001 2010.08.10 15:54:51 1.30867 2010.08.10 17:17:51 1.31037 1.30742 0.87797 0.57627 0.45424
965 1242944 1243333 sell 0.1 1.30988 2010.08.10 15:55:03 1.30867 2010.08.10 17:17:55 1.31037 1.30742 0.83390 0.57627 0.41017
pos[293] id 1291817 EURUSD
966 1367532 1367788 sell 0.4 1.28904 2010.09.06 00:24:01 1.28768 2010.09.06 02:53:21 1.28965 1.28710 0.76078 0.77255 0.53333

これが変換された情報がどのように見えるか示したものであり;読者に全てを 丹念に検討できるように(そして比較して理解できるように)した 、元の取引の履歴を別のファイルに保存した;これは多くのトレーダーが今はなくした履歴であり、彼らはそれをMetaTrader 4の [Results] セクションで見たものだった。


結論

結論として、私は開発者にエキスパートアドバイザーを最適化する可能性を加えることを勧めたい、それはカスタム可能なパラメーターに依るだけでなく、それを標準のものと組合せて作る可能性であり、それは他の最適化ファンクションで行ったものである。この論説を要約すると、基本のみを含んでいると言え、初期機能の可能性である;そして読者は自身のニーズに従ってクラスを強化できると期待する。成功を祈る!