今回の記事は、MQL4のPrint()関数についての3回目の記事になります。
Print()関数を利用すると、変数の値や、プログラムの流れが確認できるので、プログラミングの理解が深まり、様々な問題解決に役立ちます。
1回目の記事はPrint()関数のサンプルコードを利用してログの出力まで説明致しました。
2回目の記事は出力させた結果から「プログラム」の動きや「変数」の変化を補足説明致しました。
今回は、Print()関数を利用していて私が気づいた「注意点」などをお伝え致します。
*パソコンに関する内容はWindows10の場合です。
☑MQL4のPrint()関数を使用するとMT4の動きが悪い原因を知りたい
☑MQL4のPrint()関数エラーの原因を知りたい
目次
Print関数の注意点
Print()はとても便利なのですが、その反面、沢山使用してしまうとエラーなど予測不能なことが起こります。
私はエラーの原因が分かるまでかなりの時間を取られました。そのときの経験を元に、注意点についてお伝え致します。
ログを削除する
1回目の記事でも少し触れていますが、Print()を多用する場合は毎回ログを削除した方がいいと思います。
理由は、前に行ったバックテストのログが残っている為、今回バックテストした所が分かりにくくなってしまいます。この後にも説明致しますが、ログのデータ量が多くなってしまい、バックテストなどに支障が出る場合もあります。
しかし、前回の結果と比較したい場合もあり、削除したくないときもあると思います、その際は、削除しないで別のフォルダにコピーして、分かりやすい名前に変更すると使いやすいです。
●ログの削除方法
①MT4の「ファイル」から「データフォルダ」をクリックします。
②「データフォルダ」が出ましたら、MT4を終了させて下さい。
③開いたフォルダの中に「tester」フォルダがありますので、そこをダブルクリックして下さい。
④開いたフォルダの中に「logs」フォルダがありますので、そこをダブルクリックして下さい。その中に先ほどの「logファイル」があります。
⑤ログファイルを削除して下さい。
全て削除しても良いですし、当日の日付の物だけ削除しても構いません。
ログの容量
Print()関数を沢山の箇所で使用すると、ログのデータ量が多くなりバックテスト中に止まってしまったり、バックテストの時間がかかったり、パソコンの動きが悪くなったりします。
その際はPrint()関数の使用する箇所を減らすか、バックテストの期間を短くして下さい。
私は、Print()関数を使い始めたとき、ほぼ全ての行にPrint()関数を入れたことがあります。コードにもよりますが、10年位のバックテスト期間を取った際、500GB以上になってビックリした記憶があります。
Print()関数の箇所をあまり減らしたく無い場合はログの保存場所を移動させる方法もあります。
詳しくは、別の記事で説明致します。
バックテスト時間への影響
Print()関数を沢山の箇所で使用すると、バックテストの時間も掛かります。
Print()関数を入れていないときは5分程度で終わるバックテストでも、Print()関数を入れると平気で2~3時間以上掛かる場合もあります。
その際は、バックテストの期間を、エラーの出る期間や動きを確認したい期間に狭めてバックテストをします。
期間を狭めることが出来ない場合は、就寝中や仕事中にバックテストが可能でしたら、その時間を利用するのも良いと思います。
私の場合は、時間が掛かるバックテストは電気代の安い夜に行っています。
時間の掛かるバックテストは、前述した通り、容量にも注意が必要です。
先頭に番号を付ける
Print()が重複すると、どこのPrint()か分からなくなりますので、ご自身の使いやすい方法で、表示を変えると分かりやすくなります。
例えば1回目の記事のサンプルコードは、Print("OrdersTotal()= ",OrdersTotal());を2回使用しています。
決済のときは、Print("決済のOrdersTotal()= ",OrdersTotal());
エントリーのときは、Print("エントリーのOrdersTotal()= ",OrdersTotal());
と「決済の」と「エントリーの」と分けて表示させています。
私はこの「決済の」と「エントリーの」の部分を番号にしています。
例えば「決済」=1000番台、「エントリー」=2000番台と決めたとした場合のサンプルコードです。
//+------------------------------------------------------------------+
//| MA-Print-Test.mq4 |
//| |
//| https://mql-creation.com/ |
//+------------------------------------------------------------------+
#property copyright ""
#property link "https://mql-creation.com/"
#property version "1.00"
#property strict
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//---
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
}
//+------------------------------------------------------------------+
//|変数設定 |
//+------------------------------------------------------------------+
bool Closed, Ticket = false;
extern int magicnum = 2021;//マジックナンバー
extern double Lots=0.01; // スタートロット数
bool BUY = true;
bool SELL = true;
extern int mas=50;//エントリーのMA短期
extern int mal=100;//エントリーのMA長期
extern int masc=10;//決済のMA短期
extern int malc=20;//決済のMA長期
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
Print("~~~OnTick()開始~~~");
//+------------------------------------------------------------------+
//決済条件
Print("~~~決済条件開始(1000番台)~~~");
double mac_1,mac_2;
double mac_1a,mac_2a;
//一つ前の決済用短期線
mac_1 = iMA(NULL,0,masc,0,MODE_SMA,PRICE_CLOSE,1);
//一つ前の決済用長期線
mac_2 = iMA(NULL,0,malc,0,MODE_SMA,PRICE_CLOSE,1);
//現在の決済用短期線
mac_1a = iMA(NULL,0,masc,0,MODE_SMA,PRICE_CLOSE,0);
//現在の決済用長期線
mac_2a = iMA(NULL,0,malc,0,MODE_SMA,PRICE_CLOSE,0);
Print("~~~~~決済用MA条件~~~~~");
Print("1010 mac_1= ",mac_1);
Print("1020 mac_2= ",mac_2);
Print("1030 mac_1a= ",mac_1a);
Print("1040 mac_2a= ",mac_2a);
Print("1050 ( mac_1 > mac_2 && mac_1a <= mac_2a)= ",( mac_1 > mac_2 && mac_1a <= mac_2a));
Print("1060 ( mac_1 < mac_2 && mac_1a >= mac_2a) = ",( mac_1 < mac_2 && mac_1a >= mac_2a));
Print("1070 OrdersTotal()= ",OrdersTotal());
for(int i = OrdersTotal()-1; i>=0; i--)
{
Print("1080 変数【i】の値= ",i);
Print("1090 if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) == true)= ",(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) == true));
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) == true)
{
Print("1100 if(OrderSymbol() == Symbol() && OrderMagicNumber() == magicnum)= ",(OrderSymbol() == Symbol() && OrderMagicNumber() == magicnum));
if(OrderSymbol() == Symbol() && OrderMagicNumber() == magicnum)
{
Print("1110 if(OrderType()==OP_BUY) ",(OrderType()==OP_BUY));
if(OrderType()==OP_BUY)
{
//決済の短期線が上から下に決済の長期線をクロスしたら
if( mac_1 > mac_2 && mac_1a <= mac_2a)
{
Closed = OrderClose(OrderTicket(),OrderLots(), Bid, 30,White);
}
}
else
if(OrderType()==OP_SELL)
{
//決済の短期線が下から上に決済の長期線をクロスしたら
if( mac_1 < mac_2 && mac_1a >= mac_2a)
{
Closed = OrderClose(OrderTicket(),OrderLots(), Bid, 30,White);
}
}
}
}
}///決済条件 for(int i = OrdersTotal()-1; i>=0; i--)
//+------------------------------------------------------------------+
//エントリー条件
Print("~~~エントリー条件開始(2000番台)~~~");
//変数
double ma_1,ma_2;
double ma_1a,ma_2a;
//一つ前のエントリー用短期線
ma_1 = iMA(NULL,0,mas,0,MODE_SMA,PRICE_CLOSE,1);
//一つ前のエントリー用長期線
ma_2 = iMA(NULL,0,mal,0,MODE_SMA,PRICE_CLOSE,1);
//現在のエントリー用短期線
ma_1a = iMA(NULL,0,mas,0,MODE_SMA,PRICE_CLOSE,0);
//現在のエントリー用長期線
ma_2a = iMA(NULL,0,mal,0,MODE_SMA,PRICE_CLOSE,0);
Print("~~~~~エントリー用MA条件~~~~~");
Print("2010 ma_1= ",ma_1);
Print("2020 ma_2= ",ma_2);
Print("2030 ma_1a= ",ma_1a);
Print("2040 ma_2a= ",ma_2a);
Print("2050 ( ma_1 < ma_2 && ma_1a >= ma_2a) = ",( ma_1 < ma_2 && ma_1a >= ma_2a) );
Print("2060 ( ma_1 > ma_2 && ma_1a <= ma_2a)= ",( ma_1 > ma_2 && ma_1a <= ma_2a));
Print("2070 OrdersTotal()= ",OrdersTotal());
Print("2080 BUY= ",BUY);
Print("2090 SELL= ",SELL);
if(BUY == true && OrdersTotal()==0)
{
//エントリー用短期線が下から上にエントリー用長期線をクロスしたら
if( ma_1 < ma_2 && ma_1a >= ma_2a)
{
Ticket = OrderSend(NULL,OP_BUY,Lots,Ask,30,0,0,NULL,magicnum,0,White);
}
}
if(SELL == true && OrdersTotal()==0)
{
//エントリー用短期線が上から下にエントリー用長期線をクロスしたら
if( ma_1 > ma_2 && ma_1a <= ma_2a)
{
Ticket = OrderSend(NULL,OP_SELL,Lots,Bid,30,0,0,NULL,magicnum,0,White);
}
}
} //void OnTick()
//+------------------------------------------------------------------+
この様に、番号を付けるなどして区別出来るようにすると、ログを確認する場合、番号で流れを追えるので分かりやすくなると思います。
番号でログを確認すると、番号が飛んでいる箇所などがあり、読み込まれていなかったり、戻ったりしていることが分かります。
オリジナル関数を使う
EAのバックテストをしていると、保有中のポジションの様々な情報を知りたいときがあると思います。
さきほどのOrdersTotal()は、「保有&待機ポジション数」を表しますが、他にも知りたい情報が沢山あるとします。
例えば、約定価格 「OrderOpenPrice()」やロット数 「OrderLots()」などが知りたい場合、その都度入力するのは面倒です。
この様な場合私のは、様々な情報出力するPrint関数をひとまとめにしたオリジナル関数を使用することもあります。
保有ポジションの様々情報を表示するオリジナル関数(例)
double Order_info()//保有ポジション詳細
{
Print("現在 売値 Bid=",Bid);
Print("現在 買値 Ask=",Ask);
Print("保有&待機ポジション数は? OrdersTotal=",OrdersTotal());
Print("選択した注文は売り?(OrderType()==OP_SELL)=",OrderType()==OP_SELL);
Print("選択した注文は買い?(OrderType()==OP_BUY)=",OrderType()==OP_BUY);
Print("選択した注文のロット数 OrderLots()=" ,OrderLots());
Print("選択した注文の約定価格 OrderOpenPrice=",OrderOpenPrice());
Print("選択した注文のマジックナンバー:" ,OrderMagicNumber());
Print("選択した注文のチケット番号 OrderTicket()=" ,OrderTicket());
Print("選択した注文の注文時間 OrderOpenTime()=" ,OrderOpenTime());
Print("選択した注文の注文タイプは? OrderType()=" ,OrderType());
Print("選択した注文のロット数 OrderLots() =" ,OrderLots());
Print("選択した注文の通貨ペア OrderSymbol()=" ,OrderSymbol());
Print("選択した注文のストップロス価格 OrderStopLoss()=" ,OrderStopLoss());
Print("選択した注文のリミット価格 OrderTakeProfit()=" ,OrderTakeProfit());
Print("選択した注文の決済時間 OrderCloseTime()=" ,OrderCloseTime());
Print("選択した注文の決済価格 OrderClosePrice()=" ,OrderClosePrice());
Print("選択した注文の手数料 OrderCommission()=" ,OrderCommission());
Print("選択した注文の保留有効期限 OrderExpiration()=" ,OrderExpiration());
Print("選択した注文のコメント OrderComment()=" ,OrderComment());
Print("選択した注文の損益(OrderProfit)=",OrderProfit());
Print("選択した注文のSwapは?(OrderSwap())=",OrderSwap());
Print("選択した注文の損益+Swop=",OrderProfit()+OrderSwap());
return(0);
} //Order_info()
少し極端ですが、保有ポジションのほとんどの情報がわかります。
上記の中から不要なものは、先頭に「//」を付けて実行させないのもいいと思います。
この関数を、先ほどの先頭に番号を付けたコードに加えると以下の様になります。
//+------------------------------------------------------------------+
//| MA-Print-Test.mq4 |
//| |
//| https://mql-creation.com/ |
//+------------------------------------------------------------------+
#property copyright ""
#property link "https://mql-creation.com/"
#property version "1.00"
#property strict
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//---
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
}
//+------------------------------------------------------------------+
//|変数設定 |
//+------------------------------------------------------------------+
bool Closed, Ticket = false;
extern int magicnum = 2021;//マジックナンバー
extern double Lots=0.01; // スタートロット数
bool BUY = true;
bool SELL = true;
extern int mas=50;//エントリーのMA短期
extern int mal=100;//エントリーのMA長期
extern int masc=10;//決済のMA短期
extern int malc=20;//決済のMA長期
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
Print("~~~OnTick()開始~~~");
//+------------------------------------------------------------------+
//決済条件
Print("~~~決済条件開始(1000番台)~~~");
double mac_1,mac_2;
double mac_1a,mac_2a;
//一つ前の決済用短期線
mac_1 = iMA(NULL,0,masc,0,MODE_SMA,PRICE_CLOSE,1);
//一つ前の決済用長期線
mac_2 = iMA(NULL,0,malc,0,MODE_SMA,PRICE_CLOSE,1);
//現在の決済用短期線
mac_1a = iMA(NULL,0,masc,0,MODE_SMA,PRICE_CLOSE,0);
//現在の決済用長期線
mac_2a = iMA(NULL,0,malc,0,MODE_SMA,PRICE_CLOSE,0);
Print("~~~~~決済用MA条件~~~~~");
Print("1010 mac_1= ",mac_1);
Print("1020 mac_2= ",mac_2);
Print("1030 mac_1a= ",mac_1a);
Print("1040 mac_2a= ",mac_2a);
Print("1050 ( mac_1 > mac_2 && mac_1a <= mac_2a)= ",( mac_1 > mac_2 && mac_1a <= mac_2a));
Print("1060 ( mac_1 < mac_2 && mac_1a >= mac_2a) = ",( mac_1 < mac_2 && mac_1a >= mac_2a));
Print("1070 OrdersTotal()= ",OrdersTotal());
for(int i = OrdersTotal()-1; i>=0; i--)
{
Print("1080 変数【i】の値= ",i);
Print("1090 if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) == true)= ",(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) == true));
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) == true)
{
Order_info();
Print("1100 if(OrderSymbol() == Symbol() && OrderMagicNumber() == magicnum)= ",(OrderSymbol() == Symbol() && OrderMagicNumber() == magicnum));
if(OrderSymbol() == Symbol() && OrderMagicNumber() == magicnum)
{
Print("1110 if(OrderType()==OP_BUY) ",(OrderType()==OP_BUY));
if(OrderType()==OP_BUY)
{
//決済の短期線が上から下に決済の長期線をクロスしたら
if( mac_1 > mac_2 && mac_1a <= mac_2a)
{
Closed = OrderClose(OrderTicket(),OrderLots(), Bid, 30,White);
}
}
else
if(OrderType()==OP_SELL)
{
//決済の短期線が下から上に決済の長期線をクロスしたら
if( mac_1 < mac_2 && mac_1a >= mac_2a)
{
Closed = OrderClose(OrderTicket(),OrderLots(), Bid, 30,White);
}
}
}
}
}///決済条件 for(int i = OrdersTotal()-1; i>=0; i--)
//+------------------------------------------------------------------+
//エントリー条件
Print("~~~エントリー条件開始(2000番台)~~~");
//変数
double ma_1,ma_2;
double ma_1a,ma_2a;
//一つ前のエントリー用短期線
ma_1 = iMA(NULL,0,mas,0,MODE_SMA,PRICE_CLOSE,1);
//一つ前のエントリー用長期線
ma_2 = iMA(NULL,0,mal,0,MODE_SMA,PRICE_CLOSE,1);
//現在のエントリー用短期線
ma_1a = iMA(NULL,0,mas,0,MODE_SMA,PRICE_CLOSE,0);
//現在のエントリー用長期線
ma_2a = iMA(NULL,0,mal,0,MODE_SMA,PRICE_CLOSE,0);
Print("~~~~~エントリー用MA条件~~~~~");
Print("2010 ma_1= ",ma_1);
Print("2020 ma_2= ",ma_2);
Print("2030 ma_1a= ",ma_1a);
Print("2040 ma_2a= ",ma_2a);
Print("2050 ( ma_1 < ma_2 && ma_1a >= ma_2a) = ",( ma_1 < ma_2 && ma_1a >= ma_2a) );
Print("2060 ( ma_1 > ma_2 && ma_1a <= ma_2a)= ",( ma_1 > ma_2 && ma_1a <= ma_2a));
Print("2070 OrdersTotal()= ",OrdersTotal());
Print("2080 BUY= ",BUY);
Print("2090 SELL= ",SELL);
if(BUY == true && OrdersTotal()==0)
{
//エントリー用短期線が下から上にエントリー用長期線をクロスしたら
if( ma_1 < ma_2 && ma_1a >= ma_2a)
{
Ticket = OrderSend(NULL,OP_BUY,Lots,Ask,30,0,0,NULL,magicnum,0,White);
}
}
if(SELL == true && OrdersTotal()==0)
{
//エントリー用短期線が上から下にエントリー用長期線をクロスしたら
if( ma_1 > ma_2 && ma_1a <= ma_2a)
{
Ticket = OrderSend(NULL,OP_SELL,Lots,Bid,30,0,0,NULL,magicnum,0,White);
}
}
} //void OnTick()
//+------------------------------------------------------------------+
double Order_info()//保有ポジション詳細
{
Print("現在 売値 Bid=",Bid);
Print("現在 買値 Ask=",Ask);
Print("保有&待機ポジション数は? OrdersTotal=",OrdersTotal());
Print("選択した注文は売り?(OrderType()==OP_SELL)=",OrderType()==OP_SELL);
Print("選択した注文は買い?(OrderType()==OP_BUY)=",OrderType()==OP_BUY);
Print("選択した注文のロット数 OrderLots()=" ,OrderLots());
Print("選択した注文の約定価格 OrderOpenPrice=",OrderOpenPrice());
Print("選択した注文のマジックナンバー:" ,OrderMagicNumber());
Print("選択した注文のチケット番号 OrderTicket()=" ,OrderTicket());
Print("選択した注文の注文時間 OrderOpenTime()=" ,OrderOpenTime());
Print("選択した注文の注文タイプは? OrderType()=" ,OrderType());
Print("選択した注文のロット数 OrderLots() =" ,OrderLots());
Print("選択した注文の通貨ペア OrderSymbol()=" ,OrderSymbol());
Print("選択した注文のストップロス価格 OrderStopLoss()=" ,OrderStopLoss());
Print("選択した注文のリミット価格 OrderTakeProfit()=" ,OrderTakeProfit());
Print("選択した注文の決済時間 OrderCloseTime()=" ,OrderCloseTime());
Print("選択した注文の決済価格 OrderClosePrice()=" ,OrderClosePrice());
Print("選択した注文の手数料 OrderCommission()=" ,OrderCommission());
Print("選択した注文の保留有効期限 OrderExpiration()=" ,OrderExpiration());
Print("選択した注文のコメント OrderComment()=" ,OrderComment());
Print("選択した注文の損益(OrderProfit)=",OrderProfit());
Print("選択した注文のSwapは?(OrderSwap())=",OrderSwap());
Print("選択した注文の損益+Swop=",OrderProfit()+OrderSwap());
return(0);
} //Order_info()
//+------------------------------------------------------------------+
コードの最後にオリジナル関数を加えて、100行目に Order_info(); と入力するとオリジナル関数を読み込みます。
注)今回の場合は必ず、OrderSelect() の後に入れる必要があります。
この様すると、ポジションを持った際に、選択したポジションの様々な情報がログに記録されます。
この情報を元にすると、EAの理解や改善に役立ちます。
Print関数でログ出力!問題解決方法(3)のまとめ
3回に渡って、Print関数についてお伝えしてきました。
私のように多数のPrint関数を使用する必要が無いかも知れませんが、私は記憶力が悪く頭の中だけでは理解出来ないので、Print関数の多用はとても便利です。
特に最後のオリジナル関数は私にとってはとても役に立ちました。
あなたも、理解出来ない箇所がある場合は、Print関数を使用してみるのも手だと思います。
今回のPrint関数の記事は、私の入会しているこちらのプログラミングスクールのコミュニティを参照しています。
コミュニティでは、積極的に質問することはもちろん可能ですが、私のような質問が苦手な方は閲覧だけでもとても学びになります。