トレードラブ博士または いかに心配することを止め、自習 Expert Advisorを作成したか
Roman Zamozhnyy | 28 10月, 2015
コンセプト
Expert Advisor を作成したあと、われわれは内蔵ストラテジーテスタ を使用して最適なパラメータを選択するためすべて再ソートします。それらを選択する際、Expert Advisor を実行し、そこに何か重大な変更が起これば Expert Advisor が停止しストラテジーテスタを用いて繰り返し最適化する、などです。
再最適化の意思決定、そしてその動作を当然妨げないで Expert Advisorに対するプロセスとしての再最適化を割り当てることはできるのでしょうか?
この問題に対する解決法のひとつは Quantum によって彼の記事 "Adaptive Trading Systems and Their Use in MetaTrader5 Terminal"の中で提案されました。その内容は、これまでもっとも収益を上げてきた戦略が選択される仮想トレーディング戦略のいくつか(数に制限なし)と共に現実のトレーディング戦略を利用することに特化したものです。トレーディング戦略を変更する判断は特定の固定されたバーの値を越えたあとに行われます。
jooの記事 "Genetic Algorithms - It's Easy!"から借用して遺伝的アルゴリズム(GA)のコードセットを利用することを提案します。そのような Expert Advisor の実装を見ていきます(下記の例の一つは自動売買チャンピオンシップ 2011に参加するために提案されたEAです)。
進行中の作業
というわけで Expert Advisor の機能を決める必要があります。まず言うまでもありませんが、選択された戦略を用いてトレードを行うことです。二番目に再最適化を行う(あらためて入力パラメータの最適化を行うこと)タイミングかどうか判断することです。三番目に GAを利用して再最適化を行うことです。まず初めに、もっともシンプルな再最適化を再検討します。 - 戦略はあるので新しいパラメータを選択するだけです。それから GAを利用して変動するマーケットにおいて別の戦略を選択できるか、もしできるならどのようにそれを行うか見ていきます。
のちに適応度関数(以降FF)内でのシミュレーションを促すため、一つのインスツルメントにおいて完了したバーのみトレードを行うように決めます。他にポジションはなく部分的なクローズもありません。トレーリングストップ同様固定されたストップやテイクを利用することを好む方は記事"Tick Generation Algorithm in MetaTrader5 Strategy Tester"を参照してFF内で「ストップロス」および「テイクプロフィット」のチェックを取り入れてください。私は下記の賢いフレーズを拡張します。
FF内で「テスター」に"Open Prices Only(始値のみ)"として知られる検証モードをシミュレーションします。しかし!これがFFにおける唯一可能な検証プロセスシミュレーションであるというわけではありません。きちょうめんな方は「ティック毎」モードでFF検証を実装したいと思われるかもしれません。そういう方には一から始めたり「ティック毎」を作成することのないよう、MetaQuotesが作った既存のアルゴリズムに注目いただきたいと思います。別の言い方をすると、本稿を読んだあと FF内でストップやテイクを正しくシミュレーションするために必要な条件のFFにおいて「ティック毎」をシミュレーションすることができるようになっているのです。
重点である戦略実装に進む前に、専門的な用語を振り返りポジションオープンおよびクローズを行い、新規バーのオープンを判断する予備関数を実装します。
//+------------------------------------------------------------------+ //| Define whether a new bar has opened | //+------------------------------------------------------------------+ bool isNewBars() { CopyTime(s,tf,0,1,curBT); TimeToStruct(curBT[0],curT); if(tf==PERIOD_M1|| tf==PERIOD_M2|| tf==PERIOD_M3|| tf==PERIOD_M4|| tf==PERIOD_M5|| tf==PERIOD_M6|| tf==PERIOD_M10|| tf==PERIOD_M12|| tf==PERIOD_M15|| tf==PERIOD_M20|| tf==PERIOD_M30) if(curT.min!=prevT.min) { prevBT[0]=curBT[0]; TimeToStruct(prevBT[0],prevT); return(true); }; if(tf==PERIOD_H1|| tf==PERIOD_H2|| tf==PERIOD_H3|| tf==PERIOD_H4|| tf==PERIOD_H6|| tf==PERIOD_H8|| tf==PERIOD_M12) if(curT.hour!=prevT.hour) { prevBT[0]=curBT[0]; TimeToStruct(prevBT[0],prevT); return(true); }; if(tf==PERIOD_D1|| tf==PERIOD_W1) if(curT.day!=prevT.day) { prevBT[0]=curBT[0]; TimeToStruct(prevBT[0],prevT); return(true); }; if(tf==PERIOD_MN1) if(curT.mon!=prevT.mon) { prevBT[0]=curBT[0]; TimeToStruct(prevBT[0],prevT); return(true); }; return(false); } //+------------------------------------------------------------------+ //| ClosePosition | //+------------------------------------------------------------------+ void ClosePosition() { request.action=TRADE_ACTION_DEAL; request.symbol=PositionGetSymbol(0); if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY) request.type=ORDER_TYPE_SELL; else request.type=ORDER_TYPE_BUY; request.type_filling=ORDER_FILLING_FOK; if(SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST|| SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT) { request.sl=NULL; request.tp=NULL; request.deviation=100; } while(PositionsTotal()>0) { request.volume=NormalizeDouble(MathMin(PositionGetDouble(POSITION_VOLUME),SymbolInfoDouble(PositionGetSymbol(0),SYMBOL_VOLUME_MAX)),2); if(SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST|| SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT) { if(request.type==ORDER_TYPE_SELL) request.price=SymbolInfoDouble(s,SYMBOL_BID); else request.price=SymbolInfoDouble(s,SYMBOL_ASK); } OrderSend(request,result); Sleep(10000); } } //+------------------------------------------------------------------+ //| OpenPosition | //+------------------------------------------------------------------+ void OpenPosition() { double vol; request.action=TRADE_ACTION_DEAL; request.symbol=s; request.type_filling=ORDER_FILLING_FOK; if(SymbolInfoInteger(s,SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST|| SymbolInfoInteger(s,SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT) { request.sl=NULL; request.tp=NULL; request.deviation=100; } vol=MathFloor(AccountInfoDouble(ACCOUNT_FREEMARGIN)*optF*AccountInfoInteger(ACCOUNT_LEVERAGE) /(SymbolInfoDouble(s,SYMBOL_TRADE_CONTRACT_SIZE)*SymbolInfoDouble(s,SYMBOL_VOLUME_STEP)))*SymbolInfoDouble(s,SYMBOL_VOLUME_STEP); vol=MathMax(vol,SymbolInfoDouble(s,SYMBOL_VOLUME_MIN)); vol=MathMin(vol,GetPossibleLots()*0.95); if(SymbolInfoDouble(s,SYMBOL_VOLUME_LIMIT)!=0) vol=NormalizeDouble(MathMin(vol,SymbolInfoDouble(s,SYMBOL_VOLUME_LIMIT)),2); request.volume=NormalizeDouble(MathMin(vol,SymbolInfoDouble(s,SYMBOL_VOLUME_MAX)),2); while(PositionSelect(s)==false) { if(SymbolInfoInteger(s,SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST|| SymbolInfoInteger(s,SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT) { if(request.type==ORDER_TYPE_SELL) request.price=SymbolInfoDouble(s,SYMBOL_BID); else request.price=SymbolInfoDouble(s,SYMBOL_ASK); } OrderSend(request,result); Sleep(10000); PositionSelect(s); } while(PositionGetDouble(POSITION_VOLUME)<vol) { request.volume=NormalizeDouble(MathMin(vol-PositionGetDouble(POSITION_VOLUME),SymbolInfoDouble(s,SYMBOL_VOLUME_MAX)),2); if(SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST|| SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT) { if(request.type==ORDER_TYPE_SELL) request.price=SymbolInfoDouble(s,SYMBOL_BID); else request.price=SymbolInfoDouble(s,SYMBOL_ASK); } OrderSend(request,result); Sleep(10000); PositionSelect(s); } } //+------------------------------------------------------------------+
慎重に見るとポジションオープン関数内に重要なパラメータが3つあるのが判ります。変数 s およびoptF と GetPossibleLots() 関数呼び出しです。
- s - トレーディングインスツルメント。GAによって最適化される変数の一つです。
- optF - トレーディングに使用されるデポジットの部分(GAによって最適化されるもう一つの変数です。)
- GetPossibleLots() 関数 - トレーディングに使用されるデポジットの部分を返します。
//+------------------------------------------------------------------+ //| GetPossibleLots | //+------------------------------------------------------------------+ double GetPossibleLots() { request.volume=1.0; if(request.type==ORDER_TYPE_SELL) request.price=SymbolInfoDouble(s,SYMBOL_BID); else request.price=SymbolInfoDouble(s,SYMBOL_ASK); OrderCheck(request,check); return(NormalizeDouble(AccountInfoDouble(ACCOUNT_FREEMARGIN)/check.margin,2)); }
記述の順序をわずかに壊し、全 Expert Advisorsに共通で第二段階の基礎である関数をもう2件導入します。
//+------------------------------------------------------------------+ //| InitRelDD | //+------------------------------------------------------------------+ void InitRelDD() { ulong DealTicket; double curBalance; prevBT[0]=D'2000.01.01 00:00:00'; TimeToStruct(prevBT[0],prevT); curBalance=AccountInfoDouble(ACCOUNT_BALANCE); maxBalance=curBalance; HistorySelect(D'2000.01.01 00:00:00',TimeCurrent()); for(int i=HistoryDealsTotal();i>0;i--) { DealTicket=HistoryDealGetTicket(i); curBalance=curBalance+HistoryDealGetDouble(DealTicket,DEAL_PROFIT); if(curBalance>maxBalance) maxBalance=curBalance; } } //+------------------------------------------------------------------+ //| GetRelDD | //+------------------------------------------------------------------+ double GetRelDD() { if(AccountInfoDouble(ACCOUNT_BALANCE)>maxBalance) maxBalance=AccountInfoDouble(ACCOUNT_BALANCE); return((maxBalance-AccountInfoDouble(ACCOUNT_BALANCE))/maxBalance); }
ここでどんなことが判るでしょうか?最初の関数はアカウント残高の最大値を判断します。二番間の関数はそれに関連するアカウントの現在ドローダウンを計算します。それらの特殊性は第二段階の詳しい記述に述べられます。
- 1件は移動平均の交点(ゴールデンクロス:インスツルメントを買う。デスクロス-インスツルメントを売る)を利用してトレードを行うもの。
- もう1件は過去の5件のトレーディングについて [0..1] の範囲で価格変動を受け取るシンプルなニューラルネットワークです。
アルゴリズムとしては、自己最適化 Expert Advisor は以下のように実証されます。
-
Expert Advisorによって使用される変数の初期化:インディケータバッファを得意義し初期化、またはニューラルネットワークのトポロジーを設定(階層内の階層/ニューロン数;ニューロン数が全階層で同一であるシンプルなニューラルネットワークが例として提供されています)し、時間枠を設定します。そののち、おそらくもっとも重要なステップです。-これを最初の関数であるFFを呼び出す「遺伝的最適化」関数と言います。
重要! それぞれのトレーディング戦略には新しい FF があります。すなわちFFは毎回新たに作成されます。たとえば単一移動平均に対する FF は完全に2つの移動平均に対する FF とは異なり、ニューラルネットワーク FFとも大きく異なります。
私の Expert Advisors でのFFパフォーマンス結果は相対ドローダウンが外部変数として設定されている 臨界値(われわれの例では0.5)を越えていなければ残高最大値です。言い換えると、次の GA 実行による残高が 100.000で、相対ドローダウンが-0.6なら、FF=0.0ということです。親愛なる読者のみなさんの場合は FF 結果は全く異なる基準で現れるかもしれません。
「遺伝的アルゴリズム」のパフォーマンス結果を収集します。移動平均の交点に対しては明らかに移動平均期間があり、ニューラルネットワークの場合シナプスのウェイトがあります。そして両者に共通の結果(私の他の Expert Advisorsについても)は、次回の再最適化までトレードが行わわれるインスツルメントです。すでにわれわれにとってはなじみのある optF、すなわちトレーディングに使用されるデポジットの一部です。みなさんはご自身の判断において自由に FF に最適化されたパラメータを追加することができます。たとえば時間枠や他のパラメータも自由に選択できます。
初期化の最終ステップは最大アカウント残高値を求めることです。なぜそれが重要なのでしょうか?それは、これが最適化意思決定の出発点だからです。
重要! 再最適化についての判断はどのように行われるのでしょうか。相対「残高」ドローダウンが外部変数として設定された特定の臨界値(われわれの例では0.2となっています)に到達するやいなや再最適化が必要です。Expert Advisor が臨界ドローダウンに到達したバーごとに再最適化を行うことのないように、残高最高値を現在値と置換します。
- トレードは進行しています。
- 各クローズポジションで残高ドローダウンが臨界値に達したかどうかチェックします。臨界値に達していたら GA を実行し、処理結果を収集します。(それが再最適化です!)
-
そしてforexの責任者からの世界を破産させないようにというお願い、または(こちらの方がより頻繁にありますが)ストップアウト、マージンコール、救急車、、などの要請の連絡を待ちます。
移動平均戦略(ソースコードも利用可能です)およびニューラルネットワークを使用して上記手順を Expert Advisorに実装するプログラム を下記ですべてソースコードとしてご覧ください。
コードは GPL ライセンスの条件下提供されています。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { tf=Period(); //---for bar-to-bar test... prevBT[0]=D'2001.01.01'; //---... long ago TimeToStruct(prevBT[0],prevT); //--- historical depth (should be set since the optimisation is based on historical data) depth=10000; //--- copies at a time (should be set since the optimisation is based on historical data) count=2; ArrayResize(LongBuffer,count); ArrayResize(ShortBuffer,count); ArrayInitialize(LongBuffer,0); ArrayInitialize(ShortBuffer,0); //--- calling the neural network genetic optimisation function GA(); //--- getting the optimised neural network parameters and other variables GetTrainResults(); //--- getting the account drawdown InitRelDD(); return(0); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { if(isNewBars()==true) { bool trig=false; CopyBuffer(MAshort,0,0,count,ShortBuffer); CopyBuffer(MAlong,0,0,count,LongBuffer); if(LongBuffer[0]>LongBuffer[1] && ShortBuffer[0]>LongBuffer[0] && ShortBuffer[1]<LongBuffer[1]) { if(PositionsTotal()>0) { if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY) { ClosePosition(); trig=true; } } } if(LongBuffer[0]<LongBuffer[1] && ShortBuffer[0]<LongBuffer[0] && ShortBuffer[1]>LongBuffer[1]) { if(PositionsTotal()>0) { if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL) { ClosePosition(); trig=true; } } } if(trig==true) { //--- if the account drawdown has exceeded the allowable value: if(GetRelDD()>maxDD) { //--- calling the neural network genetic optimisation function GA(); //--- getting the optimised neural network parameters and other variables GetTrainResults(); //--- readings of the drawdown will from now on be based on the current balance instead of the maximum balance maxBalance=AccountInfoDouble(ACCOUNT_BALANCE); } } CopyBuffer(MAshort,0,0,count,ShortBuffer); CopyBuffer(MAlong,0,0,count,LongBuffer); if(LongBuffer[0]>LongBuffer[1] && ShortBuffer[0]>LongBuffer[0] && ShortBuffer[1]<LongBuffer[1]) { request.type=ORDER_TYPE_SELL; OpenPosition(); } if(LongBuffer[0]<LongBuffer[1] && ShortBuffer[0]<LongBuffer[0] && ShortBuffer[1]>LongBuffer[1]) { request.type=ORDER_TYPE_BUY; OpenPosition(); } }; } //+------------------------------------------------------------------+ //| Preparing and calling the genetic optimizer | //+------------------------------------------------------------------+ void GA() { //--- number of genes (equal to the number of optimised variables), //--- all of them should be specified in the FitnessFunction()) GeneCount =OptParamCount+2; //--- number of chromosomes in a colony ChromosomeCount=GeneCount*11; //--- minimum search range RangeMinimum =0.0; //--- maximum search range RangeMaximum =1.0; //--- search pitch Precision =0.0001; //--- 1 is a minimum, anything else is a maximum OptimizeMethod =2; ArrayResize(Chromosome,GeneCount+1); ArrayInitialize(Chromosome,0); //--- number of epochs without any improvement Epoch =100; //--- ratio of replication, natural mutation, artificial mutation, gene borrowing, //--- crossingover, interval boundary displacement ratio, every gene mutation probabilty, % UGA(100.0,1.0,1.0,1.0,1.0,0.5,1.0); } //+------------------------------------------------------------------+ //| Fitness function for neural network genetic optimizer: | //| selecting a pair, optF, synapse weights; | //| anything can be optimised but it is necessary | //| to carefully monitor the number of genes | //+------------------------------------------------------------------+ void FitnessFunction(int chromos) { int b; //--- is there an open position? bool trig=false; //--- direction of an open position string dir=""; //--- opening price double OpenPrice=0; //--- intermediary between a gene colony and optimised parameters int z; //--- current balance double t=cap; //--- maximum balance double maxt=t; //--- absolute drawdown double aDD=0; //--- relative drawdown double rDD=0.000001; //--- fitness function proper double ff=0; //--- GA is selecting a pair z=(int)MathRound(Colony[GeneCount-1][chromos]*12); switch(z) { case 0: {s="AUDUSD"; break;}; case 1: {s="AUDUSD"; break;}; case 2: {s="EURAUD"; break;}; case 3: {s="EURCHF"; break;}; case 4: {s="EURGBP"; break;}; case 5: {s="EURJPY"; break;}; case 6: {s="EURUSD"; break;}; case 7: {s="GBPCHF"; break;}; case 8: {s="GBPJPY"; break;}; case 9: {s="GBPUSD"; break;}; case 10: {s="USDCAD"; break;}; case 11: {s="USDCHF"; break;}; case 12: {s="USDJPY"; break;}; default: {s="EURUSD"; break;}; } MAshort=iMA(s,tf,(int)MathRound(Colony[1][chromos]*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN); MAlong =iMA(s,tf,(int)MathRound(Colony[2][chromos]*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN); dig=MathPow(10.0,(double)SymbolInfoInteger(s,SYMBOL_DIGITS)); //--- GA is selecting the optimal F optF=Colony[GeneCount][chromos]; leverage=AccountInfoInteger(ACCOUNT_LEVERAGE); contractSize=SymbolInfoDouble(s,SYMBOL_TRADE_CONTRACT_SIZE); b=MathMin(Bars(s,tf)-1-count-MaxMAPeriod,depth); //--- for a neural network using historical data - where the data is copied from for(from=b;from>=1;from--) { CopyBuffer(MAshort,0,from,count,ShortBuffer); CopyBuffer(MAlong,0,from,count,LongBuffer); if(LongBuffer[0]>LongBuffer[1] && ShortBuffer[0]>LongBuffer[0] && ShortBuffer[1]<LongBuffer[1]) { if(trig==false) { CopyOpen(s,tf,from,count,o); OpenPrice=o[1]; dir="SELL"; trig=true; } else { if(dir=="BUY") { CopyOpen(s,tf,from,count,o); if(t>0) t=t+t*optF*leverage*(o[1]-OpenPrice)*dig/contractSize; else t=0; if(t>maxt) {maxt=t; aDD=0;} else if((maxt-t)>aDD) aDD=maxt-t; if((maxt>0) && (aDD/maxt>rDD)) rDD=aDD/maxt; OpenPrice=o[1]; dir="SELL"; trig=true; } } } if(LongBuffer[0]<LongBuffer[1] && ShortBuffer[0]<LongBuffer[0] && ShortBuffer[1]>LongBuffer[1]) { if(trig==false) { CopyOpen(s,tf,from,count,o); OpenPrice=o[1]; dir="BUY"; trig=true; } else { if(dir=="SELL") { CopyOpen(s,tf,from,count,o); if(t>0) t=t+t*optF*leverage*(OpenPrice-o[1])*dig/contractSize; else t=0; if(t>maxt) {maxt=t; aDD=0;} else if((maxt-t)>aDD) aDD=maxt-t; if((maxt>0) && (aDD/maxt>rDD)) rDD=aDD/maxt; OpenPrice=o[1]; dir="BUY"; trig=true; } } } } if(rDD<=trainDD) ff=t; else ff=0.0; AmountStartsFF++; Colony[0][chromos]=ff; } //+---------------------------------------------------------------------+ //| getting the optimized neural network parameters and other variables | //| should always be equal to the number of genes | //+---------------------------------------------------------------------+ void GetTrainResults() { //--- intermediary between a gene colony and optimised parameters int z; MAshort=iMA(s,tf,(int)MathRound(Chromosome[1]*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN); MAlong =iMA(s,tf,(int)MathRound(Chromosome[2]*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN); CopyBuffer(MAshort,0,from,count,ShortBuffer); CopyBuffer(MAlong,0,from,count,LongBuffer); //--- save the best pair z=(int)MathRound(Chromosome[GeneCount-1]*12); switch(z) { case 0: {s="AUDUSD"; break;}; case 1: {s="AUDUSD"; break;}; case 2: {s="EURAUD"; break;}; case 3: {s="EURCHF"; break;}; case 4: {s="EURGBP"; break;}; case 5: {s="EURJPY"; break;}; case 6: {s="EURUSD"; break;}; case 7: {s="GBPCHF"; break;}; case 8: {s="GBPJPY"; break;}; case 9: {s="GBPUSD"; break;}; case 10: {s="USDCAD"; break;}; case 11: {s="USDCHF"; break;}; case 12: {s="USDJPY"; break;}; default: {s="EURUSD"; break;}; } //--- saving the best optimal F optF=Chromosome[GeneCount]; } //+------------------------------------------------------------------+
アルゴリズムの主要関数FFを見ていきます。
自己最適化 Expert Advisor の背後にある考えはすべてFFにおける期間内(たとえば1,000バーの履歴)トレーディングプロセス(MetaQuotesによる標準的Tester における)のシミュレーションに基づいています。 FFは「遺伝的アルゴリズム」(GA function())から最適化された変数のインプットを受け取ります。移動平均の交点を基にしたアルゴリズムの場合、最適化された変数は以下を含みます。
- インスツルメント(forexにおける-通貨ペア);そうです。それは典型的な複数通貨対応 Expert Advisor で「遺伝的アルゴリズム」がインスツルメントを選択します(コードはチャンピオンシップに参加するために提案された Expert Advisor から借用しているため、通貨ペアはチャンピオンシップの通貨ペアと一致しています。通常はブローカーによって値付けされるインスツルメントであればなんでもありえます)。
注意:残念ながら Expert Advisor は検証モードではMarketWatch ウィンドウから通貨ペアリストを取得することはできません。 (MetaQuotes ユーザーのお陰でここでそれがはっきりしましたありえない!)よって外為や株式とは別にテスターで Expert Advisor を実行したければ、FF およびGetTrainResults() 関数内にご自身のインスツルメントを指定することです。
- トレーディングに使用されるデポジット部分
- 2つの移動平均の期間
下記の例は「テスト用」Expert Advisor のバリアントです。
「実際のトレード」コードは Market Watch ウィンドウからのインスツルメントリストを利用することで大幅に簡素化することができます。
FF および GetTrainResults() 関数内でコメント "//--- GA is selecting a pair" and "//--- saving the best pair" を使用してこれを行うには以下を書くだけです。
//--- GA is selecting a pair z=(int)MathRound(Colony[GeneCount-1][chromos]*(SymbolsTotal(true)-1)); s=SymbolName(z,true);
そこで必要ならば履歴に基づくトレーディングのシミュレーションのための変数をFF の冒頭で指定し初期化します。次の段階で「遺伝的アルゴリズムから最適化された変数の異なる値を収集します。たとえばこの "optF=Colony[GeneCount][chromos]からです。それにより、デポジット部分の値が GAからFFに転送されます。
のちに履歴内の利用可能なバー番号で、1万番目のバーか「始値のみ」モードでクオートを受け取るプロセスをシミュレーションするのに利用可能な最初のバーのどちらか、そしてトレーディング判断をするものを確認します。
- 移動平均値をバッファにコピーします。
- それがデスクロスかどうか確認します。
- デスクロスがあり、オープンポジションがない場合(trig==falseであれば)、仮想「売り」ポジション(始値と方向だけ記憶します)をオープンします。
- デスクロスがあり、オープンした「買い」ポジションがある場合(dir=="BUY"であれば)、始値のバーを取り、以下のようにひじょうに重要な3行に注目します。
- ポジションのクローズをシミュレーションし、残高において変更します。現在残高は現在残高値んいよって増加します。それはトレードされるデポジット部分によって乗じられ、始値と終値の差で乗じられ、 ピップ価格(おおよその)によって乗じられます。
- 現在残高がトレードシミュレーションの履歴について最高値に達したか確認し、達していなければ資金の残高ドローダウン最高値を計算します。
- 以前に計算された資金ドローダウンを残高における相対ドローダウンに換算します。
- 仮想「売り」ポジションを(始値と方向だけ記憶します)をオープンします。
- ゴールドクロスに対して同様の確認および計算を行います。
利用可能は履歴と仮想トレードのシミュレーション全体を確認したら、最終的なFF 値を計算します。計算された残高に対する相対ドローダウンが検証用セットより低ければ、FF=balanceです。それ以外はotherwise FF=0です。「遺伝的アルゴリズム」が狙いとするのはFFの最大化なのです!
すべてが終わると、インスツルメントのさまざまな値、デポジット部分、移動平均期間の条件で、「遺伝的アルゴリズム」が相対ドローダウン最低値(最低値はユーザーが設定します)で残高を最大にする値を見つけます。
おわりに
手短に結論を述べます。自己トレーニング Expert Advisorを作成するのは簡単です。難しいのは何をインプットするか見つけることです(重要なことは考え方です。実装は単に技術的な問題に過ぎません)。
悲観的な方からの「それは動作するの?」という質問を想定します。答えは「動作しますとも」。楽天的な方への私の言葉はこうです。「これは 『魔法の機器』ではないのですから」
本稿で提案した手法とQuantumによる手法との根本的違いは何でしょうか?MAを使用するExpert Advisorsを比較するともっともよくできます。
- 「適応型トレーディングシステム」において MA期間を判断するのはコンパイルし厳密にコード化しする前で、選択はこの制限されたバリアントの数以外で可能です。「遺伝的に最適化された」Expert Advisorではコンパイル前に期間判断はせず、この判断は GAによって行われ、バリアント数は常識の範囲で決められます。
- 適応型トレーディングシステムにおける仮想トレードはバーからバーです。「遺伝的に最適化された」Expert Advisorでは それは稀で、再最適化に対する条件が発生するときのみ行われます。増加する戦略、パラメータ、インスツルメント数へのコンピュータパフォーマンスは適応型トレーディングシステムに対する制約要因となるかもしれません。
付録
最適化せずテスターでニューラルネットワークを実行する場合の結果があります。それは2010年1月1日からの日次チャートに基づくものです。
ストラテジーテスタレポート |
||||||||||||
MetaQuotesデモ (Build 523) |
||||||||||||
Expert Advisor: | ANNExample | |||||||||||
シンボル: | EURUSD | |||||||||||
期間: | 日次 (2010.01.01 - 2011.09.30) | |||||||||||
入力パラメータ: | trainDD=0.9 | |||||||||||
maxDD=0.1 | ||||||||||||
ブローカー: | Alpari NZ Limited | |||||||||||
通貨: | USD | |||||||||||
初期デポジット: | 10 000.00 | |||||||||||
レバレッジ: | 1:100 | |||||||||||
結果 |
||||||||||||
履歴クオリティー: | 100% | |||||||||||
バー: | 454 | ティック: | 2554879 | |||||||||
トータル純利益: | -9 094.49 | 粗利: | 29 401.09 | 総損失: | -38 495.58 | |||||||
プロフィットファクター: | 0.76 | 予想ペイオフ: | -20.53 | マージンレベル: | 732.30% | |||||||
リカバリファクター: | -0.76 | Sharpe 比率: | -0.06 | OnTester 結果: | 0 | |||||||
残高ドローダウン: | ||||||||||||
Abs. 残高ドローダウン: | 9 102.56 | 残高ドローダウン最高値: | 11 464.70 (92.74%) | 相対残高ドローダウン: | 92.74% (11 464.70) | |||||||
資本ドローダウン: | ||||||||||||
Abs. 資本ドローダウン: | 9 176.99 | 資本ドローダウン最高値: | 11 904.00 (93.53%) | 相対資本ローダウン: | 93.53% (11 904.00) | |||||||
トレードトータル: | 443 | ショート (勝利 %): | 7 (14.29%) | ロング (勝利 %): | 436 (53.44%) | |||||||
取り引きトータル: | 886 | 収益 (トータル% ): | 234 (52.82%) | 損失 (トータル% ): | 209 (47.18%) | |||||||
最大収益: | 1 095.57 | 最大損失: | -1 438.85 | |||||||||
平均収益: | 125.65 | 平均損失: | -184.19 | |||||||||
最大連続勝利(資金における収益) : | 8 (397.45) | 最大連続敗北 (資金における損失): | 8 (-1 431.44) | |||||||||
最大連続収益 (勝利数): | 1 095.57 (1) | 最大連続損失 (敗北数): | -3 433.21 (6) | |||||||||
平均連続勝利: | 2 | 平均連続敗北: | 2 |
以下は選択された再最適化の3とおりのバリアントです。:
1番目...
時刻 | 取り引き | シンボル | タイプ | 方向 | ボリューム | 価格 | 注文 | スワップ | 収益 | 残高 |
2010.01.01 00:00 | 1 | 残高 | 0.00 | 10 000.00 | 10 000.00 | |||||
2010.01.04 00:00 | 2 | AUDUSD | 買い | イン | 0.90 | 0.89977 | 2 | 0.00 | 0.00 | 10 000.00 |
2010.01.05 00:00 | 3 | AUDUSD | 売り | アウト | 0.90 | 0.91188 | 3 | 5.67 | 1 089.90 | 11 095.57 |
2010.01.05 00:00 | 4 | AUDUSD | 買い | イン | 0.99 | 0.91220 | 4 | 0.00 | 0.00 | 11 095.57 |
2010.01.06 00:00 | 5 | AUDUSD | 売り | アウト | 0.99 | 0.91157 | 5 | 6.24 | -62.37 | 11 039.44 |
2010.01.06 00:00 | 6 | AUDUSD | 買い | イン | 0.99 | 0.91190 | 6 | 0.00 | 0.00 | 11 039.44 |
2010.01.07 00:00 | 7 | AUDUSD | 売り | アウト | 0.99 | 0.91924 | 7 | 18.71 | 726.66 | 11 784.81 |
2番目..
時刻 | 取り引き | シンボル | タイプ | 方向 | ボリューム | 価格 | 注文 | コミッション | スワップ | 収益 | 残高 |
2010.05.19 00:00 | 189 | AUDUSD | 売り | アウト | 0.36 | 0.86110 | 189 | 0.00 | 2.27 | -595.44 | 4 221.30 |
2010.05.19 00:00 | 190 | EURAUD | 売り | イン | 0.30 | 1.41280 | 190 | 0.00 | 0.00 | 0.00 | 4 221.30 |
2010.05.20 00:00 | 191 | EURAUD | 買い | アウト | 0.30 | 1.46207 | 191 | 0.00 | 7.43 | -1 273.26 | 2 955.47 |
2010.05.20 00:00 | 192 | AUDUSD | 買い | イン | 0.21 | 0.84983 | 192 | 0.00 | 0.00 | 0.00 | 2 955.47 |
3番目
時刻 | 取り引き | シンボル | タイプ | 方向 | ボリューム | 価格 | 注文 | スワップ | 収益 | 残高 |
2010.06.16 00:00 | 230 | GBPCHF | 買い | イン | 0.06 | 1.67872 | 230 | 0.00 | 0.00 | 2 128.80 |
2010.06.17 00:00 | 231 | GBPCHF | 売り | アウト | 0.06 | 1.66547 | 231 | 0.13 | -70.25 | 2 058.68 |
2010.06.17 00:00 | 232 | GBPCHF | 買い | イン | 0.06 | 1.66635 | 232 | 0.00 | 0.00 | 2 058.68 |
2010.06.18 00:00 | 233 | GBPCHF | 売り | アウト | 0.06 | 1.64705 | 233 | 0.04 | -104.14 | 1 954.58 |
2010.06.18 00:00 | 234 | AUDUSD | 買い | イン | 0.09 | 0.86741 | 234 | 0.00 | 0.00 | 1 954.58 |
2010.06.21 00:00 | 235 | AUDUSD | 売り | アウト | 0.09 | 0.87184 | 235 | 0.57 | 39.87 | 1 995.02 |
2010.06.21 00:00 | 236 | AUDUSD | 買い | イン | 0.09 | 0.88105 | 236 | 0.00 | 0.00 | 1 995.02 |
2010.06.22 00:00 | 237 | AUDUSD | 売り | アウト | 0.09 | 0.87606 | 237 | 0.57 | -44.91 | 1 950.68 |
2010.06.22 00:00 | 238 | AUDUSD | 買い | イン | 0.09 | 0.87637 | 238 | 0.00 | 0.00 | 1 950.68 |
2010.06.23 00:00 | 239 | AUDUSD | 売り | アウト | 0.09 | 0.87140 | 239 | 0.57 | -44.73 | 1 906.52 |
2010.06.23 00:00 | 240 | AUDUSD | 買い | イン | 0.08 | 0.87197 | 240 | 0.00 | 0.00 | 1 906.52 |
2010.06.24 00:00 | 241 | AUDUSD | 売り | アウト | 0.08 | 0.87385 | 241 | 1.51 | 15.04 | 1 923.07 |
2010.06.24 00:00 | 242 | AUDUSD | 買い | イン | 0.08 | 0.87413 | 242 | 0.00 | 0.00 | 1 923.07 |
2010.06.25 00:00 | 243 | AUDUSD | 売り | アウト | 0.08 | 0.86632 | 243 | 0.50 | -62.48 | 1 861.09 |
2010.06.25 00:00 | 244 | AUDUSD | 買い | イン | 0.08 | 0.86663 | 244 | 0.00 | 0.00 | 1 861.09 |
2010.06.28 00:00 | 245 | AUDUSD | 売り | アウト | 0.08 | 0.87375 | 245 | 0.50 | 56.96 | 1 918.55 |
2010.06.28 00:00 | 246 | AUDUSD | 買い | イン | 0.08 | 0.87415 | 246 | 0.00 | 0.00 | 1 918.55 |
2010.06.29 00:00 | 247 | AUDUSD | 売り | アウト | 0.08 | 0.87140 | 247 | 0.50 | -22.00 | 1 897.05 |
2010.06.29 00:00 | 248 | AUDUSD | 買い | イン | 0.08 | 0.87173 | 248 | 0.00 | 0.00 | 1 897.05 |
2010.07.01 00:00 | 249 | AUDUSD | 売り | アウト | 0.08 | 0.84053 | 249 | 2.01 | -249.60 | 1 649.46 |
2010.07.01 00:00 | 250 | EURGBP | 売り | イン | 0.07 | 0.81841 | 250 | 0.00 | 0.00 | 1 649.46 |
2010.07.02 00:00 | 251 | EURGBP | 買い | アウト | 0.07 | 0.82535 | 251 | -0.04 | -73.69 | 1 575.73 |
2010.07.02 00:00 | 252 | EURGBP | 売り | イン | 0.07 | 0.82498 | 252 | 0.00 | 0.00 | 1 575.73 |
2010.07.05 00:00 | 253 | EURGBP | 買い | アウト | 0.07 | 0.82676 | 253 | -0.04 | -18.93 | 1 556.76 |
2010.07.05 00:00 | 254 | EURGBP | 売り | イン | 0.06 | 0.82604 | 254 | 0.00 | 0.00 | 1 556.76 |
2010.07.06 00:00 | 255 | EURGBP | 買い | アウト | 0.06 | 0.82862 | 255 | -0.04 | -23.43 | 1 533.29 |
追記 宿題:特定システムのパラメータを選択するだけでなく、定められた時にマーケットに最も適したシステムを選択するには?(ヒント-システムバンクより)