ストラテジーテスターの操作履歴を追いかけるのもありですが、わかりやすくファイルに出力できれば、テストがはかどります。
今回はCSV形式で取引結果をファイルに出力し、シグナルと取引が正しいかどうか確認したいと思います。
前回の記事で書いた通り#ifdefを使用してデバッグコードを埋め込みます。
シグナルとポジション数をCSVに出力してシグナル通りに売買が行われているか確認したいと思います。Sample1のコードを下記のように追加&修正しました。
// デバッグモード #define DEBUG ��略) //------------------------------------------------------------------ // 気配値表示処理 void OnTick() { // 前回取引時間 static datetime beforeTime = TimeCurrent(); // 前回シグナル static int beforeSignal = 0; int signal = GetSignal(); // 前回までの売買シグナルと違う値の場合、ポジションをいったん全決済する。 if( signal != 0 && signal != beforeSignal ) { m_wrapper.CloseOrderAll(Slippage); beforeSignal = signal; } // 購入戦略。シグナルが出ている間、1分毎に時間をずらして購入していく。 datetime currentTime= TimeCurrent(); m_wrapper.RefreshPositions(); if( MathAbs(currentTime - beforeTime) >= 60 && m_wrapper.GetPositionCount() < MaxPosition ) { m_wrapper.SendOrder( signal == 1 ? OP_BUY : OP_SELL, Lot, 0, Slippage, Limit, 0); currentTime = currentTime; #ifdef DEBUG //SendOrderの結果をCSVに出力する。 WriteTradeLog(signal); #endif } //トレーリングを行う。 if( m_wrapper.GetPositionCount() > 0 ) { Trainling(Limit); } } ��略) #ifdef DEBUG //------------------------------------------------------------------ // 取引ログを出力する。 void WriteTradeLog(int signal) { //ログファイル名が重複するのを避けるため、後ろに番号をつける。 static string filename = ""; if( filename == "" ) { for( int i = 0 ; i < 9999; i++ ) { filename = "sample1Log_" + IntegerToString(i) + ".csv"; if( !FileIsExist(filename) ) break ; } } int handle = FileOpen(filename, FILE_CSV|FILE_READ|FILE_WRITE, ','); // とりあえずログなのでファイルオープンできなかった場合は、何もしない。 if( handle < 0 ) return ; // ヘッダ出力 if( FileSize(handle) == 0 ) { FileWrite(handle, "日本時間","シグナル","合計ポジション数", "マジックNo一致数", "シンボル一致数", "買","売","指値買","指値売","逆指値買","逆指値売"); } // 最終行へ追加 FileSeek(handle, 0, SEEK_END); int total = OrdersTotal(); int matchingMagicCount = 0 ; int matchingSymbol = 0 ; int orderCount[6]; ArrayInitialize(orderCount, 0); // 現時点でのオーダー数を取得する。 for( int i = 0 ; i <total; i++) { if( OrderSelect(i, SELECT_BY_POS) ) { if( OrderMagicNumber() != MagicNumber ) continue; matchingMagicCount++; if( OrderSymbol() != Symbol() ) continue; matchingSymbol++; int orderType = OrderType(); orderCount[orderType]++; } } FileWrite(handle, TimeToStr(TimeGMT() + 32400), signal, total, matchingMagicCount, matchingSymbol, orderCount[0], orderCount[1], orderCount[2], orderCount[3], orderCount[4], orderCount[5]); FileClose(handle); } #endif
コンパイルが通ったので早速バックテストを再度実行します。
ここで生成したファイルはWindows7の場合、次のフォルダに出力されていました。
c:\Users\(ユーザ名)\AppData\Roaming\MetaQuotes\Terminal\(口座毎のシステムID)\tester\files
sample1Log(バックテスト実行日時).csvファイルが作成されていれば成功です。
さっそく中身を見てみましょう。
・・・・・おっと、いきなりシグナルが0状態で売ポジションが1になっています。
バグ発見!
このようにわかりやすい形でファイルを出力することによりテストが行いやすくなります。
ログを工夫することでさまざまなデータをとることが可能です。
バグを修正したコードがこちらです。
//------------------------------------------------------------------ // 気配値表示処理 void OnTick() { // 前回取引時間 static datetime beforeTime = TimeCurrent(); static datetime signalChangingTime = TimeCurrent(); // 前回シグナル static int beforeSignal = 0; int signal = GetSignal(); // 前回までの売買シグナルと違う値の場合、ポジションをいったん全決済する。 if( signal != 0 && signal != beforeSignal ) { m_wrapper.CloseOrderAll(Slippage); beforeSignal = signal; signalChangingTime = TimeCurrent(); } // 購入戦略。シグナルが出ている間、1分毎に時間をずらして購入していく。 datetime currentTime= TimeCurrent(); m_wrapper.RefreshPositions(); if( signal != 0 && MathAbs(currentTime - beforeTime) >= 60 && MathAbs(currentTime - signalChangingTime) <= 60 * MaxPosition && m_wrapper.GetPositionCount() < MaxPosition ) { m_wrapper.SendOrder( signal == 1 ? OP_BUY : OP_SELL, Lot, 0, Slippage, Limit, 0); beforeTime = currentTime; #ifdef DEBUG //SendOrderの結果をCSVに出力する。 WriteTradeLog(signal); #endif } //トレーリングを行う。 if( m_wrapper.GetPositionCount() > 0 ) { Trainling(Limit); } }
修正した結果のログはこちらです。
シグナルの方向と売買の方向が一致するようになりました。
もうちょっと調べてみないといけない項目のようです。
(追記)
時間について、少し調査してみましたが、TimeCurrent()の値が時間足の値になっているわけではなさそうです。
もう少し調べてみます。
バックテスト中はTimeCurrent()とTimeGMT()が同じ値が戻ってくるというのもちょっとショックです・・・。、今作っている休日判断ロジックがバックテスト中は正しく動作しない様子です・・・。(泣)
��追記2)
落ちとしては、ストラテジーテスタのエキスパート設定を開きパラメータをリセットしたら正しく動作しました・・。ソース上でデフォルト値を変更しても、エキスパート設定でリセットしないとデフォルト値は反映されない事をすっかり失念していました。バグとしてもう一か所、初期化時にMaxPositionの設定をしていない箇所がありましたので修正しました。
//------------------------------------------------------------------
//初期化
int OnInit()
{
if( Period() < PERIOD_M5 ) return (INIT_PARAMETERS_INCORRECT);
m_wrapper = new CTradingWrapper(Symbol(), MagicNumber, MaxPosition);
return(INIT_SUCCEEDED);
}
テストは大切!!!ということを改めて感じました。