MQL5特有の仕様 ディール/オーダー/ポジション/ヘッジ/ネッティング
目次/もくじ
MQL4プログラマがMQL5を始める前に知らないと失敗すること
MQL4はできるけどMQL5と言語仕様が違うと聞いて、MQL5は保留状態だった方は多いと思います。このページに来たということは、何かしらの理由でMQL5を始めてみよう、あるいは、始めてみたけどよく分からんって方だと思います。
MQL4は開発されたのがかなり昔の話なので、「もっとこうした方が良いよね」とか「本来こうすべきだったよね」というのをMQL5では改良されています。
逆に言うと、MQL5では、慣例的に周知された仕様よりも厳密性や定義を重視するので、それまでのMQL4の仕様を一切無視した仕様などもあります。
MQL5を始める前に、そうした泥沼にはまるような仕様の違いについてご紹介します。基本的にMQL5をディスっているのでMT5の良いところを知りたい方はMT5がMT4よりも優れている点 一覧
ヘッジアカウントとネッティングアカウントの大幅な差
MT5ではアカウントの種類にヘッジタイプとネッティングタイプがあります。これはFX業者の口座タイプに依存したもので、トレーダー側・プログラマ側がMT5内で変更することはできません。
そのMT5がどちらのアカウントタイプかは
AccountInfoInteger(ACCOUNT_MARGIN_MODE)
で調べられます。
中にはヘッジタイプかネッティングタイプかを選択できるFX業者さんも存在します。裁量トレードの場合は、ネッティングタイプの方が便利ですが、EAを稼働させる場合はヘッジアカウントの方が対応しているものが多いです。
また、現時点では9割型のMT5アカウントがヘッジモードです。ネッティングアカウントはわざわざ探さないと見つからないレベルではありますが、EA開発者であるならば必ず知っておかなければならないモードです。
ネッティングアカウントの場合
ネッティングタイプとはオーダーを合算させる仕様の口座タイプです。例えば、両建てをした場合、ヘッジタイプではMT4のように表示されますが、ネッティングタイプでは前に持っていたポジションが決済されて終了です。
そもそも両建て、ナンピンはできない
また、同じ通貨ペアで同じ方向にロットを増やした場合(ピラミッティングした場合)、ヘッジタイプでは新たなオーダーとして区別されますが、ネッティングタイプの場合はオーダーが合体させられます。
つまり、ネッティングアカウントでは、ナンピン、ピラミッティング、両建てなどの手法は一切できません。
オーダーコメントは上書きされる
同じ通貨ペアでポジションを新規にとると、すでに保有しているポジションのチケットナンバーでロットだけ増加します。
同じように、もしEAにコメント付きでオーダーを送信させて、そのあと裁量で同じ通貨ペアのオーダーをすると、
オーダーコメントが上書きされてしまいます。
コメントでオーダーを管理するというのは、安定性に欠けるので避けるべきではありますが、
それでも複雑な開発要件の場合、どうしても使用せざるをえないことがあります。
しかし、MT5ではコメントがリアルタイムで変化する可能性があるので、
コメントを使ったプログラミングは比較的穴になる可能性があります。
(見かけ上は古いコメントが消滅したように見えますが、中では残っているようです。)
オーダー決済の仕方
ネッティングアカウントの場合、決済は反対売買によって行います。そのため、もっているポジションの逆向きの同じロットの注文を送信すれば決済されます。
ヘッジアカウントの場合
ヘッジタイプは、ポジション、オーダーの扱いに関しては従来のMT4とまったく同じタイプのアカウントです。CTradeという標準ライブラリを使えばMQL4と同様に処理できます。
MQL5ではポジション、オーダー、ディールを厳密に区別する必要がある
MT4ではオーダーとポジションがあまり厳密に区別されることはありませんが、MT5では新たにディールという概念が追加され、この3つを厳密に区別しています。
MT5でのオーダーとは
MT5/MQL5でいうところのオーダーとはブローカーサーバーに送信された注文そのものを指します。
基本的には約定前の予約注文、決済注文をサーバーに送ることなどを指します。
そのため、一回の往復の取引で、オーダーは少なくとも2つ存在します。(エントリー時と決済)
MT4/MQL4では、約定済みのポジションも未約定のオーダーも一緒にしてオーダーと呼んでいましたが、MQL5では約定済みのオーダーはポジションとなり、約定していないオーダーはオーダーのままになります。
オーダーにはオーダーチケット(≠ポジションID ≠ディールチケット)が付与されます。
MT5でのディールとは
MT5/MQL5ではDeal(ディール)というものが存在します。ディールはMT4にはなかった言葉です。
サーバーに送信されその後約定した注文をディールと呼びます。オーダーが実際には上手くいくかどうかわからないリクエストのようなものであるのに対して、ディールは実際に注文が取ったことを表します。
また、厄介な点として、ロットが大きい場合には一つのオーダーに対してディールが分割されることがあります。言い換えると、指定したロットのオーダーを単一の価格で約定させることができなかったために、ロットと価格を分割して約定させるモードがありますが、それによって約定した場合、ディールが分割されます。
例えば、USDJPYの成行買い注文を30ロットで出したとします。30ロットというのはかなり大きなロットなので、ほとんどの業者で分割されます。15ロットは100.001で約定(ディール)し、25ロットは100.002で約定(ディール)が発生します。
この場合、それぞれにディールチケットを持つディールがMT5に表示されます。MQL5でのディールの取得の際もそれぞれ別個のものとして扱われます。
1回の往復の取引で、ディールは少なくとも2つ以上存在する可能性があります。
ディールにはディールチケットが付与されます。(≠オーダーチケット ≠ ポジションID )
また、ディールの場合、エントリーか決済かをIN/OUTで区別することが可能です。
オーダーとディール(左に矢印がついているものがディール)
MT5でのポジションとは
約定したオーダーです。予約注文(未約定オーダー)は含まれません。また、未約定中の予約注文(指値注文、逆指値注文)にはそもそもポジションID(MQL4で言うところのチケット)が付与されません。
MQL5では、オーダーが約定したタイミングでポジションごとにポジションID(≠ディールチケット nor ≠オーダーチケット)が振り分けられます。
オーダーがエントリーの場合、オーダーチケットとポジションIDは一致します。
MT5のポジション履歴
以前のMT5のbuildバージョンではターミナル上でポジション履歴を表示する機能がありませんでしたが、修正されて閲覧できるようになりました。ポジション一覧はMT4のポジション一覧と同じような見た目なので、分かりやすいと思います。
まとめると、ポジション、ディール、オーダーはこのように構成されます。
オーダー、ディール、ポジションの記事はこちら
https://www.mql5.com/en/articles/211
オーダー/ポジションの注文の仕方
MQL5ではオーダーはOrderSendの関数を使いますが、MQL4のOrderSendとは全くの別物です。構造体を使ってオーダーを送ります。
MqlTradeRequest CRequest; MqlTradeResult CResult; ZeroMemory(CRequest); ZeroMemory(CResult); CRequest.type = ORDER_TYPE_BUY; CRequest.price = SymbolInfoDouble("USDJPY",SYMBOL_ASK); CRequest.action = TRADE_ACTION_DEAL; CRequest.symbol = "USDJPY"; CRequest.volume = 0.01; CRequest.sl=0; CRequest.tp=0; CRequest.magic = 1234; CRequest.comment = "trade comment"; CRequest.type_filling = ORDER_FILLING_IOC; OrderSend(CRequest,CResult);
標準ライブラリ(CTrade)を使ったオーダーの注文
また、あまりのオーダー関数の使いずらさにクレームが大量に行った関係で、その後に標準ライブラリが実装されました。MT5にデフォルトで搭載されています。標準ライブラリのCTradeを使えば、MQL4ライクな実装が可能です。
#include <Trade\Trade.mqh> class CTrade : public CObject
//CTradeの呼び出し MT5に標準搭載されているのでダウンロードする必要はありません。
bool OrderOpen(
const string symbol, // シンボル
ENUM_ORDER_TYPE order_type, // 注文の種類
double volume, // 注文のボリューム
double limit_price, // ストップリミット価格
double price, // 実行価格
double sl, // 決済逆指値
double tp, // 決済指値
ENUM_ORDER_TYPE_TIME type_time, // 期限によっての種類
datetime expiration, // 期限
const string comment="" // コメント
)
OrderOpenとPositionOpenがあるので混同に注意してください。
ポジション決済の仕方
CTrade
#include <Trade\Trade.mqh> CTrade m_trade; m_trade.PositionClose(チケット,スリッページ);
デフォルト関数
bool PositionClose( const string symbol, // シンボル ulong deviation=ULONG_MAX // 偏差値 )
こちらの場合、そのシンボルのポジションが全部決済されてしまうので、チケットごとに決済したい場合は上の書き方の方がおすすめです。
MQL5でのオーダー、ディール、ポジションの識別処理
MT4では何かと御世話になるOrdersTotalですが、MT5では注意深い使用が必要です。類似の関数にPositionsTotalというものができているのですが、MQL5では約定したポジションと予約注文が分かれています。
Order~ Position~系は完全に関数が分離しています。
ここら辺をしっかり把握せずにMQL4と同じようにコーディングすると手痛い洗礼を受けます。
MQL4ではOrderSelect一つで済んでいたことが、MQL5では状況に応じて関数の使い分けが必要です。
では、その関数はなんなの?というと…
OrdersTotal()とOrderSelectの併用の代わりに、MQL5では6つの関数の使い分けが必要
OrderSelect(ticket):
引数はチケット番号。MQL4で言うところの、OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADE)です。ただし、対象はオーダー。SELECT_BY_POSとかSELECT_BY_TICKETの指定はもうありません。チケットでしか渡せません。
PositionSelectByTicket(ticket):
チケットを引数に入れると、MQL4で言うところのOrderSelectした状態になります。MQL4で言うところの、OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADE)です。ただし、対象はポジション。
PositionSelect(symbol):
引数はシンボル。MT5のネッティング口座の場合、シンボル毎にオーダーが集約されるので、シンボルを選択すればオーダーの情報が引っ張ってこれるよね、って仕組みです。
ヘッジ口座の場合は意図しないポジションも一気に処理してしまうので、これは使えません。
HistorySelect(from_date, to_date ):
MQL4のOrderSelect(i,SELECT_BY_POS, MODE_HISTORY)と見せかけて違います。HistoryOrderSelectとセットで使い、履歴を参照する期間をUNIXタイムで指定します。
(HistoryReadPeriod()とかの名前の方が良かったんじゃないの?)
HistoryOrderSelect(ticket):
MQL4で言うところの、OrderSelect(ticket,SELECT_BY_TICKET,MODE_HISOTRY)です。
呼び出せば、
HistoryOrderGetDouble(), HistoryOrderGetInteger(), HistoryOrderGetString() が使えるようになります。
対象はオーダーです。
HistoryDealSelect(ticket):
ディールごとに履歴を参照できます。
呼び出せば、HistoryDealGetDouble(), HistoryDealGetInteger()を利用できるようになります。
HistorySelectByPosition(position_id):
position_idは、全ての新しくオープンしたポジションに割り当てられ、ポジションのライフタイムに一貫する固有の番号です。これに関してはあまり馴染みがないと思います。指定した条件のポジション履歴を参照・回収します。こいつを使うとHistoryDealsTotal() とHistoryOrdersTotal()の値が変わります。
でも、上の関数を従来のように使うにはチケットがわかってないと駄目だよねってことで、
チケットを取得する関数
オーダーチケット、ディールチケット、ポジションID(MQL4で言うところのオーダーチケット)の混同に注意してください。
OrderGetTicket(index):
MQL4で言うところの、OrderSelect(index,SELECT_BY_POS,MODE_TRADE)ですが、チケットを返すと同時にOrderSelectの役割も自動的に果たすので、OrderTicket()とOrderSelectの合体版のようなやつです。(逆にこいつのあとでOrderSelectするとバグります。) 注:約定済みのポジションは範囲外なので、予約注文のみを返します。
PositionGetTicket(index):
ポジションの数未満の数値を引数に入れると、そのポジションのチケットを返します。
PositionGetSymbol(index):
持っているポジションの数以下の数値を引数に入れると、そのポジションのシンボルを返します。返すのはチケットではありませんが、PositionSelect(symbol)と組み合わせて使えます。
HistoryOrderGetTicket(index):
履歴のチケットを返します。使うには事前にHistorySelectで期間を指定しておく必要があります。
HistoryDealGetTicket(index):
履歴のディールのチケットを返します。
オーダー情報に関するサンプルコード
未約定のオーダー情報が欲しい場合のコード
for(int i=0;i<OrdersTotal();i++) { if( OrderGetTicket(i)>0 ) { OrderTicket[i] = OrderGetInteger(ORDER_TICKET); OrderSymbol[i] = OrderGetString(ORDER_SYMBOL); OrderType[i] = OrderGetInteger(ORDER_TYPE); OrderLots[i] = OrderGetDouble(ORDER_VOLUME_CURRENT); OrderOpenPrice[i] = OrderGetDouble(ORDER_PRICE_OPEN); OrderOpenTime[i] = OrderGetInteger(ORDER_TIME_SETUP); OrderStopLoss[i] = OrderGetDouble(ORDER_SL); OrderTakeProfit[i] = OrderGetDouble(ORDER_TP); OrderMagicNumber[i] = OrderGetInteger(ORDER_MAGIC); } }
ストップロスとテイクプロフィットはそのオーダーがエントリーのときのみ取得可能です。決済時のオーダーではSL/TPが0になります。
未決済のポジションの情報が欲しい場合のコード
for(int i=0;i<PositionsTotal();i++) { if( PositionGetTicket(i)>0 ) { PositionTicket[i] = PositionGetInteger(POSITION_TICKET); PositoinSYmbol[i] = PositionGetString(POSITION_SYMBOL); PositionType[i] = PositionGetInteger(POSITION_TYPE); PositionOpenPrice[i] = PositionGetDouble(POSITION_PRICE_OPEN); PositionOpenTime[i] = PositionGetInteger(POSITION_TIME); PositionStopLoss[i] = PositionGetDouble(POSITION_SL); PositionTakeProfit[i] = PositionGetDouble(POSITION_TP); PositionMagicNumber[i] = PositionGetInteger(POSITION_MAGIC); } }
MQL4と同じように生きているオーダー、ポジションの取得をしたい場合は、上の二つを回せば何とかなります。ディールに関してはポジションの中に内包されるので、よっぽど細かいディールデータが欲しい場合を除いて必要ありません。
履歴のオーダー情報が欲しい場合のコード
履歴の取引のデータの取得も基本的に流れは変わりませんが、事前にHistorySelectで取得する期間を指定する必要があります。
void OnStart() { datetime from=0; datetime to=TimeCurrent(); //--- request the entire history HistorySelect(from,to); //--- variables for returning values from order properties ulong ticket; double open_price; double initial_volume; datetime time_setup; datetime time_done; string symbol; string type; long order_magic; long positionID; //--- number of current pending orders uint total=HistoryOrdersTotal(); //--- go through orders in a loop for(uint i=0;i<total;i++) { //--- return order ticket by its position in the list if((ticket=HistoryOrderGetTicket(i))>0) { //--- return order properties open_price =HistoryOrderGetDouble(ticket,ORDER_PRICE_OPEN); time_setup =(datetime)HistoryOrderGetInteger(ticket,ORDER_TIME_SETUP); time_done =(datetime)HistoryOrderGetInteger(ticket,ORDER_TIME_DONE); symbol =HistoryOrderGetString(ticket,ORDER_SYMBOL); order_magic =HistoryOrderGetInteger(ticket,ORDER_MAGIC); positionID =HistoryOrderGetInteger(ticket,ORDER_POSITION_ID); initial_volume=HistoryOrderGetDouble(ticket,ORDER_VOLUME_INITIAL); type =GetOrderType(HistoryOrderGetInteger(ticket,ORDER_TYPE)); //--- prepare and show information about the order printf("#ticket %d %s %G %s at %G was set up at %s => done at %s, pos ID=%d", ticket, // order ticket type, // type initial_volume, // placed volume symbol, // symbol open_price, // specified open price TimeToString(time_setup),// time of order placing TimeToString(time_done), // time of order execution or deletion positionID // ID of a position , to which the deal of the order is included ); } } //--- } //+------------------------------------------------------------------+ //| Returns the string name of the order type | //+------------------------------------------------------------------+ string GetOrderType(long type) { string str_type="unknown operation"; switch(type) { case (ORDER_TYPE_BUY): return("buy"); case (ORDER_TYPE_SELL): return("sell"); case (ORDER_TYPE_BUY_LIMIT): return("buy limit"); case (ORDER_TYPE_SELL_LIMIT): return("sell limit"); case (ORDER_TYPE_BUY_STOP): return("buy stop"); case (ORDER_TYPE_SELL_STOP): return("sell stop"); case (ORDER_TYPE_BUY_STOP_LIMIT): return("buy stop limit"); case (ORDER_TYPE_SELL_STOP_LIMIT):return("sell stop limit"); } return(str_type); }
履歴のディール情報が欲しい場合のコード
void OnStart() { ulong deal_ticket; // deal ticket ulong order_ticket; // ticket of the order the deal was executed on datetime transaction_time; // time of a deal execution long deal_type ; // type of a trade operation long position_ID; // position ID string deal_description; // operation description double volume; // operation volume string symbol; // symbol of the deal //--- set the start and end date to request the history of deals datetime from_date=0; // from the very beginning datetime to_date=TimeCurrent();// till the current moment //--- request the history of deals in the specified period HistorySelect(from_date,to_date); //--- total number in the list of deals int deals=HistoryDealsTotal(); //--- now process each trade for(int i=0;i<deals;i++) { deal_ticket= HistoryDealGetTicket(i); volume= HistoryDealGetDouble(deal_ticket,DEAL_VOLUME); transaction_time=(datetime)HistoryDealGetInteger(deal_ticket,DEAL_TIME); order_ticket= HistoryDealGetInteger(deal_ticket,DEAL_ORDER); deal_type= HistoryDealGetInteger(deal_ticket,DEAL_TYPE); symbol= HistoryDealGetString(deal_ticket,DEAL_SYMBOL); position_ID= HistoryDealGetInteger(deal_ticket,DEAL_POSITION_ID); deal_description= GetDealDescription(deal_type,volume,symbol,order_ticket,position_ID); //--- perform fine formatting for the deal number string print_index=StringFormat("% 3d",i); //--- show information on the deal Print(print_index+": deal #",deal_ticket," at ",transaction_time,deal_description); } } //+------------------------------------------------------------------+ //| Returns the string description of the operation | //+------------------------------------------------------------------+ string GetDealDescription(long deal_type,double volume,string symbol,long ticket,long pos_ID) { string descr; //--- switch(deal_type) { case DEAL_TYPE_BALANCE: return ("balance"); case DEAL_TYPE_CREDIT: return ("credit"); case DEAL_TYPE_CHARGE: return ("charge"); case DEAL_TYPE_CORRECTION: return ("correction"); case DEAL_TYPE_BUY: descr="buy"; break; case DEAL_TYPE_SELL: descr="sell"; break; case DEAL_TYPE_BONUS: return ("bonus"); case DEAL_TYPE_COMMISSION: return ("additional commission"); case DEAL_TYPE_COMMISSION_DAILY: return ("daily commission"); case DEAL_TYPE_COMMISSION_MONTHLY: return ("monthly commission"); case DEAL_TYPE_COMMISSION_AGENT_DAILY: return ("daily agent commission"); case DEAL_TYPE_COMMISSION_AGENT_MONTHLY: return ("monthly agent commission"); case DEAL_TYPE_INTEREST: return ("interest rate"); case DEAL_TYPE_BUY_CANCELED: descr="cancelled buy deal"; break; case DEAL_TYPE_SELL_CANCELED: descr="cancelled sell deal"; break; } descr=StringFormat("%s %G %s (order #%d, position ID %d)", descr, // current description volume, // deal volume symbol, // deal symbol ticket, // ticket of the order that caused the deal pos_ID // ID of a position, in which the deal is included ); return(descr); //--- }
履歴のポジション情報が欲しい場合のコード
未決済のポジションの取得はPosition関数があるので簡単ですが、履歴のポジションデータを取得するには専用の関数HistoryPositionGet***の関数群がありません。そのため、MT4と同じように履歴にあるポジションを取得するにはかなり遠回りをしなくてはなりません。
〇=取得可能 ×=取得不可
Order | Deal | |
シンボル | 〇 | 〇 |
ロット | 〇 | ×(分割されたロットなら可) |
注文方式 | 〇(エントリー時のみ可能) | × |
マジックナンバー | 〇 | 〇 |
注文価格 | 〇 | × |
注文時間 | 〇 | × |
約定価格 | × | 〇 |
約定時間 | × | 〇 |
SL | 〇(エントリー時のみ可能) | × |
TP | 〇(エントリー時のみ可能) | × |
決済注文価格 | 〇 | × |
決済注文時間 | 〇 | × |
決済約定価格 | × | 〇 |
決済約定時間 | × | 〇 |
ポジションID | 〇 | 〇 |
オーダーチケット | 〇 | 〇 |
ディールチケット | × | 〇 |
履歴の取引情報を取得する際の比較表
また、MT4の場合はキャンセルされた予約注文はポジション扱いとして履歴に残りますが、MT5ではポジション欄の履歴に削除済みのオーダーは表示されません。(オーダーの履歴には残ります。) また、削除された指値注文、逆指値注文にはポジションIDが付与されないので、別個に処理する必要があります。
履歴のポジション | 履歴のディール | 履歴のオーダー | 削除された予約注文 | |||||
HistorySelect | + | HistoryOrdersTotal() | + | HistoryOrderGetTicket | × | × | 〇 | 〇 |
HistorySelect | + | HistoryOrdersTotal() | + | HistoryDealGetTicket | × | 〇 | × | × |
HistorySelectByPosition | + | HistoryOrdersTotal() | + | HistoryOrderGetTicket | × | × | 〇 | – |
HistorySelectByPosition | + | HistoryOrdersTotal() | + | HistoryDealGetTicket | × | 〇 | × | × |
関数の組み合わせと取得可能なデータ
流れとしては、
履歴のポジションチケット(ポジションID)を履歴のオーダーをループさせることで配列に取得
↓
取得したポジションチケットには順不同の重複があるので、ソートして重複を削除
↓
精製した一意の連番のポジションチケットからHistorySelectByPositionを使ってオーダーとディールを検索
↓
検索結果のオーダーとディールから対応した約定価格などの数値を取ってくる
↓
上の処理とは別に、削除された予約注文は、ポジションIDが0になっている履歴のオーダーから取得する
という形です。
//履歴のオーダーをサーバーに通信する------------------------------------------------------------------------- //過去3か月分の履歴のポジションIDを重複なく取得し、配列に入れる----------------------------------------------------------- uint HistoryPositionID[]; datetime CheckHistoryFrom = TimeCurrent()-60*60*24*31*3; HistorySelect(CheckHistoryFrom,TimeCurrent()); ArrayResize(HistoryPositionID,HistoryOrdersTotal()); ArrayInitialize(HistoryPositionID,0); for(int r=HistoryOrdersTotal();r>=0;r--) { if( HistoryOrderGetTicket(r) > 0 ) { HistoryPositionID[r] = HistoryOrderGetInteger(HistoryOrderGetTicket(r),ORDER_POSITION_ID); } } ArraySort(HistoryPositionID); bool WhileBreak = false; while( true ) { //重複する配列があったら削除 for(int k=1;k<ArraySize(HistoryPositionID);k++) { //ループの最後まで重複する配列がなかったら、上のwhileをbreak if( k == ArraySize(HistoryPositionID)-1 ) WhileBreak = true; if( HistoryPositionID[k-1] == HistoryPositionID[k] ) { ArrayRemove(HistoryPositionID,k,1); break; } } if( WhileBreak ) break; } int HistoryPositionsTotal = ArraySize(HistoryPositionID); //ArrayPrint(HistoryPositionID); //----------------------------------------------------------------------------------------------- for(int P=0; P<HistoryPositionsTotal; P++) { string PositionSymbol = ""; datetime PositionOrderOpenTime = 0; double PositionOrderOpenPrice = 0; datetime PositionDealOpenTime = 0; double PositionDealOpenPrice = 0; datetime PositionOrderCloseTime = 0; double PositionOrderClosePrice = 0; datetime PositionDealCloseTime = 0; double PositionDealClosePrice = 0; double PositionStopLoss = 0; double PositionTakeProfit = 0; int PositionMagicNumber = 0; double PositionLot = 0; int PositionType = 0; //指定のポジションのオーダーとディールのみ取得 HistorySelectByPosition(HistoryPositionID[P]); for(int i=HistoryOrdersTotal();i>=0;i--) { if( HistoryOrderGetTicket(i) > 0 ) { PositionSymbol = HistoryOrderGetString(HistoryOrderGetTicket(i),ORDER_SYMBOL); PositionLot = HistoryOrderGetDouble(HistoryOrderGetTicket(i),ORDER_VOLUME_INITIAL); PositionMagicNumber = HistoryOrderGetInteger(HistoryOrderGetTicket(i),ORDER_MAGIC); //INの場合 if( HistoryOrderGetTicket(i) == HistoryPositionID[P] ) { PositionOrderOpenTime = HistoryOrderGetInteger(HistoryOrderGetTicket(i),ORDER_TIME_SETUP); PositionOrderOpenPrice = HistoryOrderGetDouble(HistoryOrderGetTicket(i),ORDER_PRICE_OPEN); PositionStopLoss = HistoryOrderGetDouble(HistoryOrderGetTicket(i),ORDER_SL); PositionTakeProfit = HistoryOrderGetDouble(HistoryOrderGetTicket(i),ORDER_TP); PositionType = HistoryOrderGetInteger(HistoryOrderGetTicket(i),ORDER_TYPE); } //OUTの場合 else { PositionOrderCloseTime = HistoryOrderGetInteger(HistoryOrderGetTicket(i),ORDER_TIME_DONE); PositionOrderClosePrice = HistoryOrderGetDouble(HistoryOrderGetTicket(i),ORDER_PRICE_CURRENT); } } } for(int q=HistoryDealsTotal();q>=0;q--) { if( HistoryDealGetTicket(q) > 0 ) { //INの場合 if( HistoryDealGetInteger(HistoryDealGetTicket(q),DEAL_ENTRY) == DEAL_ENTRY_IN ) { PositionDealOpenPrice = HistoryDealGetDouble(HistoryDealGetTicket(q),DEAL_PRICE);//約定価格 PositionDealOpenTime = HistoryDealGetInteger(HistoryDealGetTicket(q),DEAL_TIME);//約定時刻 } //OUTの場合 if( HistoryDealGetInteger(HistoryDealGetTicket(q),DEAL_ENTRY) == DEAL_ENTRY_OUT ) { PositionDealClosePrice = HistoryDealGetDouble(HistoryDealGetTicket(q),DEAL_PRICE);//約定価格 PositionDealCloseTime = HistoryDealGetInteger(HistoryDealGetTicket(q),DEAL_TIME);//約定時刻 } } } int PositionSymbolDigits = SymbolInfoInteger(PositionSymbol,SYMBOL_DIGITS); OrderTicket = HistoryPositionID[P]; OrderSymbol = PositionSymbol; OrderType = PositionType; OrderLots = PositionLot; OrderOpenPrice = PositionDealOpenPrice; OrderOpenTime = PositionDealOpenTime; OrderClosePrice = PositionOrderClosePrice; OrderCloseTime = PositionOrderCloseTime; OrderStopLoss = PositionStopLoss; OrderTakeProfit = PositionTakeProfit; OrderMagicNumber = PositionMagicNumber; } //キャンセルされた予約注文は履歴ディール履歴オーダーに表示されないので、MQL4の仕様に合わせて取る------------------------ //全部Orderからとる HistorySelect(CheckHistoryFrom,TimeCurrent()); int CancelledPendingOrdersTotal = 0; for(int r=0;r<HistoryOrdersTotal();r++) { if( HistoryOrderGetTicket(r) > 0 ) { //キャンセルされた予約注文の場合(履歴にあるオーダーで、ポジションIDが付与されていない場合) if( HistoryOrderGetInteger(HistoryOrderGetTicket(r),ORDER_POSITION_ID) == 0 ) { int OrderTicket = HistoryOrderGetTicket(r); int PositionSymbolDigits = SymbolInfoInteger(HistoryOrderGetString(HistoryOrderGetTicket(r),ORDER_SYMBOL) ,SYMBOL_DIGITS); OrderTicket = HistoryOrderGetTicket(r); OrderSymbol = HistoryOrderGetString(OrderTicket,ORDER_SYMBOL); OrderType = HistoryOrderGetInteger(OrderTicket,ORDER_TYPE); OrderLots = HistoryOrderGetDouble(OrderTicket,ORDER_VOLUME_CURRENT); OrderOpenPrice = HistoryOrderGetDouble(OrderTicket,ORDER_PRICE_OPEN); OrderOpenTime = HistoryOrderGetInteger(OrderTicket,ORDER_TIME_SETUP); OrderClosePrice = HistoryOrderGetDouble(OrderTicket,ORDER_PRICE_CURRENT); OrderCloseTime = HistoryOrderGetInteger(OrderTicket,ORDER_TIME_DONE); OrderStopLoss = HistoryOrderGetDouble(OrderTicket,ORDER_SL); OrderTakeProfit = HistoryOrderGetDouble(OrderTicket,ORDER_TP); OrderMagicNumber = HistoryOrderGetInteger(OrderTicket,ORDER_MAGIC); ; CancelledPendingOrdersTotal++; } } }
時系列予約変数・関数がない
Open[],Close[],iOpen(),iClose()がない
MQL4では始値、終値、高値、安値を取得する関数がありましたが、
MQL5ではありません。「いや、でも名前が変わっても似たような関数があるんでしょ?」
と思うのが普通だと思います。
それがないんですね~
MQL5では、自分で配列を宣言して、配列の向きを変えて、配列に値を入れないと始値~安値を取得できない!
さすがにバージョンアップで実装されたようです。逆にオリジナル関数で代用していた人の場合、定義済みの関数としてバグを吐き出すので、ほんと大変ですよね。
iMA, iMACD, iRSIなどのテクニカル指標の返値がint
移動平均や、MACDなどをはじめとしたテクニカル指標i〇〇関数の返値はすべて整数でハンドルが返ってきます。そのため、例えばATRの値が欲しい場合は、これまではiATR(Symbol(),Period(),14)とすればよかったところ、
int MA_Period = 14; // The value of the averaging period for the indicator calculation int Count = 5; // Amount to copy int ATRHandle; // Variable to store the handle of ATR double ATRValue[]; // Variable to store the value of ATR ATRHandle = iATR(_Symbol,0,MA_Period); // returns a handle for ATR ArraySetAsSeries( ATRValue,true ); // Set the ATRValue to timeseries, 0 is the oldest. if( CopyBuffer( ATRHandle,0,0,Count,ATRValue ) > 0 ) // Copy value of ATR to ATRValue { for( int i=0;i<Count;i++ ) { Print(ATRValue[i]); // Print the value of the ATR } }
とする必要があります。
その他
ところどころ良くなっているところはもちろんあるのですが、
その他にも
- MT5を複数インストールフォルダにコピーするとEAの紐づけがバグる
- 特定の条件でコンパイルしてもEAが初期化されないバグ(コンパイルの失敗)
- ヒストリーデータがいじれない(import,export)
- コンパイルしたときにエラーが一番上に来ない
- 決済後のトレードのコメントを表示できない
などあります。
チャートにセットした実行中のEAのパラメータは変えられない
【追記】バージョンアップで変えられるようになりました
MT4ではEAが稼働しているチャート上で右クリックを押せばパラメータを変更できましたが、MT5では変更できません。(変更できないなら、あたかも変更できるように見せかけるUIやめてほしいものです)
実行中のEAのパラメータを変更するには一度EAを停止するか、新規にEAをチャートにセットしなければなりません。
そのため、ちょっとだけパラメータを変更したい場合でも、リセットされた状態からすべて設定しなおさなければなりません。
つまり、MT4では「とりあえずEAをセットしてもらってから、後でパラメータを変更してもらう」ということができましたが、MT5ではなるべく最初の時点ですべてのパラメータをきっちり入力できるようにコーディングする必要があります。
インジケーターバッファの最大が512
MT4のインジケーターはバッファが最大8なので、どんなに頑張ってもシグナルに関連する数値は8までしか出すことができませんでした。
しかし、MT5ではバッファが512なので、サブウィンドウ上で8以上のラインを表示させることも可能です。
MQL5ではBarsが使えない
MQL4ではBars, MQL5ではChartGetInteger(Chart_ID,CHART_VISIBLE_BARS)。→バージョンアップでMQL4由来の配列も使えるようになりました。
オブジェクトの扱い
MQL5では、関数の引数のチャートIDの省略が不可になりました。
MQL5にはMarketInfoがない
MQL4ではMarketInfo(),
MQL5では返値の型に応じて関数を使い分けます。
AccountInfoInteger()~AccountInfoDouble()
MQL5ではIsDemoの返値の順番が違う
MQL4では0-ライブ 1-デモ、
MQL5では0-デモ、1-コンテスト、2-ライブ、
いや、「is demo?」がfalseでデモになるのは頭おかしい
AccountInfoInteger(ACCOUNT_TRADE_MODE)で取り出します。
MQL5のこれまでのざっくりした変遷
MQL5の発表。ただし、MQL4との互換性なしということで誰も使わない。(MQL4の関数がない)
↓
あまりに使われないのでMQL4をMQL5にちょっとずつ似せる
↓
MQL4の関数も一部使えるようになる。(オリジナル関数を作っていたコードはコンパイルでエラーを吐き出すようになる)
↓
公式が互換関数(標準ライブラリ)を実装する。
↓
ヘッジアカウントとネッティングアカウントの2タイプに分かれる。
↓
【現状】
・同じMT5なのにそれぞれの口座タイプに合わせたEAを作らなければいけなくなる。
・古いMQL5のコードは書き直しをしないとエラーでコンパイルできない。
レート取得の対応
MQL4 | MQL5 | 内容 |
---|---|---|
double Ask
|
MqlTick last_tick; SymbolInfoTick(_Symbol,last_tick); double Ask=last_tick.ask; |
SymbolInfoDouble(Symbol(),SYMBOL_ASK); でも可 |
int Bars |
int Bars=Bars(_Symbol,_Period); |
Barsは変わりなし |
double Bid
|
MqlTick last_tick; SymbolInfoTick(_Symbol,last_tick); double Bid=last_tick.bid; |
SymbolInfoDouble(Symbol(),SYMBOL_BID)でよくね? |
double Close[]
|
double Close[]; int count; // number of elements to copy ArraySetAsSeries(Close,true); CopyClose(_Symbol,_Period,0,count,Close); |
さすがに批判殺到で、 iClose(Symbol(),Period(),shift)が使えるようになりました。 |
int Digits |
int Digits=_Digits; |
Digitsは_Digitsです。 アンダーバーを忘れずに |
double High[]
|
double High[]; int count; // number of elements to copy ArraySetAsSeries(High,true); CopyHigh(_Symbol,_Period,0,count,High); |
バージョンアップでiHigh(Symbol(),Period(),shift)が使えるようになりました。 |
double Low[]
|
double Low[]; int count; // number of elements to copy ArraySetAsSeries(Low,true); CopyLow(_Symbol,_Period,0,count,Low); |
MQL4と同じようにiLow()が使えます。 |
double Open[]
|
double Open[]; int count; // number of elements to copy ArraySetAsSeries(Open,true); CopyOpen(_Symbol,_Period,0,count,Open); |
MQL4と同じようにiOpen()が使えます。 |
double Point |
double Point=_Point; |
Pointは_Pointです。 アンダーバーを忘れずに。 |
datetime Time[]
|
datetime Time[]; int count; // number of elements to copy ArraySetAsSeries(Time,true); CopyTime(_Symbol,_Period,0,count,Time); |
iTime()が使えます。 |
double Volume[]
|
long Volume[]; int count; // number of elements to copy ArraySetAsSeries(Volume,true); CopyTickVolume(_Symbol,_Period,0,count,Volume); |
iVolumeが使えます。 |
関連記事
-
-
保護中: 単体のEAコードでMT4/MT5両方でコンパイル “条件付きコンパイル”
MT4,MT5のEAの統合にはいろいろな方法があります。 手でMQL4からMQL5にコードを変換す
-
-
初心者向けMT5の使い方 インストールからバックテスト
MT5はMT4に慣れ親しんだトレーダーであれば比較的簡単に移行することができるトレードプラットフォー
-
-
株価指数両建て裁定取引のやり方と検証
日経225やダウ、SP500のチャートには強い相関関係があります。かつては「日経はニューヨークの後追
-
-
日経アメリカ株式市場アービトラージ プログラミングで説検証
よく「日本の株式市場は前日のニューヨーク市場の後追いをする」と言われています。実際に裁量トレードする
-
-
MQL4⇔MQL5 プログラム変換表
MQL4をMQL5に変換、あるいはMQL5をMQL4に書き換えるための変換表です。MQL5ではMQL