MetaTraderプログラムを簡単かつ迅速に開発するためのライブラリ(第24部): 基本取引クラス - 無効なパラメータの自動修正
内容
前の記事では、取引クラスに無効なパラメータの制御を追加しました。取引メソッドに渡された値の有効性が確認されます。パラメータのいずれかが無効であることが判明した場合、取引メソッドはエラーメッセージを伴って終了されます。この動作は、EAが意図的に無効な注文で取引サーバに過負荷をかけるのを防ぎますが、EAの動作を完全に制御することはできません。代わりに、無効な値を修正できるかどうかを確認できます。修正できる場合は、修正して調整された取引注文をサーバに送信するのが合理的です。
一般的に、EAは、取引注文のエラーを処理するユーザ定義のロジックに従って、状況に応じて行動できる必要があります。したがって、取引注文のエラーが検出された場合、EAに次の指示を与えることができます。
- 単に取引メソッドを終了して、ユーザに自分で誤った注文の無効なパラメータのハンドラを作成させる。
- 取引注文の無効な値を修正できる場合は、直ちに修正してサーバに送信する。
- 状況とエラーが適切な場合、一時停止後に取引リクエストを繰り返すか、単に同じパラメータでリクエストを繰り返す。
取引注文パラメータのエラーを処理した結果は次のいずれかになる可能性があります。
- エラーの原因がユーザによって除去される前にEAを使用して取引を続けることができない。
- 取引注文を送信できないため、取引メソッドを終了する。
- 無効な値を修正し、固定取引注文を送信する。
- 初期パラメータを使用した取引注文を直ちに送信する(ここでは、取引条件が改善されたと仮定する)。
- 待機し、相場データを更新し、初期パラメータを使用した取引注文を送信する。
本稿では、エラーとその原因を確認し、エラー処理メソッドを返す取引注文エラーハンドラを開発します。
- 取引操作の無効化
- 取引操作の中断
- 無効なパラメータの修正
- 初期パラメータを持つ取引リクエスト
- 待機後の取引リクエスト(一時的解決法)
- 未決の取引リクエストの作成(後続の記事で)
概念
サーバ上で取引が完全に無効になっている場合、またはEAが取引リクエストのみを無用にしている場合、取引操作を無効にする必要があります。この場合、EAは分析アシスタントとしてのみ使用できます。これを実現するには、最初の取引試行中に設定し、取引の不可能性を判断するグローバルフラグが必要です。
取引操作の中断: エラーが発生した場合は、取引メソッドを終了して、ユーザに同じパラメータで取引を継続できるようにします。
無効なパラメータの修正は次のように機能します。取引リクエストの有効性を確認するとき、検出されたすべてのエラーのリストをコンパイルします。パラメータを確認するメソッドは、リストからすべてのエラーを調べ、取引メソッドの動作コードを返します。エラーによってそれ以上の取引ができない場合、取引注文を送信しても肯定的な結果が得られないため、メソッドは取引メソッドを終了するコードを返します。エラーを修正できる場合、適切な取引注文の値を修正するメソッドが呼び出され、成功した検証の結果が返されます。また、このメソッドは、「待機して繰り返す」、「データを更新して繰り返す」、および「未決リクエストを作成する」取引メソッドの動作コードを返します。
何を意味するのでしょうか?「待機して繰り返す」動作は、ストップレベルの変更または注文の削除/ポジションの決済を試みる際に市場が注文ストップレベルまたはその発動価格のいずれかに近い場合に必要になることがあります。ストップレベルの発動価格が取引操作の凍結エリア内にある場合、サーバは注文値の変更の禁止を返します。この場合、解決策は1つだけです。価格がエリアを離れるのを待ってしばらく待つことです。その後、取引リクエストを送信して、注文/ポジションパラメータを変更するか、待機後に注文を削除します。
価格が最新でなくなり、取引注文の処理中にリクオートを受け取った場合、「データを更新して繰り返す」動作が必要になることがあります。
「未決リクエストを作成する」動作とは、何を意味するのでしょうか?
前の2つの処理メソッドを詳しく見ると、待機時間が終わるまで取引メソッドで待機することが明らかになります。待機中に取引環境を分析する必要がない場合、このような動作は正当化されます。プログラムを取引メソッド内に「立つ」必要性から解放するために、必要なパラメータと待機時間と繰り返し回数を含む未決取引リクエストを作成するだけです。
始める前に、前の記事で取引イベントの定義に変更を加え始めたことを思い出してください。
最後の取引イベントを受信したときに検出されたエラーに関して、いくつかのユーザレポートを受け取りました。取引イベントを受信するメソッドを説明する記事に関連するテストEAは、以前のイベント値を現在のイベント値と比較することにより、発生した取引イベントに関するデータを取得します。取引イベントに関する記事を書く際にカスタムアプリケーションで未完成のライブラリバージョンを使用するつもりはなかったため、これはライブラリによる取引イベントの追跡をテストする目的には十分です。しかし、取引イベントに関する情報を取得することが非常に求められており、最後に発生したイベントを正確に知ることが重要であることが判明しました。
取引イベントを取得するメソッドの実装では、一部のイベントがスキップされる場合があります。たとえば、指値注文を連続して2回設定した場合、2番目の指値注文は、注文自体が実際に違ったとしても1回目のものに一致するため(指値注文の発注)プログラム内で追跡されません(ライブラリはすべてのイベントを追跡します)。
したがって、この動作を修正します。今日、プログラムにイベントを通知する単純なフラグを実装し、プログラムでイベントのデータを表示できるようにします。次の記事では、同時に発生したすべてのイベントの完全なリストを作成してプログラムに送信することにより、プログラムでの取引イベントの取得を完了します。したがって、発生した取引イベントを見つけることができるだけでなく、口座および銘柄収集イベントで行われているように、同時に発生したすべてのイベントも表示できます。
そのため、取引クラスでの作業を再開する前に、この機能を変更する作業を完了しましょう。
取引イベント定義の修正
すべてのオブジェクトは実際には、イベントのリストとオブジェクトの発生イベントフラグを返すメソッドを備えたすべてのライブラリオブジェクトの基本オブジェクトに基づいているため、すべての取引イベントを基本オブジェクトイベントリストに追加します。 イベントフラグは、IsEvent()メソッドを使用してクラスから取得できます。イベントフラグはクラスによって自動的に設定されます。ただし、発生した取引イベントのフラグを他のクラスとそのイベントハンドラから設定できる必要があります。
これを行うには、基本オブジェクトイベントフラグを設定するメソッドをBaseObj.mqhファイルのCEventBaseObjクラスに追加します。
//--- Set/return the occurred event flag to the object data void SetEvent(const bool flag) { this.m_is_event=flag; } bool IsEvent(void) const { return this.m_is_event; }
CEventsCollection取引イベントコレクションクラスに新しいイベントが定時された場合、イベントの説明を作成し、すべてのオブジェクトの基本クラスの新しいイベントのリストに配置して、新しいイベントフラグを設定する必要があります。
したがって、新しく発生したすべてのイベントの説明は、銘柄コレクションの基本クラス取引イベントのリストに配置されます。そのリストからは、プログラム内でリストを簡単に読み取り、リスト内の各イベントを処理できます。
EventsCollection.mqh取引イベントクラスファイルに必要なすべての改善を加えましょう。
2つの新しいメソッドの定義をクラスのpublicセクションに追加します。これらは、
リスト内のインデックスによる基本イベントオブジェクトの受信メソッドおよび
新しいイベントの数を返すメソッドです。
//--- Return (1) the last trading event on an account, (2) base event object by index and (3) number of new events ENUM_TRADE_EVENT GetLastTradeEvent(void) const { return this.m_trade_event; } CEventBaseObj *GetTradeEventByIndex(const int index) { return this.GetEvent(index,false); } int GetTradeEventsTotal(void) const { return this.m_list_events.Total(); } //--- 直近の取引イベントをリセットする
メソッドはGetEvent()基本オブジェクトメソッドを必要なイベントインデックスで呼び出してインデックスによって基本イベントオブジェクトを返します。呼び出しでは、インデックスが返された場合に返されるイベントを修正しないようにインデックスの範囲を確認するリセットフラグは(false)にします。つまり、存在しないインデックスを渡すと、メソッドはNULLを返します。フラグ値にtrueを渡すと、メソッドはここで不要な最後のイベントを返します。
新しいイベントの数を返すメソッドは単に基本オブジェクトリストのサイズを返します。
履歴および成行注文とポジションのリストはタイマーの取引イベントコレクションクラスで常に表示されるため、基本取引イベントのリストをクリアし、Refresh()メソッドで並び替え済みリストフラグを設定する必要があります。
//+------------------------------------------------------------------+ //| Update the event list | //+------------------------------------------------------------------+ void CEventsCollection::Refresh(CArrayObj* list_history, CArrayObj* list_market, CArrayObj* list_changes, CArrayObj* list_control, const bool is_history_event, const bool is_market_event, const int new_history_orders, const int new_market_pendings, const int new_market_positions, const int new_deals, const double changed_volume) { //--- Exit if the lists are empty if(list_history==NULL || list_market==NULL) return; //--- this.m_is_event=false; this.m_list_events.Clear(); this.m_list_events.Sort(); //--- If the event is in the market environment if(is_market_event) {
イベントが新しいCreateNewEvent()イベントを作成するすべてのメソッドで文字列を送信した後、基本イベントのリストにイベントを追加する必要があります。
event.SendEvent(); CBaseObj::EventAdd(this.m_trade_event,order.Ticket(),order.Price(),order.Symbol());
これはすでにメソッドリストで設定されているため、記事のスペースを節約するためにここでは説明しません。すべては添付ファイルでご覧になれます。
次に、CEngineライブラリ基本オブジェクトのクラスのpublicセクションに、リスト内のインデックスによって基本イベントオブジェクトを返すメソッドおよび新しいイベントの数を返すメソッドを追加します。
//--- Return (1) the list of order, deal and position events, (2) base trading event object by index and the (3) number of new trading events CArrayObj *GetListAllOrdersEvents(void) { return this.m_events.GetList(); } CEventBaseObj *GetTradeEventByIndex(const int index) { return this.m_events.GetTradeEventByIndex(index); } int GetTradeEventsTotal(void) const { return this.m_events.GetTradeEventsTotal(); } //--- 直近の取引イベントをリセットする
これらのメソッドは、上記の同名の取引イベントコレクションクラスメソッドを呼び出すだけです。
これらはすべて必要な変更であり、同時に発生したイベントを追跡して、単一のバンドルでプログラムに送信できます。これは、記事で説明されている機能をテストするときに、後で見られます。
これで、取引クラスのさらなる改良を開始できます。
取引リクエストパラメータエラーのハンドラ
まず、必要なメッセージのインデックスをDatas.mqhファイルに追加します。
MSG_LIB_TEXT_REQUEST_REJECTED_DUE, // Request was rejected before sending to the server due to: MSG_LIB_TEXT_INVALID_REQUEST, // Invalid request: MSG_LIB_TEXT_NOT_ENOUTH_MONEY_FOR, // Insufficient funds for performing a trade MSG_LIB_TEXT_UNSUPPORTED_PRICE_TYPE_IN_REQ, // Unsupported price parameter type in a request MSG_LIB_TEXT_TRADING_DISABLE, // Trading disabled for the EA until the reason is eliminated MSG_LIB_TEXT_TRADING_OPERATION_ABORTED, // Trading operation is interrupted MSG_LIB_TEXT_CORRECTED_TRADE_REQUEST, // Correcting trading request parameters MSG_LIB_TEXT_CREATE_PENDING_REQUEST, // Creating a pending request MSG_LIB_TEXT_NOT_POSSIBILITY_CORRECT_LOT, // Unable to correct a lot };
インデックスに対応するテキストも追加します。
{"Запрос отклонён до отправки на сервер по причине:","Request rejected before being sent to server due to:"}, {"Ошибочный запрос:","Invalid request:"}, {"Недостаточно средств для совершения торговой операции","Not enough money to perform trading operation"}, {"Неподдерживаемый тип параметра цены в запросе","Unsupported price parameter type in request"}, {"Торговля отключена для эксперта до устранения причины запрета","Trading for the expert is disabled until this ban is eliminated"}, {"Торговая операция прервана","Trading operation aborted"}, {"Корректировка параметров торгового запроса ...","Correction of trade request parameters ..."}, {"Создание отложенного запроса","Create pending request"}, {"Нет возможности скорректировать лот","Unable to correct the lot"}, };
Defines.mqhファイルに、取引リクエストのエラーと取引サーバから返されたエラーを処理する方法を定義して返す必要がある列挙を追加します。
EAのエラーを直接受け取ったときにアクティブ化される動作を設定するには、取引リクエストでエラーを検出したとき、または取引サーバからエラーが返されたときに考えられるEA動作を説明する列挙を追加します。
//+------------------------------------------------------------------+ //| EA behavior when handling errors | //+------------------------------------------------------------------+ enum ENUM_ERROR_HANDLING_BEHAVIOR { ERROR_HANDLING_BEHAVIOR_BREAK, // Abort trading attempt ERROR_HANDLING_BEHAVIOR_CORRECT, // Correct invalid parameters ERROR_HANDLING_BEHAVIOR_PENDING_REQUEST, // Create a pending request }; //+------------------------------------------------------------------+
列挙パラメータの1つを指定することにより、EA設定でエラーを処理するときにEAの望ましい動作を設定できます。
取引リクエストのパラメータ値を確認するとき、さまざまなエラー処理方法が可能です。取引注文のパラメータを確認するときに検出されるエラーと、これらのエラーに影響する取引条件を見つけるには、可能なエラー処理メソッドのフラグを列挙に追加します。
//+------------------------------------------------------------------+ //| Flags indicating the trading request error handling methods | //+------------------------------------------------------------------+ enum ENUM_TRADE_REQUEST_ERR_FLAGS { TRADE_REQUEST_ERR_FLAG_NO_ERROR = 0, // No error TRADE_REQUEST_ERR_FLAG_FATAL_ERROR = 1, // Disable trading for an EA (critical error) - exit TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR = 2, // Library internal error - exit TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST = 4, // Error in the list - handle (ENUM_ERROR_CODE_PROCESSING_METHOD) }; //+------------------------------------------------------------------+
取引注文パラメータとそれを実行する機能を確認する場合、エラー処理動作フラグを追加します。
- 0 — エラーなし、取引注文を送信できます、
- 1 — 重大なエラー—取引の試行には意味がありません。EAは非取引分析アシスタントモードに切り替える必要があります。
- 2 — エラーが発生し、ライブラリに障害が発生しました。取引クラスの誤動作を避けるために、取引メソッドの実行を中断してください。
- 4 — エラーは修正可能で、エラーを修正するためのメソッドを呼び出すためにエラーリストに書き込まれます。
エラー確認メソッドは、検出されたエラーを正しく処理する方法を返します。
これを行うには、取引注文エラーを処理する可能性のあるメソッドの列挙と、取引サーバから返されたものを追加します。
//+------------------------------------------------------------------+ //| The methods of handling errors and server return codes | //+------------------------------------------------------------------+ enum ENUM_ERROR_CODE_PROCESSING_METHOD { ERROR_CODE_PROCESSING_METHOD_OK, // No errors ERROR_CODE_PROCESSING_METHOD_DISABLE, // Disable trading for the EA ERROR_CODE_PROCESSING_METHOD_EXIT, // Exit the trading method ERROR_CODE_PROCESSING_METHOD_REFRESH, // Update data and repeat ERROR_CODE_PROCESSING_METHOD_WAIT, // Wait and repeat ERROR_CODE_PROCESSING_METHOD_PENDING, // Create a pending request }; //+------------------------------------------------------------------+
エラーを処理するメソッドは、列挙定数の説明から理解できます。
また、基本取引オブジェクトを改善します。
チャート上のバーの構築メソッドに応じて、取引はAskとBidまたはAskとLastのいずれかによって実行されます。現在、AskとBidの価格による取引のみが基本取引クラスに配置されています。チャートの構築に使用される価格を確認する機能を追加しましょう。また、取引に使用する価格を調整します。その上、MQL5は、MqlTradeResult取引リクエスト結果構造体、ならびにエラーコードとエラーコードの説明をそれぞれ含む「retcode」フィールドと「comment」フィールドを備えています。これにより、取引注文をサーバに送信した後、取引サーバから返されたコードを確認できます。MQL4にはそのような機能がないため、エラーコードは最後のエラーコードを返すGetLastError()関数によって読み取られる必要があります。ライブラリはマルチプラットフォームのライブラリであるため、MQL4の場合、サーバに送信した後、取引リクエスト構造体のフィールドに入力する必要があります。
価格に対するストップ注文の距離を確認するとき、銘柄に設定された最小許容ストップレベル(StopLevel)の距離も考慮します。SYMBOL_TRADE_STOPS_LEVELプロパティIDでSymbolInfoInteger()関数によって返されるStopLevel値がゼロに等しい場合、これはストップ注文の価格のポイントでの最小シフトがないことを意味しません レベルが変動していることを意味するだけです。したがって、価格からストップレベルシフトの値を修正するには、「in situ」レベルを選択するか、特定の値を乗算した現在のスプレッドをシフト値として使用する必要があります。ダブルスプレッドは通常、ストップレベルのなめらかな調整に使用されます。各銘柄の取引オブジェクトに乗数を設定できるように、乗数と戻り値および設定メソッドを取引オブジェクトに追加します。TradeObj.mqhファイルでCTradeObj取引オブジェクトクラスに必要な変化を追加します。
クラスのprivateセクションで、バーを構築するための価格タイプの保存およびストップレベルを調整するスプレッド乗数に使用される2つのクラスメンバー変数を宣言します。
SActions m_datas; MqlTick m_tick; // Tick structure for receiving prices MqlTradeRequest m_request; // Trade request structure MqlTradeResult m_result; // trade request execution result ENUM_SYMBOL_CHART_MODE m_chart_mode; // Price type for constructing bars ENUM_ACCOUNT_MARGIN_MODE m_margin_mode; // Margin calculation mode ENUM_ORDER_TYPE_FILLING m_type_filling; // Filling policy ENUM_ORDER_TYPE_TIME m_type_expiration; // Order expiration type int m_symbol_expiration_flags; // Flags of order expiration modes for a trading object symbol ulong m_magic; // Magic number string m_symbol; // Symbol string m_comment; // Comment ulong m_deviation; // Slippage in points double m_volume; // Volume datetime m_expiration; // Order expiration time (for ORDER_TIME_SPECIFIED type order) bool m_async_mode; // Flag of asynchronous sending of a trade request ENUM_LOG_LEVEL m_log_level; // Logging level int m_stop_limit; // Distance of placing a StopLimit order in points bool m_use_sound; // The flag of using sounds of the object trading events uint m_multiplier; // The spread multiplier to adjust levels relative to StopLevel
クラスのpublicセクションに、スプレッド乗数を設定するメソッドと乗数値を返すメソッドを追加します。
public: //--- コンストラクタ CTradeObj(); //--- Set/return the spread multiplier void SetSpreadMultiplier(const uint value) { this.m_multiplier=(value==0 ? 1 : value); } uint SpreadMultiplier(void) const { return this.m_multiplier; } //--- Set default values
スプレッド乗数を設定するとき、メソッドに渡される値がゼロに等しいかどうかを確認します。等しい場合、1を割り当てます。
また、クラスのpublicセクションに、取引リクエストエラーコードを設定するメソッドと取引リクエストエラーコードの説明を設定するメソッドの2つのメソッドを追加します。
//--- Set the error code in the last request result void SetResultRetcode(const uint retcode) { this.m_result.retcode=retcode; } void SetResultComment(const string comment) { this.m_result.comment=comment; } //--- Data on the last request result:
クラスコンストラクタで、スプレッド乗数にデフォルト値の1を割り当てます。
//+------------------------------------------------------------------+ //| コンストラクタ | //+------------------------------------------------------------------+ CTradeObj::CTradeObj(void) : m_magic(0), m_deviation(5), m_stop_limit(0), m_expiration(0), m_async_mode(false), m_type_filling(ORDER_FILLING_FOK), m_type_expiration(ORDER_TIME_GTC), m_comment(::MQLInfoString(MQL_PROGRAM_NAME)+" by DoEasy"), m_log_level(LOG_LEVEL_ERROR_MSG) { //--- Margin calculation mode this.m_margin_mode= ( #ifdef __MQL5__ (ENUM_ACCOUNT_MARGIN_MODE)::AccountInfoInteger(ACCOUNT_MARGIN_MODE) #else /* MQL4 */ ACCOUNT_MARGIN_MODE_RETAIL_HEDGING #endif ); //--- Spread multiplier this.m_multiplier=1; //--- Set default sounds and flags of using sounds this.m_use_sound=false; this.InitSounds(); } //+------------------------------------------------------------------+
取引オブジェクトパラメータのデフォルト値を定義するInit()メソッドで、バー構築価格を格納するm_chart_mode変数値を設定します。
//+------------------------------------------------------------------+ //| Set default values | //+------------------------------------------------------------------+ void CTradeObj::Init(const string symbol, const ulong magic, const double volume, const ulong deviation, const int stoplimit, const datetime expiration, const bool async_mode, const ENUM_ORDER_TYPE_FILLING type_filling, const ENUM_ORDER_TYPE_TIME type_expiration, ENUM_LOG_LEVEL log_level) { this.SetSymbol(symbol); this.SetMagic(magic); this.SetDeviation(deviation); this.SetVolume(volume); this.SetExpiration(expiration); this.SetTypeFilling(type_filling); this.SetTypeExpiration(type_expiration); this.SetAsyncMode(async_mode); this.SetLogLevel(log_level); this.m_symbol_expiration_flags=(int)::SymbolInfoInteger(this.m_symbol,SYMBOL_EXPIRATION_MODE); this.m_volume=::SymbolInfoDouble(this.m_symbol,SYMBOL_VOLUME_MIN); this.m_chart_mode=#ifdef __MQL5__ (ENUM_SYMBOL_CHART_MODE)::SymbolInfoInteger(this.m_symbol,SYMBOL_CHART_MODE) #else SYMBOL_CHART_MODE_BID #endif ; } //+------------------------------------------------------------------+
ここでは、MQL5ではSYMBOL_CHART_MODE IDでSymbolInfoInteger()を使用してデータを取得する一方、MQL4では、 Bid価格によってバーを構築します。
ここで、各取引メソッドに取引サーバの戻り構造体を追加する必要があります。
例としてポジションを開くメソッドを使用してみましょう。
//+------------------------------------------------------------------+ //| Open a position | //+------------------------------------------------------------------+ bool CTradeObj::OpenPosition(const ENUM_POSITION_TYPE type, const double volume, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const ulong deviation=ULONG_MAX) { ::ResetLastError(); //--- If failed to get the current prices, write the error code and description, send the message to the journal and return 'false' if(!::SymbolInfoTick(this.m_symbol,this.m_tick)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_GET_PRICE),CMessage::Text(this.m_result.retcode)); return false; } //--- Clear the structures ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Fill in the request structure this.m_request.action = TRADE_ACTION_DEAL; this.m_request.symbol = this.m_symbol; this.m_request.magic = (magic==ULONG_MAX ? this.m_magic : magic); this.m_request.type = OrderTypeByPositionType(type); this.m_request.price = (type==POSITION_TYPE_BUY ? this.m_tick.ask : (this.m_chart_mode==SYMBOL_CHART_MODE_BID ? this.m_tick.bid : this.m_tick.last)); this.m_request.volume = volume; this.m_request.sl = sl; this.m_request.tp = tp; this.m_request.deviation= (deviation==ULONG_MAX ? this.m_deviation : deviation); this.m_request.comment = (comment==NULL ? this.m_comment : comment); //--- Return the result of sending a request to the server #ifdef __MQL5__ return(!this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result)); #else ::ResetLastError(); int ticket=::OrderSend(m_request.symbol,m_request.type,m_request.volume,m_request.price,(int)m_request.deviation,m_request.sl,m_request.tp,m_request.comment,(int)m_request.magic,m_request.expiration,clrNONE); if(ticket!=WRONG_VALUE) { ::SymbolInfoTick(this.m_symbol,this.m_tick); this.m_result.retcode=::GetLastError(); this.m_result.ask=this.m_tick.ask; this.m_result.bid=this.m_tick.bid; this.m_result.deal=ticket; this.m_result.price=(::OrderSelect(ticket,SELECT_BY_TICKET) ? ::OrderOpenPrice() : this.m_request.price); this.m_result.volume=(::OrderSelect(ticket,SELECT_BY_TICKET) ? ::OrderLots() : this.m_request.volume); this.m_result.comment=CMessage::Text(this.m_result.retcode); return true; } else { ::SymbolInfoTick(this.m_symbol,this.m_tick); this.m_result.retcode=::GetLastError(); this.m_result.ask=this.m_tick.ask; this.m_result.bid=this.m_tick.bid; this.m_result.comment=CMessage::Text(this.m_result.retcode); return false; } #endif } //+------------------------------------------------------------------+
ここでは、MQL5では依然と同様にOrderSend()関数操作の結果を返す一方、MQL4ではMQL4注文送信関数によって戻されたチケット番号を確認します。取引リクエストが正常に実行されると、関数は出された注文チケットを返します。エラーはWRONG_VALUEをもたらします。したがって、関数が-1以外の値を返すことを確認してください。確認できた場合、銘柄の相場を更新し、適切なデータを使用して取引リクエストの結果構造体に入力してtrueを返します。関数は正常に実行されます。
注文送信関数が-1を返す場合、最後のエラーコード、現在の価格、最後のエラーコードの定義を取引リクエストの結果構造体に追加します。残りの構造体フィールドはゼロのままです。結果として、false(取引注文送信エラー)を返します。
この改良により、取引注文の送信結果に関係なく、クラスメソッドを使用してリクエスト結果を確認できます。
//--- Data on the last request result: //--- Return (1) operation result code, (2) performed deal ticket, (3) placed order ticket, //--- (4) deal volume confirmed by a broker, (5) deal price confirmed by a broker, //--- (6) current market Bid (requote) price, (7) current market Ask (requote) price //--- (8) broker comment to operation (by default, it is filled by the trade server return code description), //--- (9) request ID set by the terminal when sending, (10) external trading system return code uint GetResultRetcode(void) const { return this.m_result.retcode; } ulong GetResultDeal(void) const { return this.m_result.deal; } ulong GetResultOrder(void) const { return this.m_result.order; } double GetResultVolume(void) const { return this.m_result.volume; } double GetResultPrice(void) const { return this.m_result.price; } double GetResultBid(void) const { return this.m_result.bid; } double GetResultAsk(void) const { return this.m_result.ask; } string GetResultComment(void) const { return this.m_result.comment; } uint GetResultRequestID(void) const { return this.m_result.request_id; } uint GetResultRetcodeEXT(void) const { return this.m_result.retcode_external;}
残りの取引メソッドは同様の方法で最終化されているため、ここで検討する意味はありません。必要なことはすべて以下の添付ファイルで見つけることができます。
Account.mqhファイルのCAccount口座オブジェクトクラスで、ポジションを開くために必要な証拠金を返すまたは未決注文を設定するメソッドを改善します。
//+------------------------------------------------------------------+ //| Return the margin required for opening a position | //| or placing a pending order | //+------------------------------------------------------------------+ double CAccount::MarginForAction(const ENUM_ORDER_TYPE action,const string symbol,const double volume,const double price) const { double margin=EMPTY_VALUE; #ifdef __MQL5__ return(!::OrderCalcMargin(ENUM_ORDER_TYPE(action%2),symbol,volume,price,margin) ? EMPTY_VALUE : margin); #else return this.MarginFree()-::AccountFreeMarginCheck(symbol,ENUM_ORDER_TYPE(action%2),volume); #endif } //+------------------------------------------------------------------+
ここで追加する必要があるのは、メソッドに渡されるMQL5およびMQL4関数がこの注文型のみを必要とするため、メソッドに渡される注文タイプをORDER_TYPE_BUYまたはORDER_TYPE_SELLの2つの可能な値に変換することです。
覚えているかもしれませんが、注文型定数を2で除算した残りの部分は、常に2つの値のいずれかを返します。
- 0 (ORDER_TYPE_BUY)
- または(ORDER_TYPE_SELL)
これは、正確な注文タイプに変換するために必要なものです。
Trading.mqhファイルのCTradingクラスの取引注文価格パラメータを入力するためのカスタム構造体は既に作成しました。
struct SDataPrices { double open; // Open price double limit; // Limit order price double sl; // StopLoss price double tp; // TakeProfit price }; SDataPrices m_req_price; // Trade request prices
しかし、MQLにはMqlTradeRequest構造体があるため、冗長な構造体を避けるためには、
クラスのprivateセクションでカスタム構造体を標準構造体に置き換え、さらに
取引リクエストにエラーソースフラグを格納するためのクラスメンバー変数および
取引注文の送信時にエラーが発生した場合のEAの動作を保存する変数を宣言します。
//+------------------------------------------------------------------+ //| Trading class | //+------------------------------------------------------------------+ class CTrading { private: CAccount *m_account; // Pointer to the current account object CSymbolsCollection *m_symbols; // Pointer to the symbol collection list CMarketCollection *m_market; // Pointer to the list of the collection of market orders and positions CHistoryCollection *m_history; // Pointer to the list of the collection of historical orders and deals CArrayInt m_list_errors; // Error list bool m_is_trade_disable; // Flag disabling trading bool m_use_sound; // The flag of using sounds of the object trading events ENUM_LOG_LEVEL m_log_level; // Logging level MqlTradeRequest m_request; // Trade request prices ENUM_TRADE_REQUEST_ERR_FLAGS m_error_reason_flags; // Flags of error source in a trading method ENUM_ERROR_HANDLING_BEHAVIOR m_err_handling_behavior; // Behavior when handling error //--- Add the error code to the list bool AddErrorCodeToList(const int error_code);
また、プライベートクラスのprivateセクションで、エラーソースフラグを格納する変数内のフラグを返すメソッド、
エラーリストにエラーコードの存在を返すメソッド、および
エラーを処理するときにアクションを配置して返すメソッドを書きます。
//--- Return the flag presence in the trading event error reason bool IsPresentErrorFlag(const int code) const { return (this.m_error_reason_flags & code)==code; } //--- Return the error code in the list bool IsPresentErorCode(const int code) { this.m_list_errors.Sort(); return this.m_list_errors.Search(code)>WRONG_VALUE; } //--- Set/return the error handling action void SetErrorHandlingBehavior(const ENUM_ERROR_HANDLING_BEHAVIOR behavior) { this.m_err_handling_behavior=behavior; } ENUM_ERROR_HANDLING_BEHAVIOR ErrorHandlingBehavior(void) const { return this.m_err_handling_behavior; } //--- Check trading limitations
資金不足を確認するメソッドから、操作ログで資金不足メッセージを表示するためのコードを削除します。
if(money_free<=0 #ifdef __MQL4__ || ::GetLastError()==134 #endif ) { if(this.m_log_level>LOG_LEVEL_NO_MSG) { //--- create a message text string message= ( symbol_obj.Name()+" "+::DoubleToString(volume,symbol_obj.DigitsLot())+" "+ ( order_type>ORDER_TYPE_SELL ? OrderTypeDescription(order_type,false,false) : PositionTypeDescription(PositionTypeByOrderType(order_type)) )+" ("+::DoubleToString(money_free,(int)this.m_account.CurrencyDigits())+")" ); //--- display a journal message if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_NOT_ENOUTH_MONEY_FOR),": ",message); this.AddErrorCodeToList(MSG_LIB_TEXT_NOT_ENOUTH_MONEY_FOR); } return false; }
これで、資金不足メッセージが別のメソッドから表示されるようになります。
現在のメソッドでは単に、エラーリストでエラーを検索するように指示するフラグを追加し、エラーリストにエラーコードを追加します。
//+------------------------------------------------------------------+ //| Check if the funds are sufficient | //+------------------------------------------------------------------+ bool CTrading::CheckMoneyFree(const double volume, const double price, const ENUM_ORDER_TYPE order_type, const CSymbol *symbol_obj, const string source_method, const bool mess=true) { ::ResetLastError(); //--- Get the type of a market order by a trading operation type ENUM_ORDER_TYPE action=this.DirectionByActionType((ENUM_ACTION_TYPE)order_type); //--- Get the value of free funds to be left after conducting a trading operation double money_free= ( //--- For MQL5, calculate the difference between free funds and the funds required to conduct a trading operation #ifdef __MQL5__ this.m_account.MarginFree()-this.m_account.MarginForAction(action,symbol_obj.Name(),volume,price) //--- For MQL4, use the operation result of the standard function returning the amount of funds left #else/*__MQL4__*/::AccountFreeMarginCheck(symbol_obj.Name(),action,volume) #endif ); //--- If no free funds are left, inform of that and return 'false' if(money_free<=0 #ifdef __MQL4__ || ::GetLastError()==134 #endif ) { this.m_error_reason_flags &=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_LIB_TEXT_NOT_ENOUTH_MONEY_FOR); return false; } //--- Verification successful return true; } //+------------------------------------------------------------------+
ストップ注文および未決注文の価格を修正するメソッド、取引注文のボリュームを修正するメソッド、エラーの処理方法を指定するメソッド、および取引注文エラーを修正するメソッドを宣言します。
//--- Return the correct (1) StopLoss, (2) TakeProfit and (3) order placement price double CorrectStopLoss(const ENUM_ORDER_TYPE order_type, const double price_set, const double stop_loss, const CSymbol *symbol_obj, const uint spread_multiplier=1); double CorrectTakeProfit(const ENUM_ORDER_TYPE order_type, const double price_set, const double take_profit, const CSymbol *symbol_obj, const uint spread_multiplier=1); double CorrectPricePending(const ENUM_ORDER_TYPE order_type, const double price_set, const double price, const CSymbol *symbol_obj, const uint spread_multiplier=1); //--- Return the volume, at which it is possible to open a position double CorrectVolume(const double price, const ENUM_ORDER_TYPE order_type, const CSymbol *symbol_obj, const string source_method); //--- Return the error handling method ENUM_ERROR_CODE_PROCESSING_METHOD ResultProccessingMethod(void); //--- Correct errors ENUM_ERROR_CODE_PROCESSING_METHOD RequestErrorsCorrecting(MqlTradeRequest &request,const ENUM_ORDER_TYPE order_type,const uint spread_multiplier,CSymbol *symbol_obj); public:
制限とエラーをチェックするメソッドの仕様を補完し、返された型をboolからENUM_ERROR_CODE_PROCESSING_METHODに置き換えます。
//--- Check limitations and errors ENUM_ERROR_CODE_PROCESSING_METHOD CheckErrors(const double volume, const double price, const ENUM_ACTION_TYPE action, const ENUM_ORDER_TYPE order_type, CSymbol *symbol_obj, const CTradeObj *trade_obj, const string source_method, const double limit=0, double sl=0, double tp=0);
このメソッドはより完全になりました。メソッドは取引注文のエラーを修正するための可能なメソッドをすぐに確認し、検出されたエラーを処理する方法を戻します。以前は、単に確認が成功したというフラグを返していました。
スプレッド乗数を設定するメソッドを宣言します。
//--- Set the following for symbol trading objects: //--- (1) correct filling policy, (2) filling policy, //--- (3) correct order expiration type, (4) order expiration type, //--- (5) magic number, (6) comment, (7) slippage, (8) volume, (9) order expiration date, //--- (10) the flag of asynchronous sending of a trading request, (11) logging level, (12) spread multiplier void SetCorrectTypeFilling(const ENUM_ORDER_TYPE_FILLING type=ORDER_FILLING_FOK,const string symbol=NULL); void SetTypeFilling(const ENUM_ORDER_TYPE_FILLING type=ORDER_FILLING_FOK,const string symbol=NULL); void SetCorrectTypeExpiration(const ENUM_ORDER_TYPE_TIME type=ORDER_TIME_GTC,const string symbol=NULL); void SetTypeExpiration(const ENUM_ORDER_TYPE_TIME type=ORDER_TIME_GTC,const string symbol=NULL); void SetMagic(const ulong magic,const string symbol=NULL); void SetComment(const string comment,const string symbol=NULL); void SetDeviation(const ulong deviation,const string symbol=NULL); void SetVolume(const double volume=0,const string symbol=NULL); void SetExpiration(const datetime expiration=0,const string symbol=NULL); void SetAsyncMode(const bool mode=false,const string symbol=NULL); void SetLogLevel(const ENUM_LOG_LEVEL log_level=LOG_LEVEL_ERROR_MSG,const string symbol=NULL); void SetSpreadMultiplier(const uint value=1,const string symbol=NULL);
EAで取引を有効にするフラグを設定するメソッドと返すメソッドを追加します。
//--- Set/return the flag enabling sounds void SetUseSounds(const bool flag); bool IsUseSounds(void) const { return this.m_use_sound; } //--- Set/return the flag enabling trading void SetTradingDisableFlag(const bool flag) { this.m_is_trade_disable=flag; } bool IsTradingDisable(void) const { return this.m_is_trade_disable;} //--- Open (1) Buy, (2) Sell position
アカウントでの取引の完全な禁止など、検出時にそれ以上の取引を妨げるエラーがある可能性があります。このフラグは、そのようなエラーが検出されると設定され、それ以上の無駄な取引注文を送信できなくなります。
クラスコンストラクタで、取引を無効にするフラグをリセットし、取引リクエストの場合のデフォルトのEA動作を「正しいパラメータ」として設定します。
//+------------------------------------------------------------------+ //| コンストラクタ | //+------------------------------------------------------------------+ CTrading::CTrading() { this.m_list_errors.Clear(); this.m_list_errors.Sort(); this.m_log_level=LOG_LEVEL_ALL_MSG; this.m_is_trade_disable=false; this.m_err_handling_behavior=ERROR_HANDLING_BEHAVIOR_CORRECT; ::ZeroMemory(this.m_request); } //+------------------------------------------------------------------+
取引方法のエラー時のEAの動作は、EA設定から設定できます。ただし、すべてのハンドラーの準備ができるまで、自動修正メソッドを使用します。
取引サーバの戻りコードを処理するメソッドは現在、成功フラグのみを返します。
//+------------------------------------------------------------------+ //| Return the error handling method | //+------------------------------------------------------------------+ ENUM_ERROR_CODE_PROCESSING_METHOD CTrading::ResultProccessingMethod(void) { return ERROR_CODE_PROCESSING_METHOD_OK; } //+------------------------------------------------------------------+
何故でしょうか。取引サーバの戻りコードを処理するメソッドは次の記事で実装するため、本稿では考慮しません。ただし、簡素化されてはいますが、このメソッドはすでに説明および実装されています。
以下は、取引注文のエラーを修正するメソッドの実装です。
//+------------------------------------------------------------------+ //| Correct errors | //+------------------------------------------------------------------+ ENUM_ERROR_CODE_PROCESSING_METHOD CTrading::RequestErrorsCorrecting(MqlTradeRequest &request, const ENUM_ORDER_TYPE order_type, const uint spread_multiplier, CSymbol *symbol_obj) { //--- The empty error list means no errors are detected, return success int total=this.m_list_errors.Total(); if(total==0) return ERROR_CODE_PROCESSING_METHOD_OK; //--- In the current implementation, all these codes are temporarily handled by interrupting a trading request if( this.IsPresentErorCode(MSG_LIB_TEXT_ACCOUNT_NOT_TRADE_ENABLED) || // Trading is disabled for the current account this.IsPresentErorCode(MSG_LIB_TEXT_ACCOUNT_EA_NOT_TRADE_ENABLED) || // Trading on the trading server side is disabled for EAs on the current account this.IsPresentErorCode(MSG_LIB_TEXT_TERMINAL_NOT_TRADE_ENABLED) || // Trading operations are disabled in the terminal this.IsPresentErorCode(MSG_LIB_TEXT_EA_NOT_TRADE_ENABLED) || // Trading operations are disabled for the EA this.IsPresentErorCode(MSG_SYM_TRADE_MODE_DISABLED) || // Trading on a symbol is disabled this.IsPresentErorCode(MSG_SYM_TRADE_MODE_CLOSEONLY) || // Close only this.IsPresentErorCode(MSG_SYM_MARKET_ORDER_DISABLED) || // Market orders disabled this.IsPresentErorCode(MSG_SYM_LIMIT_ORDER_DISABLED) || // Limit orders are disabled this.IsPresentErorCode(MSG_SYM_STOP_ORDER_DISABLED) || // Stop orders are disabled this.IsPresentErorCode(MSG_SYM_STOP_LIMIT_ORDER_DISABLED) || // StopLimit orders are disabled this.IsPresentErorCode(MSG_SYM_TRADE_MODE_SHORTONLY) || // Only short positions are allowed this.IsPresentErorCode(MSG_SYM_TRADE_MODE_LONGONLY) || // Only long positions are allowed this.IsPresentErorCode(MSG_SYM_CLOSE_BY_ORDER_DISABLED) || // CloseBy orders are disabled this.IsPresentErorCode(MSG_LIB_TEXT_MAX_VOLUME_LIMIT_EXCEEDED) || // Exceeded maximum allowed aggregate volume of orders and positions in one direction this.IsPresentErorCode(MSG_LIB_TEXT_CLOSE_BY_ORDERS_DISABLED) || // Close by is disabled this.IsPresentErorCode(MSG_LIB_TEXT_CLOSE_BY_SYMBOLS_UNEQUAL) || // Symbols of opposite positions are not equal this.IsPresentErorCode(MSG_LIB_TEXT_UNSUPPORTED_PRICE_TYPE_IN_REQ) || // Unsupported price parameter type in a request this.IsPresentErorCode(MSG_LIB_TEXT_TRADING_DISABLE) || // Trading disabled for the EA until the reason is eliminated this.IsPresentErorCode(10006) || // Request rejected this.IsPresentErorCode(10011) || // Request handling error this.IsPresentErorCode(10012) || // Request rejected due to expiration this.IsPresentErorCode(10013) || // Invalid request this.IsPresentErorCode(10017) || // Trading disabled this.IsPresentErorCode(10018) || // Market closed this.IsPresentErorCode(10023) || // Order status changed this.IsPresentErorCode(10025) || // No changes in the request this.IsPresentErorCode(10026) || // Auto trading disabled by server this.IsPresentErorCode(10027) || // Auto trading disabled by client terminal this.IsPresentErorCode(10032) || // Transaction is allowed for live accounts only this.IsPresentErorCode(10033) || // The maximum number of pending orders is reached this.IsPresentErorCode(10034) // You have reached the maximum order and position volume for this symbol ) return ERROR_CODE_PROCESSING_METHOD_EXIT; //--- View the full list of errors and correct trading request parameters for(int i=0;i<total;i++) { int err=this.m_list_errors.At(i); if(err==NULL) continue; switch(err) { //--- Correct an invalid volume and stop levels in a trading request case MSG_LIB_TEXT_REQ_VOL_LESS_MIN_VOLUME : case MSG_LIB_TEXT_REQ_VOL_MORE_MAX_VOLUME : case MSG_LIB_TEXT_INVALID_VOLUME_STEP : request.volume=symbol_obj.NormalizedLot(request.volume); break; case MSG_SYM_SL_ORDER_DISABLED : request.sl=0; break; case MSG_SYM_TP_ORDER_DISABLED : request.tp=0; break; case MSG_LIB_TEXT_PR_LESS_STOP_LEVEL : request.price=this.CorrectPricePending(order_type,request.price,0,symbol_obj,spread_multiplier); break; case MSG_LIB_TEXT_SL_LESS_STOP_LEVEL : request.sl=this.CorrectStopLoss(order_type,request.price,request.sl,symbol_obj,spread_multiplier); break; case MSG_LIB_TEXT_TP_LESS_STOP_LEVEL : request.tp=this.CorrectTakeProfit(order_type,request.price,request.tp,symbol_obj,spread_multiplier); break; //--- If unable to select the position lot, return "abort trading attempt" since the funds are insufficient even for the minimum lot case MSG_LIB_TEXT_NOT_ENOUTH_MONEY_FOR : request.volume=this.CorrectVolume(request.volume,request.price,order_type,symbol_obj,DFUN); if(request.volume==0) return ERROR_CODE_PROCESSING_METHOD_EXIT; break; //--- Proximity to the order activation level is handled by five-second waiting - during this time, the price may go beyond the freeze level case MSG_LIB_TEXT_SL_LESS_FREEZE_LEVEL : case MSG_LIB_TEXT_TP_LESS_FREEZE_LEVEL : case MSG_LIB_TEXT_PR_LESS_FREEZE_LEVEL : return (ENUM_ERROR_CODE_PROCESSING_METHOD)5000; // ERROR_CODE_PROCESSING_METHOD_WAIT - wait 5 seconds default: break; } } return ERROR_CODE_PROCESSING_METHOD_OK; } //+------------------------------------------------------------------+
メソッドのロジックは、コードのコメントで説明されています。つまり、まだ処理できないエラーコードを検出したら、「取引試行中止」処理メソッドを返します。エラーを修正できる場合は、パラメータ値を修正してОКを返します。
以下は、取引制限と取引リクエストエラーを確認するメソッドを改善したものです。
//+------------------------------------------------------------------+ //| Check limitations and errors | //+------------------------------------------------------------------+ ENUM_ERROR_CODE_PROCESSING_METHOD CTrading::CheckErrors(const double volume, const double price, const ENUM_ACTION_TYPE action, const ENUM_ORDER_TYPE order_type, CSymbol *symbol_obj, const CTradeObj *trade_obj, const string source_method, const double limit=0, double sl=0, double tp=0) { //--- Check the previously set flag disabling trading for an EA if(this.IsTradingDisable()) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_FATAL_ERROR; if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE)); return ERROR_CODE_PROCESSING_METHOD_DISABLE; } //--- result of all checks and error flags this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_NO_ERROR; bool res=true; //--- Clear the error list this.m_list_errors.Clear(); this.m_list_errors.Sort(); //--- Check trading limitations res &=this.CheckTradeConstraints(volume,action,symbol_obj,source_method,sl,tp); //--- Check the funds sufficiency for opening positions/placing orders if(action<ACTION_TYPE_CLOSE_BY) res &=this.CheckMoneyFree(volume,price,order_type,symbol_obj,source_method); //--- Check parameter values by StopLevel and FreezeLevel res &=this.CheckLevels(action,order_type,price,limit,sl,tp,symbol_obj,source_method); //--- If there are limitations, display the header and the error list if(!res) { //--- Request was rejected before sending to the server due to: int total=this.m_list_errors.Total(); if(this.m_log_level>LOG_LEVEL_NO_MSG) { //--- For MQL5, first display the list header followed by the error list #ifdef __MQL5__ ::Print(source_method,CMessage::Text(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK ? MSG_LIB_TEXT_REQUEST_REJECTED_DUE : MSG_LIB_TEXT_INVALID_REQUEST)); for(int i=0;i<total;i++) ::Print((total>1 ? string(i+1)+". " : ""),CMessage::Text(m_list_errors.At(i))); //--- For MQL4, the journal messages are displayed in the reverse order: the error list in the reverse loop is followed by the list header #else for(int i=total-1;i>WRONG_VALUE;i--) ::Print((total>1 ? string(i+1)+". " : ""),CMessage::Text(m_list_errors.At(i))); ::Print(source_method,CMessage::Text(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK ? MSG_LIB_TEXT_REQUEST_REJECTED_DUE : MSG_LIB_TEXT_INVALID_REQUEST)); #endif } //--- If the action is performed at the "abort trading operation" error if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK) return ERROR_CODE_PROCESSING_METHOD_EXIT; //--- If the action is performed at the "create a pending request" error if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_PENDING_REQUEST) return ERROR_CODE_PROCESSING_METHOD_PENDING; //--- If the action is performed at the "correct parameters" error if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_CORRECT) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(CMessage::Text(MSG_LIB_TEXT_CORRECTED_TRADE_REQUEST)); //--- Return the result of an attempt to correct the request parameters return this.RequestErrorsCorrecting(this.m_request,order_type,trade_obj.SpreadMultiplier(),symbol_obj); } } //--- No limitations return ERROR_CODE_PROCESSING_METHOD_OK; } //+------------------------------------------------------------------+
補完されたコードは黄色でマークされています。取引を無効にするフラグが最初にメソッドで確認されるようになっています。設定されている場合、「EAの取引を無効にする」エラー処理タイプが返されます。次に、エラー時の指定されたEAの動作とエラーコードに応じて、必要なエラー処理メソッドが返されます。エラーがない場合、エラー処理を必要としないコードが返されます。
取引制限を確認する方法は、さまざまなエラータイプの存在とそれらの処理方法を示す必要なフラグの追加に関連する複数の同様の変更を受けています。
メソッドで実行されるすべてのアクションとそのロジックは、コードのコメントで詳細に説明されています。したがって、最終的なメソッドを見てみましょう。
//+------------------------------------------------------------------+ //| Check trading limitations | //+------------------------------------------------------------------+ bool CTrading::CheckTradeConstraints(const double volume, const ENUM_ACTION_TYPE action_type, const CSymbol *symbol_obj, const string source_method, double sl=0, double tp=0) { //--- the result of conducting all checks bool res=true; //--- Check connection with the trade server (not in the test mode) if(!::TerminalInfoInteger(TERMINAL_CONNECTED)) { if(!::MQLInfoInteger(MQL_TESTER)) { //--- Write the error code to the list and return 'false' - there is no point in further checks this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(10031); return false; } } //--- Check if trading is enabled for an account (if there is a connection with the trade server) else if(!this.m_account.TradeAllowed()) { //--- Write the error code to the list and return 'false' - there is no point in further checks this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_LIB_TEXT_ACCOUNT_NOT_TRADE_ENABLED); return false; } //--- Check if trading is allowed for any EAs/scripts for the current account if(!this.m_account.TradeExpert()) { //--- Write the error code to the list and return 'false' - there is no point in further checks this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_LIB_TEXT_ACCOUNT_EA_NOT_TRADE_ENABLED); return false; } //--- Check if auto trading is allowed in the terminal. //--- AutoTrading button (Options --> Expert Advisors --> "Allowed automated trading") if(!::TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) { //--- Write the error code to the list and return 'false' - there is no point in further checks this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_LIB_TEXT_TERMINAL_NOT_TRADE_ENABLED); return false; } //--- Check if auto trading is allowed for the current EA. //--- (F7 --> Common --> Allow Automated Trading) if(!::MQLInfoInteger(MQL_TRADE_ALLOWED)) { //--- Write the error code to the list and return 'false' - there is no point in further checks this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_LIB_TEXT_EA_NOT_TRADE_ENABLED); return false; } //--- Check if trading is enabled on a symbol. //--- If trading is disabled, write the error code to the list and return 'false' - there is no point in further checks if(symbol_obj.TradeMode()==SYMBOL_TRADE_MODE_DISABLED) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_SYM_TRADE_MODE_DISABLED); return false; } //--- If not closing/removal/modification if(action_type<ACTION_TYPE_CLOSE_BY) { //--- In case of close-only, write the error code to the list and return 'false' - there is no point in further checks if(symbol_obj.TradeMode()==SYMBOL_TRADE_MODE_CLOSEONLY) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_SYM_TRADE_MODE_CLOSEONLY); return false; } //--- Check the minimum volume if(volume<symbol_obj.LotsMin()) { //--- The volume in a request is less than the minimum allowed one. //--- add the error code to the list this.m_error_reason_flags &=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_LIB_TEXT_REQ_VOL_LESS_MIN_VOLUME); //--- If the EA behavior during the trading error is set to "abort trading operation", //--- return 'false' - there is no point in further checks if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK) return false; //--- If the EA behavior during a trading error is set to //--- "correct parameters" or "create a pending request", //--- write 'false' to the result else res &=false; } //--- Check the maximum volume else if(volume>symbol_obj.LotsMax()) { //--- The volume in the request exceeds the maximum acceptable one. //--- add the error code to the list this.m_error_reason_flags &=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_LIB_TEXT_REQ_VOL_MORE_MAX_VOLUME); //--- If the EA behavior during the trading error is set to "abort trading operation", //--- return 'false' - there is no point in further checks if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK) return false; //--- If the EA behavior during a trading error is set to //--- "correct parameters" or "create a pending request", //--- write 'false' to the result else res &=false; } //--- Check the minimum volume gradation double step=symbol_obj.LotsStep(); if(fabs((int)round(volume/step)*step-volume)>0.0000001) { //--- The volume in the request is not a multiple of the minimum gradation of the lot change step //--- add the error code to the list this.m_error_reason_flags &=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_LIB_TEXT_INVALID_VOLUME_STEP); //--- If the EA behavior during the trading error is set to "abort trading operation", //--- return 'false' - there is no point in further checks if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK) return false; //--- If the EA behavior during a trading error is set to //--- "correct parameters" or "create a pending request", //--- write 'false' to the result else res &=false; } } //--- When opening a position if(action_type<ACTION_TYPE_BUY_LIMIT) { //--- Check if sending market orders is allowed on a symbol. //--- If using market orders is disabled, write the error code to the list and return 'false' - there is no point in further checks if(!symbol_obj.IsMarketOrdersAllowed()) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_SYM_MARKET_ORDER_DISABLED); return false; } } //--- When placing a pending order else if(action_type>ACTION_TYPE_SELL && action_type<ACTION_TYPE_CLOSE_BY) { //--- If there is a limitation on the number of pending orders on an account and placing a new order exceeds it if(this.m_account.LimitOrders()>0 && this.OrdersTotalAll()+1 > this.m_account.LimitOrders()) { //--- The limit on the number of pending orders is reached - write the error code to the list and return 'false' - there is no point in further checks this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(10033); return false; } //--- Check if placing limit orders is allowed on a symbol. if(action_type==ACTION_TYPE_BUY_LIMIT || action_type==ACTION_TYPE_SELL_LIMIT) { //--- If setting limit orders is disabled, write the error code to the list and return 'false' - there is no point in further checks if(!symbol_obj.IsLimitOrdersAllowed()) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_SYM_LIMIT_ORDER_DISABLED); return false; } } //--- Check if placing stop orders is allowed on a symbol. else if(action_type==ACTION_TYPE_BUY_STOP || action_type==ACTION_TYPE_SELL_STOP) { //--- If setting stop orders is disabled, write the error code to the list and return 'false' - there is no point in further checks if(!symbol_obj.IsStopOrdersAllowed()) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_SYM_STOP_ORDER_DISABLED); return false; } } //--- For MQL5, check if placing stop limit orders is allowed on a symbol. #ifdef __MQL5__ else if(action_type==ACTION_TYPE_BUY_STOP_LIMIT || action_type==ACTION_TYPE_SELL_STOP_LIMIT) { //--- If setting stop limit orders is disabled, write the error code to the list and return 'false' - there is no point in further checks if(!symbol_obj.IsStopLimitOrdersAllowed()) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_SYM_STOP_LIMIT_ORDER_DISABLED); return false; } } #endif } //--- In case of opening/placing/modification if(action_type!=ACTION_TYPE_CLOSE_BY) { //--- If not modification if(action_type!=ACTION_TYPE_MODIFY) { //--- When buying, check if long trading is enabled on a symbol if(this.DirectionByActionType(action_type)==ORDER_TYPE_BUY) { //--- If only short positions are enabled, write the error code to the list and return 'false' - there is no point in further checks if(symbol_obj.TradeMode()==SYMBOL_TRADE_MODE_SHORTONLY) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_SYM_TRADE_MODE_SHORTONLY); return false; } //--- If a symbol has the limitation on the total volume of an open position and pending orders in the same direction if(symbol_obj.VolumeLimit()>0) { //--- (If the total volume of placed long orders and open long positions)+open volume exceed the maximum one if(this.OrdersTotalVolumeLong()+this.PositionsTotalVolumeLong()+volume > symbol_obj.VolumeLimit()) { //--- Exceeded maximum allowed aggregate volume of orders and positions in one direction //--- write the error code to the list and return 'false' - there is no point in further checks this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_LIB_TEXT_MAX_VOLUME_LIMIT_EXCEEDED); return false; } } } //--- When selling, check if short trading is enabled on a symbol else if(this.DirectionByActionType(action_type)==ORDER_TYPE_SELL) { //--- If only long positions are enabled, write the error code to the list and return 'false' - there is no point in further checks if(symbol_obj.TradeMode()==SYMBOL_TRADE_MODE_LONGONLY) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_SYM_TRADE_MODE_LONGONLY); return false; } //--- If a symbol has the limitation on the total volume of an open position and pending orders in the same direction if(symbol_obj.VolumeLimit()>0) { //--- (If the total volume of placed short orders and open short positions)+open volume exceed the maximum one if(this.OrdersTotalVolumeShort()+this.PositionsTotalVolumeShort()+volume > symbol_obj.VolumeLimit()) { //--- Exceeded maximum allowed aggregate volume of orders and positions in one direction //--- write the error code to the list and return 'false' - there is no point in further checks this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_LIB_TEXT_MAX_VOLUME_LIMIT_EXCEEDED); return false; } } } } //--- If the request features StopLoss and its placing is not allowed if(sl>0 && !symbol_obj.IsStopLossOrdersAllowed()) { //--- add the error code to the list this.m_error_reason_flags &=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_SYM_SL_ORDER_DISABLED); //--- If the EA behavior during the trading error is set to "abort trading operation", //--- return 'false' - there is no point in further checks if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK) return false; //--- If the EA behavior during a trading error is set to //--- "correct parameters" or "create a pending request", //--- write 'false' to the result else res &=false; } //--- If the request features TakeProfit and its placing is not allowed if(tp>0 && !symbol_obj.IsTakeProfitOrdersAllowed()) { //--- add the error code to the list this.m_error_reason_flags &=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_SYM_TP_ORDER_DISABLED); //--- If the EA behavior during the trading error is set to "abort trading operation", //--- return 'false' - there is no point in further checks if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK) return false; //--- If the EA behavior during a trading error is set to //--- "correct parameters" or "create a pending request", //--- write 'false' to the result else res &=false; } } //--- 反対ポジションによる決済 else if(action_type==ACTION_TYPE_CLOSE_BY) { //--- When closing by an opposite position is disabled if(!symbol_obj.IsCloseByOrdersAllowed()) { //--- write the error code to the list and return 'false' this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_LIB_TEXT_CLOSE_BY_ORDERS_DISABLED); return false; } } return res; } //+------------------------------------------------------------------+
StopLevelおよびFreezeLevelによってパラメータ値を返すメソッドで、エラーをエラーリストで表示する必要があることを示すフラグを、検出された各エラーに追加します。
//+------------------------------------------------------------------+ //| Check parameter values by StopLevel and FreezeLevel | //+------------------------------------------------------------------+ bool CTrading::CheckLevels(const ENUM_ACTION_TYPE action, const ENUM_ORDER_TYPE order_type, double price, double limit, double sl, double tp, const CSymbol *symbol_obj, const string source_method) { //--- the result of conducting all checks bool res=true; //--- StopLevel //--- If this is not a position closure/order removal if(action!=ACTION_TYPE_CLOSE && action!=ACTION_TYPE_CLOSE_BY) { //--- When placing a pending order if(action>ACTION_TYPE_SELL) { //--- If the placement distance in points is less than StopLevel if(!this.CheckPriceByStopLevel(order_type,price,symbol_obj)) { //--- add the error code to the list and write 'false' to the result this.m_error_reason_flags &=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_LIB_TEXT_PR_LESS_STOP_LEVEL); res &=false; } } //--- If StopLoss is present if(sl>0) { //--- If StopLoss distance in points from the open price is less than StopLevel double price_open=(action==ACTION_TYPE_BUY_STOP_LIMIT || action==ACTION_TYPE_SELL_STOP_LIMIT ? limit : price); if(!this.CheckStopLossByStopLevel(order_type,price_open,sl,symbol_obj)) { //--- add the error code to the list and write 'false' to the result this.m_error_reason_flags &=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_LIB_TEXT_SL_LESS_STOP_LEVEL); res &=false; } } //--- If TakeProfit is present if(tp>0) { double price_open=(action==ACTION_TYPE_BUY_STOP_LIMIT || action==ACTION_TYPE_SELL_STOP_LIMIT ? limit : price); //--- If TakeProfit distance in points from the open price is less than StopLevel if(!this.CheckTakeProfitByStopLevel(order_type,price_open,tp,symbol_obj)) { //--- add the error code to the list and write 'false' to the result this.m_error_reason_flags &=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_LIB_TEXT_TP_LESS_STOP_LEVEL); res &=false; } } } //--- FreezeLevel //--- If this is a position closure/order removal/modification if(action>ACTION_TYPE_SELL_STOP_LIMIT) { //--- If this is a position if(order_type<ORDER_TYPE_BUY_LIMIT) { //--- StopLoss modification if(sl>0) { //--- If the distance from the price to StopLoss is less than FreezeLevel if(!this.CheckStopLossByFreezeLevel(order_type,sl,symbol_obj)) { //--- add the error code to the list and write 'false' to the result this.m_error_reason_flags &=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_LIB_TEXT_SL_LESS_FREEZE_LEVEL); res &=false; } } //--- TakeProfit modification if(tp>0) { //--- If the distance from the price to StopLoss is less than FreezeLevel if(!this.CheckTakeProfitByFreezeLevel(order_type,tp,symbol_obj)) { //--- add the error code to the list and write 'false' to the result this.m_error_reason_flags &=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_LIB_TEXT_TP_LESS_FREEZE_LEVEL); res &=false; } } } //--- If this is a pending order else { //--- Placement price modification if(price>0) { //--- If the distance from the price to the order activation price is less than FreezeLevel if(!this.CheckPriceByFreezeLevel(order_type,price,symbol_obj)) { //--- add the error code to the list and write 'false' to the result this.m_error_reason_flags &=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_LIB_TEXT_PR_LESS_FREEZE_LEVEL); res &=false; } } } } return res; } //+------------------------------------------------------------------+
取引リクエストの価格を設定する方法で、適切なエラーコードで更新エラーが発生した場合に価格を更新して終了します。
//+------------------------------------------------------------------+ //| Set trading request prices | //+------------------------------------------------------------------+ template <typename PR,typename SL,typename TP,typename PL> bool CTrading::SetPrices(const ENUM_ORDER_TYPE action,const PR price,const SL sl,const TP tp,const PL limit,const string source_method,CSymbol *symbol_obj) { //--- Reset prices ::ZeroMemory(this.m_request); //--- Update all data by symbol if(!symbol_obj.RefreshRates()) { this.AddErrorCodeToList(10021); return false; } //--- Open/close price
また、取引リクエスト価格を設定するメソッドでの価格計算も変更されました。
//--- Calculate the order price switch((int)action) { //--- Pending order case ORDER_TYPE_BUY_LIMIT : this.m_request.price=::NormalizeDouble(symbol_obj.Ask()-price*symbol_obj.Point(),symbol_obj.Digits()); break; case ORDER_TYPE_BUY_STOP : case ORDER_TYPE_BUY_STOP_LIMIT : this.m_request.price=::NormalizeDouble(symbol_obj.Ask()+price*symbol_obj.Point(),symbol_obj.Digits()); break; case ORDER_TYPE_SELL_LIMIT : this.m_request.price=::NormalizeDouble(symbol_obj.BidLast()+price*symbol_obj.Point(),symbol_obj.Digits()); break; case ORDER_TYPE_SELL_STOP : case ORDER_TYPE_SELL_STOP_LIMIT : this.m_request.price=::NormalizeDouble(symbol_obj.BidLast()-price*symbol_obj.Point(),symbol_obj.Digits()); break; //--- Default - current position open prices default : this.m_request.price= ( this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY ? ::NormalizeDouble(symbol_obj.Ask(),symbol_obj.Digits()) : ::NormalizeDouble(symbol_obj.BidLast(),symbol_obj.Digits()) ); break; }
Bid()銘柄オブジェクトクラスのメソッドは、チャート構築モードに応じてBidまたはLast価格を返すBidLast()メソッドに置き換えられました。
以下は、すべての銘柄の取引オブジェクトのスプレッド乗数を設定するメソッドです。
//+------------------------------------------------------------------+ //| Set the spread multiplier | //| for trading objects of all symbols | //+------------------------------------------------------------------+ void CTrading::SetSpreadMultiplier(const uint value=1,const string symbol=NULL) { CSymbol *symbol_obj=NULL; if(symbol==NULL) { CArrayObj *list=this.m_symbols.GetList(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol_obj=list.At(i); if(symbol_obj==NULL) continue; CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) continue; trade_obj.SetSpreadMultiplier(value); } } else { CTradeObj *trade_obj=this.GetTradeObjBySymbol(symbol,DFUN); if(trade_obj==NULL) return; trade_obj.SetSpreadMultiplier(value); } } //+------------------------------------------------------------------+
このメソッドは、乗数値(デフォルトは1)と銘柄名(デフォルトはNULL)を受け取ります。
NULLが銘柄として渡された場合、乗数は既存の銘柄コレクションのすべての銘柄の取引オブジェクトに設定されます。
それ以外の場合、値はメソッドに名前が渡された銘柄の取引オブジェクトに割り当てられます。
新しいエラー処理により、すべての取引メソッドが改善されました。
買いポジションを開くメソッドのコードを考えてみましょう。
//+------------------------------------------------------------------+ //| Open Buy position | //+------------------------------------------------------------------+ template<typename SL,typename TP> bool CTrading::OpenBuy(const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const string comment=NULL, const ulong deviation=ULONG_MAX) { //--- Set the trading request result as 'true' and the error flag as "no errors" bool res=true; this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_NO_ERROR; ENUM_ACTION_TYPE action=ACTION_TYPE_BUY; ENUM_ORDER_TYPE order_type=ORDER_TYPE_BUY; //--- Get a symbol object by a symbol name. If failed to get CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(symbol); //--- If failed to get - write the "internal error" flag, display the message in the journal and return 'false' if(symbol_obj==NULL) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR; if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ)); return false; } //--- get a trading object from a symbol object CTradeObj *trade_obj=symbol_obj.GetTradeObj(); //--- If failed to get - write the "internal error" flag, display the message in the journal and return 'false' if(trade_obj==NULL) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR; if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } //--- Set the prices //--- If failed to set - write the "internal error" flag, display the message in the journal and return 'false' if(!this.SetPrices(order_type,0,sl,tp,0,DFUN,symbol_obj)) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR; if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(10021)); return false; } //--- Write the volume to the request structure this.m_request.volume=volume; //--- Get the method of handling errors from the CheckErrors() method while checking for errors ENUM_ERROR_CODE_PROCESSING_METHOD method=this.CheckErrors(this.m_request.volume,symbol_obj.Ask(),action,order_type,symbol_obj,trade_obj,DFUN,0,this.m_request.sl,this.m_request.tp); //--- In case of trading limitations, funds insufficiency, //--- if there are limitations by StopLevel or FreezeLevel ... if(method!=ERROR_CODE_PROCESSING_METHOD_OK) { //--- If trading is disabled completely, display a journal message, play the error sound and exit if(method==ERROR_CODE_PROCESSING_METHOD_DISABLE) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE)); if(this.IsUseSounds()) trade_obj.PlaySoundError(action,order_type); return false; } //--- If the check result is "abort trading operation" - display a journal message, play the error sound and exit if(method==ERROR_CODE_PROCESSING_METHOD_EXIT) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(CMessage::Text(MSG_LIB_TEXT_TRADING_OPERATION_ABORTED)); if(this.IsUseSounds()) trade_obj.PlaySoundError(action,order_type); return false; } //--- If the check result is "waiting", display the message in the journal if(method==ERROR_CODE_PROCESSING_METHOD_EXIT) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(CMessage::Text(MSG_LIB_TEXT_CREATE_PENDING_REQUEST)); //--- Instead of creating a pending request, we temporarily wait the required time period (the CheckErrors() method result is returned) ::Sleep(method); //--- after waiting, update all data symbol_obj.Refresh(); } //--- If the check result is "create a pending request", do nothing temporarily if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_PENDING_REQUEST) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(CMessage::Text(MSG_LIB_TEXT_CREATE_PENDING_REQUEST)); } } //--- Send the request res=trade_obj.OpenPosition(POSITION_TYPE_BUY,this.m_request.volume,this.m_request.sl,this.m_request.tp,magic,comment,deviation); //--- If the request is successful, play the success sound set for a symbol trading object for this type of trading operation if(res) { if(this.IsUseSounds()) trade_obj.PlaySoundSuccess(action,order_type); } //--- If the request is not successful, play the error sound set for a symbol trading object for this type of trading operation else { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(trade_obj.GetResultRetcode())); if(this.IsUseSounds()) trade_obj.PlaySoundError(action,order_type); } //--- Return the result of sending a trading request in a symbol trading object return res; } //+------------------------------------------------------------------+
すべての説明は、コードのコメントで詳細に説明されています。他の取引メソッドも同様に改善されています。すべてが明確であることを願っています。いずれにせよ、コメントセクションを使用してください。
以下は、計算された有効なストップ価格と未決注文価格を返すメソッドです。
//+------------------------------------------------------------------+ //| StopLevelに相対した正しいストップロスを返す | //+------------------------------------------------------------------+ double CTrading::CorrectStopLoss(const ENUM_ORDER_TYPE order_type,const double price_set,const double stop_loss,const CSymbol *symbol_obj,const uint spread_multiplier=1) { if(stop_loss==0) return 0; uint lv=(symbol_obj.TradeStopLevel()==0 ? symbol_obj.Spread()*spread_multiplier : symbol_obj.TradeStopLevel()); double price=(order_type==ORDER_TYPE_BUY ?symbol_obj.BidLast() : order_type==ORDER_TYPE_SELL ?symbol_obj.Ask() : price_set); return (this.DirectionByActionType((ENUM_ACTION_TYPE)order_type)==ORDER_TYPE_BUY ? ::NormalizeDouble(fmin(price-lv*symbol_obj.Point(),stop_loss),symbol_obj.Digits()) : ::NormalizeDouble(fmax(price+lv*symbol_obj.Point(),stop_loss),symbol_obj.Digits()) ); } //+------------------------------------------------------------------+ //| StopLevelに相対した正しいテイクプロフィットを返す | //+------------------------------------------------------------------+ double CTrading::CorrectTakeProfit(const ENUM_ORDER_TYPE order_type,const double price_set,const double take_profit,const CSymbol *symbol_obj,const uint spread_multiplier=1) { if(take_profit==0) return 0; uint lv=(symbol_obj.TradeStopLevel()==0 ? symbol_obj.Spread()*spread_multiplier : symbol_obj.TradeStopLevel()); double price=(order_type==ORDER_TYPE_BUY ?symbol_obj.BidLast() : order_type==ORDER_TYPE_SELL ?symbol_obj.Ask() : price_set); return (this.DirectionByActionType((ENUM_ACTION_TYPE)order_type)==ORDER_TYPE_BUY ? ::NormalizeDouble(fmax(price+lv*symbol_obj.Point(),take_profit),symbol_obj.Digits()) : ::NormalizeDouble(fmin(price-lv*symbol_obj.Point(),take_profit),symbol_obj.Digits()) ); } //+------------------------------------------------------------------+ //| StopLevelに相対した | //| relative to StopLevel | //+------------------------------------------------------------------+ double CTrading::CorrectPricePending(const ENUM_ORDER_TYPE order_type,const double price_set,const double price,const CSymbol *symbol_obj,const uint spread_multiplier=1) { uint lv=(symbol_obj.TradeStopLevel()==0 ? symbol_obj.Spread()*spread_multiplier : symbol_obj.TradeStopLevel()); double pp=0; switch((int)order_type) { case ORDER_TYPE_BUY_LIMIT : pp=(price==0 ?symbol_obj.Ask() : price); return ::NormalizeDouble(fmin(pp-lv*symbol_obj.Point(),price_set),symbol_obj.Digits()); case ORDER_TYPE_BUY_STOP : case ORDER_TYPE_BUY_STOP_LIMIT : pp=(price==0 ?symbol_obj.Ask() : price); return ::NormalizeDouble(fmax(pp+lv*symbol_obj.Point(),price_set),symbol_obj.Digits()); case ORDER_TYPE_SELL_LIMIT : pp=(price==0 ?symbol_obj.BidLast() : price); return ::NormalizeDouble(fmax(pp+lv*symbol_obj.Point(),price_set),symbol_obj.Digits()); case ORDER_TYPE_SELL_STOP : case ORDER_TYPE_SELL_STOP_LIMIT : pp=(price==0 ?symbol_obj.BidLast() : price); return ::NormalizeDouble(fmin(pp-lv*symbol_obj.Point(),price_set),symbol_obj.Digits()); default : if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_INVALID_ORDER_TYPE),::EnumToString(order_type)); return 0; } } //+------------------------------------------------------------------+
ここでは、コードコメントがなくてもすべてが明確になっているはずです。メソッドに渡された価格は、始値からStopLevelをシフトして取得された価格と比較されます。有効な(注文タイプに応じてより高い/低い)価格が呼び出し元プログラムに返されます。
以下は、ポジションを開くことができる、ボリュームを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the volume, at which it is possible to open a position | //+------------------------------------------------------------------+ double CTrading::CorrectVolume(const double price,const ENUM_ORDER_TYPE order_type,CSymbol *symbol_obj,const string source_method) { //--- If funds are insufficient for the minimum lot, inform of that and return zero if(!this.CheckMoneyFree(symbol_obj.LotsMin(),price,order_type,symbol_obj,source_method)) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(CMessage::Text(MSG_LIB_TEXT_NOT_POSSIBILITY_CORRECT_LOT)); return 0; } //--- Update account and symbol data this.m_account.Refresh(); symbol_obj.RefreshRates(); //--- Calculate the lot, which is closest to the acceptable one double vol=symbol_obj.NormalizedLot(this.m_account.Equity()*this.m_account.Leverage()/symbol_obj.TradeContractSize()/(symbol_obj.CurrencyBase()=="USD" ? 1.0 : symbol_obj.BidLast())); //--- Calculate a sufficient lot double margin=this.m_account.MarginForAction(order_type,symbol_obj.Name(),1.0,price); if(margin!=EMPTY_VALUE) vol=symbol_obj.NormalizedLot(this.m_account.MarginFree()/margin); //--- If the calculated lot is invalid or the margin calculation returns an error if(!this.CheckMoneyFree(vol,price,order_type,symbol_obj,source_method)) { //--- In the do-while loop, while the calculated valid volume exceeds the minimum lot do { //--- Subtract the minimum lot from the valid lot value vol-=symbol_obj.LotsStep(); //--- If the calculated lot allows opening a position/setting an order, return the lot value if(this.CheckMoneyFree(symbol_obj.NormalizedLot(vol),price,order_type,symbol_obj,source_method)) return vol; } while(vol>symbol_obj.LotsMin() && !::IsStopped()); } //--- If the lot is calculated correctly, return the calculated lot else return vol; //--- If the current stage is reached, the funds are insufficient. Inform of that and return zero if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(CMessage::Text(MSG_LIB_TEXT_NOT_POSSIBILITY_CORRECT_LOT)); return 0; } //+------------------------------------------------------------------+
コードはここでもコメントされています。
まず、最小ロットで開く能力を確認します。これが不可能な場合、それ以上の計算は無意味なので、ゼロを返します。
次に、おおよその許容ロットを計算します(可能な調整の場合に必要なロットを最大値から「選択」しないため)。
次に、利用可能なすべての機能を使用してポジションを開くことができる最大ロットを計算します。何故でしょうか。資金がポジションを開くのに不十分な場合、これは必要なボリュームが大きかったことを意味します。つまり、可能な最大ボリュームを計算する必要があります。
この計算では、エラーの場合にfalseを返す可能性のあるOrderCalcMargin()関数を使用しますが、この関数を使用するCAccountクラスのMarginForAction()メソッドは、DBL_MAX定数値(double型で表現可能な最大値)に対応するEMPTY_VALUEを返します)。この値を受け取った場合、エラーが発生しており、ロットは計算されていません。
この場合(エラーの場合だけでなく、計算の有効性をチェックする場合も)、取引注文で計算された最大可能量からロットステップを単純に減算することにより、必要な最大ロットの「選択」を使用します。これは、以前に計算された利用可能なおおよそのボリュームが必要な場所です。正確なボリュームを計算できなかった場合、ロット減少ループは、(シンボルに設定された最大ロットではなく)最も近いロットから開始し、ループの反復回数を大幅に削減します。
ところで、確認中に、ロットの計算時にOrderCalcMargin()関数のエラーを受け取りませんでした。ただし、無効な計算はまだ発生しています(おおよそ、1ロット変更ステップごとに)。
これで、取引クラスの変更と改善は終了です。
テスト
テストを実行するには、前の記事のEAを使用して、 \MQL5\Experts\TestDoEasy\Part24\でTestDoEasyPart24.mq5として保存します。
ストラテジーテスターでの作業であることを示すフラグをグローバル変数のリストに追加します。
//--- グローバル変数 CEngine engine; SDataButt butt_data[TOTAL_BUTT]; string prefix; double lot; double withdrawal=(InpWithdrawal<0.1 ?0.1 : InpWithdrawal); ulong magic_number; uint stoploss; uint takeprofit; uint distance_pending; uint distance_stoplimit; uint slippage; bool trailing_on; double trailing_stop; double trailing_step; uint trailing_start; uint stoploss_to_modify; uint takeprofit_to_modify; int used_symbols_mode; string used_symbols; string array_used_symbols[]; bool testing; //+------------------------------------------------------------------+
OnInit()ハンドラで、ストラテジーテスターでの作業であることを示すフラグの値を設定します。
//+------------------------------------------------------------------+ //| エキスパート初期化関数 | //+------------------------------------------------------------------+ int OnInit() { //--- Calling the function displays the list of enumeration constants in the journal //--- (the list is set in the strings 22 and 25 of the DELib.mqh file) for checking the constants validity //EnumNumbersTest(); //--- Set EA global variables prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_"; testing=engine.IsTester(); for(int i=0;i<TOTAL_BUTT;i++) { butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i); butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i); }
テスターでの作業中にOnDoEasyEvent()ライブラリイベントハンドラにイベントを送信するには、特別な関数であるEventsHandling()が必要です。マイナーな改善が行われました。
//+------------------------------------------------------------------+ //| Working with events in the tester | //+------------------------------------------------------------------+ void EventsHandling(void) { //--- If a trading event is present if(engine.IsTradeEvent()) { //--- Number of trading events occurred simultaneously int total=engine.GetTradeEventsTotal(); for(int i=0;i<total;i++) { //--- Get the next event from the list of simultaneously occurred events by index CEventBaseObj *event=engine.GetTradeEventByIndex(i); if(event==NULL) continue; long lparam=i; double dparam=event.DParam(); string sparam=event.SParam(); OnDoEasyEvent(CHARTEVENT_CUSTOM+event.ID(),lparam,dparam,sparam); } } //--- If there is an account event if(engine.IsAccountsEvent()) { //--- Get the list of all account events occurred simultaneously CArrayObj* list=engine.GetListAccountEvents(); if(list!=NULL) { //--- Get the next event in a loop int total=list.Total(); for(int i=0;i<total;i++) { //--- take an event from the list CEventBaseObj *event=list.At(i); if(event==NULL) continue; //--- Send an event to the event handler long lparam=event.LParam(); double dparam=event.DParam(); string sparam=event.SParam(); OnDoEasyEvent(CHARTEVENT_CUSTOM+event.ID(),lparam,dparam,sparam); } } } //--- If there is a symbol collection event if(engine.IsSymbolsEvent()) { //--- Get the list of all symbol events occurred simultaneously CArrayObj* list=engine.GetListSymbolsEvents(); if(list!=NULL) { //--- Get the next event in a loop int total=list.Total(); for(int i=0;i<total;i++) { //--- take an event from the list CEventBaseObj *event=list.At(i); if(event==NULL) continue; //--- Send an event to the event handler long lparam=event.LParam(); double dparam=event.DParam(); string sparam=event.SParam(); OnDoEasyEvent(CHARTEVENT_CUSTOM+event.ID(),lparam,dparam,sparam); } } } } //+------------------------------------------------------------------+
コードのコメントはここでは非常に明確です。
新しい取引イベントのリストを作成したので、OnDoEasyEvent()ライブラリイベントハンドラでイベントインデックスによってのすべての新しい取引イベントのリストから各イベントを取得し、操作ログのリストから取得したすべてのイベントの説明を表示します。
//+------------------------------------------------------------------+ //| Handling DoEasy library events | //+------------------------------------------------------------------+ void OnDoEasyEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { int idx=id-CHARTEVENT_CUSTOM; //--- Retrieve (1) event time milliseconds, (2) reason and (3) source from lparam, as well as (4) set the exact event time ushort msc=engine.EventMSC(lparam); ushort reason=engine.EventReason(lparam); ushort source=engine.EventSource(lparam); long time=TimeCurrent()*1000+msc; //--- Handling symbol events if(source==COLLECTION_SYMBOLS_ID) { CSymbol *symbol=engine.GetSymbolObjByName(sparam); if(symbol==NULL) return; //--- Number of decimal places in the event value - in case of a 'long' event, it is 0, otherwise - Digits() of a symbol int digits=(idx<SYMBOL_PROP_INTEGER_TOTAL ? 0 : symbol.Digits()); //--- Event text description string id_descr=(idx<SYMBOL_PROP_INTEGER_TOTAL ? symbol.GetPropertyDescription((ENUM_SYMBOL_PROP_INTEGER)idx) : symbol.GetPropertyDescription((ENUM_SYMBOL_PROP_DOUBLE)idx)); //--- Property change text value string value=DoubleToString(dparam,digits); //--- Check event reasons and display its description in the journal if(reason==BASE_EVENT_REASON_INC) { Print(symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits)); } if(reason==BASE_EVENT_REASON_DEC) { Print(symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits)); } if(reason==BASE_EVENT_REASON_MORE_THEN) { Print(symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits)); } if(reason==BASE_EVENT_REASON_LESS_THEN) { Print(symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits)); } if(reason==BASE_EVENT_REASON_EQUALS) { Print(symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits)); } } //--- Handling account events else if(source==COLLECTION_ACCOUNT_ID) { CAccount *account=engine.GetAccountCurrent(); if(account==NULL) return; //--- Number of decimal places in the event value - in case of a 'long' event, it is 0, otherwise - Digits() of a symbol int digits=int(idx<ACCOUNT_PROP_INTEGER_TOTAL ? 0 : account.CurrencyDigits()); //--- Event text description string id_descr=(idx<ACCOUNT_PROP_INTEGER_TOTAL ? account.GetPropertyDescription((ENUM_ACCOUNT_PROP_INTEGER)idx) : account.GetPropertyDescription((ENUM_ACCOUNT_PROP_DOUBLE)idx)); //--- Property change text value string value=DoubleToString(dparam,digits); //--- Checking event reasons and handling the increase of funds by a specified value, //--- In case of a property value increase if(reason==BASE_EVENT_REASON_INC) { //--- Display an event in the journal Print(account.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits)); //--- if this is an equity increase if(idx==ACCOUNT_PROP_EQUITY) { //--- すべてのポジションのリストを取得する CArrayObj* list_positions=engine.GetListMarketPosition(); //--- Select positions with the profit exceeding zero list_positions=CSelect::ByOrderProperty(list_positions,ORDER_PROP_PROFIT_FULL,0,MORE); if(list_positions!=NULL) { //--- 手数料とスワップを考慮して、リストを利益順に並べ替える list_positions.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the position index with the highest profit int index=CSelect::FindOrderMax(list_positions,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list_positions.At(index); if(position!=NULL) { //--- Get a ticket of a position with the highest profit and close the position by a ticket engine.ClosePosition(position.Ticket()); } } } } } //--- Other events are simply displayed in the journal if(reason==BASE_EVENT_REASON_DEC) { Print(account.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits)); } if(reason==BASE_EVENT_REASON_MORE_THEN) { Print(account.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits)); } if(reason==BASE_EVENT_REASON_LESS_THEN) { Print(account.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits)); } if(reason==BASE_EVENT_REASON_EQUALS) { Print(account.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits)); } } //--- Handling market watch window events else if(idx>MARKET_WATCH_EVENT_NO_EVENT && idx<SYMBOL_EVENTS_NEXT_CODE) { //--- Market Watch window event string descr=engine.GetMWEventDescription((ENUM_MW_EVENT)idx); string name=(idx==MARKET_WATCH_EVENT_SYMBOL_SORT ? "" : ": "+sparam); Print(TimeMSCtoString(lparam)," ",descr,name); } //--- Handling trading events else if(idx>TRADE_EVENT_NO_EVENT && idx<TRADE_EVENTS_NEXT_CODE) { //--- Get the list of trading events CArrayObj *list=engine.GetListAllOrdersEvents(); if(list==NULL) return; //--- get the event index shift relative to the end of the list //--- in the tester, the shift is passed by the lparam parameter to the event handler //--- outside the tester, events are sent one by one and handled in OnChartEvent() int shift=(testing ? (int)lparam : 0); CEvent *event=list.At(list.Total()-1-shift); if(event==NULL) return; //--- Accrue the credit if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_CREDIT) { Print(DFUN,event.TypeEventDescription()); } //--- Additional charges if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_CHARGE) { Print(DFUN,event.TypeEventDescription()); } //--- Correction if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_CORRECTION) { Print(DFUN,event.TypeEventDescription()); } //--- Enumerate bonuses if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_BONUS) { Print(DFUN,event.TypeEventDescription()); } //--- Additional commissions if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_COMISSION) { Print(DFUN,event.TypeEventDescription()); } //--- Daily commission if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_COMISSION_DAILY) { Print(DFUN,event.TypeEventDescription()); } //--- Monthly commission if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_COMISSION_MONTHLY) { Print(DFUN,event.TypeEventDescription()); } //--- Daily agent commission if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_DAILY) { Print(DFUN,event.TypeEventDescription()); } //--- Monthly agent commission if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY) { Print(DFUN,event.TypeEventDescription()); } //--- Interest rate if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_INTEREST) { Print(DFUN,event.TypeEventDescription()); } //--- Canceled buy deal if(event.TypeEvent()==TRADE_EVENT_BUY_CANCELLED) { Print(DFUN,event.TypeEventDescription()); } //--- Canceled sell deal if(event.TypeEvent()==TRADE_EVENT_SELL_CANCELLED) { Print(DFUN,event.TypeEventDescription()); } //--- Dividend operations if(event.TypeEvent()==TRADE_EVENT_DIVIDENT) { Print(DFUN,event.TypeEventDescription()); } //--- Accrual of franked dividend if(event.TypeEvent()==TRADE_EVENT_DIVIDENT_FRANKED) { Print(DFUN,event.TypeEventDescription()); } //--- Tax charges if(event.TypeEvent()==TRADE_EVENT_TAX) { Print(DFUN,event.TypeEventDescription()); } //--- Replenishing account balance if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_BALANCE_REFILL) { Print(DFUN,event.TypeEventDescription()); } //--- Withdrawing funds from balance if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL) { Print(DFUN,event.TypeEventDescription()); } //--- Pending order placed if(event.TypeEvent()==TRADE_EVENT_PENDING_ORDER_PLASED) { Print(DFUN,event.TypeEventDescription()); } //--- Pending order removed if(event.TypeEvent()==TRADE_EVENT_PENDING_ORDER_REMOVED) { Print(DFUN,event.TypeEventDescription()); } //--- Pending order activated by price if(event.TypeEvent()==TRADE_EVENT_PENDING_ORDER_ACTIVATED) { Print(DFUN,event.TypeEventDescription()); } //--- Pending order partially activated by price if(event.TypeEvent()==TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL) { Print(DFUN,event.TypeEventDescription()); } //--- Position opened if(event.TypeEvent()==TRADE_EVENT_POSITION_OPENED) { Print(DFUN,event.TypeEventDescription()); } //--- Position opened partially if(event.TypeEvent()==TRADE_EVENT_POSITION_OPENED_PARTIAL) { Print(DFUN,event.TypeEventDescription()); } //--- Position closed if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED) { Print(DFUN,event.TypeEventDescription()); } //--- Position closed by an opposite one if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_BY_POS) { Print(DFUN,event.TypeEventDescription()); } //--- Position closed by StopLoss if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_BY_SL) { Print(DFUN,event.TypeEventDescription()); } //--- Position closed by TakeProfit if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_BY_TP) { Print(DFUN,event.TypeEventDescription()); } //--- Position reversal by a new deal (netting) if(event.TypeEvent()==TRADE_EVENT_POSITION_REVERSED_BY_MARKET) { Print(DFUN,event.TypeEventDescription()); } //--- Position reversal by activating a pending order (netting) if(event.TypeEvent()==TRADE_EVENT_POSITION_REVERSED_BY_PENDING) { Print(DFUN,event.TypeEventDescription()); } //--- Position reversal by partial market order execution (netting) if(event.TypeEvent()==TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL) { Print(DFUN,event.TypeEventDescription()); } //--- Position reversal by activating a pending order (netting) if(event.TypeEvent()==TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL) { Print(DFUN,event.TypeEventDescription()); } //--- Added volume to a position by a new deal (netting) if(event.TypeEvent()==TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET) { Print(DFUN,event.TypeEventDescription()); } //--- Added volume to a position by partial execution of a market order (netting) if(event.TypeEvent()==TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL) { Print(DFUN,event.TypeEventDescription()); } //--- Added volume to a position by activating a pending order (netting) if(event.TypeEvent()==TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING) { Print(DFUN,event.TypeEventDescription()); } //--- Added volume to a position by partial activation of a pending order (netting) if(event.TypeEvent()==TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL) { Print(DFUN,event.TypeEventDescription()); } //--- Position closed partially if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL) { Print(DFUN,event.TypeEventDescription()); } //--- Position partially closed by an opposite one if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS) { Print(DFUN,event.TypeEventDescription()); } //--- Position closed partially by StopLoss if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL) { Print(DFUN,event.TypeEventDescription()); } //--- Position closed partially by TakeProfit if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP) { Print(DFUN,event.TypeEventDescription()); } //--- StopLimit order activation if(event.TypeEvent()==TRADE_EVENT_TRIGGERED_STOP_LIMIT_ORDER) { Print(DFUN,event.TypeEventDescription()); } //--- Changing order price if(event.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_PRICE) { Print(DFUN,event.TypeEventDescription()); } //--- Changing order and StopLoss price if(event.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS) { Print(DFUN,event.TypeEventDescription()); } //--- Changing order and TakeProfit price if(event.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_PRICE_TAKE_PROFIT) { Print(DFUN,event.TypeEventDescription()); } //--- Changing order, StopLoss and TakeProfit price if(event.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS_TAKE_PROFIT) { Print(DFUN,event.TypeEventDescription()); } //--- Changing order's StopLoss and TakeProfit price if(event.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_STOP_LOSS_TAKE_PROFIT) { Print(DFUN,event.TypeEventDescription()); } //--- Changing order's StopLoss if(event.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_STOP_LOSS) { Print(DFUN,event.TypeEventDescription()); } //--- Changing order's TakeProfit if(event.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_TAKE_PROFIT) { Print(DFUN,event.TypeEventDescription()); } //--- Changing position's StopLoss and TakeProfit if(event.TypeEvent()==TRADE_EVENT_MODIFY_POSITION_STOP_LOSS_TAKE_PROFIT) { Print(DFUN,event.TypeEventDescription()); } //--- Changing position StopLoss if(event.TypeEvent()==TRADE_EVENT_MODIFY_POSITION_STOP_LOSS) { Print(DFUN,event.TypeEventDescription()); } //--- Changing position TakeProfit if(event.TypeEvent()==TRADE_EVENT_MODIFY_POSITION_TAKE_PROFIT) { Print(DFUN,event.TypeEventDescription()); } } } //+------------------------------------------------------------------+
より簡単にするために、単にインデックスからリストからイベントを受け取って(テスターではインデックスはEventsHandling()関数によってlparamパラメータに渡されますが、デモ口座および実際の口座ではすべてのイベントはリストからではなく独立したイベントとしてOnChartEvent()に送信されるため、インデックスは常にゼロです)、取得したイベントの説明を操作ログに表示します。
それらの処理を手配するのはあなた次第です。実際の処理は別の関数で実行されますが、同じコードに処理を直接実装するか、発生したイベントのフラグを設定するイベントフラグのリストをここで宣言できます。
これらは、すべての取引イベントを同時に制御するために必要なすべての変更と改善です。ライブラリには、取引リクエストパラメータエラーの自動修正に必要なすべての機能が既に備わっています。EAでの変更は必要ありません(今のところ)。さらに、エラー処理のすべてのメソッドを作成した後、エラー時のEAの動作を示す追加の入力を導入します。
EAをコンパイルし、テスターで起動します。複数の未決注文を出し、それらすべてを単一のループで削除します。
EAは操作ログに4つのイベントを表示します。これらのイベントは、[Delete pending]をクリックした後、単一のループで4つの未決注文を削除するときに発生したものです。
ストラテジーテスターのEA設定でより大きいロット(例: 100.0)を設定し、指値注文を設定するか、ポジションを開いてみます。
指値注文を設定し、100.0ロットのボリュームでポジションを開こうとすると、資金不足とボリューム調整を通知するメッセージが操作ログに表示されました。注文が設定され、その後ポジションが開かれました。
次の段階
次の記事では、取引サーバによって返されるエラーの処理を実装します。
現在のバージョンのライブラリのすべてのファイルは、テスト用EAファイルと一緒に以下に添付されているので、テストするにはダウンロードしてください。
質問、コメント、提案はコメント欄にお願いします。
シリーズのこれまでの記事:
第1部: 概念、データ管理第2部: 過去の注文と取引のコレクション
第3部: 注文と取引のコレクション、検索と並び替え
第4部: 取引イベント概念
第5部: 取引イベントのクラスとコレクション取引イベントのプログラムへの送信
第6部: ネッティング勘定イベント
第7部: StopLimit注文発動イベント、注文およびポジション変更イベントの機能の準備
第8部: 注文とポジションの変更イベント
第9部: MQL4との互換性 - データの準備
第10部: MQL4との互換性 - ポジションオープンイベントと指値注文発動イベント
第11部: MQL4との互換性 - ポジション決済イベント
第12部: 口座オブジェクトクラスと口座オブジェクトコレクション
第13部: 口座オブジェクトイベント第14部: 銘柄オブジェクト
第15部: 銘柄オブジェクトコレクション
第16部: 銘柄コレクションイベント
第17部: ライブラリオブジェクトの相互作用
第18部:口座と他のライブラリオブジェクトの相互作用
第19部:ライブラリメッセージのクラス
第20部:プログラムリソースの作成と格納
第21部:取引クラス - 基本クロスプラットフォーム取引オブジェクト
第22部:取引クラス - 基本取引クラス、制限の検証
第23部:取引クラス - 基本取引クラス、パラメータ有効性の検証
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/7326
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索