480 lines
29 KiB
MQL5
480 lines
29 KiB
MQL5
#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);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
|
|
|