モスクワ証券取引所(MOEX)の為のトレードロボット作成は何から始めたら良いか
モスクワ証券取引所のトレーダーの多くが、自分のトレードアルゴリズムを自動化したいと考えていても、何から始めたら良いかわからずにいます。MQL5言語は膨大な取引機能を提供するだけでなく、アルゴトレードにおける最初の一歩を最大限に簡単なものにするクラスも提供しています。この記事では、MQL5取引戦略言語が、モスクワ証券取引所のアルゴリズムトレーダーに提供しているツールをご紹介します。
モスクワ証券取引所(MOEX)の2つの取引注文方法
モスクワ証券取引所(MOEX)は、成行注文とリミット注文の2つの取引注文方法をサポートしています。 - 成行注文は、すぐに取引所に送信され、最も良い価格で実行されます。これらの注文の送信には、MQL5では成行注文タイプのORDER_TYPE_BUYとORDER_TYPE_SELLが使用されます。
- リミット注文は、取引所のサーバーに保存され、当て嵌まる注文が現れた時にすぐさま取引が実行されます。MQL5言語ではこれらの注文は、ORDER_TYPE_BUY_LIMITとORDER_TYPE_SELL_LIMITの注文タイプが対応しています。
成行注文は(いつもではないが)取引の実行が保証されますが、価格は保証されません。これはつまり、取引操作の結果として、現在の条件とは大きく異なる価格で取引が実行されることがあるということです。同時に、リミット注文は価格は保証されますが、(市場の価格がその価格になって)その価格で取引が実行されるという保証はありません。結果的に注文の実行を待ち切れずに、市場外に留まったままということもあります。
モスクワ証券取引所のトレーダーに提供されるその他の注文タイプは全て、トレーダーが取引所と相互作用するソフトウェアシステムの一部です。言い換えるなら。他の注文はアルゴリズムということです。これはモスクワ証券取引所の外で処理および保存され、内部処理の結果、成行またはリミット注文としてモスクワ証券取引所に送信されます。
MetaTrader 5プラットフォームは、モスクワ証券取引所での取引の為に使用できる次の取引注文タイプを提供しています。
識別子 | 説明 | 保存と実行 |
ORDER_TYPE_BUY | 成行き買い注文 | 現在の最も良い売り価格で成行き買い注文として取引所に送信されます |
ORDER_TYPE_SELL | 成行き売り注文 | 現在の最も良い買い価格で成行き売り注文として取引所に送信されます |
ORDER_TYPE_BUY_LIMIT | 指値注文 Buy Limit | 指値買い注文として取引所に送信され、指定したまたはそれ以上に良い価格の売りが発生した場合に決済されます。 |
ORDER_TYPE_SELL_LIMIT | 指値注文 Sell Limit | 指値売り注文として取引所に送信され、指定したまたはそれ以上に良い価格の売りが発生した場合に決済されます。 |
ORDER_TYPE_BUY_STOP | 指値注文 Buy Stop | MetaTrader 5のサーバーに保存され、発動時に取引所へ送信されます。
|
ORDER_TYPE_SELL_STOP | 指値注文 Sell Stop | MetaTrader 5のサーバーに保存され、発動時に取引所へ送信されます。
|
ORDER_TYPE_BUY_STOP_LIMIT | 指値注文 BUY STOP LIMIT | MetaTrader 5のサーバーで保存され、発動時に指値買い注文が取引所へ送信されます |
ORDER_TYPE_SELL_STOP_LIMIT | 指値注文 SELL STOP LIMIT | MetaTrader 5のサーバーで保存され、発動時に指値売り注文が取引所へ送信されます |
MetaTrader 5プラットフォームの未決ポジションに、MetaTrader 5の取引サーバーに保存され、取引口座への接続がない場合でも自動的に作動するテイクプロフィット/ストップロスレベルを指定することができます。
- テイクプロフィットレベルは、有利な方向でのポジション決済の価格を指定し、作動時にはテイクプロフィットレベルの価格の指値注文が取引所に送信されます。
- ストップロスレベルは、不利な方向での損切り実装の為のもので、作動時には株式や為替にはストップロス価格の成行き注文が、FORTSには悪化した価格での指値注文が送信されます。
その他にもMetaTrader 5プラットフォームは、指値注文の為のストップロス/テイクプロフィットレベル
を指定・修正し、全ての指値注文の作動レベルを調整することができます。
MetaTrader 5の取引操作
MetaTrader 5は、取引を行う上で必要となる複数の主要な取引操作タイプを提供しています。
- 現在の価格での買い/売り
- いくつかの条件による指値買い/売り注文の設定
- 指値注文の修正/削除
- ポジションの決済/増量/縮減/反転
MQL5の全ての取引操作は、取引注文が正常にモスクワ証券取引所に送信された時にプログラムに制御を返すOrderSend()関数を使用し実装されています。注文ステータスは、ORDER_STATE_PLACED値を取りますが、これはあなたの注文が正常に実行されるということを意味するわけではありません(注文ステータスORDER_STATE_FILLEDまたはORDER_STATE_PARTIAL)。あなたの取引指示実行の最終的な結果は現在の市場に依存し、注文は様々な理由により拒否されることがあります(ステータスORDER_STATE_REJECTED)。
同様に、この関数の非同期バージョンーOrderSendAsync()もあり、これは取引所の取引システムへ指示が送信されるのを待たない為、OrderSend()よりも遥かに早く動作します。MetaTrader 5ターミナルによって外部へ注文が送信されるとすぐにこの関数への応答が送られます。これはつまり、あなたの取引注文がターミナル自体の基本的なチェックを通過し、MetaTrader 5取引サーバーへの処理に送信されたということです。取引所の列にあなたの指示を設置するのにどれくらいの時間がかかるかや、いつその指示か実行されるまたは拒否されるかは、全て取引所の混雑具合やあなたのインターネットの接続速度に依存します。
様々な取引操作は全て、取引注文の記述を含むMqlTradeRequest構造体で書かれます。したがって、取引操作の唯一の難しさは、MqlTradeRequest構造体を正確に埋めることと、発注結果の処理にあるといえます。
あなたの取引システムのルールに従って、市場価格で買いや売り(BUYまたはSELL)を実行したり、市場の現在価格から一定の距離の場所で買い/売りの指値注文を実行する設定をすることができます。
- BUY STOP、SELL STOPは、指定したレベルに達した場合(現在価格よりも良くない)の買いまたは売りです。このタイプの注文はMetaTrader 5の取引サーバーに保存され、条件発動時に成行き(株式/為替セクション)または指値注文(FORTS)がモスクワ証券取引所に送信されます。
- BUY LIMIT、SELL LIMITは、指定したレベルに達した場合(現在価格よりも良い)の買いまたは売りです。このタイプの注文は、指値注文としてすぐにモスクワ証券取引所へ送信されます。モスクワ証券取引所では指値注文にスプレッド内またはスプレッドの反対側でもレベルを指定できる点にご留意ください。このように、決済時のスリッページを制限します。
- BUY STOP LIMIT、SELL STOP LIMITは、指定した価格に到達した場合にBUY LIMIT注文またはSELL LIMIT注文を設定します。このタイプの注文はMetaTrader 5の取引サーバーに保存され、条件の発動時に通常の指値注文がモスクワ証券取引所に送信されます。このような指値注文の発注レベルは、注文自体の発動価格よりも高くしたり低くしたりすることができます。
BUY STOP、SELL STOP、BUY LIMIT、SELL LIMITの使用の原則、またこれらの注文を下記の板注文画面から直接発注する方法もあります。
その他にも、指値注文を修正または削除する必要がでた場合にも、OrderSend()/OrderSendAsync()関数を使用します。全ての取引操作の実行の結果で行われるものなので、未決ポジションの操作も難しいものではありません。
この記事では、買いや売りを簡単にMQL5でプログラミングする方法をご紹介し、取引口座やシンボルプロパティの操作方法をデモンストレーションします。今回は標準ライブラリの取引クラスが役に立ちます。
ロボットを使った取引所での取引は簡単です
MQL5言語は最初から全てのMetaTrader 5プラットフォームの取引機能をサポートしており、注文やポジション取引リクエストを操作する多くの取引関数があります。この時、あなたがどんな市場(先物、株式、オプションなど)で取引をしているかは重要ではありません。
MQL5を使うことで、取引リクエストを作成し、それをOrderSend()またはOrderSendAsync()関数を使用しサーバーに送信してその実行結果を取得したり、取引履歴を参照したり、商品の契約仕様を調べたり、取引イベントを処理したり、より多くの必要な情報を取得することができます。
トレードロボットの開発者は、1つの重要な事実を知っておく必要があります。未決ポジションやストップロス/テイクプロフィットの設定、または逆の取引でのポジション決済などの取引操作は、常にモスクワ証券取引所やMetaTrader 5のサーバーで実行される多くのトランザクションから構成されます。これがどのように行われるかを見るには、ご自身の口座上にTradeTransactionイベントを聞き取り、それについての簡単な情報を出力するTradeTransactionListener.mql5エキスパートアドバイザを起動させます。
//+------------------------------------------------------------------+ //| TradeTransactionListener.mq5 | //| Copyright 2016, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2016, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- PrintFormat("LAST PING=%.f ms", TerminalInfoInteger(TERMINAL_PING_LAST)/1000.); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+ //| TradeTransaction function | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result) { //--- static int counter=0; // OnTradeTransaction()呼び出しのカウンター static uint lasttime=0; // OnTradeTransaction()の最後の呼び出し時間 //--- uint time=GetTickCount(); //--- もし最後のトランザクションが1秒以上前の場合 if(time-lasttime>1000) { counter=0; // つまり、これは新しい取引操作でカウンターをリセットできる if(IS_DEBUG_MODE) Print("新しい取引操作"); } lasttime=time; counter++; Print(counter,". ",__FUNCTION__); //--- 取引リクエスト実行結果 ulong lastOrderID =trans.order; ENUM_ORDER_TYPE lastOrderType =trans.order_type; ENUM_ORDER_STATE lastOrderState=trans.order_state; //--- トランザクションが起こったシンボル名 string trans_symbol=trans.symbol; //--- トランザクションタイプ ENUM_TRADE_TRANSACTION_TYPE trans_type=trans.type; switch(trans.type) { case TRADE_TRANSACTION_POSITION: // ポジションの変更 { ulong pos_ID=trans.position; PrintFormat("MqlTradeTransaction: Position #%I64u %s modified: SL=%.5f TP=%.5f", pos_ID,trans_symbol,trans.price_sl,trans.price_tp); } break; case TRADE_TRANSACTION_REQUEST: // 取引リクエストの送信 PrintFormat("MqlTradeTransaction: TRADE_TRANSACTION_REQUEST"); break; case TRADE_TRANSACTION_DEAL_ADD: // 取引の追加 { ulong lastDealID =trans.deal; ENUM_DEAL_TYPE lastDealType =trans.deal_type; double lastDealVolume=trans.volume; //--- 外部システムの取引識別子ーモスクワ証券取引所によって割り当てられたチケット string Exchange_ticket=""; if(HistoryDealSelect(lastDealID)) Exchange_ticket=HistoryDealGetString(lastDealID,DEAL_EXTERNAL_ID); if(Exchange_ticket!="") Exchange_ticket=StringFormat("(MOEX deal=%s)",Exchange_ticket); PrintFormat("MqlTradeTransaction: %s deal #%I64u %s %s %.2f lot %s",EnumToString(trans_type), lastDealID,EnumToString(lastDealType),trans_symbol,lastDealVolume,Exchange_ticket); } break; case TRADE_TRANSACTION_HISTORY_ADD: // 履歴への注文の追加 { //--- 外部システムの注文識別子ーモスクワ証券取引所によって割り当てられたチケット string Exchange_ticket=""; if(lastOrderState==ORDER_STATE_FILLED) { if(HistoryOrderSelect(lastOrderID)) Exchange_ticket=HistoryOrderGetString(lastOrderID,ORDER_EXTERNAL_ID); if(Exchange_ticket!="") Exchange_ticket=StringFormat("(MOEX ticket=%s)",Exchange_ticket); } PrintFormat("MqlTradeTransaction: %s order #%I64u %s %s %s %s",EnumToString(trans_type), lastOrderID,EnumToString(lastOrderType),trans_symbol,EnumToString(lastOrderState),Exchange_ticket); } break; default: // その他のトランザクション { //--- 外部システムの注文識別子ーモスクワ証券取引所によって割り当てられたチケット string Exchange_ticket=""; if(lastOrderState==ORDER_STATE_PLACED) { if(OrderSelect(lastOrderID)) Exchange_ticket=OrderGetString(ORDER_EXTERNAL_ID); if(Exchange_ticket!="") Exchange_ticket=StringFormat("MOEX ticket=%s",Exchange_ticket); } PrintFormat("MqlTradeTransaction: %s order #%I64u %s %s %s",EnumToString(trans_type), lastOrderID,EnumToString(lastOrderType),EnumToString(lastOrderState),Exchange_ticket); } break; } //--- 注文チケット ulong orderID_result=result.order; string retcode_result=GetRetcodeID(result.retcode); if(orderID_result!=0) PrintFormat("MqlTradeResult: order #%d retcode=%s ",orderID_result,retcode_result); //--- } //+------------------------------------------------------------------+ //| ニーモニックコード列へ応答文字列を変換 | //+------------------------------------------------------------------+ string GetRetcodeID(int retcode) { switch(retcode) { case 10004: return("TRADE_RETCODE_REQUOTE"); break; case 10006: return("TRADE_RETCODE_REJECT"); break; case 10007: return("TRADE_RETCODE_CANCEL"); break; case 10008: return("TRADE_RETCODE_PLACED"); break; case 10009: return("TRADE_RETCODE_DONE"); break; case 10010: return("TRADE_RETCODE_DONE_PARTIAL"); break; case 10011: return("TRADE_RETCODE_ERROR"); break; case 10012: return("TRADE_RETCODE_TIMEOUT"); break; case 10013: return("TRADE_RETCODE_INVALID"); break; case 10014: return("TRADE_RETCODE_INVALID_VOLUME"); break; case 10015: return("TRADE_RETCODE_INVALID_PRICE"); break; case 10016: return("TRADE_RETCODE_INVALID_STOPS"); break; case 10017: return("TRADE_RETCODE_TRADE_DISABLED"); break; case 10018: return("TRADE_RETCODE_MARKET_CLOSED"); break; case 10019: return("TRADE_RETCODE_NO_MONEY"); break; case 10020: return("TRADE_RETCODE_PRICE_CHANGED"); break; case 10021: return("TRADE_RETCODE_PRICE_OFF"); break; case 10022: return("TRADE_RETCODE_INVALID_EXPIRATION"); break; case 10023: return("TRADE_RETCODE_ORDER_CHANGED"); break; case 10024: return("TRADE_RETCODE_TOO_MANY_REQUESTS"); break; case 10025: return("TRADE_RETCODE_NO_CHANGES"); break; case 10026: return("TRADE_RETCODE_SERVER_DISABLES_AT"); break; case 10027: return("TRADE_RETCODE_CLIENT_DISABLES_AT"); break; case 10028: return("TRADE_RETCODE_LOCKED"); break; case 10029: return("TRADE_RETCODE_FROZEN"); break; case 10030: return("TRADE_RETCODE_INVALID_FILL"); break; case 10031: return("TRADE_RETCODE_CONNECTION"); break; case 10032: return("TRADE_RETCODE_ONLY_REAL"); break; case 10033: return("TRADE_RETCODE_LIMIT_ORDERS"); break; case 10034: return("TRADE_RETCODE_LIMIT_VOLUME"); break; case 10035: return("TRADE_RETCODE_INVALID_ORDER"); break; case 10036: return("TRADE_RETCODE_POSITION_CLOSED"); break; default: return("TRADE_RETCODE_UNKNOWN="+IntegerToString(retcode)); break; } //--- } //+------------------------------------------------------------------+
この『聞き手』の動作例:
2016.06.09 14:51:19.763 TradeTransactionListener (Si-6.16,M15) LAST PING=14 ms 買い 2016.06.09 14:51:24.856 TradeTransactionListener (Si-6.16,M15) 1. OnTradeTransaction 2016.06.09 14:51:24.856 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_ORDER_ADD order #49118594 ORDER_TYPE_BUY ORDER_STATE_STARTED 2016.06.09 14:51:24.859 TradeTransactionListener (Si-6.16,M15) 2. OnTradeTransaction 2016.06.09 14:51:24.859 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_REQUEST 2016.06.09 14:51:24.859 TradeTransactionListener (Si-6.16,M15) MqlTradeResult: order #49118594 retcode=TRADE_RETCODE_PLACED 2016.06.09 14:51:24.859 TradeTransactionListener (Si-6.16,M15) 3. OnTradeTransaction 2016.06.09 14:51:24.859 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_ORDER_UPDATE order #49118594 ORDER_TYPE_BUY ORDER_STATE_REQUEST_ADD 2016.06.09 14:51:24.881 TradeTransactionListener (Si-6.16,M15) 4. OnTradeTransaction 2016.06.09 14:51:24.881 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_ORDER_UPDATE order #49118594 ORDER_TYPE_BUY ORDER_STATE_PLACED 2016.06.09 14:51:24.881 TradeTransactionListener (Si-6.16,M15) 5. OnTradeTransaction 2016.06.09 14:51:24.881 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_ORDER_DELETE order #49118594 ORDER_TYPE_BUY ORDER_STATE_PLACED 2016.06.09 14:51:24.884 TradeTransactionListener (Si-6.16,M15) 6. OnTradeTransaction 2016.06.09 14:51:24.884 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_HISTORY_ADD order #49118594 ORDER_TYPE_BUY Si-6.16 ORDER_STATE_FILLED (MOEX ticket=3377179723) 2016.06.09 14:51:24.884 TradeTransactionListener (Si-6.16,M15) 7. OnTradeTransaction 2016.06.09 14:51:24.885 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_DEAL_ADD deal #6945344 DEAL_TYPE_BUY Si-6.16 1.00 lot (MOEX deal=185290434) SL/TPの設定 2016.06.09 14:51:50.872 TradeTransactionListener (Si-6.16,M15) 1. OnTradeTransaction 2016.06.09 14:51:50.872 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_REQUEST 2016.06.09 14:51:50.872 TradeTransactionListener (Si-6.16,M15) 2. OnTradeTransaction 2016.06.09 14:51:50.872 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: Position #0 Si-6.16 modified: SL=62000.00000 TP=67000.00000 ポジション決済(売り) 2016.06.09 14:52:24.063 TradeTransactionListener (Si-6.16,M15) 1. OnTradeTransaction 2016.06.09 14:52:24.063 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_ORDER_ADD order #49118750 ORDER_TYPE_SELL ORDER_STATE_STARTED 2016.06.09 14:52:24.067 TradeTransactionListener (Si-6.16,M15) 2. OnTradeTransaction 2016.06.09 14:52:24.067 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_REQUEST 2016.06.09 14:52:24.067 TradeTransactionListener (Si-6.16,M15) MqlTradeResult: order #49118750 retcode=TRADE_RETCODE_PLACED 2016.06.09 14:52:24.067 TradeTransactionListener (Si-6.16,M15) 3. OnTradeTransaction 2016.06.09 14:52:24.067 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_ORDER_UPDATE order #49118750 ORDER_TYPE_SELL ORDER_STATE_REQUEST_ADD 2016.06.09 14:52:24.071 TradeTransactionListener (Si-6.16,M15) 4. OnTradeTransaction 2016.06.09 14:52:24.071 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_ORDER_UPDATE order #49118750 ORDER_TYPE_SELL ORDER_STATE_PLACED 2016.06.09 14:52:24.073 TradeTransactionListener (Si-6.16,M15) 5. OnTradeTransaction 2016.06.09 14:52:24.073 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_DEAL_ADD deal #6945378 DEAL_TYPE_SELL Si-6.16 1.00 lot (MOEX deal=185290646) 2016.06.09 14:52:24.075 TradeTransactionListener (Si-6.16,M15) 6. OnTradeTransaction 2016.06.09 14:52:24.075 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_ORDER_DELETE order #49118750 ORDER_TYPE_SELL ORDER_STATE_PLACED 2016.06.09 14:52:24.077 TradeTransactionListener (Si-6.16,M15) 7. OnTradeTransaction 2016.06.09 14:52:24.077 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_HISTORY_ADD order #49118750 ORDER_TYPE_SELL Si-6.16 ORDER_STATE_FILLED (MOEX ticket=3377182821)
ここで、ソースコード例の検討に取り掛かります。
取引口座を使った作業
初めに、トレードロボットの起動時に、ロボットがトレードを行う取引口座の情報を取得する必要があります。
これらの目的の為に特別に開発されたCAccountInfoクラスがあります。私達のコードにAccountInfo.mqhファイルの接続を追加し、accountクラスの変数を宣言します。
#include <Trade\AccountInfo.mqh> //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- 口座を使用したオブジェクト CAccountInfo account; //--- エキスパートアドバイザが起動する口座番号を取得 long login=account.Login(); Print("Login=",login); //--- 口座通貨を出力 Print("口座通貨: ",account.Currency()); //--- 口座上の残高と現在の利益を出力 Print("Balance=",account.Balance()," Profit=",account.Profit()," Equity=",account.Equity()); //--- 口座タイプを出力 Print("口座タイプ: ",account.TradeModeDescription()); //--- この口座で取引が可能かを調べる if(account.TradeAllowed()) Print("この口座での取引が許可されています"); else Print("この口座での取引は禁止されています:投資家パスワードでログインされた可能性があります"); //--- マージン計算モード Print("マージン計算モード: ",account.MarginModeDescription()); //--- エキスパートアドバイザを使った取引が可能かを調べる if(account.TradeExpert()) Print("口座上での自動売買は許可されています"); else Print("エキスパートアドバイザやスクリプトを使用した自動売買は禁止されています"); //--- 発注可能数が指定されているかどうか int orders_limit=account.LimitOrders(); if(orders_limit!=0)Print("有効な指値注文の最大数: ",orders_limit); //--- 会社とサーバー名を出力 Print(account.Company(),": server ",account.Server()); Print(__FUNCTION__," completed"); //--- }
上記のコードから分かるように、OnInit()関数のaccount変数を使って多くの有益な情報を得ることができます。このコードを自分のエキスパートアドバイザに追加することで、エキスパートアドバイザの動作を分析する際にログを解析するのが遥かに楽になります。
スクリプトを起動した結果は以下の通りです。
金融商品の特性の取得
口座についての情報は取得しましたが、取引操作を実行するには、私達が取引をしようと考えている資産の特性を知る必要があります。この為に、多くのメソッドを持つもう一つの便利なクラス CSymbolInfoがあります。この一部を例の中でご紹介します。
#include<Trade\SymbolInfo.mqh> //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- シンボルの特性を取得するオブジェクト CSymbolInfo symbol_info; //--- 情報を取得したいシンボルの名前を指定する symbol_info.Name(_Symbol); //--- 現在の相場を取得し出力する symbol_info.RefreshRates(); Print(symbol_info.Name()," (",symbol_info.Description(),")", " Bid=",symbol_info.Bid()," Ask=",symbol_info.Ask()); //--- 小数点以下の桁数とポイントサイズを取得 Print("Digits=",symbol_info.Digits(), ", Point=",DoubleToString(symbol_info.Point(),symbol_info.Digits())); //--- 注文実行タイプ、制限はないかをリクエストする Print("取引操作への制限: ",EnumToString(symbol_info.TradeMode()), " (",symbol_info.TradeModeDescription(),")"); //--- 取引決定モードを調べる Print("取引実行モード: ",EnumToString(symbol_info.TradeExecution()), " (",symbol_info.TradeExecutionDescription(),")"); //--- 契約価格の計算方法を調べる Print("契約価格の計算: ",EnumToString(symbol_info.TradeCalcMode()), " (",symbol_info.TradeCalcModeDescription(),")"); //--- 契約サイズ Print("標準契約サイズ: ",symbol_info.ContractSize()); //--- 1つの契約の初期証拠金サイズ Print("標準契約の初期証拠金: ",symbol_info.MarginInitial()," ",symbol_info.CurrencyBase()); //--- 取引操作内の最小/最大ボリュームサイズ Print("Volume info: LotsMin=",symbol_info.LotsMin()," LotsMax=",symbol_info.LotsMax(), " LotsStep=",symbol_info.LotsStep()); //--- Print(__FUNCTION__," completed"); }
画像では、モスクワ証券取引所(FORTS)のデリバティブ市場セクションの、Si-6.16シンボルの特性が表示されています。これで、取引に直接移る準備が整いました。
取引操作のプログラミング
取引指示の送信の為に、MQL5言語にはOrderSend()とOrderSendAsync()の2つの関数があります。実際にはこれは1つの関数の2つの実装です。OrderSend()が取引リクエストを送信し、その実行結果を待っている場合、非同期OrderSendAsync()はリクエストを発動し、取引サーバーの応答を待たずにプログラムに動作させることができます。このように、MQL5での取引は本当に簡単で、全ての取引操作には1つの関数を使用するだけで十分なのです。
2つの関数は、最初のパラメータとして、10以上のフィールドを持つMqlTradeRequest構造体を取得します。必要なフィールドの構成は取引操作タイプに依存するので、全てのフィールドを埋める必要はありません。数値が間違っているまたは必要なフィールドが欠けている場合には、リクエストはターミナル内の検査を通過することなく、サーバーに送信されません。この時、5つのフィールドに事前定義されたリストからの正確な数値を記入する必要があります。
取引リクエストの多くのフィールドは、実行ポリシーによって変わる可能性のある注文プロパティや有効時間、その他のパラメータを記入する必要がある為呼び出されました。これらの全ての詳細を暗記する必要はありません。ただCTradeクラスを使用しましょう。あなたのトレードロボットでこのクラスがどのように使用されるかは、大体以下のようになります。
#include<Trade\Trade.mqh> //--- 取引操作実行オブジェクト CTrade trade; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- 自分の注文の識別子の為のMagicNumberを指定する int MagicNumber=123456; trade.SetExpertMagicNumber(MagicNumber); //--- 買い/売り実行時のポイントでの可能スリッページを指定する int deviation=10; trade.SetDeviationInPoints(deviation); //--- 注文実行モードーサーバーによって許可されているモードを使用する必要がある trade.SetTypeFilling(ORDER_FILLING_RETURN); //--- ログモード: このメソッドは呼び出さない方が良い、クラスは自分で最適なモードを設置する trade.LogLevel(1); //--- 取引にどの関数を使用するか:true - OrderSendAsync()、false - OrderSend() trade.SetAsyncMode(true); //--- return(0); }
取引所での取引は、原則として、ORDER_FILLING_RETURN実行モードを使用します。ヘルプでは、
このモードは『成行実行』と『エクスチェンジ実行』モードでのみ使用されます(成行(ORDER_TYPE_BUYとORDER_TYPE_SELL)、指値や逆指値注文(ORDER_TYPE_BUY_LIMIT、ORDER_TYPE_SELL_LIMIT、ORDER_TYPE_BUY_STOP_LIMIT и ORDER_TYPE_SELL_STOP_LIMIT))。残りのボリュームをもつ成行または指値注文の部分的な実行の場合、削除はされず効力を保ちます。
ORDER_TYPE_BUY_STOP_LIMITとORDER_TYPE_SELL_STOP_LIMIT注文には、有効化の時にORDER_FILLING_RETURN実行タイプのORDER_TYPE_BUY_LIMIT/ORDER_TYPE_SELL_LIMIT指値注文が作成されます。
CTradeがどのように取引操作に役立つかを見ていきましょう。
現在価格での買い/売り
取引戦略では現在の価格で今すぐに買いや売りを実行しなければならないことがよくあります。CTradeはこういった状況をよく知っており、取引操作の必要ボリュームだけを要求します。全ての残りのパラメータ(オープン価格やシンボル名、ストップロスやテイクプロフィットレベル、注文へのコメント)は指定しなくてもいいです。
//--- 1. 現在のシンボルでの買いの例 if(!trade.Buy(1)) { //--- エラーを報告する Print("Buy()メソッドは失敗しました。リターンコード=",trade.ResultRetcode(), ". コード説明",trade.ResultRetcodeDescription()); } else { Print("Buy()メソッドは正常に実行されました。リターンコード=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
デフォルトでは、商品名が指定されていない場合、CTradeは起動されているチャート上のシンボル名を使用します。これは簡単な戦略にとってはとても便利です。複数の商品で一度に取引を行うロボットには、毎回取引操作が行われるシンボルを明示的に指定する必要があります。
//--- 2. 指定したシンボルでの買いの例 if(!trade.Buy(1,"Si-6.16")) { //--- エラーを報告する Print("Buy()メソッドは失敗しました。リターンコード=",trade.ResultRetcode(), ". コード説明",trade.ResultRetcodeDescription()); } else { Print("Buy()メソッドは正常に実行されました。リターンコード=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
全てのパラメータを指定することができます: Stop Loss/Take Profitレベル、オープン価格とコメント。
//--- 3. ストップロス/テイクプロフィットを伴う指定したシンボルでの買いの例 double volume=1; // 取引操作ボリュームを設定 string symbol="Si-6.16"; // 操作が行われるシンボルを指定 int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // 小数点以下の桁数 double point=SymbolInfoDouble(symbol,SYMBOL_POINT); // ポイント double bid=SymbolInfoDouble(symbol,SYMBOL_BID); // LONG決済の為の現在価格 double SL=bid-100*point; // SLの正規化されていない値 SL=NormalizeDouble(SL,digits); // Stop Lossを正規化 double TP=bid+100*point; // TPの正規化されていない値 TP=NormalizeDouble(TP,digits); // Take Profitを正規化 //--- LONGポジションの為のオープン現在価格を取得 double open_price=SymbolInfoDouble(symbol,SYMBOL_ASK); string comment=StringFormat("Buy %s %G lots at %s, SL=%s TP=%s", symbol,volume, DoubleToString(open_price,digits), DoubleToString(SL,digits), DoubleToString(TP,digits)); if(!trade.Buy(volume,symbol,open_price,SL,TP,comment)) { //--- エラーを報告する Print("Buy()メソッドは失敗しました。リターンコード=",trade.ResultRetcode(), ". コード説明",trade.ResultRetcodeDescription()); } else { Print("Buy()メソッドは正常に実行されました。リターンコード=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
MagicNumberと許容スリッページは、CTradeのインスタンスの初期化時に指定している為、必要ありません。しかし、必要に応じて、各取引操作の前に直接設定することも可能です。
指値注文の発注
指値注文の送信には、対応するBuyLimit()またはSellLimit()クラスのメソッドが使用されます。多くの場合、オープン価格とボリュームだけがしていされている場合には短縮版が適してします。BuyLimitのオープン価格は、現在の価格よりも低い必要があり、SellLimitのオープン価格は高い必要があります。つまり、サポートレベルからのリバウンドを計算する戦略などで、これらの注文は最良の価格で市場にエントリーするのに使用されます。この時、エキスパートアドバイザが起動しているシンボルが使用されます。
//--- 1. 指値注文 BuyLimitの設定例 string symbol="Si-6.16"; // 注文が発注されるシンボルを指定する int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // 小数点以下の桁数 double point=SymbolInfoDouble(symbol,SYMBOL_POINT); // ポイント double ask=SymbolInfoDouble(symbol,SYMBOL_ASK); // 現在の買い価格 double price=ask-100*point; // 正規化されていないオープン価格 price=NormalizeDouble(price,digits); // オープン価格を正規化 //--- 指値注文Buy Limitをサーバーへ送信する準備が完了 if(!trade.BuyLimit(1,price)) { //--- エラーを報告する Print("BuyLimit()メソッドは失敗しました。リターンコード=",trade.ResultRetcode(), ". コード説明",trade.ResultRetcodeDescription()); } else { Print("BuyLimit()メソッドは正常に実行されました。リターンコード=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
SL/TPレベルや有効時間、商品名や注文へのコメントなど、全てのパラメータを指定したより詳細なバージョンを使用することもできます。
//--- 2. 全てのパラメータを持つ指値注文 BuyLimitの設定例 double volume=1; string symbol="Si-6.16"; // 注文が発注されるシンボルを指定する int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // 小数点以下の桁数 double point=SymbolInfoDouble(symbol,SYMBOL_POINT); // ポイント double ask=SymbolInfoDouble(symbol,SYMBOL_ASK); // 現在の買い価格 double price=ask-100*point; // 正規化されていないオープン価格 price=NormalizeDouble(price,digits); // オープン価格を正規化 int SL_pips=100; // ポイント上のStop Loss int TP_pips=100; // ポイント上のTake Profit double SL=price-SL_pips*point; // SLの正規化されていない値 SL=NormalizeDouble(SL,digits); // Stop Lossを正規化 double TP=price+TP_pips*point; // TPの正規化されていない値 TP=NormalizeDouble(TP,digits); // Take Profitを正規化 datetime expiration=TimeTradeServer()+PeriodSeconds(PERIOD_D1); string comment=StringFormat("Buy Limit %s %G lots at %s, SL=%s TP=%s", symbol,volume, DoubleToString(price,digits), DoubleToString(SL,digits), DoubleToString(TP,digits)); //--- 指値注文Buy Limitをサーバーへ送信する準備が完了 if(!trade.BuyLimit(volume,price,symbol,SL,TP,ORDER_TIME_DAY,expiration,comment)) { //--- エラーを報告する Print("BuyLimit()メソッドは失敗しました。リターンコード=",trade.ResultRetcode(), ". コード説明",trade.ResultRetcodeDescription()); } else { Print("BuyLimit()メソッドは正常に実行されました。リターンコード=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
2つ目のバージョンでは、ストップロスとテイクプロフィットのレベルを正確に指定する必要があります。買いの為のテイクプロフィットレベルはオープン価格よりも高く、ストップロスレベルはオープン価格よりも低い必要があります。SellLimit注文の場合全て逆になります。履歴データ上でのエキスパートアドバイザのテスト時の自分の間違いについて簡単に知ることができ、CTradeクラスはこのような場合に自動的にメッセージを出力します(もしあなたが自分でLogLevel関数を呼び出さなかった場合)。
ストップオーダーの発注
ストップオーダーの送信には、BuyStop()とSellStop()メソッドが使用されます。Buy Stopの為のオープン価格は、現在の価格よりも高く、SellStopは現在の価格よりも低い必要があります。ストップオーダーは、レジスタンスレベルの一定のブレイクアウトを含む戦略で、また損失を制限する為に使用されます。簡単なバージョン:
//--- 1. 指値注文 Buy Stopの設定方法 string symbol="RTS-6.16"; // 注文が発注されるシンボルを指定 int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // 小数点以下の桁数 double point=SymbolInfoDouble(symbol,SYMBOL_POINT); // ポイント double ask=SymbolInfoDouble(symbol,SYMBOL_ASK); // 現在の買い価格 double price=ask+100*point; // 正規化されていないオープン価格 price=NormalizeDouble(price,digits); // オープン価格を正規化 //--- 指値注文Buy Stopをサーバーに送信する準備が完了 if(!trade.BuyStop(1,price)) { //--- エラーを報告する Print("BuyStop()メソッドは失敗しました。リターンコード=",trade.ResultRetcode(), ". コード説明",trade.ResultRetcodeDescription()); } else { Print("BuyStop()メソッドは正常に実行されました。リターンコード=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
そして、指値注文BuyStopに最大限のパラメータを指定する必要がある場合のより詳細な例:
//--- 2. 全てのパラメータを備えた指値注文Buy Stopの設定例 double volume=1; string symbol="RTS-6.16"; // 注文が発注されるシンボルを指定 int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // 小数点以下の桁数 double point=SymbolInfoDouble(symbol,SYMBOL_POINT); // ポイント double ask=SymbolInfoDouble(symbol,SYMBOL_ASK); // 現在の買い価格 double price=ask+100*point; // 正規化されていないオープン価格 price=NormalizeDouble(price,digits); // オープン価格を正規化 int SL_pips=100; // ポイント上のStop Loss int TP_pips=100; // ポイント上のTake Profit double SL=price-SL_pips*point; // SLの正規化されていない値 SL=NormalizeDouble(SL,digits); // Stop Lossを正規化 double TP=price+TP_pips*point; // TPの正規化されていない値 TP=NormalizeDouble(TP,digits); // Take Profitを正規化 datetime expiration=TimeTradeServer()+PeriodSeconds(PERIOD_D1); string comment=StringFormat("Buy Stop %s %G lots at %s, SL=%s TP=%s", symbol,volume, DoubleToString(price,digits), DoubleToString(SL,digits), DoubleToString(TP,digits)); //--- 指値注文Buy Stopをサーバーに送信する準備が完了 if(!trade.BuyStop(volume,price,symbol,SL,TP,ORDER_TIME_DAY,expiration,comment)) { //--- エラーを報告する Print("BuyStop()メソッドは失敗しました。リターンコード=",trade.ResultRetcode(), ". コード説明",trade.ResultRetcodeDescription()); } else { Print("BuyStop()メソッドは正常に実行されました。リターンコード=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
SellStop注文の送信には、CTradeクラスのメソッドが適用され、大事なのは価格を正確に指定することです。
ポジションの使用
Buy()とSell()メソッドを使用する代わりに、未決ポジションの為のメソッドを使用することができます。この場合、より詳細に指定する必要があります。
//--- 小数点以下の桁数 int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); //--- ポイントの値 double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); //--- 買い価格の取得 double price=SymbolInfoDouble(_Symbol,SYMBOL_ASK); //--- ストップロスとテイクプロフィットレベルを計算し正規化 double SL=NormalizeDouble(price-100*point,digits); double TP=NormalizeDouble(price+100*point,digits); //--- コメントを埋める string comment="Buy "+_Symbol+" 1 at "+DoubleToString(price,digits); //--- 準備が完了したので、ロングポジションを開く試みをします if(!trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,1,price,SL,TP,comment)) { //--- エラーを報告する Print("PositionOpen()メソッドは失敗しました。リターンコード=",trade.ResultRetcode(), ". コード説明",trade.ResultRetcodeDescription()); } else { Print("PositionOpen()メソッドは正常に実行されました。リターンコード=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
ポジション決済の為には、商品名を指定すれば十分で、あとはCTradeクラスが自分で行います。
//--- 現在のシンボルのポジションを決済する if(!trade.PositionClose(_Symbol)) { //--- エラーを報告する Print("PositionClose()メソッドは失敗しました。リターンコード=",trade.ResultRetcode(), ". コード説明",trade.ResultRetcodeDescription()); } else { Print("PositionClose()メソッドは正常に実行されました。リターンコード=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
未決ポジションでは、ストップロスとテイクプロフィットレベルを変更することができます。これはModifyPosition()メソッドを使用して行われます。
//--- 小数点以下の桁数 int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); //--- ポイントの値 double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); //--- 現在のBid価格を取得 double price=SymbolInfoDouble(_Symbol,SYMBOL_BID); //--- ストップロスとテイクプロフィットレベルを計算し正規化 double SL=NormalizeDouble(price-100*point,digits); double TP=NormalizeDouble(price+100*point,digits); //--- 準備が完了したので、ロングポジションの変更を試みます if(!trade.PositionModify(_Symbol,SL,TP)) { //--- エラーを報告する Print("PositionModify()メソッドは失敗しました。リターンコード=",trade.ResultRetcode(), ". コード説明",trade.ResultRetcodeDescription()); } else { Print("PositionModify()メソッドは正常に実行されました。リターンコード=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
注文の削除と修正
指値注文のパラメータの変更の為に、CTradeクラスには全ての必要なパラメータを引き渡す必要があるOrderModify()メソッドがあります。
//--- 注文の有無をチェック if(!OrderSelect(ticket)) { Print("Ордер #",ticket,"未検出"); return; } //--- シンボル string symbol=OrderGetString(ORDER_SYMBOL); //--- 小数点以下の桁数 int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); //--- ポイントの値 double point=SymbolInfoDouble(symbol,SYMBOL_POINT); //--- オープン価格を取得 double price=OrderGetDouble(ORDER_PRICE_OPEN); //--- ストップロスとテイクプロフィットレベルを計算し正規化 double SL=NormalizeDouble(price-200*point,digits); double TP=NormalizeDouble(price+200*point,digits); //--- 準備が完了したので、注文の変更を試みます if(!trade.OrderModify(ticket,price,SL,TP,ORDER_TIME_DAY,0)) { //--- エラーを報告する Print("OrderModify()メソッドは失敗しました。リターンコード=",trade.ResultRetcode(), ". コード説明",trade.ResultRetcodeDescription()); } else { Print("OrderModify()メソッドは正常に実行されました。リターンコード=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
変更する必要がある注文のチケットを取得する必要があり、そのタイプによっては、ストップロスとテイクプロフィットの正確なレベルを指定する必要があります。また、新しいオープン価格は、現在価格に対して正確なものでなければなりません。
指値注文の削除には、そのチケットを知るだけで十分です。
//--- 注文の有無をチェック if(!OrderSelect(ticket)) { Print("Ордер #",ticket,"未検出"); return; } //--- 準備が完了したので、注文の削除を試みます if(!trade.OrderDelete(ticket)) { //--- エラーを報告する Print("OrderDelete()メソッドは失敗しました。リターンコード=",trade.ResultRetcode(), ". コード説明",trade.ResultRetcodeDescription()); } else { Print("OrderDelete()メソッドは正常に実行されました。リターンコード=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
またクラスには、あらゆるタイプの指値注文を発注できる一般的なOrderOpen()メソッドがあります。BuyLimit、BuyStop、SellLimit、SellStopの特別なメソッドとの違いは、これは必要なパラメータを多く指定する必要があることです。人によっては、より快適に思うかもしれません。
取引クラスの他に見るべきもの
この記事では、買いと売りの取引操作のプログラミングの例と、指値注文を使った作業をご紹介しました。しかし、取引クラスのページには、まだいくつかのMQL5でロボットを開発する為の便利なサポーターがあります:
- COrderInfo — 注文を操作する為のもの
- CHistoryOrderInfo — 取引履歴に入る処理済みの注文を操作する為のもの
- CPositionInfo — ポジションを操作する為のもの
- CDealInfo — 取引を操作する為のもの
- CTerminalInfo — ターミナルについての情報を取得する為のもの
これらのクラスを使うことで、全ての技術的な問題は最小限にして、あなたの取引の戦略だけに集中することができます。また、CTradeクラスは、デバッグ下などの、取引リクエストの研究の為に使用することができます。そして時間をかけて、それをベースにした取引リクエストの実行結果を処理するあなたに必要なロジックを実装する独自のクラスを作成することができます。
アルゴトレードへの道は簡単なスクリプトから始めましょう
記事で紹介したMQL5のトレードロボットの開発方法は、まず第一に初心者の為でありますが、多くの経験豊富な開発者も自分の為に何か新しく有益なものを見つけることができると思います。この記事の簡単なスクリプトの実行から始めることで、トレードロボットの作成はあなたが考えている以上に遥かに簡単なことだとお分かりいただけると思います。
更に先に進みたい方の為に、このテーマの2つの記事をご紹介します。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/2513
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索