#define TOSTR(A) #A+" = "+(string)A+" " //+------------------------------------------------------------------+ //| ローソク足が確定したティックならtrue //+------------------------------------------------------------------+ bool IsNewBar(ENUM_TIMEFRAMES tf = PERIOD_CURRENT) { static datetime time = 0; static long msc = 0; //--- ティック情報の取得 MqlTick tick; SymbolInfoTick(_Symbol, tick); if(iTime(_Symbol, tf, 0) == time && msc != tick.time_msc) return false; time = iTime(_Symbol, tf, 0); msc = tick.time_msc; return true; } //+------------------------------------------------------------------+ //| ティックがn回目ならtrue //+------------------------------------------------------------------+ bool IsSpecifiedTick(const int n = 100) { static int tick_count = 0; if(tick_count < n){ tick_count++; return false; } tick_count = 0; return true; } //+------------------------------------------------------------------+ //| 対数差収益率 //+------------------------------------------------------------------+ double iR(string sym, ENUM_TIMEFRAMES tf, int i) { double c0 = iClose(sym,tf,i); double c1 = iClose(sym,tf,i+1); return(log(c0) - log(c1)); } //+------------------------------------------------------------------+ //| 上昇したティックであればtrue //+------------------------------------------------------------------+ bool IsRisedTick() { static double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); bool ret = false; if(SymbolInfoDouble(_Symbol, SYMBOL_BID) > bid) ret = true; bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); return ret; } //+------------------------------------------------------------------+ //| 下降したティックであればtrue //+------------------------------------------------------------------+ bool IsDescntTick() { static double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); bool ret = false; if(SymbolInfoDouble(_Symbol, SYMBOL_BID) < bid) ret = true; bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); return ret; } //+------------------------------------------------------------------+ //| ストップのレートを計算する(引数1:ストップまでの距離(%) 引数2:買い(1)か売り(-1) ) //+------------------------------------------------------------------+ double CalculateStopRate(double range, int buysell) { if(range <= 0) return (double)"nan"; if(buysell != 1 && buysell != -1){ Print("ストップのレート計算で不正な値を受け取りました。"); return (double)"nan"; } double nowprice = iClose(_Symbol, PERIOD_CURRENT, 0); return nowprice - (nowprice * range * buysell); } //+------------------------------------------------------------------+ //| 最後の約定価格を返す //+------------------------------------------------------------------+ double GetLastDealPrice(long magic,ulong second = 600) { ulong ticket_history_deal = 0; // ディールチケット double deal_price = (double)"nan"; if(!HistorySelect(TimeCurrent() - second, TimeCurrent())) return deal_price; uint total_deals = HistoryDealsTotal(); if(total_deals == 0) return deal_price; //--- 全ての取引を配列に取得 for(uint i = 0; i < total_deals; i++) { if((ticket_history_deal = HistoryDealGetTicket(i)) > 0) { if(HistoryDealGetInteger(ticket_history_deal, DEAL_MAGIC) != magic) continue; deal_price = HistoryDealGetDouble(ticket_history_deal, DEAL_PRICE); } } return deal_price; } //+------------------------------------------------------------------+ //| 最後の約定時刻を返す //+------------------------------------------------------------------+ datetime GetLastDealTime(long magic,ulong second = 3600) { ulong ticket_history_deal = 0; // ディールチケット datetime deal_time = 0; if(!HistorySelect(TimeCurrent() - second, TimeCurrent())) return deal_time; uint total_deals = HistoryDealsTotal(); if(total_deals == 0) return deal_time; //--- 全ての取引を配列に取得 for(uint i = 0; i < total_deals; i++) { if((ticket_history_deal = HistoryDealGetTicket(i)) > 0) { if(HistoryDealGetInteger(ticket_history_deal, DEAL_MAGIC) != magic) continue; deal_time = (datetime)HistoryDealGetInteger(ticket_history_deal, DEAL_TIME); } } return deal_time; } //+------------------------------------------------------------------+ //| バックテスト結果をCSV出力する関数 //| //| ファイルパス: \MetaQuotes\Tester\(MT5アカウントフォルダ)\Agent-127.0.0.1-3000\MQL5\Files //| OnDeinit関数でCsvOutputBTResults(__FILE__)と書く //+------------------------------------------------------------------+ bool OutputResultsToCSV(string ea_name = "BackTestEA") { //--- 単一バックテストのみ if(!MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_OPTIMIZATION)) return false; //--- ファイル名 string fn = FileName(ea_name, TimeCurrent()); //--- ファイルハンドルの取得 int handle = FileOpen(fn + ".csv", FILE_CSV | FILE_WRITE); if( handle == INVALID_HANDLE ) { Print("CSV出力する関数内でファイルハンドル取得に失敗:", GetLastError()); return false; } long DealTimeIn[], DealTimeOut[], DealType[]; double DealPriceIn[], DealPriceOut[], DealVolume[], DealProfit[], DealSwap[]; string DealSymbol[]; int total_deals = 0; //--- 取引損益を配列に書き込み if(!GetTradeResults(DealTimeIn, DEAL_TIME, DEAL_ENTRY_IN) || !GetTradeResults(DealTimeOut, DEAL_TIME, DEAL_ENTRY_OUT) || !GetTradeResults(DealType, DEAL_TYPE, DEAL_ENTRY_IN) || !GetTradeResults(DealPriceIn, DEAL_PRICE, DEAL_ENTRY_IN) || !GetTradeResults(DealPriceOut, DEAL_PRICE, DEAL_ENTRY_OUT) || !GetTradeResults(DealVolume, DEAL_VOLUME, DEAL_ENTRY_OUT) || !GetTradeResults(DealProfit, DEAL_PROFIT, DEAL_ENTRY_OUT) || !GetTradeResults(DealSwap, DEAL_SWAP, DEAL_ENTRY_OUT) || !GetTradeResults(DealSymbol, DEAL_SYMBOL, DEAL_ENTRY_OUT) ){ FileClose(handle); return false; } total_deals = ArraySize(DealProfit); FileWrite(handle, "in_date", "in_time", "out_date", "out_time", "symbol", "buy_sell", "in_price", "out_price", "volume", "profit", "swap"); //--- CVSにデータを追加する for(int i = 0; i < total_deals; i++) { //--- ENUM_DEAL_TYPEをstring型の"Buy/Sell"で表現する string deal_type = "Buy"; if(DEAL_TYPE_SELL == (ENUM_DEAL_TYPE)DealType[i]) deal_type = "Sell"; //--- 書き込み FileWrite(handle, TimeToString((datetime)DealTimeIn[i], TIME_DATE), TimeToString((datetime)DealTimeIn[i], TIME_MINUTES), TimeToString((datetime)DealTimeOut[i], TIME_DATE), TimeToString((datetime)DealTimeOut[i], TIME_MINUTES), DealSymbol[i], deal_type, DealPriceIn[i], DealPriceOut[i], DealVolume[i], DealProfit[i], DealSwap[i]); } FileClose(handle); Print("CSV出力が完了しました"); return true; } //+------------------------------------------------------------------+ //| バックテスト結果をCSV出力する関数(日付細かく) //| //| ファイルパス: \MetaQuotes\Tester\(MT5アカウント)\Agent-127.0.0.1-3000\MQL5\Files //| OnDeinit関数でOutputResultsToCSV(__FILE__)と書く //+------------------------------------------------------------------+ bool OutputResultsToCSV2(string ea_name = "BackTestEA") { //--- 単一バックテストのみ if(!MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_OPTIMIZATION)) return false; //--- ファイル名 string fn = FileName(ea_name, TimeCurrent()); //--- ファイルハンドルの取得 int handle = FileOpen(fn + ".csv", FILE_CSV | FILE_WRITE); if( handle == INVALID_HANDLE ) { Print("CSV出力する関数内でファイルハンドル取得に失敗:", GetLastError()); return false; } long DealTimeIn[], DealTimeOut[], DealType[]; double DealPriceIn[], DealPriceOut[], DealVolume[], DealProfit[], DealSwap[]; string DealSymbol[], DealComment[]; int total_deals = 0; MqlDateTime dtin,dtout; //--- 取引損益を配列に書き込み if(!GetTradeResults(DealTimeIn, DEAL_TIME, DEAL_ENTRY_IN) || !GetTradeResults(DealTimeOut, DEAL_TIME, DEAL_ENTRY_OUT) || !GetTradeResults(DealType, DEAL_TYPE, DEAL_ENTRY_IN) || !GetTradeResults(DealPriceIn, DEAL_PRICE, DEAL_ENTRY_IN) || !GetTradeResults(DealPriceOut, DEAL_PRICE, DEAL_ENTRY_OUT) || !GetTradeResults(DealVolume, DEAL_VOLUME, DEAL_ENTRY_OUT) || !GetTradeResults(DealProfit, DEAL_PROFIT, DEAL_ENTRY_OUT) || !GetTradeResults(DealSwap, DEAL_SWAP, DEAL_ENTRY_OUT) || !GetTradeResults(DealSymbol, DEAL_SYMBOL, DEAL_ENTRY_OUT) || !GetTradeResults(DealComment, DEAL_COMMENT, DEAL_ENTRY_IN) ){ FileClose(handle); return false; } total_deals = ArraySize(DealProfit); FileWrite(handle, "in年", "in月", "in日", "in曜日", "in時", "in分", "out年", "out月", "out日", "out曜日", "out時", "out分", "通貨ペア", "Buy/Sell", "in約定価格", "out約定価格", "ロット数", "損益", "スワップ", "inコメント"); //--- CVSにデータを追加する for(int i = 0; i < total_deals; i++) { //--- ENUM_DEAL_TYPEをstring型の"Buy/Sell"で表現する string deal_type = "Buy"; if(DEAL_TYPE_SELL == (ENUM_DEAL_TYPE)DealType[i]) deal_type = "Sell"; //--- TimeToStruct(DealTimeIn[i], dtin); TimeToStruct(DealTimeOut[i], dtout); //--- 書き込み FileWrite(handle, dtin.year, dtin.mon, dtin.day, dtin.day_of_week, dtin.hour, dtin.min, dtout.year, dtout.mon, dtout.day, dtout.day_of_week, dtout.hour, dtout.min, DealSymbol[i], deal_type, DealPriceIn[i], DealPriceOut[i], DealVolume[i], DealProfit[i], DealSwap[i], DealComment[i]); } FileClose(handle); Print("CSV出力が完了しました"); return true; } //+------------------------------------------------------------------+ //| ファイル名(名前とバックテスト終了日付) //+------------------------------------------------------------------+ string FileName(string ea_name, datetime time) { string name = ea_name; if(".mq5" == StringSubstr(name, StringFind(name, "."), 4) || ".ex5" == StringSubstr(name, StringFind(name, "."), 4) || ".mqh" == StringSubstr(name, StringFind(name, "."), 4)) { StringSetLength(name, name.Length() - 4); } name += "_" + TimeToString(time, TIME_DATE); StringReplace(name, ".", ""); StringReplace(name, " ", "_"); StringReplace(name, ":", ""); return name; } //+------------------------------------------------------------------+ //| long型の約定情報を配列に書き込む //+------------------------------------------------------------------+ bool GetTradeResults(long &results[], const ENUM_DEAL_PROPERTY_INTEGER EDPI, const ENUM_DEAL_ENTRY EDE) { //--- 全期間の取引履歴をリクエストする if(!HistorySelect(0, TimeCurrent())) return (false); uint total_deals = HistoryDealsTotal(); //--- 配列を、履歴の取引数にリサイズ ArrayResize(results, total_deals); int counter = 0; // 取引回数カウンター ulong ticket_history_deal = 0; // ディールチケット //--- 全ての取引を配列に取得 for(uint i = 0; i < total_deals; i++) { //--- 取引を選択する if((ticket_history_deal = HistoryDealGetTicket(i)) > 0) { ENUM_DEAL_ENTRY deal_entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket_history_deal, DEAL_ENTRY); long deal_type = HistoryDealGetInteger(ticket_history_deal, DEAL_TYPE); long deal_info = HistoryDealGetInteger(ticket_history_deal, EDPI); //--- 取引操作以外はスルー if((deal_type != DEAL_TYPE_BUY) && (deal_type != DEAL_TYPE_SELL)) continue; //--- ENUM_DEAL_ENTRYで指定した情報のみ if(deal_entry == EDE) { //--- 取引結果を配列に書き込み、取引のカウントアップ results[counter] = deal_info; counter++; } } } //--- 配列の最終サイズを設定する ArrayResize(results, counter); return (true); } //+------------------------------------------------------------------+ //| Double型の約定情報を配列に書き込む //+------------------------------------------------------------------+ bool GetTradeResults(double &results[], const ENUM_DEAL_PROPERTY_DOUBLE EDPD, const ENUM_DEAL_ENTRY EDE) { //--- 全期間の取引履歴をリクエストする if(!HistorySelect(0, TimeCurrent())) return (false); uint total_deals = HistoryDealsTotal(); //--- 配列を、履歴の取引数にリサイズ ArrayResize(results, total_deals); int counter = 0; // 取引回数カウンター ulong ticket_history_deal = 0; // ディールチケット //--- 全ての取引を配列に取得 for(uint i = 0; i < total_deals; i++) { //--- 取引を選択する if((ticket_history_deal = HistoryDealGetTicket(i)) > 0) { ENUM_DEAL_ENTRY deal_entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket_history_deal, DEAL_ENTRY); long deal_type = HistoryDealGetInteger(ticket_history_deal, DEAL_TYPE); double deal_info = HistoryDealGetDouble(ticket_history_deal, EDPD); //--- 取引操作以外はスルー if((deal_type != DEAL_TYPE_BUY) && (deal_type != DEAL_TYPE_SELL)) continue; //--- ENUM_DEAL_ENTRYで指定した情報のみ if(deal_entry == EDE) { //--- 取引結果を配列に書き込み、取引のカウントアップ results[counter] = deal_info; counter++; } } } //--- 配列の最終サイズを設定する ArrayResize(results, counter); return (true); } //+------------------------------------------------------------------+ //| string型の約定情報を配列に書き込む //+------------------------------------------------------------------+ bool GetTradeResults(string &results[], const ENUM_DEAL_PROPERTY_STRING EDPS, const ENUM_DEAL_ENTRY EDE) { //--- 全期間の取引履歴をリクエストする if(!HistorySelect(0, TimeCurrent())) return (false); uint total_deals = HistoryDealsTotal(); //--- 配列を、履歴の取引数にリサイズ ArrayResize(results, total_deals); int counter = 0; // 取引回数カウンター ulong ticket_history_deal = 0; // ディールチケット //--- 全ての取引を配列に取得 for(uint i = 0; i < total_deals; i++) { //--- 取引を選択する if((ticket_history_deal = HistoryDealGetTicket(i)) > 0) { ENUM_DEAL_ENTRY deal_entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket_history_deal, DEAL_ENTRY); long deal_type = HistoryDealGetInteger(ticket_history_deal, DEAL_TYPE); string deal_info = HistoryDealGetString(ticket_history_deal, EDPS); //--- 取引操作以外はスルー if((deal_type != DEAL_TYPE_BUY) && (deal_type != DEAL_TYPE_SELL)) continue; //--- ENUM_DEAL_ENTRYで指定した情報のみ if(deal_entry == EDE) { //--- 取引結果を配列に書き込み、取引のカウントアップ results[counter] = deal_info; counter++; } } } //--- 配列の最終サイズを設定する ArrayResize(results, counter); return (true); } //+------------------------------------------------------------------+