myfxbookも良いけど、自前で管理する方が何かと便利
EAによるトレード結果を分析することは、EAの稼働継続/停止を判断する上でとても大切です。
MT4の[口座履歴]タブでトレード結果を確認できますが、マジックナンバーは分かりませんし、各トレードについてメモを残したり、任意の期間で抽出したり、何らかの指標を計算したりするのには適していません。レポートも出力できますが、html形式なので手軽に加工できず、分析には不向きです。
トレード結果分析といえば、myfxbookが有名ですね。トレード結果を手軽にグラフィカルに分析できる、便利な無料サービスです。主要な指標は自動で計算してくれますし、オシャレでセンスが良い!私も登録しています。
申し分ないサービスなのですが、他人様が作ったサービスでGUIがあるため、どうしても操作の自由が制限されてしまいます。私は欲しいものを欲しい形で手に入れたい!具体的にはトレード結果を望む形式で自由に取得したい!
というわけで、トレード結果分析に必要なトレード履歴データをCSVファイルとして出力できるスクリプトを作成してみました。その名も「OutputTradeHistory」です。特長はマジックナンバーとオーダー時コメントでフィルタリングできるところです。
//+------------------------------------------------------------------+
//| OutputTradeHistory.mq4 |
//| Copyright (c) 2015, りゅーき |
//| https://autofx100.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright (c) 2015, りゅーき"
#property link "https://autofx100.com/"
#property version "1.0"
#property show_inputs
//+------------------------------------------------------------------+
//| パラメータ設定情報 |
//+------------------------------------------------------------------+
extern bool DisplayHeader = false;
extern datetime CloseTimeFrom = NULL;
extern datetime CloseTimeTo = D'2038.01.01';
extern string FileName = "";
extern string Help = "true: AND false: OR";
extern bool FilterSwitch = true;
extern string CommentFilter1 = "";
extern string CommentFilter2 = "";
extern string CommentFilter3 = "";
extern string MagicNumberFilter1 = "";
extern string MagicNumberFilter2 = "";
extern string MagicNumberFilter3 = "";
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
void OnStart()
{
int historyTotal = OrdersHistoryTotal();
// トレード履歴の有無チェック
if(historyTotal <= 0){
Print("トレード履歴データはありません。");
return;
}
int ticket[];
ArrayResize(ticket, historyTotal);
ArrayInitialize(ticket, -1);
for(int i = 0; i < historyTotal; i++){
if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)){
if(OrderType() == OP_BUY || OrderType() == OP_SELL){
ticket[i] = OrderTicket();
OrderPrint();
}else{
Print("OK");
ticket[i] = -1;
}
}
}
// ファイル名
string ymdhms = replace(TimeToStr(TimeLocal(), TIME_DATE|TIME_SECONDS), ":", ".");
if(FileName == ""){
string flNm = "TradeHistory_" + ymdhms + ".csv";
}else{
flNm = "TradeHistory_" + FileName + "_" + ymdhms + ".csv";
}
Print("flNm = ", flNm);
int fileHandle = FileOpen(flNm, FILE_CSV|FILE_WRITE, ",");
if(fileHandle < 0){
Print("ファイルハンドルがありません。");
return;
}
// ヘッダー出力
if(DisplayHeader){
FileWrite(fileHandle,
"注文番号",
"マジックナンバー",
"コメント",
"通貨ペア",
"取引種別",
"ロットサイズ",
"約定時刻",
"約定価格",
"決済時刻",
"決済価格",
"手数料",
"スワップ損益",
"トレード損益",
"総損益",
"獲得Pips"
);
}
for(i = 0; i < historyTotal; i++){
if(ticket[i] != -1){
if(OrderSelect(ticket[i], SELECT_BY_TICKET, MODE_HISTORY)){
int matchFlg = 0;
//出力フィルタリング
if(FilterSwitch){ //AND
if(MagicNumberFilter1 == "" && MagicNumberFilter2 == "" && MagicNumberFilter3 == ""){
matchFlg = 1;
}
if(MagicNumberFilter1 != "" && StrToInteger(MagicNumberFilter1) == OrderMagicNumber()){
matchFlg = 1;
}
if(MagicNumberFilter2 != "" && StrToInteger(MagicNumberFilter2) == OrderMagicNumber()){
matchFlg = 1;
}
if(MagicNumberFilter3 != "" && StrToInteger(MagicNumberFilter3) == OrderMagicNumber()){
matchFlg = 1;
}
if(matchFlg == 0){
continue;
}
matchFlg = 0;
if(CommentFilter1 == "" && CommentFilter2 == "" && CommentFilter3 == ""){
matchFlg = 1;
}
if(CommentFilter1 != "" && StringFind(OrderComment(), CommentFilter1) != -1){
matchFlg = 1;
}
if(CommentFilter2 != "" && StringFind(OrderComment(), CommentFilter2) != -1){
matchFlg = 1;
}
if(CommentFilter3 != "" && StringFind(OrderComment(), CommentFilter3) != -1){
matchFlg = 1;
}
if(matchFlg == 0){
continue;
}
}else{ //OR
if(MagicNumberFilter1 == "" && MagicNumberFilter2 == "" && MagicNumberFilter3 == "" && CommentFilter1 == "" && CommentFilter2 == "" && CommentFilter3 == ""){
matchFlg = 1;
}
if(MagicNumberFilter1 != "" && StrToInteger(MagicNumberFilter1) == OrderMagicNumber()){
matchFlg = 1;
}
if(MagicNumberFilter2 != "" && StrToInteger(MagicNumberFilter2) == OrderMagicNumber()){
matchFlg = 1;
}
if(MagicNumberFilter3 != "" && StrToInteger(MagicNumberFilter3) == OrderMagicNumber()){
matchFlg = 1;
}
if(CommentFilter1 != "" && StringFind(OrderComment(), CommentFilter1) != -1){
matchFlg = 1;
}
if(CommentFilter2 != "" && StringFind(OrderComment(), CommentFilter2) != -1){
matchFlg = 1;
}
if(CommentFilter3 != "" && StringFind(OrderComment(), CommentFilter3) != -1){
matchFlg = 1;
}
if(matchFlg == 0){
continue;
}
}
string displayOpenTime = TimeToStr(OrderOpenTime());
string displayCloseTime = TimeToStr(OrderCloseTime());
if(StrToTime(displayCloseTime) < CloseTimeFrom || StrToTime(displayCloseTime) > CloseTimeTo){
continue;
}
string oTicket = ticket[i];
if(OrderType() == OP_BUY){
string oType = "BUY";
}else if(OrderType() == OP_SELL){
oType = "SELL";
}else{
oType = "";
}
double oPlice = OrderOpenPrice();
double cPlice = OrderClosePrice();
if(StringFind(OrderSymbol(), "JPY") != -1){
double points = 0.001;
}else{
points = 0.00001;
}
double total = OrderCommission() + OrderSwap() + OrderProfit();
int mult = 1;
if(Digits == 3 || Digits == 5){
mult = 10;
}
if(OrderType() == OP_BUY){
double calcPips = (cPlice - oPlice) / (points * mult);
}else if(OrderType() == OP_SELL){
calcPips = (oPlice - cPlice) / (points * mult);
}
FileSeek(fileHandle, 0, SEEK_END);
FileWrite(fileHandle,
oTicket,
OrderMagicNumber(),
OrderComment(),
OrderSymbol(),
oType,
OrderLots(),
displayOpenTime,
oPlice,
displayCloseTime,
cPlice,
OrderCommission(),
OrderSwap(),
OrderProfit(),
total,
calcPips
);
}
}
}
FileClose(fileHandle);
return;
}
//+------------------------------------------------------------------+
//|【関数】文字列置換 |
//| |
//|【引数】 IN OUT 引数名 説明 |
//| --------------------------------------------------------- |
//| ○ aTarget 対象文字列 |
//| ○ aSearch 検索文字列 |
//| ○ aReplace 置換文字列 |
//| |
//|【戻値】置換された文字列 |
//| |
//|【備考】なし |
//+------------------------------------------------------------------+
string replace(string aTarget, string aSearch, string aReplace = "")
{
string left;
string right;
int start=0;
int rlen = StringLen(aReplace);
int nlen = StringLen(aSearch);
while(start > -1){
start = StringFind(aTarget, aSearch, start);
if(start > -1){
if(start > 0){
left = StringSubstr(aTarget, 0, start);
}else{
left="";
}
right = StringSubstr(aTarget, start + nlen);
aTarget = left + aReplace + right;
start = start + rlen;
}
}
return(aTarget);
}
使い方は至ってシンプル。通常のスクリプトと同じで、チャートにドラッグ&ドロップして、パラメータを入力すればOKです。MT4インストール先\MQL4\Filesに「TradeHistory_yyyy.mm.dd.csv」というCSVファイルが出力されます。yyyy.mm.ddはサーバーの現在日付です。
本スクリプトを使うにあたっては、事前に[口座履歴]タブの任意の場所で右クリックして、[全履歴]を選択しておいてください。これをやっておかないと、全部出力してくれません。
スクリプトのパラメータ内容は以下の通りです。
| パラメータ名 | 説明 |
|---|---|
| DisplayHeader | 見出しを出力する場合はtrue |
| CloseTimeFrom | 出力開始となる決済日時(いつから) |
| CloseTimeTo | 出力終了となる決済日時(いつまで) |
| FilterSwitch | 以下のマジックナンバーとコメントの両方に一致するものだけを出力する場合(AND条件の場合)はtrue、どちらかに一致するものを出力する場合(OR条件の場合)はfalse |
| MagicNumberFilter1 | 絞込み対象のマジックナンバー1 |
| MagicNumberFilter2 | 絞込み対象のマジックナンバー2 |
| MagicNumberFilter3 | 絞込み対象のマジックナンバー3 |
| CommentFilter1 | 絞込み対象のコメント1(部分一致) |
| CommentFilter2 | 絞込み対象のコメント2(部分一致) |
| CommentFilter3 | 絞込み対象のコメント3(部分一致) |
FilterSwitchの説明が恐らく「ナンノコッチャ?」って感じだと思いますので、補足します。
MagicNumberFilter1~MagicNumberFilter3はそれぞれがOR条件、CommentFilter1~CommentFilter3もそれぞれがOR条件の関係になっています。
例えば、MagicNumberFilter1=111、MagicNumberFilter2=222と入力した場合は、マジックナンバーが111または222のトレード履歴データを取得し、CSVファイルに出力します。
FilterSwitch=true、MagicNumberFilter1=111、CommentFilter1=TESTと入力した場合は、マジックナンバーが111でかつ注文時のコメントがTESTの文字列を含んでいるトレード履歴データを取得し、CSVファイルに出力します。
FilterSwitch=false、MagicNumberFilter1=111、CommentFilter1=TESTと入力した場合は、マジックナンバーが111または注文時のコメントがTESTの文字列を含んでいるトレード履歴データを取得し、CSVファイルに出力します。
FilterSwitch=true、MagicNumberFilter1=11、MagicNumberFilter1=22、CommentFilter1=Fと入力した場合は、マジックナンバーが11または22でかつ注文時のコメントがFの文字列を含んでいるトレード履歴データを取得し、CSVファイルに出力します。
自作だとこんな感じで自分の思い通りの結果を得ることができて大変便利です!
