はじめに
今回は、MQL4とMQL5でどれぐらいソースコードが変わるのかをシンプルかつわかりやすく解説します。
私も長らくMQL5は学ばないとなーと思いながら敬遠していましたが、今回EAの開発依頼があったので色々勉強をしたことについてまとめています。
この記事では、
- MQL4ではこう書いていた内容がMQL5だとこうなる
を解説していきます。
前提のソースコード
今回は、各々MQL4とMQL5でEAを作り、どちらも同じ動きをするものにしていきます。
実際に動きを確認されたい場合は、MT4とMT5を用意して頂きそれぞれのソースコード(MT4はMQL4のソースコード、MT5はMQL5のソースコード)をコンパイルして確認するのがおすすめです。
EAの内容としては、
- 新しいローソク足が出来たタイミングで処理をする
- 移動平均線のクロスでエントリーする
- 最大3ポジションまでとする
- 決済はなし
- ポジションが3つになると操作履歴にメッセージ表示
簡単にこんな感じです。
それでは、実際にソースコードを見ていきましょう。
MQL4・MQL5ソースコード
これが、MQL4とMQL5での同一EAのソースコードの違いです。比較できるようにできるだけ行数も合わせていますので左右見比べてみましょう。
//+------------------------------------------------------------------+
//| p1.mq4 |
//| Copyright 2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, mef Software."
#property link "https://fx-prog.com/"
#property version "1.00"
#property strict
input int ShortMAPeriod = 10; // 短期移動平均線の期間
input int LongMAPeriod = 50; // 長期移動平均線の期間
int MagicNo =11111;
datetime prevtime;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
bool OrderKekka = false;
//新しい足ができた時だけ判定する
if(Time[0] != prevtime){
prevtime = Time[0];
}else{
return;
}
//----MA短期値取得
double shortMA0 = iMA(NULL, 0, ShortMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
double shortMA1 = iMA(NULL, 0, ShortMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
//----MA長期値取得
double longMA0 = iMA(NULL, 0, LongMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
double longMA1 = iMA(NULL, 0, LongMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
if(OrdersTotal() < 3){
// **買いエントリーの条件**
if (shortMA1 < longMA1 && shortMA0 > longMA0) {
OrderKekka = funcOrder_Send(ORDER_TYPE_BUY,0,0,"",MagicNo,0.1);
}
// **売りエントリーの条件**
else if (shortMA1 > longMA1 && shortMA0 < longMA0) {
OrderKekka = funcOrder_Send(ORDER_TYPE_BUY,0,0,"",MagicNo,0.1);
}
}else{
for(int cnt = 0; cnt < OrdersTotal(); cnt++){
if(OrderSelect(cnt,SELECT_BY_POS,MODE_TRADES) == true){
if(OrderMagicNumber() == MagicNo && OrderSymbol() == Symbol()){
Print(cnt+1,"ポジション目:",OrderTicket());
}
}
}
}
}
//+------------------------------------------------------------------+
//|【関数】新規注文関数 |
//| |
//|【引数】 ea_order_entry_Type:売買(0:買 1:売) |
//|【引数】 ea_order_stop_price:損切値 ea_order_good_price:利確値 |
//|【引数】 orderComment:オーダーコメント ea_order_MagicNo:マジックNo |
//| |
//|【戻値】True:成功 |
//| |
//|
bool funcOrder_Send(int ea_order_entry_Type,double tp,double sl,string orderComment,int ea_order_MagicNo,double ea_lots)
{
double ea_order_entry_price=0; // エントリーレート
color order_Color=0;
if(ea_order_entry_Type == OP_BUY){
ea_order_entry_price = Ask; // 買値(Ask)
order_Color = clrBlue;
}else if(ea_order_entry_Type == OP_SELL){
ea_order_entry_price = Bid; // 売値(Bid)
order_Color = clrRed;
}
int ea_ticket_res = OrderSend( // 新規エントリー注文
Symbol(), // 通貨ペア
ea_order_entry_Type, // オーダータイプ[OP_BUY / OP_SELL]
ea_lots, // ロット[0.01単位]
ea_order_entry_price, // オーダープライスレート
20, // スリップ上限
0, // ストップレート
0, // リミットレート
orderComment, // オーダーコメント
ea_order_MagicNo, // マジックナンバー(識別用)
0, // オーダーリミット時間
order_Color // オーダーアイコンカラー
);
if ( ea_ticket_res == -1) { // オーダーエラー
Print("注文失敗: エラーコード ", GetLastError());
return false;
} else {
Print("注文成功: チケット番号 ", ea_ticket_res);
}
return true;
}
//+------------------------------------------------------------------+
//| p1.mq5 |
//| Copyright 2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, mef Software."
#property link "https://fx-prog.com/"
#property version "1.00"
#property strict
input int ShortMAPeriod = 10; // 短期移動平均線の期間
input int LongMAPeriod = 50; // 長期移動平均線の期間
int MagicNo =11111;
datetime prevtime;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
double shortMA[2], longMA[2];
bool OrderKekka = false;
//新しい足ができた時だけ判定する
if(iTime(Symbol(), PERIOD_CURRENT, 0) != prevtime){
prevtime = iTime(Symbol(), PERIOD_CURRENT, 0);
}else{
return;
}
//----MA短期値取得
int shortMA_handle = iMA(NULL, 0, ShortMAPeriod, 0, MODE_SMA, PRICE_CLOSE);
CopyBuffer(shortMA_handle, 0, 0, 2, shortMA); // 移動平均線の最新2本の値を配列に格納
//----MA長期値取得
int longMA_handle = iMA(NULL, 0, LongMAPeriod, 0, MODE_SMA, PRICE_CLOSE);
CopyBuffer(longMA_handle, 0, 0, 2, longMA); // 移動平均線の最新2本の値を配列に格納
if(PositionsTotal()<3){
// **買いエントリーの条件**
if (shortMA[1] < longMA[1] && shortMA[0] > longMA[0]) {
OrderKekka = funcOrder_Send(ORDER_TYPE_BUY,0,0,"",MagicNo,0.1);
}
// **売りエントリーの条件**
else if (shortMA[1] > longMA[1] && shortMA[0] < longMA[0]) {
OrderKekka = funcOrder_Send(ORDER_TYPE_SELL,0,0,"",MagicNo,0.1);
}
}else{
for(int cnt = 0; cnt < PositionsTotal(); cnt++){
ulong ticket = PositionGetTicket(cnt); // ポジションのチケット番号を取得
if(PositionSelectByTicket(ticket)){ // ポジション情報を選択
if(PositionGetInteger(POSITION_MAGIC) == MagicNo && PositionGetString(POSITION_SYMBOL) == Symbol()){
Print(cnt+1,"ポジション目:",ticket);
}
}
}
}
}
//+------------------------------------------------------------------+
//|【関数】新規注文関数
//|
//|【引数】 ea_order_entry_Type:売買(0:買 1:売)
//|【引数】 order_stop_price:損切値 order_good_price:利確値
//|【引数】 orderComment:オーダーコメント ea_order_MagicNo:マジックNo
//|
//|【戻値】True:成功 False:失敗
//|
//|
bool funcOrder_Send(int ea_order_entry_Type,double tp,double sl,string orderComment,int ea_order_MagicNo,double ea_lots)
{
double ea_order_entry_price=0; // エントリーレート
//--- リクエストと結果の宣言と初期化
MqlTradeRequest request={};
MqlTradeResult result={};
//--- リクエストのパラメータ
if(ea_order_entry_Type == ORDER_TYPE_BUY){
ea_order_entry_price = SymbolInfoDouble(Symbol(), SYMBOL_ASK); // 買値(Ask)
}else{
ea_order_entry_price = SymbolInfoDouble(Symbol(), SYMBOL_BID); // 売値(Bid)
}
request.action =TRADE_ACTION_DEAL; // 取引操作タイプ
request.symbol =Symbol(); // 通貨ペア
request.type = ea_order_entry_Type; // オーダータイプ[ORDER_TYPE_BUY / ORDER_TYPE_SELL]
request.volume =ea_lots; // ロット[0.01単位]
request.price =ea_order_entry_price; // オーダープライスレート
request.deviation=20; // スリップ上限
request.sl = 0; // ストップレート
request.tp = 0; // リミットレート
request.comment = orderComment; // オーダーコメント
request.magic =ea_order_MagicNo; // マジックナンバー(識別用)
request.type_filling=ORDER_FILLING_IOC; //フィルポリシー
bool kekka = OrderSend(request, result); // 新規エントリー注文
if(kekka == false){
PrintFormat("注文失敗: エラーコード %d", result.retcode);
return false;
}else{
PrintFormat("注文成功: チケット番号 %I64d", result.order);
}
return true;
}
ソースコードの相違点について解説
MQL4とMQL5のソースコードを見比べてみてどうでしょうか?私は、結構違うな―という印象でしたが結構そのままだなーと思われた方もいるかもしれません。
それでは両ソースコードの違いを詳しく見ていきましょう。
MQL5はTime[]配列が無い
//新しい足ができた時だけ判定する
if(Time[0] != prevtime){
prevtime = Time[0];
}else{
return;
}
MQL4ではあった、時間が格納されているTime[]配列はMQL5では無くなっていました。
//新しい足ができた時だけ判定する
if(iTime(Symbol(), PERIOD_CURRENT, 0) != prevtime){
prevtime = iTime(Symbol(), PERIOD_CURRENT, 0);
}else{
return;
}
なので、ローソク足の時間を取得する時などはこんな感じでiTime()関数で時間を取得する必要があります。この他にも無くなった配列や関数等が複数存在しますので移植する際は注意が必要ですが、基本置き換えができると思っておいて大丈夫です。
本ソースコードでも、MQL4ではOrdersTotal()でポジション数を取得していますがMQL5ではPositionsTotal()でポジション数を取得する等に変わっていますね。
MQL5はインジケータ情報を取得する時にひと手間かかる
//----MA短期値取得
double shortMA0 = iMA(NULL, 0, ShortMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
double shortMA1 = iMA(NULL, 0, ShortMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
//----MA中期値取得
double longMA0 = iMA(NULL, 0, LongMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
double longMA1 = iMA(NULL, 0, LongMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
MQL4ではこんな感じでi〇〇だけでインジケータ(移動平均線)の情報が取得できていましたが、
double shortMA[2], longMA[2];
//----MA短期値取得
int shortMA_handle = iMA(NULL, 0, ShortMAPeriod, 0, MODE_SMA, PRICE_CLOSE);
CopyBuffer(shortMA_handle, 0, 0, 2, shortMA); // 移動平均線の最新2本の値を配列に格納
//----MA長期値取得
int longMA_handle = iMA(NULL, 0, LongMAPeriod, 0, MODE_SMA, PRICE_CLOSE);
CopyBuffer(longMA_handle, 0, 0, 2, longMA); // 移動平均線の最新2本の値を配列に格納
MQL5ではこんな感じでまずハンドルを取得しそれを元に配列に格納するという事が必要になります。配列に格納してしまうと扱い方は同じなのでそこは問題ないですね。
MQL5はOrderSend()関数も変わっている
続いてはポジションエントリーする、OrderSend()関数についてです。対象ソースコードが長いので横並びで表示しています。
double ea_order_entry_price=0; // エントリーレート
color order_Color=0;
if(ea_order_entry_Type == OP_BUY){
ea_order_entry_price = Ask; // 買値(Ask)
order_Color = clrBlue;
}else if(ea_order_entry_Type == OP_SELL){
ea_order_entry_price = Bid; // 売値(Bid)
order_Color = clrRed;
}
int ea_ticket_res = OrderSend( // 新規エントリー注文
Symbol(), // 通貨ペア
ea_order_entry_Type, // オーダータイプ[OP_BUY / OP_SELL]
ea_lots, // ロット[0.01単位]
ea_order_entry_price, // オーダープライスレート
20, // スリップ上限
0, // ストップレート
0, // リミットレート
orderComment, // オーダーコメント
ea_order_MagicNo, // マジックナンバー(識別用)
0, // オーダーリミット時間
order_Color // オーダーアイコンカラー
);
if ( ea_ticket_res == -1) { // オーダーエラー
Print("注文失敗: エラーコード ", GetLastError());
return false;
} else {
Print("注文成功: チケット番号 ", ea_ticket_res);
}
return true;
double ea_order_entry_price=0; // エントリーレート
//--- リクエストと結果の宣言と初期化
MqlTradeRequest request={};
MqlTradeResult result={};
//--- リクエストのパラメータ
if(ea_order_entry_Type == ORDER_TYPE_BUY){
ea_order_entry_price = SymbolInfoDouble(Symbol(), SYMBOL_ASK); // 買値(Ask)
}else{
ea_order_entry_price = SymbolInfoDouble(Symbol(), SYMBOL_BID); // 売値(Bid)
}
request.action =TRADE_ACTION_DEAL; // 取引操作タイプ
request.symbol =Symbol(); // 通貨ペア
request.type = ea_order_entry_Type; // オーダータイプ[ORDER_TYPE_BUY / ORDER_TYPE_SELL]
request.volume =ea_lots; // ロット[0.01単位]
request.price =ea_order_entry_price; // オーダープライスレート
request.deviation=20; // スリップ上限
request.sl = 0; // ストップレート
request.tp = 0; // リミットレート
request.comment = orderComment; // オーダーコメント
request.magic =ea_order_MagicNo; // マジックナンバー(識別用)
request.type_filling=ORDER_FILLING_IOC; //フィルポリシー
bool kekka = OrderSend(request, result); // 新規エントリー注文
if(kekka == false){
PrintFormat("注文失敗: エラーコード %d", result.retcode);
return false;
}else{
PrintFormat("注文成功: チケット番号 %I64d", result.order);
}
return true;
まず、MQL5ではrequest={}とresult={}が存在します。これは、requestは新規注文時に必要な情報を格納しておく所で、resultはエントリー後の結果が格納されます。
MQL4ではOrderSend()の引数として色々情報を渡していますが、MQL5ではOrderSend(request, result)となっており見た目上は非常にシンプルな感じになっています。
まとめると、MQL4では引数として渡していた情報を、MQL5では事前にrequestに格納しておくといった感じになります。
あと、MQL4ではModify(変更注文)、Close(決済注文)注文がありましたが、MQL5はいずれもOrderSendで行います。
MQL5はOrderSelect()関数が無い!?
これは私が一番驚いた内容でしたが、MQL5はOrderSelect()関数が無くなっています。
for(int cnt = 0; cnt < OrdersTotal(); cnt++){
if(OrderSelect(cnt,SELECT_BY_POS,MODE_TRADES) == true){
if(OrderMagicNumber() == MagicNo && OrderSymbol() == Symbol()){
Print(cnt+1,"ポジション目:",OrderTicket());
}
}
}
MQL4では見慣れたOrderSelect()でポジション情報を取得できていましたが、MQL5ではこんな感じです。
for(int cnt = 0; cnt < PositionsTotal(); cnt++){
ulong ticket = PositionGetTicket(cnt); // ポジションのチケット番号を取得
if(PositionSelectByTicket(ticket)){ // ポジション情報を選択
if(PositionGetInteger(POSITION_MAGIC) == MagicNo && PositionGetString(POSITION_SYMBOL) == Symbol()){
Print(cnt+1,"ポジション目:",ticket);
}
}
}
PositionGetTicket()関数でポジションのチケット番号を取得し、PositionSelectByTicket()関数でポジション情報を取得します。これがMQL4でいうOrderSelect()関数の変わりとなります。慣れるまでは違和感がありますが慣れると同じように使えると思います。
ポジション選択後のマジックナンバー取得方法も、MQL4だとOrderMagicNumber()で取得可能ですが、MQL5だとPositionGetInteger(POSITION_MAGIC)こんな感じで少し違います。
さいごに
以上、『MT4(MQL4)とMT5(MQL5)のソースコードの違いについて解説』でした。
MQL4からMQL5はコンバーター等もあり移植できるのですが、中にはうまく移植できないコードもありますのである程度の知識は必要になってきます。
MQL4がまだまだ現役なうちには、なかなかMQL5は使わないかもしれないですが、今後の事もありますのでどこかのタイミングで覚えるようにしていきたいですね。
コメント