トレーダーのハック: 定義と ForEach のブレンド (#define)
— What makes you cool, bro?
— The defines make you cool, bro
(с) fxsaber
現在MQL4 で書いていて、MQL5 に切り替えたいとお考えですか。 何から始めるかを紹介します! MQL5 メタエディタ で快適に作業できるようになり、同時にMQL4の表記を使用することができます (実際には、このようなものが少し前に登場しましたが、この記事では、MQL4 関数を MQL5 に移行するメソッドについて、より完全で詳細な説明を提供したいと考えています).
良いプログラマは怠惰なプログラマです。
EAを作成すると、ほとんどの場合、多くのループのタスクが伴います。 ループはどこでも囲みます。: オーダーの検索、履歴のトレード、チャートオブジェクト、相場のシンボル、インジケーターバッファ内の足などなんでもです。 プログラマの生活を少し楽にするために、メタエディタ は、最初の文字をエントリーすると、tab キーを押した後に自動的に小さなコードに変換されます。 これがスニペットです。 これは、' for ' ループスニペットがどのように動作するかです。
悪くはないが、すべてのニーズに対して適応していません。 最もシンプルな例を考えてみましょう: すべての相場のシンボルを検索する必要があると仮定します。
int total=SymbolsTotal(true); for(int i=0;i<total;i++) { string symbol=SymbolName(i,true); PrintFormat("%d. %s",i+1,symbol); }
fes (for_each_symbol) で始まる メタエディタ スニペットを開発し、次のブロックに展開すると良いでしょう。
メタエディタ にはカスタムスニペットがないので、' define ' を適用します。 #defineマクロの置換は、目標を追求した怠惰で賢いプログラマによって考案されました。 これには読みやすさと繰り返しコードを書くことの利便性があります。
多くのプログラミング言語は、標準の for ループに加えて、 for >for(<typename> element:Collection) 、for (式の識別子の型)などのバリエーションを備えています。 次のコードを書くことができれば
for(ulong order_id in History) { working with order_id }
、プログラマーは少し楽になると思います。 インターネット上では、このアプローチに対する賛成意見と反対意見があります。 ここでは、#define のマクロと同じようなことを行う方法を紹介します。
簡単なタスクから始めましょう-すべての相場のシンボルの名前を取得します。 次のマクロを書き直します。
#define ForEachSymbol(s,i) string s; int total=SymbolsTotal(true); for(int i=0;i<total;i++,s=SymbolName(i,true)) //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- ForEachSymbol(symbol,index) { PrintFormat("%d. %s",index,symbol); } }
コンパイラは、エラーを返さないこのエントリを完全に理解します。 F5キーを押してデバッグを開始し、間違いを確認します。
1. (null) 2. GBPUSD 3. USDCHF 4. USDJPY ...
この問題は、' for ' ループ内のs = SymbolName (i, true)式が繰り返し処理後に計算され、 ' 変数が最初の繰り返しで初期化されていないことです。 ' for ' ステートメント:
' for ' ステートメントは、次の3つの式と実行可能なステートメントで構成されます。
for(expression1; expression2; expression3) |
Expression1 ループの初期化について説明します。 Expression2 —ループ完了状態を確認します。 ' true ' の場合は、 for ループの本演算子が実行されます。 expression2 が ' false ' になるまですべてが繰り返されます。 ' false ' の場合、ループは終了し、次のステートメントに制御が渡されます。 ExpressionЗは、各イテレーション後 に計算されます。
このプロセスは簡単です。 少し編集を行いましょう:
#define ForEachSymbol(s,i) string s=SymbolName(0,true); int total=SymbolsTotal(true); for(int i=1;i<total;i++,s=SymbolName(i,true)) //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- ForEachSymbol(symbol,index) { PrintFormat("%d. %s",index,symbol); //インデックス+ 1はインデックスに置き換えられています } }
そして、必要な結果を得る。 通常の 'for' ループであるかのように、シンボルとindexパラメータを持つ ForEachSymbol マクロを開発し、擬似ループ本体で変数を使用して、既に宣言されているかのように初期化しました。 したがって、 SymbolInfoXXX() 関数を使用して、相場シンボルの目的のプロパティを取得できます。 例えば:
//+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- ForEachSymbol(symbol,index) { //データの準備 double spread=SymbolInfoDouble(symbol,SYMBOL_ASK)-SymbolInfoDouble(symbol,SYMBOL_BID); double point=SymbolInfoDouble(symbol,SYMBOL_POINT); long digits=SymbolInfoInteger(symbol,SYMBOL_DIGITS); string str_spread=DoubleToString(spread/point,0); string str_point=DoubleToString(point,digits); //---表示データ Print(index,". ",symbol," spread=",str_spread," points (", digits," digits",", point=",str_point,")"); } /* Sample output 1. EURUSD spread=3 points (5 digits, point=0.00001) 2. USDCHF spread=8 points (5 digits, point=0.00001) 3. USDJPY spread=5 points (3 digits, point=0.001) 4. USDCAD spread=9 points (5 digits, point=0.00001) 5. AUDUSD spread=5 points (5 digits, point=0.00001) 6. NZDUSD spread=10 points (5 digits, point=0.00001) 7. USDSEK spread=150 points (5 digits, point=0.00001) */ }
次に、チャート上のグラフィカルオブジェクトを検索するための同様のマクロを作成します。
#define ForEachObject(name,i) string name=ObjectName(0,0); int total=ObjectsTotal(0); for(int i=1;i<=total;i++,name=ObjectName(0,i-1)) //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- ForEachObject(objectname,index) { Print(index,": objectname=\"",objectname,"\", objecttype=", EnumToString((ENUM_OBJECT)ObjectGetInteger(0,objectname,OBJPROP_TYPE))); } /* Sample output 1: objectname="H1 Arrow 61067", objecttype=OBJ_ARROW_UP 2: objectname="H1 Rectangle 31152", objecttype=OBJ_RECTANGLE 3: objectname="H1 StdDev Channel 56931", objecttype=OBJ_STDDEVCHANNEL 4: objectname="H1 Trendline 6605", objecttype=OBJ_TREND */ }
ForEachObject マクロ定義文字列が少し長くなっています。 また、1つの文字列の置換コードを理解することは困難です。 しかし、この問題は既に解決されていることが判明しました。マクロ定義は、バックスラッシュ' \' を使用して文字列に分けることができます。 結果は次のとおりです。
#define ForEachObject(name,i) string name=ObjectName(0,0); \ int ob_total=ObjectsTotal(0); \ for(int i=1;i<=ob_total;i++,name=ObjectName(0,i-1))
コンパイラでは、3つの文字列はすべて1つの長い文字列のように見えますが、わかりやすくなります。 ここでは、トレードエンティティ (オーダー、ポジション、トレード) を扱うための同様のマクロを作成する必要があります。
まずはオーダーの検索から始めましょう。 ここには、 HistorySelect() 履歴の選択関数のみが追加されます。
#define ForEachOrder(ticket,i) HistorySelect(0,TimeCurrent()); \ ulong ticket=OrderGetTicket(0); \ int or_total=OrdersTotal(); \ for(int i=1;i<or_total;i++,ticket=OrderGetTicket(i)) //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- ForEachOrder(orderticket,index) { Print(index,": #",orderticket," ",OrderGetString(ORDER_SYMBOL)," ", EnumToString((ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE))); } /* Sample output 1: #13965457 CADJPY ORDER_TYPE_SELL_LIMIT 2: #14246567 AUDNZD ORDER_TYPE_SELL_LIMIT */ }
このマクロ (および前の2つ) にはエラー処理がないことがわかります。 たとえば、HistorySelect () がfalseを返す場合はどうすればいいでしょうか。 この場合、ループ内のすべてのオーダーを通過することはできません。 その上、誰が実際に HistorySelect () 実行結果を分析するのでしょうか。 したがって、このマクロは、プログラムを開発する通常の方法に対して禁じ手も含まれています。
#define を使用する最も強力な皮肉は、マクロの置換によってコードのデバッグが許可されないということです。 これには同意するが、fxsaber が完全に固定された患者に麻酔は必要ないと言うように、 デバッグされたマクロにデバッグは必要ありません。
次に、同様の方法でポジションを検索するためのマクロを開発しましょう。
#define ForEachPosition(ticket,i) HistorySelect(0,TimeCurrent()); \ ulong ticket=PositionGetTicket(0); \ int po_total=PositionsTotal(); \ for(int i=1;i<=po_total;i++,ticket=PositionGetTicket(i-1)) //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- ForEachPosition(positionid,index) { Print(index,": ",PositionGetString(POSITION_SYMBOL)," postionID #",positionid); } /* Sample output 1: AUDCAD postionID #13234934 2: EURNZD postionID #13443909 3: AUDUSD postionID #14956799 4: EURUSD postionID #14878673 */
ヒストリーの中でトレードを検索:
#define ForEachDeal(ticket,i) HistorySelect(0,TimeCurrent()); \ ulong ticket=HistoryDealGetTicket(0); \ int total=HistoryDealsTotal(); \ for(int i=1;i<=total;i++,ticket=HistoryDealGetTicket(i-1)) //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- ForEachDeal(dealticket,index) { Print(index,": deal #",dealticket,", order ticket=", HistoryDealGetInteger(dealticket,DEAL_ORDER)); } }
履歴のオーダーの:
#define ForEachHistoryOrder(ticket,i) HistorySelect(0,TimeCurrent());\ ulong ticket=HistoryOrderGetTicket(0); \ int total=HistoryOrdersTotal(); \ for(int i=1;i<=total;i++,ticket=HistoryOrderGetTicket(i-1)) //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- ForEachHistoryOrder(historyorderticket,index) { Print(index,": #",historyorderticket); } }
1つの ForEach.mqh4 ファイル内のすべてのマクロ置換を収集します。
//+------------------------------------------------------------------+ //| ForEach.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://www.mql5.com" //+------------------------------------------------------------------+ //| Market Watch symbols search loop | //+------------------------------------------------------------------+ #define ForEachSymbol(symbol,i) string symbol=SymbolName(0,true); \ int os_total=SymbolsTotal(true); \ for(int i=1;i<os_total;i++,symbol=SymbolName(i,true)) //+------------------------------------------------------------------+ //| Chart's main window objects search loop | //+------------------------------------------------------------------+ #define ForEachObject(name,i) string name=ObjectName(0,0); \ int ob_total=ObjectsTotal(0); \ for(int i=1;i<=ob_total;i++,name=ObjectName(0,i-1)) //+------------------------------------------------------------------+ //| Active orders search loop | //+------------------------------------------------------------------+ #define ForEachOrder(ticket,i) HistorySelect(0,TimeCurrent()); \ ulong ticket=OrderGetTicket(0); \ int or_total=OrdersTotal(); \ for(int i=1;i<or_total;i++,ticket=OrderGetTicket(i)) //+------------------------------------------------------------------+ //| Open positions search loop | //+------------------------------------------------------------------+ #define ForEachPosition(ticket,i) HistorySelect(0,TimeCurrent()); \ ulong ticket=PositionGetTicket(0); \ int po_total=PositionsTotal(); \ for(int i=1;i<=po_total;i++,ticket=PositionGetTicket(i-1)) //+------------------------------------------------------------------+ //| History trades search loop | //+------------------------------------------------------------------+ #define ForEachDeal(ticket,i) HistorySelect(0,TimeCurrent()); \ ulong ticket=HistoryDealGetTicket(0); \ int dh_total=HistoryDealsTotal(); \ for(int i=1;i<=dh_total;i++,ticket=HistoryDealGetTicket(i-1)) //+------------------------------------------------------------------+ //| History orders search loop | //+------------------------------------------------------------------+ #define ForEachHistoryOrder(ticket,i) HistorySelect(0,TimeCurrent());\ ulong ticket=HistoryOrderGetTicket(0); \ int oh_total=HistoryOrdersTotal(); \ for(int i=1;i<=oh_total;i++,ticket=HistoryOrderGetTicket(i-1)) //+------------------------------------------------------------------+
注: コードで複数のマクロを使用することを決定した場合、競合がないように、各マクロの ' total ' 変数の接頭辞を追加する必要がありました。 このマクロの最大の欠点は、変数宣言を非表示にしながら、その変数を外部から表示することです。 これより、コンパイル時にエラーが検出されにくくなる可能性があります。
さらに、パラメトリックマクロを使用する場合、コンパイラは関数のヒントを与えません。 これらを使用したい場合は、6つのマクロを中心に学ぶ必要があります。 困難ではありませんが、最初のパラメータは常にループされるエンティティであるため、2番目のパラメータは常に1で始まります。
最後に、逆順で検索するマクロを追加してみましょう。 この場合、リストの最後から先頭に移動する必要があります。 マクロ名に戻るサフィックスを追加し、若干の変更を加えます。 このように相場のシンボルを検索するためのマクロのようになります。
#define ForEachSymbolBack(symbol,i) int s_start=SymbolsTotal(true)-1;\ string symbol=SymbolName(s_start,true); \ for(int i=s_start;i>=0;i--,symbol=SymbolName(i,true)) //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- ForEachSymbolBack(symbol,index) { //データの準備 double spread=SymbolInfoDouble(symbol,SYMBOL_ASK)-SymbolInfoDouble(symbol,SYMBOL_BID); double point=SymbolInfoDouble(symbol,SYMBOL_POINT); long digits=SymbolInfoInteger(symbol,SYMBOL_DIGITS); string str_spread=DoubleToString(spread/point,0); string str_point=DoubleToString(point,digits); //---出力データ Print(index,". ",symbol," spread=",str_spread," points (", digits," digits",", point=",str_point,")"); } /* Sample output 3. USDJPY spread=5 points (3 digits, point=0.001) 2. USDCHF spread=8 points (5 digits, point=0.00001) 1. GBPUSD spread=9 points (5 digits, point=0.00001) 0. EURUSD spread=2 points (5 digits, point=0.00001) */ }
ご覧のとおり、 index変数の値は、ここでsize-1から0に変更されます。 #defineを完了しました。 MQL4 スタイルのアクセスの関数を開発しましょう。
1. この記事では、MQL4 関数のグループについて説明します。
注:Point, Digits、Bar、Point(), Digits()、Bar(Symbol(),Period()などの変数を置換します。
MQL4 AccountXXXX、MQL4 MarketInfo、MQL4 状態チェックおよび MQL4 定義済み変数グループは MQL5 に変換されます。 したがって、4つのファイル: AccountInfo.mqh、 MarketInfo.mqh、Check.mqhおよびPredefined.mqh が [data folder]\ MQL5\Include.SimpleCall. に追加されます。 フォルダにはMQL4 関数を MQL5 に変換するための7つのファイルがあります: AccountInfo.mqh, Check.mqh, IndicatorsMQL4.mqh, IndicatorsMQL5.mqh, MarketInfo.mqh, Predefined.mqh、 Series.mqh
これらのファイルをすべてインクルードする必要があります。 また、制限に注意してください: IndicatorsMQL4.mqhとIndicatorsMQL5.mqhファイルを一緒に含めることはできません-いずれか片方を選択することができます。 したがって、フォルダは2つのファイルを備えています: SimpleCallMQL4.mqh は、すべてのファイルに加えて、 IndicatorsMQL4.mqhが含まれています。SimpleCallMQL5.mqhは、すべてのファイルに加えて IndicatorsMQL5.mqhが含まれています。
MACD Sample.mq4に基づいた例
MACD Sample.mq4をMQL5フォルダにEAとともにコピーします。例えば、[data folder]\MQL5\Experts\で、拡張子をmq5にします。 したがって、 MACD Sample.mq5ファイルを取得します。 コンパイルして、59エラーと1つのアラートを取得します。
ここで、SimpleCallMQL4.mqh を接続します。
//+------------------------------------------------------------------+ //| MACD Sample.mq4 | //| Copyright 2005-2014, MetaQuotes Software Corp. | //| http://www.mql4.com | //+------------------------------------------------------------------+ #property copyright "2005-2014, MetaQuotes Software Corp." #property link "http://www.mql4.com" //--- #include<SimpleCallSimpleCallMQL4.mqh></SimpleCallSimpleCallMQL4.mqh> //--- input double TakeProfit =50;
もう一度コンパイルして、39エラーと1つのアラートを得ます。 ここで、BarsをBars(Symbol(),Period())に、PointをPoint()に手動で置き換えます。(Ctrl + H) コンパイル 35エラーが残ります。 これらはすべてのトレード関数に関連しています。 しかし、この記事ではトレード関数についてはしません。
1.1. MQL4 AccountXXXX
MQL4 AccountXXXX 関数は、[date folder]\MQL5\Include\SimpleCall\AccountInfo.mqhで変換されます。
MQL5 と一致する MQL4 関数の表は、次のようになります。
MQL4 | MQL5 AccountInfoXXXX | MQL5 CAccountInfo | Notes |
---|---|---|---|
AccountInfoDouble | AccountInfoDouble | CAccountInfo::InfoDouble or CAccountInfo::double methods | |
AccountInfoInteger | AccountInfoInteger | CAccountInfo::InfoInteger or CAccountInfo::integer methods | |
AccountInfoString | AccountInfoString | CAccountInfo::InfoString or CAccountInfo::text methods | |
AccountBalance | AccountInfoDouble(ACCOUNT_BALANCE) or | CAccountInfo::Balance | |
AccountCredit | AccountInfoDouble(ACCOUNT_CREDIT) or | CAccountInfo::Credit | |
AccountCompany | AccountInfoString(ACCOUNT_COMPANY) or | CAccountInfo::Company | |
AccountCurrency | AccountInfoString(ACCOUNT_CURRENCY) or | CAccountInfo::Currency | |
AccountEquity | AccountInfoDouble(ACCOUNT_EQUITY) or | CAccountInfo::Equity | |
AccountFreeMargin | AccountInfoDouble(ACCOUNT_FREEMARGIN) or | CAccountInfo::FreeMargin | |
AccountFreeMarginCheck | --- /--- | CAccountInfo::FreeMarginCheck | |
AccountFreeMarginMode | 同等のものなし | 同等のものなし | |
AccountLeverage | AccountInfoInteger(ACCOUNT_LEVERAGE) | CAccountInfo::Leverage | MQL4 ではint 型で、MQL5ではlongです |
AccountMargin | AccountInfoDouble(ACCOUNT_MARGIN) | CAccountInfo::Margin | |
AccountName | AccountInfoString(ACCOUNT_NAME) | CAccountInfo::Name | |
AccountNumber | AccountInfoInteger(ACCOUNT_LOGIN) | CAccountInfo::Login | MQL4 ではint 型で、MQL5ではlongです |
AccountProfit | AccountInfoDouble(ACCOUNT_PROFIT) | CAccountInfo::Profit | |
AccountServer | AccountInfoString(ACCOUNT_SERVER) | CAccountInfo::Server | |
AccountStopoutLevel | AccountInfoDouble(ACCOUNT_MARGIN_SO_SO) | CAccountInfo::MarginStopOut | MQL4ではint 型で、MQL5ではdoubleです |
AccountStopoutMode | AccountInfoInteger(ACCOUNT_MARGIN_SO_MODE) | CAccountInfo::StopoutMode |
MQL5 はMQL4 AccountFreeMarginModeには相当するものはないのでご注意ください。 自身の責任でさらにMQL4 AccountFreeMarginModeを使用することもできます。 MQL4 AccountFreeMarginModeを検出すると、アラートがログに送信され、NaN ( "not 番号 ") が返されます。
他の MQL4 AccountXXXX 関数については、AccountInfoXXXX または via CAccountInfo トレーディングクラスを介して2つのバージョンに相当します。 AccountInfoDouble、AccountInfoInteger、AccountInfoString については、MQL4 と MQL5 の差はありません。
ファイルヘッダには、次のコードがあります。どのように動作するでしょうか?
//--- #define OP_BUY ORDER_TYPE_BUY #define OP_SELL ORDER_TYPE_SELL //---現在のアカウントの残高を返します
まず、 #define ヘルプを見てみましょう。
#define ディレクティブを使用して、ニーモニック名を定数に割り当てることができます。 これには次の2つの形式があります。
#define identifier expression //パラメータなしフォーム #define identifier(par1,... par8) expression //パラメトリックフォーム
#define ディレクティブは、ソーステキスト内の ' 識別子 ' のすべてのさらに見つかったエントリの ' 式 ' を置き換えます。
今回のコードの場合、この説明は次のようになります (パラメータなしのフォームを使用しました)。 #define ディレクティブはORDER_TYPE_BUYまたはOP_BUYのさらに見つかったすべてのエントリをソーステキストに置き換えます。 #define ディレクティブは、 ORDER_TYPE_SELLをソーステキスト内のOP_SELLのすべてのさらに見つかったエントリに置き換えます。 言い換えると、[date folder]\MQL5\Include\SimpleCall\AccountInfo.mqhを MQL5 EA にインクルードした後、使い慣れた方法でOP_BUYとOP_SELLの MQL4 型を使用することができます。 このコンパイルはエラーを返しません。
MQL5 のタイプにもたらす関数の最初のグループ: AccountBalance ()、AccountCredit ()、AccountCompany ()、AccountCurrency ()、AccountEquity () と AccountFreeMargin ():
//---現在のアカウントの残高を返します #define AccountBalance(void) AccountInfoDouble(ACCOUNT_BALANCE) //---現在のアカウントの信用残高を返します #define AccountCredit(void) AccountInfoDouble(ACCOUNT_CREDIT) //---現在のアカウントが登録された証券会社名を返します。 #define AccountCompany(void) AccountInfoString(ACCOUNT_COMPANY) //---現在のアカウントの通貨名を返します。 #define AccountCurrency(void) AccountInfoString(ACCOUNT_CURRENCY) //---現在のアカウントの残高を返します #define AccountEquity(void) AccountInfoDouble(ACCOUNT_EQUITY) //---現在のアカウントのフリーマージン値を返します。 #define AccountFreeMargin(void) AccountInfoDouble(ACCOUNT_MARGIN_FREE)ここでは、MQL4 XXXX (void) 関数では、パラメータ #define フォームを使用して、 "void " がパラメータとして機能します。 簡単にチェックすることができます。: "ボイド "削除する場合 , コンパイル時に予期しないマクロ形式のパラメータリストのエラーを取得します:
MQL4 AccountFreeMarginCheck の場合には、異なる動作をします-AccountFreeMarginCheck は、その内部で使用される MQL5 コードだけを持つ通常の関数として設定します。
//---指定されたオーダーが開かれた後に残るフリーマージンを返します。 //(現在のアカウントの現在の価格) double AccountFreeMarginCheck(string symbol,int cmd,double volume) { double price=0.0; ENUM_ORDER_TYPE trade_operation=(ENUM_ORDER_TYPE)cmd; if(trade_operation==ORDER_TYPE_BUY) price=SymbolInfoDouble(symbol,SYMBOL_ASK); if(trade_operation==ORDER_TYPE_SELL) price=SymbolInfoDouble(symbol,SYMBOL_BID); //--- double margin_check=EMPTY_VALUE; double margin=EMPTY_VALUE; margin_check=(!OrderCalcMargin(trade_operation,symbol,volume,price,margin))?EMPTY_VALUE:margin; //--- return(AccountInfoDouble(ACCOUNT_FREEMARGIN)-margin_check); }
上記で述べたように、AccountFreeMarginMode には MQL5 に相当するものがないため、AccountFreeMarginMode がコード内で検出された場合には、 "番号は " ではなく、アラートが発行されます。
//---現在のアカウントでオーダーを開くことができるフリーマージンの計算モードを返します。 double AccountFreeMarginMode(void) { string text="MQL4 functions \"AccountFreeMarginMode()\" has no analogs in MQL5. Returned \"NAN - not a number\""; Alert(text); Print(text); return(double("nan")); }
その他の MQL4 関数については、次のような方法で進めます。
//---現在のアカウントのレバレッジを返します #define AccountLeverage(void) (int)AccountInfoInteger(ACCOUNT_LEVERAGE) //---現在のアカウントのマージン値を返します。 #define AccountMargin(void) AccountInfoDouble(ACCOUNT_MARGIN) //---現在のアカウント名を返します。 #define AccountName(void) AccountInfoString(ACCOUNT_NAME) //---現在のアカウント番号を返します。 #define AccountNumber(void) (int)AccountInfoInteger(ACCOUNT_LOGIN) //---現在のアカウントの利益を返します。 #define AccountProfit(void) AccountInfoDouble(ACCOUNT_PROFIT) //---接続されているサーバー名を返します。 #define AccountServer(void) AccountInfoString(ACCOUNT_SERVER) //---ストップアウトレベルの値を返します。 #define AccountStopoutLevel(void) (int)AccountInfoDouble(ACCOUNT_MARGIN_SO_SO) //---ストップアウトレベルの計算モードを返します。 int AccountStopoutMode(void) { ENUM_ACCOUNT_STOPOUT_MODE stopout_mode=(ENUM_ACCOUNT_STOPOUT_MODE)AccountInfoInteger(ACCOUNT_MARGIN_SO_MODE); if(stopout_mode==ACCOUNT_STOPOUT_MODE_PERCENT) return(0); return(1); }
1.2. MQL4 MarketInfo
MQL4 MarketInfotXXXX 関数は、[date folder]\MQL5\Include\SimpleCall\MarketInfo.mqhで変換されます。
MQL4 MarketInfo は double 型ですが、MQL5 では、MarketInfo が SymbolInfoInteger () に存在し (' long ' 型を取得)、SymbolInfoDouble () では ' double ' 型を取得します。 ここでは、MarketInfo (型変換) の廃止された関数を使用することの不便さを明確に確認できます。 MQL5のSYMBOL_DIGITSの例を次に示します。
MarketInfo(Symbol(),MODE_DIGITS) <= (double)SymbolInfoInteger(symbol,SYMBOL_DIGITS)
1.2.1 MQL4 MODE_TRADEALLOWED とのあいまいさ
MQL4 では、これはシンプルな ' bool ' フラグであり、MQL5 では、シンボルは複数の種類の許可/禁止を持つことができます。
ENUM_SYMBOL_TRADE_MODE
ID | Description |
---|---|
SYMBOL_TRADE_MODE_DISABLED | シンボルのトレードを無効にする |
SYMBOL_TRADE_MODE_LONGONLY | 買いのみを有効にする |
SYMBOL_TRADE_MODE_SHORTONLY | 売りのみ有効にする |
SYMBOL_TRADE_MODE_CLOSEONLY | 決済のみを有効にする |
YMBOL_TRADE_MODE_FULL | トレード制限なし |
SYMBOL_TRADE_MODE_DISABLED の場合にのみ ' false' にし、部分的な制限または完全なアクセスの場合にのみ ' true 'にします。 (アラートと部分的な制限のPrintアラートを伴う)。
1.2.2. MQL4 MODE_SWAPTYPE とのあいまいさ
MQL4 では、MODE_SWAPTYPE は4つの値のみを返します (スワップ計算メソッド。 0—point; 1—シンボルベースの通貨; 2—%; 3-資金通貨) MQL5 では、ENUM_SYMBOL_SWAP_MODE 列挙には、MQL4 のような値を持つ9つの値があります。
MQL4 MODE_SWAPTYPE | MQL5 ENUM_SYMBOL_SWAP_MODE |
---|---|
同等のものなし | SYMBOL_SWAP_MODE_DISABLED |
0-ポイント | SYMBOL_SWAP_MODE_POINTS |
1-シンボルの基本通貨 | SYMBOL_SWAP_MODE_CURRENCY_SYMBOL |
3-余剰資金通貨 | SYMBOL_SWAP_MODE_CURRENCY_MARGIN |
同等のものなし | SYMBOL_SWAP_MODE_CURRENCY_DEPOSIT |
2-% | SYMBOL_SWAP_MODE_INTEREST_CURRENT |
2-% | SYMBOL_SWAP_MODE_INTEREST_OPEN |
同等のものなし | SYMBOL_SWAP_MODE_REOPEN_CURRENT |
同等のものなし | SYMBOL_SWAP_MODE_REOPEN_BID |
MQL5 では、スワップの計算には次の2つのオプションがあります。
/*
SYMBOL_SWAP_MODE_INTEREST_CURRENT
スワップは、計算の時点で、年間% でチャージされています (バンキングモードは360日/年です)
SYMBOL_SWAP_MODE_INTEREST_OPEN
スワップは、シンボルによるポジションオープニング価格の年間% でチャージされます (バンキングモードは360日/年です)
*/
一つのタイプとして考えてみましょう。 MQL4 に等価なものがない他の MQL5 オプションについては、アラートが出され、 "not 番号 " が返されます。
1.2.3. MQL4 MODE_PROFITCALCMODE と MODE_MARGINCALCMODE とのあいまいさ
MQL4 では、MODE_PROFITCALCMODE (利益計算メソッド) は3つの値だけを返します: 0-外国為替;1— CFD;2-先物、また、MODE_MARGINCALCMODE (マージンの計算メソッド) 4つ返します。: 0-外国為替;1— CFD;2—先物;3-インデックスのCFD。 MQL5 では、ツール (ENUM_SYMBOL_CALC_MODE 列挙) の余剰資金計算メソッドを定義することができますが、利益を計算するメソッドはありません。 MQL5において、余剰資金を計算するメソッドは利益計算メソッドと等しく、したがって、MQL4 MODE_PROFITCALCMODE と MODE_MARGINCALCMODE に MQL5 ENUM_SYMBOL_CALC_MODE と同様の値が返されると仮定しています。
MQL5 ENUM_SYMBOL_CALC_MODE 列挙体には10のメソッドがあります。 MQL4 MODE_PROFITCALCMODE を MQL5 ENUM_SYMBOL_CALC_MODE と比較してみましょう:
MQL4 MODE_PROFITCALCMODE "Forex" <==> MQL5 SYMBOL_CALC_MODE_FOREX
MQL4 MODE_PROFITCALCMODE "CFD" <==> MQL5 SYMBOL_CALC_MODE_CFD
MQL4 MODE_PROFITCALCMODE "Futures" <==> MQL5 SYMBOL_CALC_MODE_FUTURES
MQL4 に等価を持たないその他の MQL5 オプションについては、アラートが発行され、 "not 番号 " が返されます。
... #define MODE_PROFITCALCMODE 1000//SYMBOL_TRADE_CALC_MODE #define MODE_MARGINCALCMODE 1001//SYMBOL_TRADE_CALC_MODE ... case MODE_PROFITCALCMODE: { ENUM_SYMBOL_CALC_MODE profit_calc_mode=(ENUM_SYMBOL_CALC_MODE)SymbolInfoInteger(symbol,SYMBOL_TRADE_CALC_MODE); switch(profit_calc_mode) { case SYMBOL_CALC_MODE_FOREX: return((double)0); case SYMBOL_CALC_MODE_FUTURES: return((double)2); case SYMBOL_CALC_MODE_CFD: return((double)1); default : { string text="MQL4 MODE_PROFITCALCMODE returned MQL5 "+EnumToString(profit_calc_mode); Alert(text); Print(text); return(double("nan")); } } } case MODE_MARGINCALCMODE: { ENUM_SYMBOL_CALC_MODE profit_calc_mode=(ENUM_SYMBOL_CALC_MODE)SymbolInfoInteger(symbol,SYMBOL_TRADE_CALC_MODE); switch(profit_calc_mode) { case SYMBOL_CALC_MODE_FOREX: return((double)0); case SYMBOL_CALC_MODE_FUTURES: return((double)2); case SYMBOL_CALC_MODE_CFD: return((double)1); default : { string text="MQL4 MODE_MARGINCALCMODE returned MQL5 "+EnumToString(profit_calc_mode); Alert(text); Print(text); return(double("nan")); } } }
1.3. MQL4 ステータスチェック
ステータスチェック MQL4 関数は、[date folder]\MQL5\Include\SimpleCall\Check.mqhで変換されます。
次の要素が含まれます。
- Digits
- Point
- IsConnected
- IsDemo
- IsDllsAllowed
- IsExpertEnabled
- IsLibrariesAllowed
- IsOptimization
- IsTesting
- IsTradeAllowed
- IsTradeContextBusy
- IsVisualMode
- TerminalCompany
- TerminalName
- TerminalPath
MQL4 | MQL5 | MQL5 classes | Note |
---|---|---|---|
Digits | MQL4 は、DigitsとDigits()を同時に使用することができます | ||
Point | MQL4は、PointとPoint()を同時に使用可能 | ||
IsConnected | TerminalInfoInteger(TERMINAL_CONNECTED) | CTerminalInfo::IsConnected | |
IsDemo | AccountInfoInteger(ACCOUNT_TRADE_MODE) | CAccountInfo::TradeMode | ENUM_ACCOUNT_TRADE_MODE 列挙体から値の1つを取得します。 |
IsDllsAllowed | TerminalInfoInteger (TERMINAL_DLLS_ALLOWED) と MQLInfoInteger (MQL_DLLS_ALLOWED) | CTerminalInfo::IsDLLsAllowed | TERMINAL_DLLS_ALLOWED は、MQL_DLLS_ALLOWED を無視することができますが、高い状態です |
IsExpertEnabled | TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) | CTerminalInfo::IsTradeAllowed | ターミナルの AutoTrading ボタンの状態 |
IsLibrariesAllowed | MQLInfoInteger(MQL_DLLS_ALLOWED) | -/- | このチェックは無意味です: プログラムがDLLを適用し、を使用することを許可しない場合 (プログラムの依存関係タブ), プログラムを実行することができなくなります. |
IsOptimization | MQLInfoInteger(MQL_OPTIMIZATION) | -/- | MQL5 プログラムには、デバッグ、コードプロファイリング、テスター、最適化の4つのモードがあります。 |
IsTesting | MQLInfoInteger(MQL_TESTER) | -/- | MQL5 プログラムには、デバッグ、コードプロファイリング、テスター、最適化の4つのモードがあります。 |
IsTradeAllowed | MQLInfoInteger(MQL_TRADE_ALLOWED) | -/- | [プログラムのプロパティ] の [自動トレードを許可する] チェックボックスの状態 |
IsTradeContextBusy | -/- | -/- | "false " が返されます |
IsVisualMode | MQLInfoInteger(MQL_VISUAL_MODE) | MQL5 プログラムには、デバッグ、コードプロファイリング、テスター、最適化の4つのモードがあります。 | |
TerminalCompany | TerminalInfoString(TERMINAL_COMPANY) | CTerminalInfo::Company | |
TerminalName | TerminalInfoString(TERMINAL_NAME) | CTerminalInfo::Name | |
TerminalPath | TerminalInfoString(TERMINAL_PATH) | CTerminalInfo::Path |
MQL4 と MQL5 は完全にここで混乱しているので、最初の2点 (DigitsとPoint) は実装できません。 特に、MQL4では、DigitsとDigits()と同時に、PointとPoint()に直面するかもしれません。 例えば、私はどのようにDigitsとDigits()をDigits()にdefineを使って変換する方法を知りません。 残りは XXXX () として見つかり、MQL5 同等物に置き換えることができます。
実装は次のとおりです。
//+------------------------------------------------------------------+ //| Check.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| http://wmua.ru/slesar/ | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "http://wmua.ru/slesar/" #property version "1.003" //---クライアントターミナルとサーバー間の接続を確認します。 #define IsConnected (bool)TerminalInfoInteger(TERMINAL_CONNECTED) //---EAがデモアカウントで実行されているかどうかを確認します #define IsDemo (bool)(AccountInfoInteger(ACCOUNT_TRADE_MODE)==(ENUM_ACCOUNT_TRADE_MODE)ACCOUNT_TRADE_MODE_DEMO) //---EAに対して DLL 関数呼び出しが許可されているかどうかを確認します #define IsDllsAllowed (bool)TerminalInfoInteger(TERMINAL_DLLS_ALLOWED) //EAが有効になっているかどうかをチェック #define IsExpertEnabled (bool)TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) //EAがライブラリ関数を呼び出すことができるかどうかをチェック #define IsLibrariesAllowed (bool)MQLInfoInteger(MQL_DLLS_ALLOWED) //EAがストラテジーテスター最適化モードで実行されているかどうかをチェック #define IsOptimization (bool)MQLInfoInteger(MQL_OPTIMIZATION) //---エキスパートアドバイザがテストモードで実行されているかどうかを確認します #define IsTesting (bool)MQLInfoInteger(MQL_TESTER) //EAがトレードを許可され、トレードコンテキストがビジーでないかどうかをチェック #define IsTradeAllowed (bool)MQLInfoInteger(MQL_TRADE_ALLOWED) //---トレードコンテキストに関する情報を返します。 #define IsTradeContextBusy false //---エキスパートアドバイザがビジュアルモードでテストされているかどうかを確認します。 #define IsVisualMode (bool)MQLInfoInteger(MQL_VISUAL_MODE) //---クライアントターミナルの会社の名前を返します。 #define TerminalCompany TerminalInfoString(TERMINAL_COMPANY) //クライアントのターミナル名を返す #define TerminalName TerminalInfoString(TERMINAL_NAME) //---クライアントターミナルが起動されたディレクトリを返します。 #define TerminalPath TerminalInfoString(TERMINAL_PATH) //+------------------------------------------------------------------+
1.4. MQL4 定義済み変数
[data folder]\MQL5\Include\SimpleCall\Predefined.mqhで実装
MQL4 定義済みの変数:
- _Digits
- _Point
- _LastError
- _Period
- _RandomSeed
- _StopFlag
- _Symbol
- _UninitReason
- Ask
- Bars
- Bid
- Close
- Digits
- High
- Low
- Open
- Point
- Time
- Volume
_XXXX 定義済み変数は、#define 非パラメトリック形式を使用して MQL5 関数に変換されます。
//_Digits 変数には小数点以下の桁数が格納されている #define _Digits Digits() //_Point 変数には、クオート通貨で現在のシンボルのポイントサイズが含まれています #define _Point Point() //_LastError 変数に最後のエラーのコードが含まれています #define _LastError GetLastError() //_Period 変数に、現在のチャートのタイムフレームの値が含まれています #define _Period Period() //#define _RandomSeed //_StopFlag 変数に、プログラムストップのフラグが含まれています #define _StopFlag IsStopped() //_Symbol 変数に現在のチャートのシンボル名が含まれています #define _Symbol Symbol() //_UninitReason 変数にプログラムのコードが含まれている #define _UninitReason UninitializeReason() //#define Bars Bars(Symbol(),Period()); //#define Digits //#define Point
唯一の例外は "_RandomSeed " に対して行われ、この変数は疑似乱数整数ジェネレーターの現在の状態を格納します。 MQL5 のBarsに変換することはできません (より正確には、Barsは手動で交換するために残されます)。 DigitsとPointの解決方法はありません。 上記のように、DigitsとDigits()だけでなく、PointとPoint()は、同時にテキスト中にあります。
MQL4 Ask および Bid は、カスタム (自己書き込み) GetAsk () および GetBid () 関数に置き換えられます。
//現在のシンボルの最も最近の知られていた売り価格 (ask価格) #define Ask GetAsk() //現在のシンボルの最新の既知の買い価格 (bid価格) #define Bid GetBid() ... //現在のシンボルの最も最近の知られていた売り価格 (ask価格) double GetAsk() { MqlTick tick; SymbolInfoTick(Symbol(),tick); return(tick.ask); } //現在のシンボルの最新の既知の買い価格 (bid価格) double GetBid() { MqlTick tick; SymbolInfoTick(Symbol(),tick); return(tick.bid); }
もちろん、簡単な方法で Ask のマクロを書くことができます:
#define Ask SymbolInfoDouble(__Symbol,SYMBOL_ASK)
しかし、 SymbolInfoDoubleは次のとおりです。
最後のティックでデータを取得するために関数が使用されている場合は、 SymbolInfoTick ()を適用することをお勧めします。ターミナルがトレード口座に接続されているため、シンボルにクオートがない可能性があります。 この場合、リクエストされた値は未定義になります。
ほとんどの場合、 SymbolInfoTick ()関数を使用すれば、1回の呼び出しごとに、最後のティックの到着時刻と同様に、Ask、Bid、最後の値、およびボリュームを取得できます。
ここで、新しいものを紹介しましょう "演算子" を使用します。
' 演算子 ' キーワードは、[] 指数化演算子をオーバーロード (再割り当て) するために使用されます。 MQL5 形式で MQL4 時系列配列 (Open []、High []、Low []、Close[]、Time []、Volume []) を変換するために必要です。
このヘルプでは、 "演算子" について説明します。
操作のオーバーロードを使用すると、複雑なオブジェクトの構造体とクラスに対して、(シンプルな式の形式で記述された) 操作記法を使うことができます。
したがって、[] 指数化演算子をオーバーロードするクラスを作成する必要があると仮定できます。
Remembering #define:
#define ディレクティブを使用して、ニーモニック名を定数に割り当てることができます。 これには次の2つの形式があります。
#define identifier expression //パラメータなしフォーム #define identifier(par1,... par8) expression //パラメトリックフォーム
#define ディレクティブは、ソーステキスト内の ' 識別子 ' のすべてのさらに見つかったエントリの ' 式 ' を置き換えます。
次のコードを読み取ることができます: #define ディレクティブは、ソーステキスト内のさらに見つかったすべてのエントリに対して 159 を置き換えます。
#define SeriesVolume(Volume,T) 159 //+-----------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { long a=SeriesVolume(Volume,long); }
つまり、OnStart のコードは次のように変換されます。
long a=159;
ステップ2
ここでは、クラス全体が #define に配置されています。
//+------------------------------------------------------------------+ //| Test_en.mq5 | //| Copyright 2012, CompanyName | //| http://www.companyname.net | //+------------------------------------------------------------------+ #define SeriesVolume(Volume,T) class CVolume \ { \ public: \ T operator[](const int i) const \ { \ long val[1]; \ if(CopyTickVolume(Symbol(),Period(),i,1,val)==1)\ return(val[0]); \ else \ return(-1); \ } \ }; CVolume Volume; //--- SeriesVolume(Volume,long) //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { Print(Volume[4]); } //+------------------------------------------------------------------+
この置換は、次のように表すことができます。
//+------------------------------------------------------------------+ //| Test_en.mq5 | //| Copyright 2012, CompanyName | //| http://www.companyname.net | //+------------------------------------------------------------------+ class CVolume { public: long operator[](const int i) const { long val[1]; if(CopyTickVolume(Symbol(),Period(),i,1,val)==1) return(val[0]); else return(-1); } }; CVolume Volume; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { Print(Volume[4]); }
つまり、OnStart では、実際には CVolume クラスの Volume オブジェクトを参照し、[] メソッドには、i インデックスを渡します。 同じ原理は、Open,High,Low,Close,Timeシリーズに適用されます。
最後に、 MQL4 iXXX 関数: iOpen、iHigh、iLow、iClose、iTime と iVolume があります。 カスタム関数宣言メソッドを適用する必要があります。
iClose の例:
//---指定されたシンボルの足に対して、時間枠とシフトで終値の値を返します。 double iClose( string symbol, //シンボル ENUM_TIMEFRAMES timeframe, //期間 int shift //シフト ) { double result=0.0; //--- double val[1]; ResetLastError(); int copied=CopyClose(symbol,timeframe,shift,1,val); if(copied>0) result=val[0]; else Print(__FUNCTION__,": CopyClose error=",GetLastError()); //--- return(result); }
2. その他のファイルの変更
[データフォルダ] \MQL5\Include\SimpleCallIndicatorsMQL4mqh では、すべての MQL4 行の名前がヘッダーに設定されました。
double NaN=double("nan"); #defineMODE_MAIN 0 #defineMODE_SIGNAL 1 #defineMODE_PLUSDI 1 #defineMODE_MINUSDI 2 #defineMODE_GATORJAW 1 #defineMODE_GATORTEETH 2 #defineMODE_GATORLIPS 3 #defineMODE_UPPER 1 #defineMODE_LOWER 2 #defineMODE_TENKANSEN 1 #defineMODE_KIJUNSEN 2 #defineMODE_SENKOUSPANA 3 #defineMODE_SENKOUSPANB 4 #defineMODE_CHIKOUSPAN 5
[データフォルダ]\MQL5\Include\SimpleCallSeries.mqhで、削除しました
#define MODE_OPEN 0 //#define MODE_LOW 1 //#define MODE_HIGH 2 #defineMODE_CLOSE 3 #defineMODE_VOLUME 4 //#define MODE_TIME 5
[データフォルダ]\MQL5\Include\SimpleCall\Header.mqhに書かれています
//+------------------------------------------------------------------+ //| Header.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| http://wmua.ru/slesar/ | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "http://wmua.ru/slesar/" //--- #defineMODE_LOW 10001 #defineMODE_HIGH 10002 #defineMODE_TIME 10005
iClose、iHigh、iLow、iOpen、iTime、 iVolume-[データフォルダ]\MQL5\Include\Simple\CallPredefined.mqh に設定されています
結論
前の記事では、MQL4 スタイルでインジケータの呼び出しを記述する方法について説明し、その結果について説明しました。 記述のシンプルさは、EAの実行の減速が組み込まれている上に、作成済みインジケーターに対して制御系がないことが判明しました。 この記事では、コードを簡略化する方法を探し続け、#define マクロの置換を見てきました。
その結果、この記事に添付されているコードを使用して、MetaTrader5 でほぼすべての MQL4 EA のタスクを行うことができます。 必要な関数と定義済みの変数をオーバーロードまたは追加する必要があるファイルだけをインクルードする必要があります。
完全な互換性にためには、MQL4特有の簡単なトレード関数が残っています。 しかし、この問題は同様に解決することができます。 結論として、このアプローチの長所と短所を再度繰り返してみましょう:
結果:
- インジケーターにアクセスするときに返されるエラーを処理する場合の制限。
- 複数のインジケータに同時にアクセスする場合は、テスト速度が落ちます。
- IndicatorsMQL5.mqh または IndicatorsMQL4.mqh が接続されているかどうかに応じて、インジケータラインを正しくハイライト表示する必要があります。
- #define マクロの置換をデバッグできません。
- パラメトリック #define 引数のツールヒントはありません。
- マクロの背後に隠された変数の潜在的な干渉。
- コード記述の簡潔さ-複数のもの代わりに1つの文字列;
- 可視性と簡潔—コード量が少ないほど、理解しやすくなります。
- マクロの置換は赤で強調表示され、ユーザーIDや関数を確認することができます。
- カスタムスニペットの等価性を開発する関数。
私自身は、古典的なMQL5 アプローチの信奉者であり、ライフハックとしてこの記事に記載されているメソッドの利用を検討しています。 おそらくこの記事は、MetaTrader5プラットフォームへの移行の心理的障壁を克服することを目的とし、MQL4 スタイルでコードを書くことに慣れた人にとって便利なものとなるでしょう。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/4332
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索