一昨日のこと、MT4から取引履歴をCSVに落とそうとして、いつものようにStatementToCSVを稼働させましたが、、、
おや?明細が1行で途切れてしまう。
悪名高い、zero divideですか。。。
これ、Pepperstoneで動かすと起きないんですけど、MyfXXXXXXで動かすと起きてしまうんですよ。
MT4の標準関数から、ブローカー設定(レートの小数点以下桁数)を取得する処理のところで0桁と返って来てしまい、pips単位に逆算するときに0割りが起きてしまっています。
以前は、XAU/USDのみで同事象が起きていましたが、ここ最近は、EUR/CADやXXX/JPYで起きてしまい、ちょっと使い物にならなくなっていました。
というところで、対策更新です。
流石に、成績集計に致命的な問題となってしまうので、こればっかりは。。。
いつもの場所に掲載すると共に、このページにも概要やソースコードを記録しておきます。
いつもの通り、ご利用は自由です。。。
対策方法:
ブローカーから、特定の通貨レートの小数点桁数が取得できない場合のハンドリングを追加。
具体的には、MarketInfo(OrderSymbol(), MODE_DIGITS)が0を返した場合、小数点以下0桁ですの意味となり、そんなことあり得ないので
ここを起点に、ブローカーからの取得エラーとみなす。
上記の場合は、ソースコード中にあらかじめ標準的な小数点以下5桁レートのブローカーの設定を定義し、
その値を使用することで0割エラーを回避し、且つ処理結果をまっとうなものにする。
//+------------------------------------------------------------------+
//| StatementToCSV.mq4 |
//| Copyright 2014, Life with FX |
//| http://lifewithfx.jp |
//+------------------------------------------------------------------+
#property copyright "Copyright 2014, Life with FX"
#property link "http://lifewithfx.jp"
#property version "1.40"
#property strict
#property show_inputs
extern string CSVOutputFolder = "-- MT4 MQL4/Files folder --";
extern bool OutputRecordDescendingOrder = false;
extern bool OutputHeaderRecord = true;
extern string ReportPeriod = "-- PeriodFilter: Order close time base --";
extern bool UsePeriodFilter = false;
extern int ReportFromDate = 20140331;
extern int ReportToDate = 20159999;
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
int OnStart()
{
int hFile = -1;
int historyTotal = OrdersHistoryTotal();
if(historyTotal <= 0)
{
Print("No History Data. Skip process...");
return(0);
}
int ticket[];
ArrayResize(ticket, historyTotal);
ArrayInitialize(ticket, -1);
for(int i=0; i<historyTotal; i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
{
switch(OrderType())
{
case OP_BUY:
case OP_SELL:
ticket[i] = OrderTicket();
break;
default:
ticket[i] = -1;
}
}
}
// output filename StatementCSV_YYYYMMDD_HHMM.csv
string fileName = "StatementCSV_" + Year();
if(Month() < 10) { fileName = fileName + "0" + Month(); }
else { fileName = fileName + Month(); }
if(Day() < 10) { fileName = fileName + "0" + Day() + "_"; }
else { fileName = fileName + Day() + "_"; }
if(Hour() < 10) { fileName = fileName + "0" + Hour(); }
else { fileName = fileName + Hour(); }
if(Minute() < 10) { fileName = fileName + "0" + Minute(); }
else { fileName = fileName + Minute(); }
fileName = fileName + ".csv";
Print("output filename: " + fileName);
//get handle
hFile = FileOpen(fileName, FILE_BIN|FILE_WRITE, ",");
if(hFile < 0) {
Print("error abort: output file creation error!!");
return(-1);
}
// CSV Record Header
string header = "Ticket,OrderType,OrderSymbol,OrderLots,OrderOpenTime,OrderOpenPrice,OrderCloseTime,OrderClosePrice,OrderProfitInPips,OrderCommission,OrderSwap,OrderProfit,TradeProfit,OrderMagicNumber,OrderComment,OrderCommentShort" + "\n";
if (OutputHeaderRecord) {
FileWriteString(hFile, header, StringLen(header));
}
const string ITEM_SEPARATOR = ",";
int startIdx;
int idxStep;
int ordersLastIdx = historyTotal - 1;
double pipScale;
double pipScaleDigits;
double takeprice;
double takepips;
if (!OutputRecordDescendingOrder) {
startIdx = 0;
idxStep = 1;
} else {
startIdx = ordersLastIdx;
idxStep = -1;
}
for (int i = startIdx; !(i < 0 || ordersLastIdx < i) ; i += idxStep) {
if(ticket[i] != -1) {
if(OrderSelect(ticket[i], SELECT_BY_TICKET, MODE_HISTORY)) {
// PeriodRangeFilter
string closeDateStr = TimeToStr(OrderCloseTime(), TIME_DATE);
int closeDateInt = StrToInteger(StringSubstr(closeDateStr, 0, 4) + StringSubstr(closeDateStr, 5, 2) + StringSubstr(closeDateStr, 8, 2));
if (!UsePeriodFilter) {
// OK
} else if (ReportFromDate <= closeDateInt &&
closeDateInt <= ReportToDate) {
// OK
} else {
continue;
}
// scale setting
if (MarketInfo(OrderSymbol(), MODE_DIGITS) != 0) {
pipScale = MarketInfo(OrderSymbol(), MODE_POINT) * 10;
pipScaleDigits = MarketInfo(OrderSymbol(), MODE_DIGITS);
} else {
pipScale = getPipScale(OrderSymbol());
pipScaleDigits =getPipScaleDigits(OrderSymbol());
}
string output = ticket[i] + ITEM_SEPARATOR;
switch(OrderType()) {
case OP_BUY:
output = output + "BUY" + ITEM_SEPARATOR;
break;
case OP_SELL:
output = output + "SELL" + ITEM_SEPARATOR;
break;
default:
output = output + "" + ITEM_SEPARATOR;
}
output = output + OrderSymbol() + ITEM_SEPARATOR;
output = output + DoubleToStr(OrderLots(),2) + ITEM_SEPARATOR;
output = output + TimeToStr(OrderOpenTime()) + ITEM_SEPARATOR;
output = output + NormalizeDouble(OrderOpenPrice(), pipScaleDigits) + ITEM_SEPARATOR;
output = output + TimeToStr(OrderCloseTime()) + ITEM_SEPARATOR;
output = output + NormalizeDouble(OrderClosePrice(), pipScaleDigits) + ITEM_SEPARATOR;
switch(OrderType()) {
case OP_BUY:
takeprice = (OrderClosePrice() / pipScale) - (OrderOpenPrice() / pipScale);
takepips = NormalizeDouble(takeprice, pipScaleDigits);
output = output + takepips + ITEM_SEPARATOR;
break;
case OP_SELL:
takeprice = (OrderOpenPrice() / pipScale) - (OrderClosePrice() / pipScale);
takepips = NormalizeDouble(takeprice, pipScaleDigits);
output = output + takepips + ITEM_SEPARATOR;
break;
}
output = output + NormalizeDouble(OrderCommission(), 2) + ITEM_SEPARATOR;
output = output + NormalizeDouble(OrderSwap(), 2) + ITEM_SEPARATOR;
output = output + OrderProfit() + ITEM_SEPARATOR;
output = output + (NormalizeDouble(OrderSwap(), 2) + NormalizeDouble(OrderCommission(), 2) + OrderProfit()) + ITEM_SEPARATOR;
output = output + OrderMagicNumber() + ITEM_SEPARATOR;
output = output + OrderComment() + ITEM_SEPARATOR;
output = output + StringSubstr(OrderComment(), 0, 2);
output = output + "\n";
FileSeek(hFile, 0, SEEK_END);
FileWriteString(hFile, output, StringLen(output));
}
}
}
if(0 < hFile) {
FileClose(hFile);
}
Print ("works fine: finished!!");
return(0);
}
double getPipScale(string symbol) {
if (symbol == "EURUSD") { return 0.0001;}
else if (symbol == "AUDCAD") { return 0.0001;}
else if (symbol == "AUDCHF") { return 0.0001;}
else if (symbol == "AUDJPY") { return 0.01;}
else if (symbol == "AUDNZD") { return 0.0001;}
else if (symbol == "AUDSGD") { return 0.0001;}
else if (symbol == "AUDUSD") { return 0.0001;}
else if (symbol == "CADCHF") { return 0.0001;}
else if (symbol == "CADJPY") { return 0.01;}
else if (symbol == "CHFJPY") { return 0.01;}
else if (symbol == "EURAUD") { return 0.0001;}
else if (symbol == "EURCAD") { return 0.0001;}
else if (symbol == "EURCHF") { return 0.0001;}
else if (symbol == "EURGBP") { return 0.0001;}
else if (symbol == "EURJPY") { return 0.01;}
else if (symbol == "EURNZD") { return 0.0001;}
else if (symbol == "GBPAUD") { return 0.0001;}
else if (symbol == "GBPCAD") { return 0.0001;}
else if (symbol == "GBPCHF") { return 0.0001;}
else if (symbol == "GBPJPY") { return 0.01;}
else if (symbol == "GBPNZD") { return 0.0001;}
else if (symbol == "GBPUSD") { return 0.0001;}
else if (symbol == "NZDJPY") { return 0.01;}
else if (symbol == "NZDUSD") { return 0.0001;}
else if (symbol == "USDCAD") { return 0.0001;}
else if (symbol == "USDCHF") { return 0.0001;}
else if (symbol == "USDHKD") { return 0.0001;}
else if (symbol == "USDJPY") { return 0.01;}
else if (symbol == "USDMXN") { return 0.0001;}
else if (symbol == "USDPLN") { return 0.0001;}
else if (symbol == "USDSEK") { return 0.0001;}
else if (symbol == "USDSGD") { return 0.0001;}
else if (symbol == "USDZAR") { return 0.0001;}
else if (symbol == "XAGEUR") { return 0.01;}
else if (symbol == "XAGUSD") { return 0.01;}
else if (symbol == "XAUEUR") { return 0.1;}
else if (symbol == "XAUUSD") { return 0.1;}
else if (symbol == "ZARJPY") { return 0.01;}
else { return 0.0001; }
}
double getPipScaleDigits(string symbol) {
if (symbol == "EURUSD") { return 5;}
else if (symbol == "AUDCAD") { return 5;}
else if (symbol == "AUDCHF") { return 5;}
else if (symbol == "AUDJPY") { return 3;}
else if (symbol == "AUDNZD") { return 5;}
else if (symbol == "AUDSGD") { return 5;}
else if (symbol == "AUDUSD") { return 5;}
else if (symbol == "CADCHF") { return 5;}
else if (symbol == "CADJPY") { return 3;}
else if (symbol == "CHFJPY") { return 3;}
else if (symbol == "EURAUD") { return 5;}
else if (symbol == "EURCAD") { return 5;}
else if (symbol == "EURCHF") { return 5;}
else if (symbol == "EURGBP") { return 5;}
else if (symbol == "EURJPY") { return 3;}
else if (symbol == "EURNZD") { return 5;}
else if (symbol == "GBPAUD") { return 5;}
else if (symbol == "GBPCAD") { return 5;}
else if (symbol == "GBPCHF") { return 5;}
else if (symbol == "GBPJPY") { return 3;}
else if (symbol == "GBPNZD") { return 5;}
else if (symbol == "GBPUSD") { return 5;}
else if (symbol == "NZDJPY") { return 3;}
else if (symbol == "NZDUSD") { return 5;}
else if (symbol == "USDCAD") { return 5;}
else if (symbol == "USDCHF") { return 5;}
else if (symbol == "USDHKD") { return 5;}
else if (symbol == "USDJPY") { return 3;}
else if (symbol == "USDMXN") { return 5;}
else if (symbol == "USDPLN") { return 5;}
else if (symbol == "USDSEK") { return 5;}
else if (symbol == "USDSGD") { return 5;}
else if (symbol == "USDZAR") { return 5;}
else if (symbol == "XAGEUR") { return 3;}
else if (symbol == "XAGUSD") { return 3;}
else if (symbol == "XAUEUR") { return 2;}
else if (symbol == "XAUUSD") { return 2;}
else if (symbol == "ZARJPY") { return 3;}
else { return 5; }
}
