内容
在上一篇文章中,我们起手实现延后交易请求,并创建了第一个开仓延后请求,应对交易类向服务器发送请求后收到错误。 在本文中,我们将继续开发延后请求,并针对设置挂单时发生错误的情况实现创建延后请求。
在测试交易类时,我检测到一些缺陷。 特别是,在类构造函数中初始化品种的交易对象时,会为其硬性设置默认值。 并非所有这些数值在品种规格中都加以支持。 这就会导致尝试下挂单时,服务器端出错 — 服务器激活“不支持的订单到期类型“错误。 该错误在任何地方都无法得到纠正,最终导致无法下挂单。 当发送包含默认值的交易请求时,一些不支持的数据也被发送到交易请求。 为了解决这个问题,我必须直接在交易请求中指定与相应品种规格对应的正确数据。
这需要了解品种规格,并直接在程序代码中手动输入准确数值,取代由函数库本身对参数值进行自动校正。 为简化起见,我们将对交易类的处理逻辑略微进行一些修改。 在 EA 的 OnInit() 处理程序中自动选择正确的参数值来初始化所有品种的交易对象。 默认情况下,将数值 -1 传递给交易类的交易方法填充订单,且到期类型表明其时间采用预设的正确默认值。 如果从程序里传递了另一个参数值,则会用它来替代默认值。 如果该值被证实无效,则当交易类进行错误处理时会加以纠正。
准备数据
修复交易类之余,我们还将添加请求描述(在日志中显示其所有参数)到延后请求对象类之中。 这样可令操控延后请求对象的测试更轻松。
首先,将所有必要的消息添加到函数库消息数组。
打开 Datas.mqh 文件,并向其中添加新消息的索引:
//+------------------------------------------------------------------+ //| List of the library's text message indices | //+------------------------------------------------------------------+ enum ENUM_MESSAGES_LIB { MSG_LIB_PARAMS_LIST_BEG=ERR_USER_ERROR_FIRST, // Beginning of the parameter list MSG_LIB_PARAMS_LIST_END, // End of the parameter list MSG_LIB_PROP_NOT_SUPPORTED, // Property not supported MSG_LIB_PROP_NOT_SUPPORTED_MQL4, // Property not supported in MQL4 MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_2155, // Property not supported in MetaTrader 5 versions lower than 2155 MSG_LIB_PROP_NOT_SUPPORTED_POSITION, // Property not supported for position MSG_LIB_PROP_NOT_SUPPORTED_PENDING, // Property not supported for pending order MSG_LIB_PROP_NOT_SUPPORTED_MARKET, // Property not supported for market order MSG_LIB_PROP_NOT_SUPPORTED_MARKET_HIST, // Property not supported for historical market order MSG_LIB_PROP_NOT_SET, // Value not set MSG_LIB_PROP_EMPTY, // Not set MSG_LIB_PROP_AS_IN_ORDER, // According to the order expiration mode MSG_LIB_SYS_ERROR, // Error
…
MSG_LIB_TEXT_FAILING_CREATE_PENDING_REQ, // Failed to create a pending request MSG_LIB_TEXT_TRY_N, // Trading attempt # MSG_LIB_TEXT_RE_TRY_N, // Repeated trading attempt # MSG_LIB_TEXT_REQUEST_ACTION, // Type of a performed action MSG_LIB_TEXT_REQUEST_MAGIC, // EA stamp (magic number) MSG_LIB_TEXT_REQUEST_ORDER, // Order ticket MSG_LIB_TEXT_REQUEST_SYMBOL, // Name of a trading instrument MSG_LIB_TEXT_REQUEST_VOLUME, // Requested volume of a deal in lots MSG_LIB_TEXT_REQUEST_PRICE, // Price MSG_LIB_TEXT_REQUEST_STOPLIMIT, // StopLimit MSG_LIB_TEXT_REQUEST_SL, // Stop Loss MSG_LIB_TEXT_REQUEST_TP, // Take Profit MSG_LIB_TEXT_REQUEST_DEVIATION, // Maximum price deviation MSG_LIB_TEXT_REQUEST_TYPE, // Order type MSG_LIB_TEXT_REQUEST_TYPE_FILLING, // Order filling type MSG_LIB_TEXT_REQUEST_TYPE_TIME, // Order lifetime type MSG_LIB_TEXT_REQUEST_EXPIRATION, // Order expiration date MSG_LIB_TEXT_REQUEST_COMMENT, // Order comment MSG_LIB_TEXT_REQUEST_POSITION, // Position ticket MSG_LIB_TEXT_REQUEST_POSITION_BY, // Opposite position ticket MSG_LIB_TEXT_REQUEST_ACTION_DEAL, // Place a market order MSG_LIB_TEXT_REQUEST_ACTION_PENDING, // Place a pending order MSG_LIB_TEXT_REQUEST_ACTION_SLTP, // Change open position Stop Loss and Take Profit MSG_LIB_TEXT_REQUEST_ACTION_MODIFY, // Change parameters of the previously placed trading order MSG_LIB_TEXT_REQUEST_ACTION_REMOVE, // Remove previously placed pending order MSG_LIB_TEXT_REQUEST_ACTION_CLOSE_BY, // Close a position by an opposite one MSG_LIB_TEXT_REQUEST_ACTION_UNCNOWN, // Unknown trading operation type MSG_LIB_TEXT_REQUEST_ORDER_FILLING_FOK, // Order is executed in the specified volume only, otherwise it is canceled MSG_LIB_TEXT_REQUEST_ORDER_FILLING_IOK, // Order is filled within an available volume, while the unfilled one is canceled MSG_LIB_TEXT_REQUEST_ORDER_FILLING_RETURN, // Order is filled within an available volume, while the unfilled one remains MSG_LIB_TEXT_REQUEST_ORDER_TIME_GTC, // Order is valid till explicitly canceled MSG_LIB_TEXT_REQUEST_ORDER_TIME_DAY, // Order is valid only during the current trading day MSG_LIB_TEXT_REQUEST_ORDER_TIME_SPECIFIED, // Order is valid till the expiration date MSG_LIB_TEXT_REQUEST_ORDER_TIME_SPECIFIED_DAY, // Order is valid till 23:59:59 of a specified day MSG_LIB_TEXT_REQUEST_DATAS, // Trading request parameters MSG_LIB_TEXT_PEND_REQUEST_DATAS, // Pending trading request parameters MSG_LIB_TEXT_PEND_REQUEST_CREATED, // Pending request created MSG_LIB_TEXT_PEND_REQUEST_DELETED, // Pending request is removed due to its expiration MSG_LIB_TEXT_PEND_REQUEST_PRICE_CREATE, // Price at the moment of request generation MSG_LIB_TEXT_PEND_REQUEST_TIME_CREATE, // Request creation time MSG_LIB_TEXT_PEND_REQUEST_TIME_ACTIVATE, // Request activation time MSG_LIB_TEXT_PEND_REQUEST_WAITING, // Waiting time between trading attempts MSG_LIB_TEXT_PEND_REQUEST_CURRENT_ATTEMPT, // Current trading attempt MSG_LIB_TEXT_PEND_REQUEST_TOTAL_ATTEMPTS, // Total number of trading attempts MSG_LIB_TEXT_PEND_REQUEST_ID, // Trading request ID MSG_LIB_TEXT_PEND_REQUEST_RETCODE, // Return code a request is based on MSG_LIB_TEXT_PEND_REQUEST_TYPE, // Pending request type MSG_LIB_TEXT_PEND_REQUEST_BY_ERROR, // Pending request generated based on the server return code MSG_LIB_TEXT_PEND_REQUEST_BY_REQUEST, // Pending request created by request MSG_LIB_TEXT_PEND_REQUEST_WAITING_ONSET, // Wait for the first trading attempt };
以及与索引对应的消息文本:
//+------------------------------------------------------------------+ string messages_library[][TOTAL_LANG]= { {"Начало списка параметров","Beginning of event parameter list"}, {"Конец списка параметров","End of parameter list"}, {"Свойство не поддерживается","Property not supported"}, {"Свойство не поддерживается в MQL4","Property not supported in MQL4"}, {"Свойство не поддерживается в MetaTrader5 версии ниже 2155","Property not supported in MetaTrader 5, build lower than 2155"}, {"Свойство не поддерживается у позиции","Property not supported for position"}, {"Свойство не поддерживается у отложенного ордера","Property not supported for pending order"}, {"Свойство не поддерживается у маркет-ордера","Property not supported for market order"}, {"Свойство не поддерживается у исторического маркет-ордера","Property not supported for historical market order"}, {"Значение не задано","Value not set"}, {"Отсутствует","Not set"}, {"В соответствии с режимом истечения ордера","In accordance with the order expiration mode"}, {"Ошибка ","Error"},
…
{"Не удалось создать отложенный запрос","Failed to create pending request"}, {"Торговая попытка #","Trading attempt #"}, {"Повторная торговая попытка #","Retry trading attempt #"}, {"Тип выполняемого действия","Trade operation type"}, {"Штамп эксперта (magic number)","Expert Advisor ID (magic number)"}, {"Тикет ордера","Order ticket"}, {"Имя торгового инструмента","Trade symbol"}, {"Запрашиваемый объем сделки в лотах","Requested volume for a deal in lots"}, {"Цена","Price"}, {"Уровень StopLimit ордера","StopLimit level of the order"}, {"Уровень Stop Loss ордера","Stop Loss level of the order"}, {"Уровень Take Profit ордера","Take Profit level of the order"}, {"Максимальное отклонение от цены","Maximal deviation from the price"}, {"Тип ордера","Order type"}, {"Тип ордера по исполнению","Order execution type"}, {"Тип ордера по времени действия","Order expiration type"}, {"Срок истечения ордера","Order expiration time"}, {"Комментарий к ордеру","Order comment"}, {"Тикет позиции","Position ticket"}, {"Тикет встречной позиции","Opposite position ticket"}, {"Поставить рыночный ордер","Place market order"}, {"Установить отложенный ордер","Place pending order"}, {"Изменить значения Stop Loss и Take Profit у открытой позиции","Modify Stop Loss and Take Profit values of an opened position"}, {"Изменить параметры ранее установленного торгового ордера","Modify the parameters of the order placed previously"}, {"Удалить ранее выставленный отложенный ордер","Delete the pending order placed previously"}, {"Закрыть позицию встречной","Close a position by an opposite one"}, {"Неизвестный тип торговой операции","Unknown trade action type"}, {"Ордер исполняется исключительно в указанном объеме, иначе отменяется (FOK)","The order is executed exclusively in the specified volume, otherwise it is canceled (FOK)"}, {"Ордер исполняется на доступный объем, неисполненный отменяется (IOK)","The order is executed on the available volume, the unfulfilled is canceled (IOK)"}, {"Ордер исполняется на доступный объем, неисполненный остаётся (Return)","The order is executed at an available volume, unfulfilled remains in the market (Return)"}, {"Ордер действителен до явной отмены","Good till cancel order"}, {"Ордер действителен только в течение текущего торгового дня","Good till current trade day order"}, {"Ордер действителен до даты истечения","Good till expired order"}, {"Ордер действителен до 23:59:59 указанного дня","The order will be effective till 23:59:59 of the specified day"}, {"Параметры торгового запроса","Trade request's parameters"}, {"Параметры отложенного торгового запроса","Pending trade request's parameters"}, {"Создан отложенный запрос","Pending request created"}, {"Отложенный запрос удалён в связи с окончанием времени его действия","Pending request deleted due to expiration"}, {"Цена в момент создания запроса: ","Price at time of request create: "}, {"Время создания запроса: ","Request creation time: "}, {"Время активации запроса: ","Request activation time: "}, {"Время ожидания между торговыми попытками: ","Waiting time between trading attempts: "}, {"Текущая торговая попытка: ","Current trading attempt: "}, {"Общее количество торговых попыток: ","Total trade attempts: "}, {"Идентификатор торгового запроса: ","Trade request ID: "}, {"Код возврата, на основании которого создан запрос: ","Return code based on which the request was created: "}, {"Тип отложенного запроса: ","Pending request type: "}, {"Отложенный запрос, созданный по коду возврата сервера","Pending request that was created as a result of the server code"}, {"Отложенный запрос, созданный по запросу","Pending request created by request"}, {"Ожидание наступления времени первой торговой попытки","Waiting for the onset time of the first trading attempt"}, }; //+---------------------------------------------------------------------+
除了上述缺陷之外,我注意到标准库中的集合 ID 与对象类型 ID 重叠。 特别是,COLLECTION_HISTORY_ID(历史订单和成交集合)的 ID 为 0x7779,该值对应于标准库的 CList 类型,即 CObject 类实例及其后代的动态列表。 对象 ID 拥有相同的数值是不合理的。
下面是标准库对象 ID 和相应的十六进制值的列表:
基类 CObject | 类型 = 0 |
数据集合 CArrayChar | 类型 = 0x77 |
数据集合 CArrayShort | 类型 = 0x82 |
数据集合 CArrayInt | 类型 = 0x82 |
数据集合 CArrayLong | 类型 = 0x84 |
数据集合 CArrayFloat | 类型 = 0x87 |
数据集合 CArrayDouble | 类型 = 0x87 |
数据集合 CArrayString | 类型 = 0x89 |
数据集合 CArrayObj | 类型 = 0x7778 |
数据集合 CList |
类型 = 0x7779 |
图形对象基类 CChartObject | 类型 = 0x8888 |
价格图表 CChart | 类型 = 0x1111 |
该列表很可能并不完整。 如我们所见,列表对象类型与函数库的历史订单和成交集合的 ID 撞号了。
我们在 Defines.mqh 文件中修复此问题,这可将所有集合的 ID 值加 1:
//--- Collection list IDs #define COLLECTION_HISTORY_ID (0x777A) // Historical collection list ID #define COLLECTION_MARKET_ID (0x777B) // Market collection list ID #define COLLECTION_EVENTS_ID (0x777C) // Event collection list ID #define COLLECTION_ACCOUNT_ID (0x777D) // Account collection list ID #define COLLECTION_SYMBOLS_ID (0x777E) // Symbol collection list ID //--- Data parameters for file operations
鉴于我想实现运用延后请求进行交易的能力,故要实现两种类型的延后请求:
- 请求的生成要基于交易服务器错误代码(我们当前正在实现的请求是这样的);
- 由交易请求对应的程序创建延后请求(由延后请求进行交易,稍后将实现)。
所以,我将引入“请求类型”的概念,以及与之匹配的 ID来分离请求类型:
//--- Symbol parameters #define CLR_DEFAULT (0xFF000000) // Default color #define SYMBOLS_COMMON_TOTAL (1000) // Total number of working symbols //--- Pending request type IDs #define PENDING_REQUEST_ID_TYPE_ERR (1) // Type of a pending request created based on the server return code #define PENDING_REQUEST_ID_TYPE_REQ (2) // Type of a pending request created by request //+------------------------------------------------------------------+
在 Defines.mqh 文件的末尾,添加延后请求类型的枚举:
//+------------------------------------------------------------------+ //| Pending request type | //+------------------------------------------------------------------+ enum ENUM_PENDING_REQUEST_TYPE { PENDING_REQUEST_TYPE_ERROR=PENDING_REQUEST_ID_TYPE_ERR, // Pending request created based on the return code or error PENDING_REQUEST_TYPE_REQUEST=PENDING_REQUEST_ID_TYPE_REQ,// Pending request created by request }; //+------------------------------------------------------------------+
为了在日志中显示交易请求描述,我们需要准备相应的函数。
在服务函数的 DELib.mqh 文件中,添加依据 Datas.mqh 文件中预设定的文本集合生成消息的必要函数,以及由函数显示的属性值。
显示订单填充模式和到期类型的函数:
//+------------------------------------------------------------------+ //| Return the order filling mode description | //+------------------------------------------------------------------+ string OrderTypeFillingDescription(const ENUM_ORDER_TYPE_FILLING type) { return ( type==ORDER_FILLING_FOK ? CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_FILLING_FOK) : type==ORDER_FILLING_IOC ? CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_FILLING_IOK) : type==ORDER_FILLING_RETURN ? CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_FILLING_RETURN): type==WRONG_VALUE ? "WRONG_VALUE" : EnumToString(type) ); } //+------------------------------------------------------------------+ //| Return the order expiration type description | //+------------------------------------------------------------------+ string OrderTypeTimeDescription(const ENUM_ORDER_TYPE_TIME type) { return ( type==ORDER_TIME_GTC ? CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_TIME_GTC) : type==ORDER_TIME_DAY ? CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_TIME_DAY) : type==ORDER_TIME_SPECIFIED ? CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_TIME_SPECIFIED) : type==ORDER_TIME_SPECIFIED_DAY ? CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER_TIME_SPECIFIED_DAY) : type==WRONG_VALUE ? "WRONG_VALUE" : EnumToString(type) ); } //+------------------------------------------------------------------+
显示 MqlTradeRequest 交易请求结构说明的函数:
//+------------------------------------------------------------------+ //| Display the trading request description in the journal | //+------------------------------------------------------------------+ void PrintRequestDescription(const MqlTradeRequest &request) { string datas= ( " - "+RequestActionDescription(request)+"/n"+ " - "+RequestMagicDescription(request)+"/n"+ " - "+RequestOrderDescription(request)+"/n"+ " - "+RequestSymbolDescription(request)+"/n"+ " - "+RequestVolumeDescription(request)+"/n"+ " - "+RequestPriceDescription(request)+"/n"+ " - "+RequestStopLimitDescription(request)+"/n"+ " - "+RequestStopLossDescription(request)+"/n"+ " - "+RequestTakeProfitDescription(request)+"/n"+ " - "+RequestDeviationDescription(request)+"/n"+ " - "+RequestTypeDescription(request)+"/n"+ " - "+RequestTypeFillingDescription(request)+"/n"+ " - "+RequestTypeTimeDescription(request)+"/n"+ " - "+RequestExpirationDescription(request)+"/n"+ " - "+RequestCommentDescription(request)+"/n"+ " - "+RequestPositionDescription(request)+"/n"+ " - "+RequestPositionByDescription(request) ); Print("================== ",CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)," ==================/n",datas,"/n"); } //+------------------------------------------------------------------+ //| Return the executed action type description | //+------------------------------------------------------------------+ string RequestActionDescription(const MqlTradeRequest &request) { int code_descr= ( request.action==TRADE_ACTION_DEAL ? MSG_LIB_TEXT_REQUEST_ACTION_DEAL : request.action==TRADE_ACTION_PENDING ? MSG_LIB_TEXT_REQUEST_ACTION_PENDING : request.action==TRADE_ACTION_SLTP ? MSG_LIB_TEXT_REQUEST_ACTION_SLTP : request.action==TRADE_ACTION_MODIFY ? MSG_LIB_TEXT_REQUEST_ACTION_MODIFY : request.action==TRADE_ACTION_REMOVE ? MSG_LIB_TEXT_REQUEST_ACTION_REMOVE : request.action==TRADE_ACTION_CLOSE_BY ? MSG_LIB_TEXT_REQUEST_ACTION_CLOSE_BY : MSG_LIB_TEXT_REQUEST_ACTION_UNCNOWN ); return CMessage::Text(MSG_LIB_TEXT_REQUEST_ACTION)+": "+CMessage::Text(code_descr); } //+------------------------------------------------------------------+ //| Return the magic number value description | //+------------------------------------------------------------------+ string RequestMagicDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_ORD_MAGIC)+": "+(string)request.magic; } //+------------------------------------------------------------------+ //| Return the order ticket value description | //+------------------------------------------------------------------+ string RequestOrderDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_ORDER)+": "+(request.order>0 ? (string)request.order : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the trading instrument name description | //+------------------------------------------------------------------+ string RequestSymbolDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_SYMBOL)+": "+request.symbol; } //+------------------------------------------------------------------+ //| Return the request volume description | //+------------------------------------------------------------------+ string RequestVolumeDescription(const MqlTradeRequest &request) { int dg=(int)DigitsLots(request.symbol); int dgl=(dg==0 ? 1 : dg); return CMessage::Text(MSG_LIB_TEXT_REQUEST_VOLUME)+": "+(request.volume>0 ? DoubleToString(request.volume,dgl) : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the request price value description | //+------------------------------------------------------------------+ string RequestPriceDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_PRICE)+": "+(request.price>0 ? DoubleToString(request.price,(int)SymbolInfoInteger(request.symbol,SYMBOL_DIGITS)) : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the request StopLimit order price description | //+------------------------------------------------------------------+ string RequestStopLimitDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_STOPLIMIT)+": "+(request.stoplimit>0 ? DoubleToString(request.stoplimit,(int)SymbolInfoInteger(request.symbol,SYMBOL_DIGITS)) : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the request StopLoss order price description | //+------------------------------------------------------------------+ string RequestStopLossDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_SL)+": "+(request.sl>0 ? DoubleToString(request.sl,(int)SymbolInfoInteger(request.symbol,SYMBOL_DIGITS)) : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the request TakeProfit order price description | //+------------------------------------------------------------------+ string RequestTakeProfitDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_TP)+": "+(request.tp>0 ? DoubleToString(request.tp,(int)SymbolInfoInteger(request.symbol,SYMBOL_DIGITS)) : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the request deviation size description | //+------------------------------------------------------------------+ string RequestDeviationDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_DEVIATION)+": "+(string)request.deviation; } //+------------------------------------------------------------------+ //| Return the request order type description | //+------------------------------------------------------------------+ string RequestTypeDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_TYPE)+": "+OrderTypeDescription(request.type); } //+------------------------------------------------------------------+ //| Return the request order filling mode description | //+------------------------------------------------------------------+ string RequestTypeFillingDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_TYPE_FILLING)+": "+OrderTypeFillingDescription(request.type_filling); } //+------------------------------------------------------------------+ //| Return the request order lifetime type description | //+------------------------------------------------------------------+ string RequestTypeTimeDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_TYPE_TIME)+": "+OrderTypeTimeDescription(request.type_time); } //+------------------------------------------------------------------+ //| Return the request order expiration time description | //+------------------------------------------------------------------+ string RequestExpirationDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_EXPIRATION)+": "+(request.expiration>0 ? TimeToString(request.expiration) : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the request order comment description | //+------------------------------------------------------------------+ string RequestCommentDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_COMMENT)+": "+(request.comment!="" && request.comment!=NULL ? "/""+request.comment+"/"" : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the request position ticket description | //+------------------------------------------------------------------+ string RequestPositionDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_POSITION)+": "+(request.position>0 ? (string)request.position : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+ //| Return the request opposite position ticket description | //+------------------------------------------------------------------+ string RequestPositionByDescription(const MqlTradeRequest &request) { return CMessage::Text(MSG_LIB_TEXT_REQUEST_POSITION_BY)+": "+(request.position_by>0 ? (string)request.position_by : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+
现在,我们修复检测到的交易类中的缺陷,并着手创建下挂单的延后请求对象。
剔除交易类的缺陷,并创建挂单延后请求
我注意到一些品种的有趣特征,其图表基于最后(Last)价格。 有时,它们没有要价(Ask)或出价(Bid),或者其中之一。 无论怎样,若要获得价格,我必须在 CSymbol 抽象品种对象类中添加其他方法(并修改现有方法),从而检查图表构建类型。 如果图表基于“最后(Last)价格”,则检查是否存在“要价(Ask)”和“出价(Bid)”。 如果存在,则采用这些价格,否则采用最后价格。
在 Symbol.mqh 文件里简化访问品种对象属性的方法块中,更改返回时间的方法的类型。 由于要以毫秒为单位返回时间,因此类型以 “ulong” 取代 “datetime”。
另外,声明三个附加方法,目的如上所述:
//+------------------------------------------------------------------+ //| Methods of a simplified access to the order object properties | //+------------------------------------------------------------------+ //--- Integer properties long Status(void) const { return this.GetProperty(SYMBOL_PROP_STATUS); } int IndexInMarketWatch(void) const { return (int)this.GetProperty(SYMBOL_PROP_INDEX_MW); } bool IsCustom(void) const { return (bool)this.GetProperty(SYMBOL_PROP_CUSTOM); } color ColorBackground(void) const { return (color)this.GetProperty(SYMBOL_PROP_BACKGROUND_COLOR); } ENUM_SYMBOL_CHART_MODE ChartMode(void) const { return (ENUM_SYMBOL_CHART_MODE)this.GetProperty(SYMBOL_PROP_CHART_MODE); } bool IsExist(void) const { return (bool)this.GetProperty(SYMBOL_PROP_EXIST); } bool IsExist(const string name) const { return this.SymbolExists(name); } bool IsSelect(void) const { return (bool)this.GetProperty(SYMBOL_PROP_SELECT); } bool IsVisible(void) const { return (bool)this.GetProperty(SYMBOL_PROP_VISIBLE); } long SessionDeals(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_DEALS); } long SessionBuyOrders(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_BUY_ORDERS); } long SessionSellOrders(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_SELL_ORDERS); } long Volume(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME); } long VolumeHigh(void) const { return this.GetProperty(SYMBOL_PROP_VOLUMEHIGH); } long VolumeLow(void) const { return this.GetProperty(SYMBOL_PROP_VOLUMELOW); } long Time(void) const { return (datetime)this.GetProperty(SYMBOL_PROP_TIME); } int Digits(void) const { return (int)this.GetProperty(SYMBOL_PROP_DIGITS); } int DigitsLot(void) const { return (int)this.GetProperty(SYMBOL_PROP_DIGITS_LOTS); } int Spread(void) const { return (int)this.GetProperty(SYMBOL_PROP_SPREAD); } bool IsSpreadFloat(void) const { return (bool)this.GetProperty(SYMBOL_PROP_SPREAD_FLOAT); } int TicksBookdepth(void) const { return (int)this.GetProperty(SYMBOL_PROP_TICKS_BOOKDEPTH); } ENUM_SYMBOL_CALC_MODE TradeCalcMode(void) const { return (ENUM_SYMBOL_CALC_MODE)this.GetProperty(SYMBOL_PROP_TRADE_CALC_MODE); } ENUM_SYMBOL_TRADE_MODE TradeMode(void) const { return (ENUM_SYMBOL_TRADE_MODE)this.GetProperty(SYMBOL_PROP_TRADE_MODE); } datetime StartTime(void) const { return (datetime)this.GetProperty(SYMBOL_PROP_START_TIME); } datetime ExpirationTime(void) const { return (datetime)this.GetProperty(SYMBOL_PROP_EXPIRATION_TIME); } int TradeStopLevel(void) const { return (int)this.GetProperty(SYMBOL_PROP_TRADE_STOPS_LEVEL); } int TradeFreezeLevel(void) const { return (int)this.GetProperty(SYMBOL_PROP_TRADE_FREEZE_LEVEL); } ENUM_SYMBOL_TRADE_EXECUTION TradeExecutionMode(void) const { return (ENUM_SYMBOL_TRADE_EXECUTION)this.GetProperty(SYMBOL_PROP_TRADE_EXEMODE); } ENUM_SYMBOL_SWAP_MODE SwapMode(void) const { return (ENUM_SYMBOL_SWAP_MODE)this.GetProperty(SYMBOL_PROP_SWAP_MODE); } ENUM_DAY_OF_WEEK SwapRollover3Days(void) const { return (ENUM_DAY_OF_WEEK)this.GetProperty(SYMBOL_PROP_SWAP_ROLLOVER3DAYS); } bool IsMarginHedgedUseLeg(void) const { return (bool)this.GetProperty(SYMBOL_PROP_MARGIN_HEDGED_USE_LEG); } int ExpirationModeFlags(void) const { return (int)this.GetProperty(SYMBOL_PROP_EXPIRATION_MODE); } int FillingModeFlags(void) const { return (int)this.GetProperty(SYMBOL_PROP_FILLING_MODE); } int OrderModeFlags(void) const { return (int)this.GetProperty(SYMBOL_PROP_ORDER_MODE); } ENUM_SYMBOL_ORDER_GTC_MODE OrderModeGTC(void) const { return (ENUM_SYMBOL_ORDER_GTC_MODE)this.GetProperty(SYMBOL_PROP_ORDER_GTC_MODE); } ENUM_SYMBOL_OPTION_MODE OptionMode(void) const { return (ENUM_SYMBOL_OPTION_MODE)this.GetProperty(SYMBOL_PROP_OPTION_MODE); } ENUM_SYMBOL_OPTION_RIGHT OptionRight(void) const { return (ENUM_SYMBOL_OPTION_RIGHT)this.GetProperty(SYMBOL_PROP_OPTION_RIGHT); } //--- Real properties double Bid(void) const { return this.GetProperty(SYMBOL_PROP_BID); } double BidHigh(void) const { return this.GetProperty(SYMBOL_PROP_BIDHIGH); } double BidLow(void) const { return this.GetProperty(SYMBOL_PROP_BIDLOW); } double Ask(void) const { return this.GetProperty(SYMBOL_PROP_ASK); } double AskHigh(void) const { return this.GetProperty(SYMBOL_PROP_ASKHIGH); } double AskLow(void) const { return this.GetProperty(SYMBOL_PROP_ASKLOW); } double Last(void) const { return this.GetProperty(SYMBOL_PROP_LAST); } double LastHigh(void) const { return this.GetProperty(SYMBOL_PROP_LASTHIGH); } double LastLow(void) const { return this.GetProperty(SYMBOL_PROP_LASTLOW); } double VolumeReal(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_REAL); } double VolumeHighReal(void) const { return this.GetProperty(SYMBOL_PROP_VOLUMEHIGH_REAL); } double VolumeLowReal(void) const { return this.GetProperty(SYMBOL_PROP_VOLUMELOW_REAL); } double OptionStrike(void) const { return this.GetProperty(SYMBOL_PROP_OPTION_STRIKE); } double Point(void) const { return this.GetProperty(SYMBOL_PROP_POINT); } double TradeTickValue(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE); } double TradeTickValueProfit(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT); } double TradeTickValueLoss(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE_LOSS); } double TradeTickSize(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_SIZE); } double TradeContractSize(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_CONTRACT_SIZE); } double TradeAccuredInterest(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_ACCRUED_INTEREST); } double TradeFaceValue(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_FACE_VALUE); } double TradeLiquidityRate(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_LIQUIDITY_RATE); } double LotsMin(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_MIN); } double LotsMax(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_MAX); } double LotsStep(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_STEP); } double VolumeLimit(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_LIMIT); } double SwapLong(void) const { return this.GetProperty(SYMBOL_PROP_SWAP_LONG); } double SwapShort(void) const { return this.GetProperty(SYMBOL_PROP_SWAP_SHORT); } double MarginInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_INITIAL); } double MarginMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_MAINTENANCE); } double MarginLongInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_LONG_INITIAL); } double MarginBuyStopInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL); } double MarginBuyLimitInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL); } double MarginBuyStopLimitInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL); } double MarginLongMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_LONG_MAINTENANCE); } double MarginBuyStopMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE); } double MarginBuyLimitMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE); } double MarginBuyStopLimitMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE); } double MarginShortInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SHORT_INITIAL); } double MarginSellStopInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL); } double MarginSellLimitInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL); } double MarginSellStopLimitInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL); } double MarginShortMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE); } double MarginSellStopMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE); } double MarginSellLimitMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE); } double MarginSellStopLimitMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE); } double SessionVolume(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_VOLUME); } double SessionTurnover(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_TURNOVER); } double SessionInterest(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_INTEREST); } double SessionBuyOrdersVolume(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME); } double SessionSellOrdersVolume(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME); } double SessionOpen(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_OPEN); } double SessionClose(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_CLOSE); } double SessionAW(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_AW); } double SessionPriceSettlement(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_SETTLEMENT); } double SessionPriceLimitMin(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN); } double SessionPriceLimitMax(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX); } double MarginHedged(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_HEDGED); } double NormalizedPrice(const double price) const; double NormalizedLot(const double volume) const; double BidLast(void) const; double BidLastHigh(void) const; double BidLastLow(void) const; double AskLast(void) const; double AskLastHigh(void) const; double AskLastLow(void) const; //--- String properties string Name(void) const { return this.GetProperty(SYMBOL_PROP_NAME); } string Basis(void) const { return this.GetProperty(SYMBOL_PROP_BASIS); } string CurrencyBase(void) const { return this.GetProperty(SYMBOL_PROP_CURRENCY_BASE); } string CurrencyProfit(void) const { return this.GetProperty(SYMBOL_PROP_CURRENCY_PROFIT); } string CurrencyMargin(void) const { return this.GetProperty(SYMBOL_PROP_CURRENCY_MARGIN); } string Bank(void) const { return this.GetProperty(SYMBOL_PROP_BANK); } string Description(void) const { return this.GetProperty(SYMBOL_PROP_DESCRIPTION); } string Formula(void) const { return this.GetProperty(SYMBOL_PROP_FORMULA); } string ISIN(void) const { return this.GetProperty(SYMBOL_PROP_ISIN); } string Page(void) const { return this.GetProperty(SYMBOL_PROP_PAGE); } string Path(void) const { return this.GetProperty(SYMBOL_PROP_PATH); } string Category(void) const { return this.GetProperty(SYMBOL_PROP_CATEGORY); } string Exchange(void) const { return this.GetProperty(SYMBOL_PROP_EXCHANGE); } //+------------------------------------------------------------------+
我们已有类似的三种针对出价的方法,尽管没有检查价格存在与否。 我们现对其进行修改:
//+------------------------------------------------------------------+ //| Return Bid or Last price | //| depending on the chart construction method and price availability| //+------------------------------------------------------------------+ double CSymbol::BidLast(void) const { return ( this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_BID) : (this.GetProperty(SYMBOL_PROP_BID)==0 ? this.GetProperty(SYMBOL_PROP_LAST) : this.GetProperty(SYMBOL_PROP_BID)) ); } //+------------------------------------------------------------------+ //| Return maximum Bid or Last price for a day | //| depending on the chart construction method and price availability| //+------------------------------------------------------------------+ double CSymbol::BidLastHigh(void) const { return ( this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_BIDHIGH) : (this.GetProperty(SYMBOL_PROP_BIDHIGH)==0 ? this.GetProperty(SYMBOL_PROP_LASTHIGH) : this.GetProperty(SYMBOL_PROP_BIDHIGH)) ); } //+------------------------------------------------------------------+ //| Return minimum Bid or Last price for a day | //| depending on the chart construction method and price availability| //+------------------------------------------------------------------+ double CSymbol::BidLastLow(void) const { return ( this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_BIDLOW) : (this.GetProperty(SYMBOL_PROP_BIDLOW)==0 ? this.GetProperty(SYMBOL_PROP_LASTLOW) : this.GetProperty(SYMBOL_PROP_BIDLOW)) ); } //+------------------------------------------------------------------+
如上所述,此处我们检查图表构建类型。 如果图表基于出价,则返回相应的出价。 如果图表基于最后价格,则另外检查品种属性返回的出价是否为零。 如果等于零,则采用相应的“最后价格”,否则采用相应的“出价”。
我们为返回要价的方法编写相同的实现:
//+------------------------------------------------------------------+ //| Return Ask or Last price | //| depending on the chart construction method and price availability| //+------------------------------------------------------------------+ double CSymbol::AskLast(void) const { return ( this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_ASK) : (this.GetProperty(SYMBOL_PROP_ASK)==0 ? this.GetProperty(SYMBOL_PROP_LAST) : this.GetProperty(SYMBOL_PROP_ASK)) ); } //+------------------------------------------------------------------+ //| Return maximum Ask or Last price for a day | //| depending on the chart construction method and price availability| //+------------------------------------------------------------------+ double CSymbol::AskLastHigh(void) const { return ( this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_ASKHIGH) : (this.GetProperty(SYMBOL_PROP_ASKHIGH)==0 ? this.GetProperty(SYMBOL_PROP_LASTHIGH) : this.GetProperty(SYMBOL_PROP_ASKHIGH)) ); } //+------------------------------------------------------------------+ //| Return minimum Ask or Last price for a day | //| depending on the chart construction method and price availability| //+------------------------------------------------------------------+ double CSymbol::AskLastLow(void) const { return ( this.ChartMode()==SYMBOL_CHART_MODE_BID ? this.GetProperty(SYMBOL_PROP_ASKLOW) : (this.GetProperty(SYMBOL_PROP_ASKLOW)==0 ? this.GetProperty(SYMBOL_PROP_LASTLOW) : this.GetProperty(SYMBOL_PROP_ASKLOW)) ); } //+------------------------------------------------------------------+
在函数库中,当计算价位以便获取要价或最后价,以及出价或最后价时调用这些方法。
当在日志中显示有关交易事件的消息时,存储在魔幻数字当中的附加数据(在上一篇文章中实现的),为我们提供了额外表示的魔换数字 ID 和两组编号。 但是,如果魔幻数字还包含延后请求 ID,则它不会显示在日志中。 我们来修复这个问题。 在 EventModify.mqh,EventOrderPlaced.mqh,EventOrderRemoved.mqh,EventPositionClose.mqh 和 EventPositionOpen.mqh 文件中,为每个事件类的相应事件简述方法添加几段代码。
在每个文件中替换以下字符串:
string magic=(this.Magic()!=0 ? ", "+CMessage::Text(MSG_ORD_MAGIC)+" "+(string)this.Magic()+magic_id+group_id1+group_id2 : "");
为以下两个字符串:
string pend_req_id=(this.GetPendReqID()>0 ? ", ID: "+(string)this.GetPendReqID() : ""); string magic=(this.Magic()!=0 ? ", "+CMessage::Text(MSG_ORD_MAGIC)+" "+(string)this.Magic()+magic_id+group_id1+group_id2+pend_req_id : "");
由此,我们便将将延后事件 ID(如果有)的描述添加到了魔幻数字描述中。
在 TradeObj.mqh 文件里,将返回 MqlTradeRequest 交易请求结构字段说明的方法添加到品种的 CTradeObj 交易对象类的公开部分:
//--- Return the description of the (1) executed action type, (2) magic number, (3) order ticket, (4) volume, //--- (5) open, (6) StopLimit order, (7) StopLoss, (8) TakeProfit price, (9) deviation, //--- type of (10) order, (11) execution, (12) lifetime, (13) order expiration date, //--- (14) comment, (15) position ticket, (16) opposite position ticket string GetRequestActionDescription(void) const { return RequestActionDescription(this.m_request); } string GetRequestMagicDescription(void) const { return RequestMagicDescription(this.m_request); } string GetRequestOrderDescription(void) const { return RequestOrderDescription(this.m_request); } string GetRequestSymbolDescription(void) const { return RequestSymbolDescription(this.m_request); } string GetRequestVolumeDescription(void) const { return RequestVolumeDescription(this.m_request); } string GetRequestPriceDescription(void) const { return RequestPriceDescription(this.m_request); } string GetRequestStopLimitDescription(void) const { return RequestStopLimitDescription(this.m_request); } string GetRequestStopLossDescription(void) const { return RequestStopLossDescription(this.m_request); } string GetRequestTakeProfitDescription(void) const { return RequestTakeProfitDescription(this.m_request); } string GetRequestDeviationDescription(void) const { return RequestDeviationDescription(this.m_request); } string GetRequestTypeDescription(void) const { return RequestTypeDescription(this.m_request); } string GetRequestTypeFillingDescription(void) const { return RequestTypeFillingDescription(this.m_request); } string GetRequestTypeTimeDescription(void) const { return RequestTypeTimeDescription(this.m_request); } string GetRequestExpirationDescription(void) const { return RequestExpirationDescription(this.m_request); } string GetRequestCommentDescription(void) const { return RequestCommentDescription(this.m_request); } string GetRequestPositionDescription(void) const { return RequestPositionDescription(this.m_request); } string GetRequestPositionByDescription(void) const { return RequestPositionByDescription(this.m_request); } //--- Open a position
这些方法简单地调用我们先前在函数库服务函数文件中创建的相应函数。
以前,我忽略了将订单填充类型传递给开仓方法。
我们将此参数添加到开仓方法的声明当中:
//--- Open a position bool 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, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
和方法的实现之中:
//+------------------------------------------------------------------+ //| 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, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) {
由于我会在将来实现运用延后请求进行交易的能力,故此我引入了延后请求类型的概念。
在 Trading.mqh交易类文件(即在 CPendingReq 延后请求类)中,将类成员变量添加到私密部分,以便存储延后请求类型:
//+------------------------------------------------------------------+ //| Pending request object class | //+------------------------------------------------------------------+ class CPendingReq : public CObject { private: MqlTradeRequest m_request; // Trade request structure uchar m_id; // Trading request ID int m_type; // Pending request type int m_retcode; // Result a request is based on double m_price_create; // Price at the moment of a request generation ulong m_time_create; // Request generation time ulong m_time_activate; // Next attempt activation time ulong m_waiting_msc; // Waiting time between requests uchar m_current_attempt; // Current attempt index uchar m_total_attempts; // Number of attempts
在类的公开部分中,添加返回服务器回馈代码的方法,返回延后请求属性说明的方法,和请求类型的方法,以及将所有交易请求数据输出到日志的方法:
public: //--- Return (1) the request structure, (2) the price at the moment of the request generation, //--- (3) request generation time, (4) current attempt time, //--- (5) waiting time between requests, (6) current attempt index, //--- (7) number of attempts, (8) request ID MqlTradeRequest MqlRequest(void) const { return this.m_request; } double PriceCreate(void) const { return this.m_price_create; } ulong TimeCreate(void) const { return this.m_time_create; } ulong TimeActivate(void) const { return this.m_time_activate; } ulong WaitingMSC(void) const { return this.m_waiting_msc; } uchar CurrentAttempt(void) const { return this.m_current_attempt; } uchar TotalAttempts(void) const { return this.m_total_attempts; } uchar ID(void) const { return this.m_id; } int Retcode(void) const { return this.m_retcode; } //--- Set (1) the price when creating a request, (2) request creation time, //--- (3) current attempt time, (4) waiting time between requests, //--- (5) current attempt index, (6) number of attempts, (7) request ID void SetPriceCreate(const double price) { this.m_price_create=price; } void SetTimeCreate(const ulong time) { this.m_time_create=time; } void SetTimeActivate(const ulong time) { this.m_time_activate=time; } void SetWaitingMSC(const ulong miliseconds) { this.m_waiting_msc=miliseconds;} void SetCurrentAttempt(const uchar number) { this.m_current_attempt=number; } void SetTotalAttempts(const uchar number) { this.m_total_attempts=number; } void SetID(const uchar id) { this.m_id=id; } //--- Return the description of the (1) request structure, (2) the price at the moment of the request generation, //--- (3) request generation time, (4) current attempt time, //--- (5) waiting time between requests, (6) current attempt index, //--- (7) number of attempts, (8) request ID string MqlRequestDescription(void) const { return RequestActionDescription(this.m_request); } string TypeDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_TYPE) + (this.Type()==PENDING_REQUEST_ID_TYPE_ERR ? CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_BY_ERROR) : CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_BY_REQUEST) ); } string PriceCreateDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_PRICE_CREATE)+": "+ ::DoubleToString(this.PriceCreate(),(int)::SymbolInfoInteger(this.m_request.symbol,SYMBOL_DIGITS)); } string TimeCreateDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_TIME_CREATE)+TimeMSCtoString(this.TimeCreate()); } string TimeActivateDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_TIME_ACTIVATE)+TimeMSCtoString(this.TimeActivate());} string WaitingMSCDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_WAITING)+(string)this.WaitingMSC(); } string CurrentAttemptDescription(void) const { return (CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_CURRENT_ATTEMPT)+ (this.CurrentAttempt()==0 ? CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_WAITING_ONSET) : (string)this.CurrentAttempt()) ); } string TotalAttemptsDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_TOTAL_ATTEMPTS)+(string)this.TotalAttempts(); } string IdDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ID)+(string)this.ID(); } string RetcodeDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_RETCODE)+(string)this.Retcode(); } string ReasonDescription(void) const { return CMessage::Text(this.m_retcode); } //--- Return a request type virtual int Type(void) const { return this.m_type; } //--- Display request data in the journal void Print(void); //--- Constructors CPendingReq(void){;} CPendingReq(const uchar id,const double price,const ulong time,const MqlTradeRequest &request,const int retcode); }; //+------------------------------------------------------------------+
返回延后请求对象属性说明的方法仅在标题中生成一个属性及其值的复合描述。 返回延后请求类型的 Type() 方法是虚拟的,因为相同的虚拟方法已定义在 CObject 基准对象派生出的延后请求对象之中。 为了实现返回派生对象类型,应在后代中重新定义该方法。 我确实已经完成了,现在该方法返回延后请求类中定义的 m_type 变量值。
在类的构造函数中,设置延后请求类型的值:
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CPendingReq::CPendingReq(const uchar id,const double price,const ulong time,const MqlTradeRequest &request,const int retcode) : m_price_create(price), m_time_create(time), m_id(id), m_retcode(retcode) { this.CopyRequest(request); this.m_type=(retcode>0 ? PENDING_REQUEST_ID_TYPE_ERR : PENDING_REQUEST_ID_TYPE_REQ); } //+------------------------------------------------------------------+
由于延后请求是依据服务器返回代码和程序请求来创建的,因此知道服务器的响应代码就足以定义延后请求的类型。 这正是我们于此所做的:如果返回码超过零(服务器返回了错误),则此请求是根据服务器返回码生成的。 如果返回码为零,则表明延后请求对象是由程序请求创建的。
改进延后请求对象的 Compare() 虚拟方法。
以前,它总是根据单一属性比较对象 — request ID:
//+------------------------------------------------------------------+ //| Compare CPendingReq objects by IDs | //+------------------------------------------------------------------+ int CPendingReq::Compare(const CObject *node,const int mode=0) const { const CPendingReq *compared_req=node; return(this.ID()>compared_req.ID() ? 1 : this.ID()<compared_req.ID() ? -1 : 0); return 0; } //+------------------------------------------------------------------+
引入延后请求类型之后,我们比较对象时需要按照两个属性 — ID 和类型。
若要实现它,请修改比较两个对象的方法:
//+------------------------------------------------------------------+ //| Compare CPendingReq objects by properties | //+------------------------------------------------------------------+ int CPendingReq::Compare(const CObject *node,const int mode=0) const { const CPendingReq *compared_req=node; return ( //--- Compare by ID mode==0 ? (this.ID()>compared_req.ID() ? 1 : this.ID()<compared_req.ID() ? -1 : 0) : //--- Compare by type (this.Type()>compared_req.Type() ? 1 : this.Type()<compared_req.Type() ? -1 : 0) ); } //+------------------------------------------------------------------+
在此,选择了 mode 变量作为比较的属性。 若为 0,则按 ID 比较对象。 若非 0,则按对象类型.进行比较。
同样,在类主体之外,编写显示所有延后请求对象属性完整说明到日志的方法:
//+------------------------------------------------------------------+ //| Display request data in the journal | //+------------------------------------------------------------------+ void CPendingReq::Print(void) { string action=" - "+RequestActionDescription(this.m_request)+"/n"; string symbol="",order="",volume="",price="",stoplimit="",sl="",tp="",deviation="",type="",type_filling=""; string type_time="",expiration="",position="",position_by="",magic="",comment="",request_data=""; string type_req=" - "+this.TypeDescription()+"/n"; if(this.m_request.action==TRADE_ACTION_DEAL) { symbol=" - "+RequestSymbolDescription(this.m_request)+"/n"; volume=" - "+RequestVolumeDescription(this.m_request)+"/n"; price=" - "+RequestPriceDescription(this.m_request)+"/n"; sl=" - "+RequestStopLossDescription(this.m_request)+"/n"; tp=" - "+RequestTakeProfitDescription(this.m_request)+"/n"; deviation=" - "+RequestDeviationDescription(this.m_request)+"/n"; type=" - "+RequestTypeDescription(this.m_request)+"/n"; type_filling=" - "+RequestTypeFillingDescription(this.m_request)+"/n"; magic=" - "+RequestMagicDescription(this.m_request)+"/n"; comment=" - "+RequestCommentDescription(this.m_request)+"/n"; request_data= ("================== "+ CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)+" ==================/n"+ action+symbol+volume+price+sl+tp+deviation+type+type_filling+magic+comment+ " ==================/n" ); } else if(this.m_request.action==TRADE_ACTION_SLTP) { symbol=" - "+RequestSymbolDescription(this.m_request)+"/n"; sl=" - "+RequestStopLossDescription(this.m_request)+"/n"; tp=" - "+RequestTakeProfitDescription(this.m_request)+"/n"; position=" - "+RequestPositionDescription(this.m_request)+"/n"; request_data= ("================== "+ CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)+" ==================/n"+ action+symbol+sl+tp+position+ " ==================/n" ); } else if(this.m_request.action==TRADE_ACTION_PENDING) { symbol=" - "+RequestSymbolDescription(this.m_request)+"/n"; volume=" - "+RequestVolumeDescription(this.m_request)+"/n"; price=" - "+RequestPriceDescription(this.m_request)+"/n"; stoplimit=" - "+RequestStopLimitDescription(this.m_request)+"/n"; sl=" - "+RequestStopLossDescription(this.m_request)+"/n"; tp=" - "+RequestTakeProfitDescription(this.m_request)+"/n"; type=" - "+RequestTypeDescription(this.m_request)+"/n"; type_filling=" - "+RequestTypeFillingDescription(this.m_request)+"/n"; type_time=" - "+RequestTypeTimeDescription(this.m_request)+"/n"; expiration=" - "+RequestExpirationDescription(this.m_request)+"/n"; magic=" - "+RequestMagicDescription(this.m_request)+"/n"; comment=" - "+RequestCommentDescription(this.m_request)+"/n"; request_data= ("================== "+ CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)+" ==================/n"+ action+symbol+volume+price+stoplimit+sl+tp+type+type_filling+type_time+expiration+magic+comment+ " ==================/n" ); } else if(this.m_request.action==TRADE_ACTION_MODIFY) { order=" - "+RequestOrderDescription(this.m_request)+"/n"; price=" - "+RequestPriceDescription(this.m_request)+"/n"; sl=" - "+RequestStopLossDescription(this.m_request)+"/n"; tp=" - "+RequestTakeProfitDescription(this.m_request)+"/n"; type_time=" - "+RequestTypeTimeDescription(this.m_request)+"/n"; expiration=" - "+RequestExpirationDescription(this.m_request)+"/n"; request_data= ("================== "+ CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)+" ==================/n"+ action+order+price+sl+tp+type_time+expiration+ " ==================/n" ); } else if(this.m_request.action==TRADE_ACTION_REMOVE) { order=" - "+RequestOrderDescription(this.m_request)+"/n"; request_data= ("================== "+ CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)+" ==================/n"+ action+order+ " ==================/n" ); } else if(this.m_request.action==TRADE_ACTION_CLOSE_BY) { position=" - "+RequestPositionDescription(this.m_request)+"/n"; position_by=" - "+RequestPositionByDescription(this.m_request)+"/n"; magic=" - "+RequestMagicDescription(this.m_request)+"/n"; request_data= ("================== "+ CMessage::Text(MSG_LIB_TEXT_REQUEST_DATAS)+" ==================/n"+ action+position+position_by+magic+ " ==================/n" ); } string datas= ( " - "+this.TypeDescription()+"/n"+ " - "+this.IdDescription()+"/n"+ " - "+this.RetcodeDescription()+" /""+this.ReasonDescription()+"/"/n"+ " - "+this.TimeCreateDescription()+"/n"+ " - "+this.PriceCreateDescription()+"/n"+ " - "+this.TimeActivateDescription()+"/n"+ " - "+this.WaitingMSCDescription()+" ("+TimeToString(this.WaitingMSC()/1000,TIME_MINUTES|TIME_SECONDS)+")"+"/n"+ " - "+this.CurrentAttemptDescription()+"/n"+ " - "+this.TotalAttemptsDescription()+"/n" ); ::Print("================== ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_DATAS)," ==================/n",datas,request_data); } //+------------------------------------------------------------------+
在该方法中,所有对象属性的说明都收集在字符串变量中,其中包括对象的交易请求结构的字段描述。 所显示数据的数量取决于交易请求的操作类型,因为不同的操作需要不同的交易请求结构字段数量。 所以,会检查 “action” 字段值,且仅显示相应的字段。 首先显示对象变量的说明,然后显示请求结构字段的说明。 故此,所有延后请求对象属性都根据交易请求的操作类型(action)显示在日志中。
之前,我们曾在 CTradeObj 类的开仓方法中添加了额外属性 — 订单填充类型。
现在,我们将相同的属性添加到 CTrading 类中的私密开仓方法定义中:
//--- (1) Open a position, (2) place a pending order template<typename SL,typename TP> bool OpenPosition(const ENUM_POSITION_TYPE type, 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, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
在公开的开立多空仓位的方法定义中添加相同的属性:
//--- Open (1) Buy, (2) Sell position template<typename SL,typename TP> bool 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, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename SL,typename TP> bool OpenSell(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, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
另外,我们在类主体之外实现这些方法时也要添加相同的参数:
//+------------------------------------------------------------------+ //| Open a position | //+------------------------------------------------------------------+ template<typename SL,typename TP> bool CTrading::OpenPosition(const ENUM_POSITION_TYPE type, 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, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { //+------------------------------------------------------------------+ //| 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, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { //--- Return the result of sending a trading request from the OpenPosition() method return this.OpenPosition(POSITION_TYPE_BUY,volume,symbol,magic,sl,tp,comment,deviation,type_filling); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Open a Sell position | //+------------------------------------------------------------------+ template<typename SL,typename TP> bool CTrading::OpenSell(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, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { //--- Return the result of sending a trading request from the OpenPosition() method return this.OpenPosition(POSITION_TYPE_SELL,volume,symbol,magic,sl,tp,comment,deviation,type_filling); } //+------------------------------------------------------------------+
在类的私密部分中,声明按 ID 返回列表中请求对象索引的方法:
//--- Look for the first free pending request ID int GetFreeID(void); //--- Return the request object index in the list by ID int GetIndexPendingRequestByID(const uchar id); public:
我略微修改了类计时器的实现。
下面是完整的计时器实现代码,其逻辑在注释中进行了描述:
//+------------------------------------------------------------------+ //| Timer | //+------------------------------------------------------------------+ void CTrading::OnTimer(void) { //--- In a loop by the list of pending requests int total=this.m_list_request.Total(); for(int i=total-1;i>WRONG_VALUE;i--) { //--- receive the next request object CPendingReq *req_obj=this.m_list_request.At(i); if(req_obj==NULL) continue; //--- get the request structure and the symbol object a trading operation should be performed for MqlTradeRequest request=req_obj.MqlRequest(); CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(request.symbol); if(symbol_obj==NULL || !symbol_obj.RefreshRates()) continue; //--- if the current attempt exceeds the defined number of trading attempts, //--- or the current time exceeds the waiting time of all attempts //--- remove the current request object and move on to the next one if(req_obj.CurrentAttempt()>req_obj.TotalAttempts() || req_obj.CurrentAttempt()>=UCHAR_MAX || (long)symbol_obj.Time()>long(req_obj.TimeCreate()+req_obj.WaitingMSC()*req_obj.TotalAttempts())) { if(this.m_log_level>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_DELETED)); req_obj.Print(); } this.m_list_request.Delete(i); continue; } //--- Get the pending request ID uchar id=this.GetPendReqID((uint)request.magic); //--- Get the list of orders/positions containing the order/position with the pending request ID CArrayObj *list=this.m_market.GetList(ORDER_PROP_PEND_REQ_ID,id,EQUAL); if(::CheckPointer(list)==POINTER_INVALID) continue; //--- If the order/position is present, the request is handled: remove it and proceed to the next if(list.Total()>0) { this.m_list_request.Delete(i); continue; } //--- Set the request activation time in the request object req_obj.SetTimeActivate(req_obj.TimeCreate()+req_obj.WaitingMSC()*(req_obj.CurrentAttempt()+1)); //--- If the current time is less than the request activation time, //--- this is not the request time - move on to the next request in the list if((long)symbol_obj.Time()<(long)req_obj.TimeActivate()) continue; //--- Set the attempt number in the request object req_obj.SetCurrentAttempt(uchar(req_obj.CurrentAttempt()+1)); //--- Display the number of the trading attempt in the journal if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(CMessage::Text(MSG_LIB_TEXT_RE_TRY_N)+(string)req_obj.CurrentAttempt()); //--- Depending on the type of action performed in the trading request switch(request.action) { //--- Open a position case TRADE_ACTION_DEAL : this.OpenPosition((ENUM_POSITION_TYPE)request.type,request.volume,request.symbol,request.magic,request.sl,request.tp,request.comment,request.deviation,request.type_filling); break; //--- Place a pending order case TRADE_ACTION_PENDING : this.PlaceOrder(request.type,request.volume,request.symbol,request.price,request.stoplimit,request.sl,request.tp,request.magic,request.comment,request.expiration,request.type_time,request.type_filling); break; //--- default: break; } } } //+------------------------------------------------------------------+
我希望此处的一切都很清晰。
在 RequestErrorsCorrecting() 错误纠正方法中,添加到期类型纠正,应对收到“无效订单到期日期”错误的情况(仅有一部分代码得以纠正):
//--- The specified type of order execution by balance is not supported if(this.IsPresentErorCode(10030)) request.type_filling=symbol_obj.GetCorrectTypeFilling(); //--- Invalid order expiration in a request - if(this.IsPresentErorCode(10022)) { //--- Set a correct order expiration type request.type_time=symbol_obj.GetCorrectTypeExpiration(); //--- if the expiration type is not supported as set by the expiration date and the expiration data is defined, reset the expiration date if(!symbol_obj.IsExpirationModeSpecified() && request.expiration>0) request.expiration=0; } //--- View the list of remaining errors and correct trading request parameters
以前,我们在品种对象中添加了获取要价的新方法,并调整了获取出价的方法。 现在我们需要将整个清单中所有出现的 “Ask()” 和 “Bid()” 字符串分别替换为 “AskLast()” 和 “BidLast()”。 最方便的方法是针对整个代码采用搜索和替换功能(Ctrl + H)。 如此,在需要品种对象的要价和出价的地方,我们能够自动选择合适的价格。
例如,设置交易请求价格的方法现在如下所示,已替换了价格:
//+------------------------------------------------------------------+ //| Set trading request prices | //+------------------------------------------------------------------+ template <typename PS,typename SL,typename TP,typename PL> bool CTrading::SetPrices(const ENUM_ORDER_TYPE action,const PS 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); // No quotes to handle the request return false; } //--- Open/close price if(price>0) { //--- price parameter type (double) - normalize the price up to Digits(), since the price has been passed if(typename(price)=="double") this.m_request.price=::NormalizeDouble(price,symbol_obj.Digits()); //--- price parameter type (int) - the distance has been passed else if(typename(price)=="int" || typename(price)=="uint" || typename(price)=="long" || typename(price)=="ulong") { //--- Calculate the order price switch((int)action) { //--- Pending order case ORDER_TYPE_BUY_LIMIT : this.m_request.price=::NormalizeDouble(symbol_obj.AskLast()-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.AskLast()+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.AskLast(),symbol_obj.Digits()) : ::NormalizeDouble(symbol_obj.BidLast(),symbol_obj.Digits()) ); break; } } //--- unsupported price types - display the message and return 'false' else { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_UNSUPPORTED_PR_TYPE)); return false; } } //--- If no price is specified, use the current prices else { this.m_request.price= ( this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY ? ::NormalizeDouble(symbol_obj.AskLast(),symbol_obj.Digits()) : ::NormalizeDouble(symbol_obj.BidLast(),symbol_obj.Digits()) ); } //--- StopLimit order price or distance if(limit>0) { //--- limit order price parameter type (double) - normalize the price up to Digits(), since the price has been passed if(typename(limit)=="double") this.m_request.stoplimit=::NormalizeDouble(limit,symbol_obj.Digits()); //--- limit order price parameter type (int) - the distance has been passed else if(typename(limit)=="int" || typename(limit)=="uint" || typename(limit)=="long" || typename(limit)=="ulong") { //--- Calculate a limit order price if(this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY) this.m_request.stoplimit=::NormalizeDouble(this.m_request.price-limit*symbol_obj.Point(),symbol_obj.Digits()); else this.m_request.stoplimit=::NormalizeDouble(this.m_request.price+limit*symbol_obj.Point(),symbol_obj.Digits()); } //--- unsupported limit order price types - display the message and return 'false' else { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_UNSUPPORTED_PL_TYPE)); return false; } } //--- Order price stop order prices are calculated from double price_open= ( (action==ORDER_TYPE_BUY_STOP_LIMIT || action==ORDER_TYPE_SELL_STOP_LIMIT) && limit>0 ? this.m_request.stoplimit : this.m_request.price ); //--- StopLoss if(sl>0) { //--- StopLoss parameter type (double) - normalize the price up to Digits(), since the price has been passed if(typename(sl)=="double") this.m_request.sl=::NormalizeDouble(sl,symbol_obj.Digits()); //--- StopLoss parameter type (int) - calculate the placement distance else if(typename(sl)=="int" || typename(sl)=="uint" || typename(sl)=="long" || typename(sl)=="ulong") { //--- Calculate the StopLoss price if(this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY) this.m_request.sl=::NormalizeDouble(price_open-sl*symbol_obj.Point(),symbol_obj.Digits()); else this.m_request.sl=::NormalizeDouble(price_open+sl*symbol_obj.Point(),symbol_obj.Digits()); } //--- unsupported StopLoss types - display the message and return 'false' else { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_UNSUPPORTED_SL_TYPE)); return false; } } //--- TakeProfit if(tp>0) { //--- TakeProfit parameter type (double) - normalize the price up to Digits(), since the price has been passed if(typename(tp)=="double") this.m_request.tp=::NormalizeDouble(tp,symbol_obj.Digits()); //--- TakeProfit parameter type (int) - calculate the placement distance else if(typename(tp)=="int" || typename(tp)=="uint" || typename(tp)=="long" || typename(tp)=="ulong") { if(this.DirectionByActionType((ENUM_ACTION_TYPE)action)==ORDER_TYPE_BUY) this.m_request.tp=::NormalizeDouble(price_open+tp*symbol_obj.Point(),symbol_obj.Digits()); else this.m_request.tp=::NormalizeDouble(price_open-tp*symbol_obj.Point(),symbol_obj.Digits()); } //--- unsupported TakeProfit types - display the message and return 'false' else { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(source_method,CMessage::Text(MSG_LIB_TEXT_UNSUPPORTED_TP_TYPE)); return false; } } //--- All prices are recorded return true; } //+------------------------------------------------------------------+
在此展示所有已执行替换后的代码毫无意义。 它们都已被整理过,并附在下面。
在下挂单的私密方法实现中,添加创建延后请求的模块,应对服务器发生错误的情况:
//+------------------------------------------------------------------+ //| Place a pending order | //+------------------------------------------------------------------+ template<typename PS,typename PL,typename SL,typename TP> bool CTrading::PlaceOrder(const ENUM_ORDER_TYPE order_type, const double volume, const string symbol, const PS price_stop, const PL price_limit=0, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { bool res=true; this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_NO_ERROR; ENUM_ACTION_TYPE action=(ENUM_ACTION_TYPE)order_type; //--- Get a symbol object by a symbol name CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(symbol); 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(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, set the error code in the return structure, //--- display the message in the journal and return 'false' if(!this.SetPrices(order_type,price_stop,sl,tp,price_limit,DFUN,symbol_obj)) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR; trade_obj.SetResultRetcode(10021); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(10021)); // No quotes to process the request return false; } //--- In case of trading limitations, funds insufficiency, //--- there are limitations on StopLevel - play the error sound and exit this.m_request.volume=volume; this.m_request.type_filling=type_filling; this.m_request.type_time=type_time; this.m_request.expiration=expiration; ENUM_ERROR_CODE_PROCESSING_METHOD method=this.CheckErrors(this.m_request.volume, this.m_request.price, action, order_type, symbol_obj, trade_obj, DFUN, this.m_request.stoplimit, this.m_request.sl, this.m_request.tp); if(method!=ERROR_CODE_PROCESSING_METHOD_OK) { //--- If trading is completely disabled if(method==ERROR_CODE_PROCESSING_METHOD_DISABLE) { trade_obj.SetResultRetcode(MSG_LIB_TEXT_TRADING_DISABLE); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); 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" - set the last error code to the return structure, //--- display a journal message, play the error sound and exit if(method==ERROR_CODE_PROCESSING_METHOD_EXIT) { int code=this.m_list_errors.At(this.m_list_errors.Total()-1); if(code!=NULL) { trade_obj.SetResultRetcode(code); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); } 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" - set the last error code to the return structure and display the message in the journal if(method==ERROR_CODE_PROCESSING_METHOD_WAIT) { int code=this.m_list_errors.At(this.m_list_errors.Total()-1); if(code!=NULL) { trade_obj.SetResultRetcode(code); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); } 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); 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)); } } //--- In the loop by the number of attempts for(int i=0;i<this.m_total_try;i++) { //--- Send the request res=trade_obj.SetOrder(order_type, this.m_request.volume, this.m_request.price, this.m_request.sl, this.m_request.tp, this.m_request.stoplimit, magic, comment, this.m_request.expiration, this.m_request.type_time, this.m_request.type_filling); //--- If the request is executed successfully or the asynchronous order sending mode is set, play the success sound //--- set for a symbol trading object for this type of trading operation and return 'true' if(res || trade_obj.IsAsyncMode()) { if(this.IsUseSounds()) trade_obj.PlaySoundSuccess(action,order_type); return true; } //--- 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_TEXT_TRY_N),string(i+1),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(trade_obj.GetResultRetcode())); if(this.IsUseSounds()) trade_obj.PlaySoundError(action,order_type); method=this.ResultProccessingMethod(trade_obj.GetResultRetcode()); //--- If "Disable trading for the EA" is received as a result of sending a request, enable the disabling flag and end the attempt loop if(method==ERROR_CODE_PROCESSING_METHOD_DISABLE) { this.SetTradingDisableFlag(true); break; } //--- If "Exit the trading method" is received as a result of sending a request, end the attempt loop if(method==ERROR_CODE_PROCESSING_METHOD_EXIT) { break; } //--- If "Correct the parameters and repeat" is received as a result of sending a request - //--- correct the parameters and start the next iteration if(method==ERROR_CODE_PROCESSING_METHOD_CORRECT) { this.RequestErrorsCorrecting(this.m_request,order_type,trade_obj.SpreadMultiplier(),symbol_obj,trade_obj); continue; } //--- If "Update data and repeat" is received as a result of sending a request - //--- update data and start the next iteration if(method==ERROR_CODE_PROCESSING_METHOD_REFRESH) { symbol_obj.Refresh(); continue; } //--- If "Wait and repeat" is received as a result of sending a request //--- create a pending request and end the loop if(method>ERROR_CODE_PROCESSING_METHOD_REFRESH) { //--- If the trading request magic number, has no pending request ID if(this.GetPendReqID((uint)magic)==0) { //--- Waiting time in milliseconds: //--- for the "Wait and repeat" handling method, the waiting value corresponds to the 'method' value, //--- for the "Create a pending request" handling method - till there is a zero waiting time ulong wait=(method>ERROR_CODE_PROCESSING_METHOD_PENDING ? method : 0); //--- Look for the least of the possible IDs. If failed to find //--- or in case of an error while updating the current symbol data, return 'false' int id=this.GetFreeID(); if(id<1 || !symbol_obj.RefreshRates()) return false; //--- Write the request ID to the magic number, while a symbol name is set in the request structure, //--- set the trading operation and order types, comment and position type, filling and expiration type uint mn=(magic==ULONG_MAX ? (uint)trade_obj.GetMagic() : (uint)magic); this.SetPendReqID((uchar)id,mn); this.m_request.magic=mn; this.m_request.symbol=symbol_obj.Name(); this.m_request.action=TRADE_ACTION_PENDING; this.m_request.type=order_type; this.m_request.comment=(comment==NULL ? trade_obj.GetComment() : comment); this.m_request.type_time=(type_time>WRONG_VALUE ? type_time : trade_obj.GetTypeExpiration()); this.m_request.type_filling=(type_filling>WRONG_VALUE ? type_filling : trade_obj.GetTypeFilling()); //--- Pass the number of trading attempts to the pending request uchar attempts=(this.m_total_try < 1 ? 1 : this.m_total_try); this.CreatePendingRequest((uchar)id,attempts,wait,this.m_request,trade_obj.GetResultRetcode(),symbol_obj); break; } } } } //--- Return the result of sending a trading request in a symbol trading object return res; } //+------------------------------------------------------------------+
与延后请求开发的所有相关操作都在代码注释中进行了描述,我相信,它们很易于理解。 如有情况,欢迎您使用评论板块。
为了简化调试(即能够查看延后请求的生成结果),在创建延后请求对象的方法中,添加新创建的请求属性 显示在日至里:
//+------------------------------------------------------------------+ //| Create a pending request | //+------------------------------------------------------------------+ bool CTrading::CreatePendingRequest(const uchar id,const uchar attempts,const ulong wait,const MqlTradeRequest &request,const int retcode,CSymbol *symbol_obj) { //--- Create a new pending request object CPendingReq *req_obj=new CPendingReq(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode); if(req_obj==NULL) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_FAILING_CREATE_PENDING_REQ)); return false; } //--- If failed to add the request to the list, display the appropriate message, //--- remove the created object and return 'false' if(!this.m_list_request.Add(req_obj)) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_FAILING_CREATE_PENDING_REQ)); delete req_obj; return false; } //--- Filled in the fields of a successfully created object by the values passed to the method req_obj.SetTimeActivate(symbol_obj.Time()+wait); req_obj.SetWaitingMSC(wait); req_obj.SetCurrentAttempt(0); req_obj.SetTotalAttempts(attempts); if(this.m_log_level>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_CREATED)," #",req_obj.ID(),":"); req_obj.Print(); } return true; } //+------------------------------------------------------------------+
在交易类清单的最末尾,实现按 ID 返回列表中请求对象索引的方法:
//+------------------------------------------------------------------+ //| Return the request object index in the list by ID | //+------------------------------------------------------------------+ int CTrading::GetIndexPendingRequestByID(const uchar id) { CPendingReq *req=new CPendingReq(); if(req==NULL) return WRONG_VALUE; req.SetID(id); this.m_list_request.Sort(); int index=this.m_list_request.Search(req); delete req; return index; } //+------------------------------------------------------------------+
方法接收必要的 ID,创建一个临时请求对象,并将 ID 传递给该方法进行设置。
接下来,为包含请求对象的列表设置已排序列表标志。 默认情况下,排序模式等于零。 该模式在 CPendingReq 类的 Compare() 虚拟方法中按 ID 比较排列对象。 如此,现在可以在对象指针的动态数组中调用 Search() 对象搜索方法。 该方法返回列表中得到的对象索引;如果找不到该对象,则返回 -1。 在退出该方法之前,删除临时请求对象,并且返回检测对象的对应索引或 -1。
现在,我们要做的就是为函数库的 CEngine 基准对象类添加一个附加的指定订单填充类型的参数。 该参数已添加到类中发送交易请求的方法定义里。
//--- Open (1) Buy, (2) Sell position template<typename SL,typename TP> bool OpenBuy(const double volume, const string symbol, const ulong magic=ULONG_MAX, SL sl=0, TP tp=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename SL,typename TP> bool OpenSell(const double volume, const string symbol, const ulong magic=ULONG_MAX, SL sl=0, TP tp=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); //--- Set (1) BuyStop, (2) BuyLimit, (3) BuyStopLimit pending order template<typename PR,typename SL,typename TP> bool PlaceBuyStop(const double volume, const string symbol, const PR price, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename PR,typename SL,typename TP> bool PlaceBuyLimit(const double volume, const string symbol, const PR price, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename PR,typename PL,typename SL,typename TP> bool PlaceBuyStopLimit(const double volume, const string symbol, const PR price_stop, const PL price_limit, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); //--- Set (1) SellStop, (2) SellLimit, (3) SellStopLimit pending order template<typename PR,typename SL,typename TP> bool PlaceSellStop(const double volume, const string symbol, const PR price, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename PR,typename SL,typename TP> bool PlaceSellLimit(const double volume, const string symbol, const PR price, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename PR,typename PL,typename SL,typename TP> bool PlaceSellStopLimit(const double volume, const string symbol, const PR price_stop, const PL price_limit, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
如果默认值为 -1,则从要执行交易操作的品种中获取正确的订单填充类型值。
将相同的参数添加到以下交易方法的代码实现中:
//+------------------------------------------------------------------+ //| Open Buy position | //+------------------------------------------------------------------+ template<typename SL,typename TP> bool CEngine::OpenBuy(const double volume, const string symbol, const ulong magic=ULONG_MAX, SL sl=0,TP tp=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.OpenBuy(volume,symbol,magic,sl,tp,comment,deviation,type_filling); } //+------------------------------------------------------------------+ //| Open a Sell position | //+------------------------------------------------------------------+ template<typename SL,typename TP> bool CEngine::OpenSell(const double volume, const string symbol, const ulong magic=ULONG_MAX, SL sl=0,TP tp=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.OpenSell(volume,symbol,magic,sl,tp,comment,deviation,type_filling); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Place BuyStop pending order | //+------------------------------------------------------------------+ template<typename PR,typename SL,typename TP> bool CEngine::PlaceBuyStop(const double volume, const string symbol, const PR price, const SL sl=0, const TP tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.PlaceBuyStop(volume,symbol,price,sl,tp,magic,comment,expiration,type_time,type_filling); } //+------------------------------------------------------------------+ //| Place BuyLimit pending order | //+------------------------------------------------------------------+ template<typename PR,typename SL,typename TP> bool CEngine::PlaceBuyLimit(const double volume, const string symbol, const PR price, const SL sl=0, const TP tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.PlaceBuyLimit(volume,symbol,price,sl,tp,magic,comment,expiration,type_time,type_filling); } //+------------------------------------------------------------------+ //| Place BuyStopLimit pending order | //+------------------------------------------------------------------+ template<typename PR,typename PL,typename SL,typename TP> bool CEngine::PlaceBuyStopLimit(const double volume, const string symbol, const PR price_stop, const PL price_limit, const SL sl=0, const TP tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.PlaceBuyStopLimit(volume,symbol,price_stop,price_limit,sl,tp,magic,comment,expiration,type_time,type_filling); } //+------------------------------------------------------------------+ //| Place SellStop pending order | //+------------------------------------------------------------------+ template<typename PR,typename SL,typename TP> bool CEngine::PlaceSellStop(const double volume, const string symbol, const PR price, const SL sl=0, const TP tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.PlaceSellStop(volume,symbol,price,sl,tp,magic,comment,expiration,type_time,type_filling); } //+------------------------------------------------------------------+ //| Place SellLimit pending order | //+------------------------------------------------------------------+ template<typename PR,typename SL,typename TP> bool CEngine::PlaceSellLimit(const double volume, const string symbol, const PR price, const SL sl=0, const TP tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.PlaceSellLimit(volume,symbol,price,sl,tp,magic,comment,expiration,type_time,type_filling); } //+------------------------------------------------------------------+ //| Place SellStopLimit pending order | //+------------------------------------------------------------------+ template<typename PR,typename PL,typename SL,typename TP> bool CEngine::PlaceSellStopLimit(const double volume, const string symbol, const PR price_stop, const PL price_limit, const SL sl=0, const TP tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.PlaceSellStopLimit(volume,symbol,price_stop,price_limit,sl,tp,magic,comment,expiration,type_time,type_filling); } //+------------------------------------------------------------------+
这些就是目前要做的所有调整和修改。
测试
为了测试下挂单的延后请求,我们采用上一篇文章中的 EA,并将其保存到 /MQL5/Experts/TestDoEasy/Part27/ 之下,命名为 TestDoEasyPart27.mq5。
在 EA 的函数库初始化函数中,为 EA 中用到的所有品种的所有交易对象,设置正确的订单填充和到期类型值:
//+------------------------------------------------------------------+ //| Initializing DoEasy library | //+------------------------------------------------------------------+ void OnInitDoEasy() { //--- Check if working with the full list is selected used_symbols_mode=InpModeUsedSymbols; if((ENUM_SYMBOLS_MODE)used_symbols_mode==SYMBOLS_MODE_ALL) { int total=SymbolsTotal(false); string ru_n="/nКоличество символов на сервере "+(string)total+"./nМаксимальное количество: "+(string)SYMBOLS_COMMON_TOTAL+" символов."; string en_n="/nNumber of symbols on server "+(string)total+"./nMaximum number: "+(string)SYMBOLS_COMMON_TOTAL+" symbols."; string caption=TextByLanguage("Внимание!","Attention!"); string ru="Выбран режим работы с полным списком./nВ этом режиме первичная подготовка списка коллекции символов может занять длительное время."+ru_n+"/nПродолжить?/n/"Нет/" - работа с текущим символом /""+Symbol()+"/""; string en="Full list mode selected./nIn this mode, the initial preparation of the collection symbols list may take a long time."+en_n+"/nContinue?/n/"No/" - working with the current symbol /""+Symbol()+"/""; string message=TextByLanguage(ru,en); int flags=(MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2); int mb_res=MessageBox(message,caption,flags); switch(mb_res) { case IDNO : used_symbols_mode=SYMBOLS_MODE_CURRENT; break; default: break; } } //--- Fill in the array of used symbols used_symbols=InpUsedSymbols; CreateUsedSymbolsArray((ENUM_SYMBOLS_MODE)used_symbols_mode,used_symbols,array_used_symbols); //--- Set the type of the used symbol list in the symbol collection engine.SetUsedSymbols(array_used_symbols); //--- Displaying the selected mode of working with the symbol object collection Print(engine.ModeSymbolsListDescription(),TextByLanguage(". Number of used symbols: ",". Number of symbols used: "),engine.GetSymbolsCollectionTotal()); //--- Create resource text files engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_01",TextByLanguage("Звук упавшей монетки 1","Falling coin 1"),sound_array_coin_01); engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_02",TextByLanguage("Звук упавших монеток","Falling coins"),sound_array_coin_02); engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_03",TextByLanguage("Звук монеток","Coins"),sound_array_coin_03); engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_04",TextByLanguage("Звук упавшей монетки 2","Falling coin 2"),sound_array_coin_04); engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_01",TextByLanguage("Звук щелчка по кнопке 1","Button click 1"),sound_array_click_01); engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_02",TextByLanguage("Звук щелчка по кнопке 2","Button click 2"),sound_array_click_02); engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_03",TextByLanguage("Звук щелчка по кнопке 3","Button click 3"),sound_array_click_03); engine.CreateFile(FILE_TYPE_WAV,"sound_array_cash_machine_01",TextByLanguage("Звук кассового аппарата","Cash machine"),sound_array_cash_machine_01); engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_green",TextByLanguage("Изображение /"Зелёный светодиод/"","Image /"Green Spot lamp/""),img_array_spot_green); engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_red",TextByLanguage("Изображение /"Красный светодиод/"","Image /"Red Spot lamp/""),img_array_spot_red); //--- Pass all existing collections to the trading class engine.TradingOnInit(); //--- Set the default magic number for all used symbols engine.TradingSetMagic(engine.SetCompositeMagicNumber(magic_number)); //--- Set synchronous passing of orders for all used symbols engine.TradingSetAsyncMode(false); //--- Set the number of trading attempts in case of an error engine.TradingSetTotalTry(InpTotalAttempts); //--- Set correct order expiration and filling types to all trading objects engine.TradingSetCorrectTypeExpiration(); engine.TradingSetCorrectTypeFilling(); //--- Set standard sounds for trading objects of all used symbols engine.SetSoundsStandart(); //--- Set the general flag of using sounds engine.SetUseSounds(InpUseSounds); //--- Set the spread multiplier for symbol trading objects in the symbol collection engine.SetSpreadMultiplier(InpSpreadMultiplier); //--- Set controlled values for symbols //--- Get the list of all collection symbols CArrayObj *list=engine.GetListAllUsedSymbols(); if(list!=NULL && list.Total()!=0) { //--- In a loop by the list, set the necessary values for tracked symbol properties //--- By default, the LONG_MAX value is set to all properties, which means "Do not track this property" //--- It can be enabled or disabled (by setting the value less than LONG_MAX or vice versa - set the LONG_MAX value) at any time and anywhere in the program /* for(int i=0;i<list.Total();i++) { CSymbol* symbol=list.At(i); if(symbol==NULL) continue; //--- Set control of the symbol price increase by 100 points symbol.SetControlBidInc(100000*symbol.Point()); //--- Set control of the symbol price decrease by 100 points symbol.SetControlBidDec(100000*symbol.Point()); //--- Set control of the symbol spread increase by 40 points symbol.SetControlSpreadInc(400); //--- Set control of the symbol spread decrease by 40 points symbol.SetControlSpreadDec(400); //--- Set control of the current spread by the value of 40 points symbol.SetControlSpreadLevel(400); } */ } //--- Set controlled values for the current account CAccount* account=engine.GetAccountCurrent(); if(account!=NULL) { //--- Set control of the profit increase to 10 account.SetControlledValueINC(ACCOUNT_PROP_PROFIT,10.0); //--- Set control of the funds increase to 15 account.SetControlledValueINC(ACCOUNT_PROP_EQUITY,15.0); //--- Set profit control level to 20 account.SetControlledValueLEVEL(ACCOUNT_PROP_PROFIT,20.0); } } //+------------------------------------------------------------------+
奇怪的是,这些是 EA 中的所有改变。 所有其他的修改均已在函数库代码里实现。
为了测试下挂单的延后请求,我们如前次执行完全相同的操作 — 禁用互联网,尝试下一笔挂单,获取交易服务器连接错误,并获取生成延后请求的日志消息通知,及其参数。 然后启用互联网,激活延后请求,以及发送下挂单请求。
我们来查验。
编译并启动 EA。 关闭互联网,然后等待以下图标出现在终端的右下角:
禁用互联网并单击“卖出”后,交易服务器返回错误,随后在日志中将显示错误和延后请求记录。
然后启用互联网,恢复与交易服务器的连接:
一旦恢复连接后,函数库将立即处理延后请求,将其发送到服务器。
结果则为,挂单已下达,随后在日志里显示以下记录:
2019.12.05 16:38:32.591 CTrading::PlaceOrder<uint,int,uint,uint>: Invalid request: 2019.12.05 16:38:32.591 No connection with the trade server 2019.12.05 16:38:32.591 Correction of trade request parameters ... 2019.12.05 16:38:32.610 Trading attempt #1. Error: No connection with the trade server 2019.12.05 16:38:32.610 Pending request created #1: 2019.12.05 16:38:32.610 ================== Pending trade request's parameters ================== 2019.12.05 16:38:32.610 - Pending request type: Pending request that was created as a result of the server code 2019.12.05 16:38:32.610 - Trade request ID: 1 2019.12.05 16:38:32.610 - Return code based on which the request was created: 10031 "No connection with the trade server" 2019.12.05 16:38:32.610 - Request creation time: 2019.12.05 11:37:39.054 2019.12.05 16:38:32.610 - Price at time of request create: : 1.10913 2019.12.05 16:38:32.610 - Request activation time: 2019.12.05 11:37:59.054 2019.12.05 16:38:32.610 - Waiting time between trading attempts: 20000 (00:00:20) 2019.12.05 16:38:32.610 - Current trading attempt: Waiting for the onset time of the first trading attempt 2019.12.05 16:38:32.610 - Total trade attempts: 5 2019.12.05 16:38:32.610 ================== Trade request's parameters ================== 2019.12.05 16:38:32.610 - Trade operation type: Place pending order 2019.12.05 16:38:32.610 - Trade symbol: EURUSD 2019.12.05 16:38:32.610 - Requested volume for a deal in lots: 0.10 2019.12.05 16:38:32.610 - Price: 1.10963 2019.12.05 16:38:32.610 - StopLimit level of the order: Value not set 2019.12.05 16:38:32.610 - Stop Loss level of the order: 1.11113 2019.12.05 16:38:32.610 - Take Profit level of the order: 1.10813 2019.12.05 16:38:32.610 - Order type: Pending order Sell Limit 2019.12.05 16:38:32.610 - Order execution type: The order is executed exclusively in the specified volume, otherwise it is canceled (FOK) 2019.12.05 16:38:32.610 - Order expiration type: Good till cancel order 2019.12.05 16:38:32.610 - Order expiration time: Value not set 2019.12.05 16:38:32.610 - Magic number: 24379515 2019.12.05 16:38:32.610 - Order comment: "Pending order SellLimit" 2019.12.05 16:38:32.610 ================== 2019.12.05 16:38:32.610 2019.12.05 16:38:45.185 Retry trading attempt #1 2019.12.05 16:38:45.185 CTrading::PlaceOrder<double,double,double,double>: Invalid request: 2019.12.05 16:38:45.185 Trading is prohibited for the current account 2019.12.05 16:38:45.185 Correction of trade request parameters ... 2019.12.05 16:38:45.185 Trading operation aborted 2019.12.05 16:38:45.512 Retry trading attempt #2 2019.12.05 16:38:45.512 CTrading::PlaceOrder<double,double,double,double>: Invalid request: 2019.12.05 16:38:45.512 Trading is prohibited for the current account 2019.12.05 16:38:45.512 Correction of trade request parameters ... 2019.12.05 16:38:45.512 Trading operation aborted 2019.12.05 16:38:45.852 Retry trading attempt #3 2019.12.05 16:38:46.405 - Pending order placed: 2019.12.05 11:38:45.933 - 2019.12.05 16:38:46.405 EURUSD Placed 0.10 Pending order Sell Limit #491179168 at price 1.10963, sl 1.11113, tp 1.10813, Magic number 24379515 (123), G1: 4, G2: 7, ID: 1 2019.12.05 16:38:46.472 OnDoEasyEvent: Pending order placed
首先,我们得到“与交易服务器无连接”错误。
接下来,获取创建 ID #1 的延后请求的有关消息,包含该对象内所有对象参数和请求结构参数。
之后,重复两次尝试进行交易 #1 和 #2。 尝试是从延后请求发送的,随后是帐户禁用交易的错误(恢复连接后,帐户尚未启用交易)。
从延后请求对象发送的第三次尝试被证明是成功的,且挂单下达成功。
在挂单的魔幻数字描述中,我们的魔幻数字是 24379515,后跟 EA 参数中设置的魔幻数字 ID(123),第一组 ID 为 “G1:4”,第二组 ID为 “G2: 7”,和延后请求 ID “ID:1”
请注意:
请勿将本文所述含有延后请求的交易类,以及所附的测试 EA 用于实盘交易!
本文,其随附的资料和结果,仅为验证延后请求概念。 在当前状态下,它不是最终成品,且无意用于实盘交易。 取而代之,它仅适用在演示模式或测试器。
下一步是什么?
在下一篇文章中,我们将继续基本延后请求功能的工作 — 修改、删除和订单/持仓平仓。
文后附有当前版本含糊库的所有文件,以及测试 EA 文件,供您测试和下载。
请在评论中留下您的问题、意见和建议。
系列中的前几篇文章:
第一部分 概念,数据管理
第二部分 历史订单和成交集合
第三部分 在场订单和持仓集合,安排搜索
第四部分 交易事件, 概念
第五部分 交易事件类和集合。 将事件发送至程序
第六部分 净持帐户事件
第七部分 StopLimit 挂单激活事件,为订单和持仓修改事件准备功能
第八部分 订单和持仓修改事件
第九部分 与 MQL4 的兼容性 — 准备数据
第十部分 与 MQL4 的兼容性 – 开仓和激活挂单事件
第十一部分 与 MQL4 的兼容性 – 平仓事件
第十二部分 帐户对象类和帐户对象集合
第十三部分 账户对象事件
第十四部分 品种对象
第十五部份 品种对象集合
第十六部分 品种集合事件
第十七部分 函数库对象之间的交互
第十八部分 帐户与任意其他函数库对象的交互
第十九部分 函数库消息类
第二十部分 创建和存储程序资源
第二十一部分 交易类 – 基准跨平台交易对象
第二十二部分 交易类 – 基准交易类,限制验证
第二十三部分 交易类 – 基准交易类,有效参数验证
第二十四部分 交易类 – 基准交易类,无效参数的自动纠正
第二十五部分 交易类 – 基准交易类,处理交易服务器返回的错误
第二十六部分 操控延后交易请求 – 首次实现
本文译自 MetaQuotes Software Corp. 撰写的俄文原文
原文地址: https://www.mql5.com/ru/articles/7418
MyFxtops迈投(www.myfxtops.com)-靠谱的外汇跟单社区,免费跟随高手做交易!
免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。