В MQL5 существуют 8 предопределенных структур, предназначенные для хранения и передачи служебной информации:
- MqlDateTime предназначена для представления даты и времени;
- MqlParam позволяет передавать входные параметры при создании хэндла индикатора с помощью функции IndicatorCreate();
- MqlRates предоставляет информацию об исторических данных, содержащих цену, объем и спред;
- MqlBookInfo для получения информации, отображаемой в стакане цен (окно котировок);
- MqlTradeRequest для создания торгового запроса при проведении торговых операций;
- MqlTradeCheckResult позволяет проверить подготовленный торговый запрос перед его отправкой;
- MqlTradeResult содержит ответ торгового сервера на торговый запрос, отправленный функцией OrderSend();
- MqlTradeTransaction содержит описание торговой транзакции;
- MqlTick предназначена для быстрого получения наиболее востребованной информации о текущих ценах.
MqlDateTime
Структура даты содержит в себе восемь полей типа int.
struct MqlDateTime { int year; // год int mon; // месяц int day; // день int hour; // час int min; // минуты int sec; // секунды int day_of_week; // день недели (0-воскресенье, 1-понедельник, ... ,6-суббота) int day_of_year; // порядковый номер в году (1 января имеет номер 0) };
Примечание
Порядковый номер в году day_of_year в високосном году, начиная с марта, будет отличаться от порядкового номера соответствующего дня в невисокосном году.
Пример:
void OnStart() { datetime date1=D'2008.03.01'; datetime date2=D'2009.03.01'; MqlDateTime str1,str2; TimeToStruct(date1,str1); TimeToStruct(date2,str2); printf("%02d.%02d.%4d, day of year = %d",str1.day,str1.mon, str1.year,str1.day_of_year); printf("%02d.%02d.%4d, day of year = %d",str2.day,str2.mon, str2.year,str2.day_of_year); } /* Результат 01.03.2008, day of year = 60 01.03.2009, day of year = 59 */
Структура входных параметров индикатора (MqlParam)
Структура MqlParam специально разработана для передачи входных параметров при создании хэндла технического индикатора с помощью функции IndicatorCreate().
struct MqlParam { ENUM_DATATYPE type; // тип входного параметра, значение перечисления ENUM_DATATYPE long integer_value; // поле для хранения целочисленного значения double double_value; // поле для хранения значения double или float string string_value; // поле для хранения значения строкового типа };
Все входные параметры индикатора передаются в виде массива типа MqlParam, поле type каждого элемента этого массива указывает тип данных, передаваемых данным элементом. Сами значения параметров индикатора необходимо предварительно поместить в соответствующие поля каждого элемента (в integer_value, в double_value или в string_value) в зависимости от того, какое значение перечисления ENUM_DATATYPE содержится в поле type.
Если функции IndicatorCreate() третьим параметром в качестве типа индикатора передается значение IND_CUSTOM, то первый элемент массива входных параметров должен иметь поле type со значением TYPE_STRING из перечисления ENUM_DATATYPE, а поле string_value должно содержать имя пользовательского индикатора.
MqlRates
Структура для хранения информации о ценах, объемах и спреде.
struct MqlRates { datetime time; // время начала периода double open; // цена открытия double high; // наивысшая цена за период double low; // наименьшая цена за период double close; // цена закрытия long tick_volume; // тиковый объем int spread; // спред long real_volume; // биржевой объем };
Пример:
void OnStart() { MqlRates rates[]; int copied=CopyRates(NULL,0,0,100,rates); if(copied<=0) Print("Ошибка копирования ценовых данных ",GetLastError()); else Print("Скопировано ",ArraySize(rates)," баров"); }
MqlBookInfo
Структура, предоставляющая информацию в стакане цен.
struct MqlBookInfo { ENUM_BOOK_TYPE type; // тип заявки из перечисления ENUM_BOOK_TYPE double price; // цена long volume; // объем };
Примечание
Структура MqlBookInfo является предопределенной, поэтому ее объявление и описание не требуется. Чтобы использовать структуру, достаточно объявить переменную данного типа.
Стакан доступен не для всех финансовых инструментов.
Пример:
MqlBookInfo priceArray[]; bool getBook=MarketBookGet(NULL,priceArray); if(getBook) { int size=ArraySize(priceArray); Print("MarketBookInfo по ",Symbol()); } else { Print("Не удалось получить содержимое стакана по символу ",Symbol()); }
Структура торгового запроса (MqlTradeRequest)
Взаимодействие клиентского терминала и торгового сервера для проведения операций постановки ордеров производится посредством торговых запросов. Запрос представлен специальной предопределенной структурой MqlTradeRequest, которая содержит все поля, необходимые для заключения торговых сделок. Результат обработки запроса представлен структурой MqlTradeResult.
struct MqlTradeRequest { ENUM_TRADE_REQUEST_ACTIONS action; // Тип выполняемого действия ulong magic; // Штамп эксперта (идентификатор magic number) ulong order; // Тикет ордера string symbol; // Имя торгового инструмента double volume; // Запрашиваемый объем сделки в лотах double price; // Цена double stoplimit; // Уровень StopLimit ордера double sl; // Уровень Stop Loss ордера double tp; // Уровень Take Profit ордера ulong deviation; // Максимально приемлемое отклонение от запрашиваемой цены ENUM_ORDER_TYPE type; // Тип ордера ENUM_ORDER_TYPE_FILLING type_filling; // Тип ордера по исполнению ENUM_ORDER_TYPE_TIME type_time; // Тип ордера по времени действия datetime expiration; // Срок истечения ордера (для ордеров типа ORDER_TIME_SPECIFIED) string comment; // Комментарий к ордеру ulong position; // Тикет позиции ulong position_by; // Тикет встречной позиции };
Описание полей
Поле |
Описание |
action |
Тип торговой операции. Значение может быть одним из значений перечисления ENUM_TRADE_REQUEST_ACTIONS |
magic |
Идентификатор эксперта. Позволяет организовать аналитическую обработку торговых ордеров. Каждый эксперт может выставлять свой собственный уникальный идентификатор при отправке торгового запроса |
order |
Тикет ордера. Требуется для модификации отложенных ордеров |
symbol |
Имя торгового инструмента, по которому выставляется ордер. Не требуется при операциях модификации ордеров и закрытии позиций |
volume |
Запрашиваемый объем сделки в лотах. Реальное значение объема при открытии сделки будет зависеть от типа ордера по исполнению. |
price |
Цена, при достижении которой ордер должен быть исполнен. Для рыночных ордеров по инструментам с типом исполнения «Market Execution» (SYMBOL_TRADE_EXECUTION_MARKET), имеющих тип TRADE_ACTION_DEAL, указание цены не требуется |
stoplimit |
Цена, по которой будет выставлен отложенный Limit ордер, при достижении ценой значения price (это условие является обязательным). До этого момента отложенный ордер в торговую систему не выводится |
sl |
Цена, по которой сработает Stop Loss ордер при движении цены в неблагоприятном направлении |
tp |
Цена, по которой сработает Take Profit ордер при движении цены в благоприятном направлении |
deviation |
Максимально приемлемое отклонение от запрашиваемой цены, задаваемое в пунктах |
type |
Тип ордера. Значение может быть одним из значений перечисления ENUM_ORDER_TYPE |
type_filling |
Тип ордера по исполнению. Значение может быть одним из значений ENUM_ORDER_TYPE_FILLING |
type_time |
Тип ордера по по истечению. Значение может быть одним из значений ENUM_ORDER_TYPE_TIME |
expiration |
Срок истечения отложенного ордера (для ордеров типа ORDER_TIME_SPECIFIED) |
comment |
Комментарий к ордеру |
position |
Тикет позиции. Следует заполнять при изменении и закрытии позиции для ее однозначной идентификации. Как правило, соответствует тикету ордера, в результате которого позиция была открыта. |
position_by |
Тикет встречной позиции. Используется при закрытии позиции встречной — открытой по тому же инструменту, но в противоположном направлении. |
При модификации или закрытии позиции в системе хеджинга обязательно указывайте ее тикет (MqlTradeRequest::position). В системе неттинга тикет также можно указывать, однако идентификации позиции осуществляется по имени символа. |
Для отправки приказов на совершение торговых операций необходимо использовать функцию OrderSend(). Для каждой торговой операции необходимо указывать обязательные поля и можно заполнять опциональные поля. Всего предусмотрено семь вариантов отправки торгового запроса:
Request Execution
Торговый ордер на открытие позиции в режиме Request Execution (режим торговли по запросу текущих цен). Требуется указание 9 полей:
- action
- symbol
- volume
- price
- sl
- tp
- deviation
- type
- type_filling
Можно также задать значения полей magic и comment.
Instant Execution
Торговый ордер на открытие позиции в режиме Instant Execution (режим торговли по потоковым ценам). Требуется указание 9 полей:
- action
- symbol
- volume
- price
- sl
- tp
- deviation
- type
- type_filling
Можно также задать значения полей magic и comment.
Market Execution
Торговый ордер на открытие позиции в режиме Market Execution (режим исполнения торговых приказов по рынку). Требуется указание 5 полей:
- action
- symbol
- volume
- type
- type_filling
Можно также задать значения полей magic и comment.
Exchange Execution
Торговый ордер на открытие позиции в режиме Exchange Execution (биржевой режим исполнения торговых приказов). Требуется указание 5 полей:
- action
- symbol
- volume
- type
- type_filling
Можно также задать значения полей magic и comment.
Пример торговой операции TRADE_ACTION_DEAL для открытия позиции Buy:
#define EXPERT_MAGIC 123456 // MagicNumber эксперта //+------------------------------------------------------------------+ //| Открытие позиции Buy | //+------------------------------------------------------------------+ void OnStart() { //--- объявление и инициализация запроса и результата MqlTradeRequest request={0}; MqlTradeResult result={0}; //--- параметры запроса request.action =TRADE_ACTION_DEAL; // тип торговой операции request.symbol =Symbol(); // символ request.volume =0.1; // объем в 0.1 лот request.type =ORDER_TYPE_BUY; // тип ордера request.price =SymbolInfoDouble(Symbol(),SYMBOL_ASK); // цена для открытия request.deviation=5; // допустимое отклонение от цены request.magic =EXPERT_MAGIC; // MagicNumber ордера //--- отправка запроса if(!OrderSend(request,result)) PrintFormat("OrderSend error %d",GetLastError()); // если отправить запрос не удалось, вывести код ошибки //--- информация об операции PrintFormat("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); }
Пример торговой операции TRADE_ACTION_DEAL для закрытия позиций:
#define EXPERT_MAGIC 123456 // MagicNumber эксперта //+------------------------------------------------------------------+ //| Закрытие всех позиций | //+------------------------------------------------------------------+ void OnStart() { //--- объявление запроса и результата MqlTradeRequest request; MqlTradeResult result; int total=PositionsTotal(); // количество открытых позиций //--- перебор всех открытых позиций for(int i=total-1; i>=0; i--) { //--- параметры ордера ulong position_ticket=PositionGetTicket(i); // тикет позиции string position_symbol=PositionGetString(POSITION_SYMBOL); // символ int digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS); // количество знаков после запятой ulong magic=PositionGetInteger(POSITION_MAGIC); // MagicNumber позиции double volume=PositionGetDouble(POSITION_VOLUME); // объем позиции ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // тип позиции //--- вывод информации о позиции PrintFormat("#%I64u %s %s %.2f %s [%I64d]", position_ticket, position_symbol, EnumToString(type), volume, DoubleToString(PositionGetDouble(POSITION_PRICE_OPEN),digits), magic); //--- если MagicNumber совпадает if(magic==EXPERT_MAGIC) { //--- обнуление значений запроса и результата ZeroMemory(request); ZeroMemory(result); //--- установка параметров операции request.action =TRADE_ACTION_DEAL; // тип торговой операции request.position =position_ticket; // тикет позиции request.symbol =position_symbol; // символ request.volume =volume; // объем позиции request.deviation=5; // допустимое отклонение от цены request.magic =EXPERT_MAGIC; // MagicNumber позиции //--- установка цены и типа ордера в зависимости от типа позиции if(type==POSITION_TYPE_BUY) { request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID); request.type =ORDER_TYPE_SELL; } else { request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK); request.type =ORDER_TYPE_BUY; } //--- вывод информации о закрытии PrintFormat("Close #%I64d %s %s",position_ticket,position_symbol,EnumToString(type)); //--- отправка запроса if(!OrderSend(request,result)) PrintFormat("OrderSend error %d",GetLastError()); // если отправить запрос не удалось, вывести код ошибки //--- информация об операции PrintFormat("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); } } }
SL & TP Modification
Торговый приказ на модификацию уровней StopLoss и/или TakeProfit. Требуется указание 4 полей:
- action
- symbol
- sl
- tp
- position
Пример торговой операции TRADE_ACTION_SLTP для изменения значений Stop Loss и Take Profit у открытой позиции:
#define EXPERT_MAGIC 123456 // MagicNumber эксперта //+------------------------------------------------------------------+ //| Модификация Stop Loss и Take Profit позиции | //+------------------------------------------------------------------+ void OnStart() { //--- объявление запроса и результата MqlTradeRequest request; MqlTradeResult result; int total=PositionsTotal(); // количество открытых позиций //--- перебор всех открытых позиций for(int i=0; i<total; i++) { //--- параметры ордера ulong position_ticket=PositionGetTicket(i);// тикет позиции string position_symbol=PositionGetString(POSITION_SYMBOL); // символ int digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS); // количество знаков после запятой ulong magic=PositionGetInteger(POSITION_MAGIC); // MagicNumber позиции double volume=PositionGetDouble(POSITION_VOLUME); // объем позиции double sl=PositionGetDouble(POSITION_SL); // Stop Loss позиции double tp=PositionGetDouble(POSITION_TP); // Take Profit позиции ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // тип позиции //--- вывод информации о позиции PrintFormat("#%I64u %s %s %.2f %s sl: %s tp: %s [%I64d]", position_ticket, position_symbol, EnumToString(type), volume, DoubleToString(PositionGetDouble(POSITION_PRICE_OPEN),digits), DoubleToString(sl,digits), DoubleToString(tp,digits), magic); //--- если MagicNumber совпадает, Stop Loss и Take Profit не заданы if(magic==EXPERT_MAGIC && sl==0 && tp==0) { //--- вычисление текущих ценовых уровней double price=PositionGetDouble(POSITION_PRICE_OPEN); double bid=SymbolInfoDouble(position_symbol,SYMBOL_BID); double ask=SymbolInfoDouble(position_symbol,SYMBOL_ASK); int stop_level=(int)SymbolInfoInteger(position_symbol,SYMBOL_TRADE_STOPS_LEVEL); double price_level; //--- если уровень минимально допустимого отступа в пунктах от текущей цены закрытия не задан if(stop_level<=0) stop_level=150; // зададим отступ в 150 пунктов от текущей цены закрытия else stop_level+=50; // уровень отступа возьмем равным (SYMBOL_TRADE_STOPS_LEVEL + 50) пунктов для надежности //--- вычисление и округление значений Stop Loss и Take Profit price_level=stop_level*SymbolInfoDouble(position_symbol,SYMBOL_POINT); if(type==POSITION_TYPE_BUY) { sl=NormalizeDouble(bid-price_level,digits); tp=NormalizeDouble(ask+price_level,digits); } else { sl=NormalizeDouble(ask+price_level,digits); tp=NormalizeDouble(bid-price_level,digits); } //--- обнуление значений запроса и результата ZeroMemory(request); ZeroMemory(result); //--- установка параметров операции request.action =TRADE_ACTION_SLTP; // тип торговой операции request.position=position_ticket; // тикет позиции request.symbol=position_symbol; // символ request.sl =sl; // Stop Loss позиции request.tp =tp; // Take Profit позиции request.magic=EXPERT_MAGIC; // MagicNumber позиции //--- вывод информации о модификации PrintFormat("Modify #%I64d %s %s",position_ticket,position_symbol,EnumToString(type)); //--- отправка запроса if(!OrderSend(request,result)) PrintFormat("OrderSend error %d",GetLastError()); // если отправить запрос не удалось, вывести код ошибки //--- информация об операции PrintFormat("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); } } }
Pending Order
Торговый приказ на установку отложенного ордера. Требуется указание 11 полей:
- action
- symbol
- volume
- price
- stoplimit
- sl
- tp
- type
- type_filling
- type_time
- expiration
Можно также задать значения полей magic и comment.
Пример торговой операции TRADE_ACTION_PENDING для установки отложенного ордера:
#property description "Пример установки отложенных ордеров" #property script_show_inputs #define EXPERT_MAGIC 123456 // MagicNumber эксперта input ENUM_ORDER_TYPE orderType=ORDER_TYPE_BUY_LIMIT; // тип ордера //+------------------------------------------------------------------+ //| Установка отложенных ордеров | //+------------------------------------------------------------------+ void OnStart() { //-- объявление и инициализация запроса и результата MqlTradeRequest request={0}; MqlTradeResult result={0}; //--- параметры для установки отложенного ордера request.action =TRADE_ACTION_PENDING; // тип торговой операции request.symbol =Symbol(); // символ request.volume =0.1; // объем в 0.1 лот request.deviation=2; // допустимое отклонение от цены request.magic =EXPERT_MAGIC; // MagicNumber ордера int offset = 50; // отступ от текущей цены для установки ордера, в пунктах double price; // цена срабатывания ордера double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); // размер пункта int digits=SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); // кол-во знаков после запятой (точность) //--- проверка типа операции if(orderType==ORDER_TYPE_BUY_LIMIT) { request.type =ORDER_TYPE_BUY_LIMIT; // тип ордера price=SymbolInfoDouble(Symbol(),SYMBOL_ASK)-offset*point; // цена для открытия request.price =NormalizeDouble(price,digits); // нормализованная цена открытия } else if(orderType==ORDER_TYPE_SELL_LIMIT) { request.type =ORDER_TYPE_SELL_LIMIT; // тип ордера price=SymbolInfoDouble(Symbol(),SYMBOL_ASK)+offset*point; // цена для открытия request.price =NormalizeDouble(price,digits); // нормализованная цена открытия } else if(orderType==ORDER_TYPE_BUY_STOP) { request.type =ORDER_TYPE_BUY_STOP; // тип ордера price =SymbolInfoDouble(Symbol(),SYMBOL_ASK)+offset*point; // цена для открытия request.price=NormalizeDouble(price,digits); // нормализованная цена открытия } else if(orderType==ORDER_TYPE_SELL_STOP) { request.type =ORDER_TYPE_SELL_STOP; // тип ордера price=SymbolInfoDouble(Symbol(),SYMBOL_ASK)-offset*point; // цена для открытия request.price =NormalizeDouble(price,digits); // нормализованная цена открытия } else Alert("Этот пример только для установки отложенных ордеров"); // если выбран не отложенный ордер //--- отправка запроса if(!OrderSend(request,result)) PrintFormat("OrderSend error %d",GetLastError()); // если отправить запрос не удалось, вывести код ошибки //--- информация об операции PrintFormat("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); }
Modify Pending Order
Торговый приказ на модификацию уровней цен отложенного ордера. Требуется указание 7 полей:
- action
- order
- price
- sl
- tp
- type_time
- expiration
Пример торговой операции TRADE_ACTION_MODIFY для модификации уровней цен отложенного ордера:
#define EXPERT_MAGIC 123456 // MagicNumber эксперта //+------------------------------------------------------------------+ //| Модификация отложенных ордеров | //+------------------------------------------------------------------+ void OnStart() { //-- объявление и инициализация запроса и результата MqlTradeRequest request={0}; MqlTradeResult result={0}; int total=OrdersTotal(); // количество установленных отложенных ордеров //--- перебор всех установленных отложенных ордеров for(int i=0; i<total; i++) { //--- параметры ордера ulong order_ticket=OrderGetTicket(i); // тикет ордера string order_symbol=Symbol(); // символ int digits=(int)SymbolInfoInteger(order_symbol,SYMBOL_DIGITS); // количество знаков после запятой ulong magic=OrderGetInteger(ORDER_MAGIC); // MagicNumber ордера double volume=OrderGetDouble(ORDER_VOLUME_CURRENT); // текущий объем ордера double sl=OrderGetDouble(ORDER_SL); // текущий Stop Loss ордера double tp=OrderGetDouble(ORDER_TP); // текущий Take Profit ордера ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE); // тип ордера int offset = 50; // отступ от текущей цены для установки ордера, в пунктах double price; // цена срабатывания ордера double point=SymbolInfoDouble(order_symbol,SYMBOL_POINT); // размер пункта //--- вывод информации об ордере PrintFormat("#%I64u %s %s %.2f %s sl: %s tp: %s [%I64d]", order_ticket, order_symbol, EnumToString(type), volume, DoubleToString(PositionGetDouble(POSITION_PRICE_OPEN),digits), DoubleToString(sl,digits), DoubleToString(tp,digits), magic); //--- если MagicNumber совпадает, Stop Loss и Take Profit не заданы if(magic==EXPERT_MAGIC && sl==0 && tp==0) { request.action=TRADE_ACTION_MODIFY; // тип торговой операции request.order = OrderGetTicket(i); // тикет ордера request.symbol =Symbol(); // символ request.deviation=5; // допустимое отклонение от цены //--- установка уровня цены, тейк-профит и стоп-лосс ордера в зависимости от его типа if(type==ORDER_TYPE_BUY_LIMIT) { price = SymbolInfoDouble(Symbol(),SYMBOL_ASK)-offset*point; request.tp = NormalizeDouble(price+offset*point,digits); request.sl = NormalizeDouble(price-offset*point,digits); request.price =NormalizeDouble(price,digits); // нормализованная цена открытия } else if(type==ORDER_TYPE_SELL_LIMIT) { price = SymbolInfoDouble(Symbol(),SYMBOL_BID)+offset*point; request.tp = NormalizeDouble(price-offset*point,digits); request.sl = NormalizeDouble(price+offset*point,digits); request.price =NormalizeDouble(price,digits); // нормализованная цена открытия } else if(type==ORDER_TYPE_BUY_STOP) { price = SymbolInfoDouble(Symbol(),SYMBOL_BID)+offset*point; request.tp = NormalizeDouble(price+offset*point,digits); request.sl = NormalizeDouble(price-offset*point,digits); request.price =NormalizeDouble(price,digits); // нормализованная цена открытия } else if(type==ORDER_TYPE_SELL_STOP) { price = SymbolInfoDouble(Symbol(),SYMBOL_ASK)-offset*point; request.tp = NormalizeDouble(price-offset*point,digits); request.sl = NormalizeDouble(price+offset*point,digits); request.price =NormalizeDouble(price,digits); // нормализованная цена открытия } //--- отправка запроса if(!OrderSend(request,result)) PrintFormat("OrderSend error %d",GetLastError()); // если отправить запрос не удалось, вывести код ошибки //--- информация об операции PrintFormat("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); //--- обнуление значений запроса и результата ZeroMemory(request); ZeroMemory(result); } } }
Delete Pending Order
Торговый приказ на удаление отложенного ордера. Требуется указание 2 полей:
- action
- order
Пример торговой операции TRADE_ACTION_REMOVE для удаления отложенных ордеров:
#define EXPERT_MAGIC 123456 // MagicNumber эксперта //+------------------------------------------------------------------+ //| Удаление отложенных ордеров | //+------------------------------------------------------------------+ void OnStart() { //-- объявление и инициализация запроса и результата MqlTradeRequest request={0}; MqlTradeResult result={0}; int total=OrdersTotal(); // количество установленных отложенных ордеров //--- перебор всех установленных отложенных ордеров for(int i=total-1; i>=0; i--) { ulong order_ticket=OrderGetTicket(i); // тикет ордера ulong magic=OrderGetInteger(ORDER_MAGIC); // MagicNumber ордера //--- если MagicNumber совпадает if(magic==EXPERT_MAGIC) { //--- обнуление значений запроса и результата ZeroMemory(request); ZeroMemory(result); //--- установка параметров операции request.action=TRADE_ACTION_REMOVE; // тип торговой операции request.order = order_ticket; // тикет ордера //--- отправка запроса if(!OrderSend(request,result)) PrintFormat("OrderSend error %d",GetLastError()); // если отправить запрос не удалось, вывести код ошибки //--- информация об операции PrintFormat("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); } } }
Структура результата проверки торгового запроса (MqlTradeCheckResult)
Прежде чем отправить торговому серверу запрос на торговую операцию, рекомендуется провести его проверку. Проверка осуществляется функцией OrderCheck(), которой передается сам проверяемый запрос и переменная типа структуры MqlTradeCheckResult. В эту переменную и будет записан результат проверки.
struct MqlTradeCheckResult { uint retcode; // Код ответа double balance; // Баланс после совершения сделки double equity; // Эквити после совершения сделки double profit; // Плавающая прибыль double margin; // Маржевые требования double margin_free; // Свободная маржа double margin_level; // Уровень маржи string comment; // Комментарий к коду ответа (описание ошибки) };
Описание полей
Поле |
Описание |
retcode |
Код возврата |
balance |
Значение баланса, которое будет после выполнения торговой операции |
equity |
Значение собственных средств, которое будет после выполнения торговой операции |
profit |
Значение плавающей прибыли, которое будет после выполнения торговой операции |
margin |
Размер маржи необходимый для требуемой торговой операции |
margin_free |
Размер свободных собственных средств, которые останутся после выполнения требуемой торговой операции |
margin_level |
Уровень маржи, который установится после выполнения требуемой торговой операции |
comment |
Комментарий к коду ответа, описание ошибки |
Структура результата торгового запроса (MqlTradeResult)
В ответ на торговый запрос постановки ордера в торговую систему, торговый сервер возвращает данные, содержащие информацию о результате обработки торгового запроса в виде специальной предопределенной структуры MqlTradeResult.
struct MqlTradeResult { uint retcode; // Код результата операции ulong deal; // Тикет сделки, если она совершена ulong order; // Тикет ордера, если он выставлен double volume; // Объем сделки, подтверждённый брокером double price; // Цена в сделке, подтверждённая брокером double bid; // Текущая рыночная цена предложения (цены реквота) double ask; // Текущая рыночная цена спроса (цены реквота) string comment; // Комментарий брокера к операции (по умолчанию заполняется расшифровкой кода возврата торгового сервера) uint request_id; // Идентификатор запроса, устанавливается терминалом при отправке uint retcode_external; // Код ответа внешней торговой системы };
Описание полей
Поле |
Описание |
retcode |
Код возврата торгового сервера |
deal |
Тикет сделки, если она совершена. Сообщается при торговой операции TRADE_ACTION_DEAL |
order |
Тикет ордера, если он выставлен. Сообщается при торговой операции TRADE_ACTION_PENDING |
volume |
Объем сделки, подтверждённый брокером. Зависит от типа ордера по исполнению |
price |
Цена в сделке, подтверждённая брокером. Зависит от поля deviation в торговом запросе и/или от типа торговой операции |
bid |
Текущая рыночная цена предложения (цены реквоты) |
ask |
Текущая рыночная цена спроса (цены реквоты) |
comment |
Комментарий брокера к операции (по умолчанию заполняется расшифровкой кода возврата торгового сервера) |
request_id |
Идентификатор запроса, проставляемый терминалом при отсылке на торговый сервер |
retcode_external |
Код ошибки, которую вернула внешняя торговая система. Проставление и виды этих ошибок зависят от брокера и внешней торговой системы, в которую выводятся торговые операции |
Результат торговой операции возвращается в переменную типа MqlTradeResult, которая передается вторым параметром в функцию OrderSend() для проведения торговых операций.
Терминал записывает идентификатор запроса в поле request_id при его отправке на торговый сервер функциями OrdersSend() и OrderSendAsync(). От торгового сервера терминал получает сообщения о совершенных торговых транзакциях и передает их на обработку в функцию OnTradeTransaction(), которая содержит в качестве параметров:
- описание самой торговой транзакции в структуре MqlTradeTransaction;
- описание торгового запроса, отправленного из функции OrderSend() или OrdersSendAsync(). Идентификатор запроса отправляется терминалом на торговый сервер, а сам запрос и его request_id сохраняются в памяти терминала;
- результат исполнения торгового запроса в виде структуры MqlTradeResult, в котором поле request_id содержит идентификатор этого самого запроса.
Функция OnTradeTransaction() получает три входных параметра, но последние два параметра имеет смысл анализировать только для торговых транзакций, имеющих тип TRADE_TRANSACTION_REQUEST. Во всех остальных случаях данные о торговом запросе и результате его выполнения не заполняются. Пример анализа параметров приведен в разделе Структура торговой транзакции.
Установка терминалом идентификатора request_id для торгового запроса при его отправке на сервер в первую очередь предназначена для работы с асинхронной функцией OrderSendAsync(). Этот идентификатор позволяет связать выполненное действие (вызов функций OrderSend или OrderSendAsync) с результатом этого действия, передаваемым в OnTradeTransaction().
Пример:
//+------------------------------------------------------------------+ //| Отправка торгового запроса с обработкой результата | //+------------------------------------------------------------------+ bool MyOrderSend(MqlTradeRequest request,MqlTradeResult result) { //--- сбросим код последней ошибки в ноль ResetLastError(); //--- отправим запрос bool success=OrderSend(request,result); //--- если результат неудачный - попробуем узнать в чем дело if(!success) { int answer=result.retcode; Print("TradeLog: Trade request failed. Error = ",GetLastError()); switch(answer) { //--- реквота case 10004: { Print("TRADE_RETCODE_REQUOTE"); Print("request.price = ",request.price," result.ask = ", result.ask," result.bid = ",result.bid); break; } //--- ордер не принят сервером case 10006: { Print("TRADE_RETCODE_REJECT"); Print("request.price = ",request.price," result.ask = ", result.ask," result.bid = ",result.bid); break; } //--- неправильная цена case 10015: { Print("TRADE_RETCODE_INVALID_PRICE"); Print("request.price = ",request.price," result.ask = ", result.ask," result.bid = ",result.bid); break; } //--- неправильный SL и/или TP case 10016: { Print("TRADE_RETCODE_INVALID_STOPS"); Print("request.sl = ",request.sl," request.tp = ",request.tp); Print("result.ask = ",result.ask," result.bid = ",result.bid); break; } //--- некорректный объем case 10014: { Print("TRADE_RETCODE_INVALID_VOLUME"); Print("request.volume = ",request.volume," result.volume = ", result.volume); break; } //--- не хватает денег на торговую операцию case 10019: { Print("TRADE_RETCODE_NO_MONEY"); Print("request.volume = ",request.volume," result.volume = ", result.volume," result.comment = ",result.comment); break; } //--- какая-то другая причина, сообщим код ответа сервера default: { Print("Other answer = ",answer); } } //--- сообщим о неудачном результате торгового запроса возвратом false return(false); } //--- OrderSend() вернул true - повторим ответ return(true); }
Структура торговой транзакции (MqlTradeTransaction)
В результате выполнения определенных действий с торговым счетом, его состояние изменяется. К таким действиям относятся:
- Отсылка торгового запроса любым MQL5-приложением в клиентском терминале при помощи функций OrderSend и OrderSendAsync и его последующее исполнение;
- Отсылка торгового запроса через графический интерфейс терминала и его последующее исполнение;
- Срабатывания отложенных ордеров и стоп-ордеров на сервере;
- Выполнения операций на стороне торгового сервера.
В результате данных действий для счета выполняются торговые транзакции:
- обработка торгового запроса;
- изменение открытых ордеров;
- изменение истории ордеров;
- изменение истории сделок;
- изменение позиций.
Например, при отсылке рыночного ордера на покупку он обрабатывается, для счета создается соответствующий ордер на покупку, происходит исполнение ордера, его удаление из списка открытых, добавление в историю ордеров, далее добавляется соответствующая сделка в историю и создается новая позиция. Все эти действия являются торговыми транзакциями.
Для получения торговых транзакций, примененных к счету, в MQL5 предусмотрен специальный обработчик OnTradeTransaction(). В первый параметр этого обработчика передается структура MqlTradeTransaction, описывающая торговые транзакции.
struct MqlTradeTransaction { ulong deal; // Тикет сделки ulong order; // Тикет ордера string symbol; // Имя торгового инструмента ENUM_TRADE_TRANSACTION_TYPE type; // Тип торговой транзакции ENUM_ORDER_TYPE order_type; // Тип ордера ENUM_ORDER_STATE order_state; // Состояние ордера ENUM_DEAL_TYPE deal_type; // Тип сделки ENUM_ORDER_TYPE_TIME time_type; // Тип ордера по времени действия datetime time_expiration; // Срок истечения ордера double price; // Цена double price_trigger; // Цена срабатывания стоп-лимитного ордера double price_sl; // Уровень Stop Loss double price_tp; // Уровень Take Profit double volume; // Объем в лотах ulong position; // Тикет позиции ulong position_by; // Тикет встречной позиции };
Описание полей
Поле |
Описание |
deal |
Тикет сделки. |
order |
Тикет ордера. |
symbol |
Имя торгового инструмента, по которому совершена транзакция. |
type |
Тип торговой транзакции. Значение может быть одним из значений перечисления ENUM_TRADE_TRANSACTION_TYPE. |
order_type |
Тип торгового ордера. Значение может быть одним из значений перечисления ENUM_ORDER_TYPE. |
order_state |
Состояние торгового ордера. Значение может быть одним из значений перечисления ENUM_ORDER_STATE. |
deal_type |
Тип сделки. Значение может быть одним из значений перечисления ENUM_DEAL_TYPE. |
type_time |
Тип ордера по истечению. Значение может быть одним из значений ENUM_ORDER_TYPE_TIME. |
time_expiration |
Срок истечения отложенного ордера (для ордеров типа ORDER_TIME_SPECIFIED и ORDER_TIME_SPECIFIED_DAY). |
price |
Цена. В зависимости от типа торговой транзакции может быть ценой ордера, сделки или позиции. |
price_trigger |
Стоп-цена (цена срабатывания) стоп-лимитного ордера (ORDER_TYPE_BUY_STOP_LIMIT и ORDER_TYPE_SELL_STOP_LIMIT). |
price_sl |
Цена Stop Loss. В зависимости от типа торговой транзакции может относиться к ордеру, сделке или позиции. |
price_tp |
Цена Take Profit. В зависимости от типа торговой транзакции может относиться к ордеру, сделке или позиции. |
volume |
Объем в лотах. В зависимости от типа торговой транзакции может указывать на текущий объем ордера, объем сделки или объем позиции. |
position |
Тикет позиции, на которую повлияла транзакция. |
position_by |
Тикет встречной позиции. Используется при закрытии позиции встречной — открытой по тому же инструменту, но в противоположном направлении. |
Определяющим параметром для анализа поступившей транзакции является ее тип, который передается в поле type. Например, если транзакция является типом TRADE_TRANSACTION_REQUEST (получен результат обработки торгового запроса сервером), то структура имеет только одно заполненное поле type, остальные поля анализировать не нужно. В этом случае можно произвести анализ двух дополнительных параметров request и result, которые передаются в обработчик OnTradeTransaction(), как это показано в примере ниже.
Зная тип торговой операции, можно принять решение об анализе текущего состояния ордеров, позиций и сделок на торговом счете. Необходимо иметь в виду, что один торговый запрос, отправленный из терминала серверу, может породить несколько торговых транзакций, очередность поступления которых в терминал не гарантируется.
Структура MqlTradeTransaction заполняется по-разному в зависимости от типа торговой транзакции (ENUM_TRADE_TRANSACTION_TYPE):
TRADE_TRANSACTION_ORDER_* и TRADE_TRANSACTION_HISTORY_*
Для торговых транзакций, касающихся обработки открытых ордеров (TRADE_TRANSACTION_ORDER_ADD, TRADE_TRANSACTION_ORDER_UPDATE и TRADE_TRANSACTION_ORDER_DELETE) и истории ордеров (TRADE_TRANSACTION_HISTORY_ADD, TRADE_TRANSACTION_HISTORY_UPDATE, TRADE_TRANSACTION_HISTORY_DELETE), в структуре MqlTradeTransaction заполняются следующие поля:
- order — тикет ордера;
- symbol — имя финансового инструмента в ордере;
- type — тип торговой транзакции;
- order_type — тип ордера;
- orders_state — текущее состояние ордера;
- time_type — тип истечения ордера;
- time_expiration — время истечения ордера (для ордеров с типом истечения ORDER_TIME_SPECIFIED и ORDER_TIME_SPECIFIED_DAY);
- price — цена в ордере, указанная клиентом;
- price_trigger — стоп-цена срабатывания стоп-лимитного ордера (только для ORDER_TYPE_BUY_STOP_LIMIT и ORDER_TYPE_SELL_STOP_LIMIT);
- price_sl — цена Stop Loss ордера (заполняется, если указана в ордере);
- price_tp — цена Take Profit ордера (заполняется, если указана в ордере);
- volume — текущий объем ордера (не исполненный). Изначальный объем ордера можно узнать из истории ордеров при помощи функций HistoryOrders*.
- position — тикет позиции, открытой, измененной или закрытой в результате исполнения ордера. Заполняется только для рыночных ордеров. Не заполняется для TRADE_TRANSACTION_ORDER_ADD.
- position_by — тикет встречной позиции. Заполняется только для ордеров на закрытие позиции встречной (close by).
TRADE_TRANSACTION_DEAL_*
Для торговых транзакций, касающихся обработки сделок (TRADE_TRANSACTION_DEAL_ADD, TRADE_TRANSACTION_DEAL_UPDATE и TRADE_TRANSACTION_DEAL_DELETE), в структуре MqlTradeTransaction заполняются следующие поля:
- deal — тикет сделки;
- order — тикет ордера, на основе которого совершена сделка;
- symbol — имя финансового инструмента в сделке;
- type — тип торговой транзакции;
- deal_type — тип сделки;
- price — цена, по которой совершена сделка;
- price_sl — цена Stop Loss (заполняется, если указана в ордере, на основе которого совершена сделка);
- price_tp — цена Take Profit (заполняется, если указана в ордере, на основе которого совершена сделка);
- volume — объем сделки в лотах.
- position — тикет позиции, открытой, измененной или закрытой в результате исполнения сделки.
- position_by — тикет встречной позиции. Заполняется только для сделок на закрытие позиции встречной (out by).
TRADE_TRANSACTION_POSITION
Для торговых транзакций, касающихся изменений позиций, не связанных с исполнением сделок (TRADE_TRANSACTION_POSITION), в структуре MqlTradeTransaction заполняются следующие поля:
- symbol — имя финансового инструмента позиции;
- type — тип торговой транзакции;
- deal_type — тип позиции (DEAL_TYPE_BUY или DEAL_TYPE_SELL);
- price — средневзвешенная цена открытия позиции;
- price_sl — цена Stop Loss;
- price_tp — цена Take Profit;
- volume — объем позиции в лотах, если он был изменен.
- position — тикет позиции.
Изменение позиции (добавление, изменение или ликвидация) в результате совершения сделки не влечет за собой появление транзакции TRADE_TRANSACTION_POSITION.
TRADE_TRANSACTION_REQUEST
Для торговых транзакций, описывающих факт, что торговый запрос обработан сервером, и результат его обработки получен (TRADE_TRANSACTION_REQUEST), в структуре MqlTradeTransaction заполняется только одно поле:
- type — тип торговой транзакции;
Для транзакций данного типа необходимо анализировать только одно поле — type (тип торговой транзакции). Для получения дополнительной информации необходимо анализировать второй и третий параметры функции OnTradeTransaction (request и result).
Пример:
input int MagicNumber=1234567; //--- подключим торговый класс CTrade и объявим переменную этого типа #include <Trade\Trade.mqh> CTrade trade; //--- флаги для установки и удаления отложенного ордера bool pending_done=false; bool pending_deleted=false; //--- здесь будем хранить тикет отложенного ордера ulong order_ticket; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- установим MagicNumber, которым будут помечаться все наши ордера trade.SetExpertMagicNumber(MagicNumber); //--- торговые запросы будем отправлять в асинхронном режиме с помощью функции OrderSendAsync() trade.SetAsyncMode(true); //--- инициализируем переменную нулем order_ticket=0; return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- установка отложенного ордера if(!pending_done) { double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK); double buy_stop_price=NormalizeDouble(ask+1000*_Point,(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS)); bool res=trade.BuyStop(0.1,buy_stop_price,_Symbol); //--- если функция BuyStop() отработала успешно if(res) { pending_done=true; //--- получим результат отправки запроса из ctrade MqlTradeResult trade_result; trade.Result(trade_result); //--- получим request_id для отправленного запроса uint request_id=trade_result.request_id; Print("Отправлен запрос на установку отложенного ордера. Идентификатор запроса Request_ID=",request_id); //--- запомним тикет ордера (при использовании асинхронного режима отправки в CTrade будет равен нулю) order_ticket=trade_result.order; //--- всё сделано, поэтому досрочно выходим из обработчика OnTick() return; } } //--- удаление отложенного ордера if(!pending_deleted) //--- дополнительная проверка if(pending_done && (order_ticket!=0)) { //--- попытаемся удалить отложенный ордер bool res=trade.OrderDelete(order_ticket); Print("OrderDelete=",res); //--- при успешной отправке запроса на удаление if(res) { pending_deleted=true; //--- получим результат выполнения запроса MqlTradeResult trade_result; trade.Result(trade_result); //--- вытащим из результата идентификатор запроса uint request_id=trade_result.request_id; //--- выведем в Журнал Print("Отправлен запрос на удаление отложенного ордера #",order_ticket, ". Идентификатор запроса Request_ID=",request_id, "\r\n"); //--- запишем из результата запроса тикет ордера order_ticket=trade_result.order; } } } //+------------------------------------------------------------------+ //| TradeTransaction function | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result) { //--- получим тип транзакции в виде значения перечисления ENUM_TRADE_TRANSACTION_TYPE type=(ENUM_TRADE_TRANSACTION_TYPE)trans.type; //--- если транзакция является результатом обработки запроса, выведем только её название if(type==TRADE_TRANSACTION_REQUEST) { Print(EnumToString(type)); //--- выведем строковое описание обработанного запроса Print("------------RequestDescription\r\n",RequestDescription(request)); //--- выведем описание результата запроса Print("------------ResultDescription\r\n",TradeResultDescription(result)); //--- запомним тикет ордера для его удаления на следующей обработке в OnTick() if(result.order!=0) { //--- удалим этот ордер по его тикету при следующем вызове OnTick() order_ticket=result.order; Print(" Тикет отложенного ордера ",order_ticket,"\r\n"); } } else // для транзакций другого типа выведем полное описание //--- выведем описание полученной транзакции в Журнал Print("------------TransactionDescription\r\n",TransactionDescription(trans)); } //+------------------------------------------------------------------+ //| Возвращает текстовое описание транзакции | //+------------------------------------------------------------------+ string TransactionDescription(const MqlTradeTransaction &trans) { string desc=EnumToString(trans.type)+"\r\n"; desc+="Symbol: "+trans.symbol+"\r\n"; desc+="Deal ticket: "+(string)trans.deal+"\r\n"; desc+="Deal type: "+EnumToString(trans.deal_type)+"\r\n"; desc+="Order ticket: "+(string)trans.order+"\r\n"; desc+="Order type: "+EnumToString(trans.order_type)+"\r\n"; desc+="Order state: "+EnumToString(trans.order_state)+"\r\n"; desc+="Order time type: "+EnumToString(trans.time_type)+"\r\n"; desc+="Order expiration: "+TimeToString(trans.time_expiration)+"\r\n"; desc+="Price: "+StringFormat("%G",trans.price)+"\r\n"; desc+="Price trigger: "+StringFormat("%G",trans.price_trigger)+"\r\n"; desc+="Stop Loss: "+StringFormat("%G",trans.price_sl)+"\r\n"; desc+="Take Profit: "+StringFormat("%G",trans.price_tp)+"\r\n"; desc+="Volume: "+StringFormat("%G",trans.volume)+"\r\n"; desc+="Position: "+(string)trans.position+"\r\n"; desc+="Position by: "+(string)trans.position_by+"\r\n"; //--- вернем полученную строку return desc; } //+------------------------------------------------------------------+ //| Возвращает текстовое описание торгового запроса | //+------------------------------------------------------------------+ string RequestDescription(const MqlTradeRequest &request) { string desc=EnumToString(request.action)+"\r\n"; desc+="Symbol: "+request.symbol+"\r\n"; desc+="Magic Number: "+StringFormat("%d",request.magic)+"\r\n"; desc+="Order ticket: "+(string)request.order+"\r\n"; desc+="Order type: "+EnumToString(request.type)+"\r\n"; desc+="Order filling: "+EnumToString(request.type_filling)+"\r\n"; desc+="Order time type: "+EnumToString(request.type_time)+"\r\n"; desc+="Order expiration: "+TimeToString(request.expiration)+"\r\n"; desc+="Price: "+StringFormat("%G",request.price)+"\r\n"; desc+="Deviation points: "+StringFormat("%G",request.deviation)+"\r\n"; desc+="Stop Loss: "+StringFormat("%G",request.sl)+"\r\n"; desc+="Take Profit: "+StringFormat("%G",request.tp)+"\r\n"; desc+="Stop Limit: "+StringFormat("%G",request.stoplimit)+"\r\n"; desc+="Volume: "+StringFormat("%G",request.volume)+"\r\n"; desc+="Comment: "+request.comment+"\r\n"; //--- вернем полученную строку return desc; } //+------------------------------------------------------------------+ //| Возвращает текстовое описание результата обработки запроса | //+------------------------------------------------------------------+ string TradeResultDescription(const MqlTradeResult &result) { string desc="Retcode "+(string)result.retcode+"\r\n"; desc+="Request ID: "+StringFormat("%d",result.request_id)+"\r\n"; desc+="Order ticket: "+(string)result.order+"\r\n"; desc+="Deal ticket: "+(string)result.deal+"\r\n"; desc+="Volume: "+StringFormat("%G",result.volume)+"\r\n"; desc+="Price: "+StringFormat("%G",result.price)+"\r\n"; desc+="Ask: "+StringFormat("%G",result.ask)+"\r\n"; desc+="Bid: "+StringFormat("%G",result.bid)+"\r\n"; desc+="Comment: "+result.comment+"\r\n"; //--- вернем полученную строку return desc; }
Структура для получения текущих цен (MqlTick)
Структура для хранения последних цен по символу. Предназначена для быстрого получения наиболее востребованной информации о текущих ценах.
struct MqlTick { datetime time; // Время последнего обновления цен double bid; // Текущая цена Bid double ask; // Текущая цена Ask double last; // Текущая цена последней сделки (Last) ulong volume; // Объем для текущей цены Last long time_msc; // Время последнего обновления цен в миллисекундах uint flags // Флаги тиков };
Переменная типа MqlTick позволяет за один вызов функции SymbolInfoTick() получить значения Ask, Bid, Last и Volume.
У каждого тика всегда заполняются все параметры, независимо от того, изменились ли данные по сравнению с предыдущим тиком. Это позволяет всегда иметь актуальное состояние цен на любой момент времени без поиска предыдущих значений по тиковой истории. Например, с тиком могла измениться только цена бид, но в структуре помимо новой цены будут указаны и остальные параметры: предыдущая цена аск, объем и т.д.
Чтобы узнать, какие именно данные изменились с текущим тиком, анализируйте его флаги:
- TICK_FLAG_BID – тик изменил цену бид
- TICK_FLAG_ASK – тик изменил цену аск
- TICK_FLAG_LAST – тик изменил цену последней сделки
- TICK_FLAG_VOLUME – тик изменил объем
- TICK_FLAG_BUY – тик возник в результате сделки на покупку
- TICK_FLAG_SELL – тик возник в результате сделки на продажу
Пример:
void OnTick() { MqlTick last_tick; if(SymbolInfoTick(Symbol(),last_tick)) { Print(last_tick.time,": Bid = ",last_tick.bid, " Ask = ",last_tick.ask," Volume = ",last_tick.volume); } else Print("SymbolInfoTick() failed, error = ",GetLastError()); }