1929 lines
164 KiB
MQL5
1929 lines
164 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| Trade.mqh |
|
|
//| Copyright 2009-2020, MetaQuotes Software Corp. |
|
|
//| http://www.mql5.com |
|
|
//| Copyright 2022, Yuriy Bykov |
|
|
//| https://www.mql5.com/ru/code/38861 |
|
|
//| https://www.mql5.com/en/code/39161 |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2022, Yuriy Bykov"
|
|
#property link "https://www.mql5.com/ru/users/antekov"
|
|
#property version "1.02"
|
|
#property strict
|
|
|
|
#include <Object.mqh>
|
|
#include "OrderInfo.mqh"
|
|
#include "SymbolInfo.mqh"
|
|
//#include "HistoryOrderInfo.mqh"
|
|
#include "PositionInfo.mqh"
|
|
//#include "DealInfo.mqh"
|
|
//+------------------------------------------------------------------+
|
|
//| enumerations |
|
|
//+------------------------------------------------------------------+
|
|
enum ENUM_LOG_LEVELS {
|
|
LOG_LEVEL_NO = 0,
|
|
LOG_LEVEL_ERRORS = 1,
|
|
LOG_LEVEL_ALL = 2
|
|
};
|
|
|
|
enum ENUM_DEAL_TYPE {
|
|
DEAL_TYPE_BUY, // Покупка
|
|
DEAL_TYPE_SELL, // Продажа
|
|
DEAL_TYPE_BALANCE, // Начисление баланса
|
|
DEAL_TYPE_CREDIT, // Начисление кредита
|
|
DEAL_TYPE_CHARGE, // Дополнительные сборы
|
|
DEAL_TYPE_CORRECTION, // Корректирующая запись
|
|
DEAL_TYPE_BONUS, // Перечисление бонусов
|
|
DEAL_TYPE_COMMISSION, // Дополнительные комиссии
|
|
DEAL_TYPE_COMMISSION_DAILY, // Комиссия, начисляемая в конце торгового дня
|
|
DEAL_TYPE_COMMISSION_MONTHLY, // Комиссия, начисляемая в конце месяца
|
|
DEAL_TYPE_COMMISSION_AGENT_DAILY, // Агентская комиссия, начисляемая в конце торгового дня
|
|
DEAL_TYPE_COMMISSION_AGENT_MONTHLY, // Агентская комиссия, начисляемая в конце месяца
|
|
DEAL_TYPE_INTEREST, // Начисления процентов на свободные средства
|
|
DEAL_TYPE_BUY_CANCELED, // Отмененная сделка покупки. Возможная ситуация, когда ранее совершенная сделка на покупку отменяется.
|
|
// В таком случае тип ранее совершенной сделки (DEAL_TYPE_BUY) меняется на DEAL_TYPE_BUY_CANCELED,
|
|
// а ее прибыль/убыток обнуляется. Ранее полученная прибыль/убыток начисляется/списывается со счета отдельной балансовой операцией
|
|
DEAL_TYPE_SELL_CANCELED, // Отмененная сделка продажи. Возможная ситуация, когда ранее совершенная сделка на продажу отменяется.
|
|
// В таком случае тип ранее совершенной сделки (DEAL_TYPE_SELL) меняется на DEAL_TYPE_SELL_CANCELED,
|
|
// а ее прибыль/убыток обнуляется. Ранее полученная прибыль/убыток начисляется/списывается со счета отдельной балансовой операцией
|
|
DEAL_DIVIDEND, // Начисление дивиденда
|
|
DEAL_DIVIDEND_FRANKED, // Начисление франкированного дивиденда (освобожденного от уплаты налога)
|
|
DEAL_TAX, // Начисление налога
|
|
};
|
|
|
|
enum ENUM_TRADE_RETCODE {
|
|
TRADE_RETCODE_REQUOTE = 10004, // Реквота
|
|
TRADE_RETCODE_REJECT = 10006, // Запрос отклонен
|
|
TRADE_RETCODE_CANCEL = 10007, // Запрос отменен трейдером
|
|
TRADE_RETCODE_PLACED = 10008, // Ордер размещен
|
|
TRADE_RETCODE_DONE = 10009, // Заявка выполнена
|
|
TRADE_RETCODE_DONE_PARTIAL = 10010, // Заявка выполнена частично
|
|
TRADE_RETCODE_ERROR = 10011, // Ошибка обработки запроса
|
|
TRADE_RETCODE_TIMEOUT = 10012, // Запрос отменен по истечению времени
|
|
TRADE_RETCODE_INVALID = 10013, // Неправильный запрос
|
|
TRADE_RETCODE_INVALID_VOLUME = 10014, // Неправильный объем в запросе
|
|
TRADE_RETCODE_INVALID_PRICE = 10015, // Неправильная цена в запросе
|
|
TRADE_RETCODE_INVALID_STOPS = 10016, // Неправильные стопы в запросе
|
|
TRADE_RETCODE_TRADE_DISABLED = 10017, // Торговля запрещена
|
|
TRADE_RETCODE_MARKET_CLOSED = 10018, // Рынок закрыт
|
|
TRADE_RETCODE_NO_MONEY = 10019, // Нет достаточных денежных средств для выполнения запроса
|
|
TRADE_RETCODE_PRICE_CHANGED = 10020, // Цены изменились
|
|
TRADE_RETCODE_PRICE_OFF = 10021, // Отсутствуют котировки для обработки запроса
|
|
TRADE_RETCODE_INVALID_EXPIRATION = 10022, // Неверная дата истечения ордера в запросе
|
|
TRADE_RETCODE_ORDER_CHANGED = 10023, // Состояние ордера изменилось
|
|
TRADE_RETCODE_TOO_MANY_REQUESTS = 10024, // Слишком частые запросы
|
|
TRADE_RETCODE_NO_CHANGES = 10025, // В запросе нет изменений
|
|
TRADE_RETCODE_SERVER_DISABLES_AT = 10026, // Автотрейдинг запрещен сервером
|
|
TRADE_RETCODE_CLIENT_DISABLES_AT = 10027, // Автотрейдинг запрещен клиентским терминалом
|
|
TRADE_RETCODE_LOCKED = 10028, // Запрос заблокирован для обработки
|
|
TRADE_RETCODE_FROZEN = 10029, // Ордер или позиция заморожены
|
|
TRADE_RETCODE_INVALID_FILL = 10030, // Указан неподдерживаемый тип исполнения ордера по остатку
|
|
TRADE_RETCODE_CONNECTION = 10031, // Нет соединения с торговым сервером
|
|
TRADE_RETCODE_ONLY_REAL = 10032, // Операция разрешена только для реальных счетов
|
|
TRADE_RETCODE_LIMIT_ORDERS = 10033, // Достигнут лимит на количество отложенных ордеров
|
|
TRADE_RETCODE_LIMIT_VOLUME = 10034, // Достигнут лимит на объем ордеров и позиций для данного символа
|
|
TRADE_RETCODE_INVALID_ORDER = 10035, // Неверный или запрещённый тип ордера
|
|
TRADE_RETCODE_POSITION_CLOSED = 10036, // Позиция с указанным POSITION_IDENTIFIER уже закрыта
|
|
TRADE_RETCODE_INVALID_CLOSE_VOLUME = 10038, // Закрываемый объем превышает текущий объем позиции
|
|
TRADE_RETCODE_CLOSE_ORDER_EXIST = 10039, // Для указанной позиции уже есть ордер на закрытие. Может возникнуть при работе в системе хеджинга:
|
|
//•при попытке закрытия позиции встречной, если уже есть ордера на закрытие этой позиции
|
|
//•при попытке полного или частичного закрытия, если суммарный объем уже имеющихся ордеров на закрытие и вновь выставляемого ордера превышает текущий объем позиции
|
|
TRADE_RETCODE_LIMIT_POSITIONS = 10040, // Количество открытых позиций, которое можно одновременно иметь на счете, может быть ограничено настройками сервера. При достижении лимита в ответ на выставление ордера сервер вернет ошибку TRADE_RETCODE_LIMIT_POSITIONS. Ограничение работает по-разному в зависимости от типа учета позиций на счете:
|
|
// •Неттинговая система — учитывается количество открытых позиции. При достижении лимита платформа не позволит выставлять новые ордера, в результате исполнения которых может увеличиться количество открытых позиций. Фактически, платформа позволит выставлять ордера только по тем символам, по которым уже есть открытые позиции. В неттинговой системе при проверке лимита не учитываются текущие отложенные ордера, поскольку их исполнение может привести к изменению текущих позиций, а не увеличению их количества.
|
|
// •Хеджинговая система — помимо открытых позиций, учитываются выставленные отложенные ордера, поскольку их срабатывание всегда приводит к открытию новой позиции. При достижении лимита платформа не позволит выставлять рыночные ордера на открытие позиций, а также отложенные ордера.
|
|
TRADE_RETCODE_REJECT_CANCEL = 10041, // Запрос на активацию отложенного ордера отклонен, а сам ордер отменен
|
|
TRADE_RETCODE_LONG_ONLY = 10042, // Запрос отклонен, так как на символе установлено правило "Разрешены только длинные позиции" (POSITION_TYPE_BUY)
|
|
TRADE_RETCODE_SHORT_ONLY = 10043, // Запрос отклонен, так как на символе установлено правило "Разрешены только короткие позиции" (POSITION_TYPE_SELL)
|
|
TRADE_RETCODE_CLOSE_ONLY = 10044, // Запрос отклонен, так как на символе установлено правило "Разрешено только закрывать существующие позиции"
|
|
TRADE_RETCODE_FIFO_CLOSE = 10045, // Запрос отклонен, так как для торгового счета установлено правило "Разрешено закрывать существующие позиции только по правилу FIFO" (ACCOUNT_FIFO_CLOSE=true)
|
|
TRADE_RETCODE_HEDGE_PROHIBITED = 10046, // Запрос отклонен, так как для торгового счета установлено правило "Запрещено открывать встречные позиции по одному символу". Например, если на счете имеется позиция Buy, то пользователь не может открыть позицию Sell или выставить отложенный ордер на продажу. Правило может применяться только на счетах с хеджинговой системой учета (ACCOUNT_MARGIN_MODE=ACCOUNT_MARGIN_MODE_RETAIL_HEDGING).
|
|
};
|
|
|
|
enum ENUM_TRADE_REQUEST_ACTIONS {
|
|
TRADE_ACTION_DEAL, // Установить торговый ордер на немедленное совершение сделки с указанными параметрами (поставить рыночный ордер)
|
|
TRADE_ACTION_PENDING, // Установить торговый ордер на совершение сделки при указанных условиях (отложенный ордер)
|
|
TRADE_ACTION_SLTP, // Изменить значения Stop Loss и Take Profit у открытой позиции
|
|
TRADE_ACTION_MODIFY, // Изменить параметры ранее установленного торгового ордера
|
|
TRADE_ACTION_REMOVE, // Удалить ранее выставленный отложенный торговый ордер
|
|
TRADE_ACTION_CLOSE_BY, // Закрыть позицию встречной
|
|
};
|
|
|
|
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; // Тикет встречной позиции
|
|
};
|
|
|
|
|
|
struct MqlTradeResult {
|
|
uint retcode; // Код результата операции
|
|
ulong deal; // Тикет сделки, если она совершена
|
|
ulong order; // Тикет ордера, если он выставлен
|
|
double volume; // Объем сделки, подтверждённый брокером
|
|
double price; // Цена в сделке, подтверждённая брокером
|
|
double bid; // Текущая рыночная цена предложения (цены реквота)
|
|
double ask; // Текущая рыночная цена спроса (цены реквота)
|
|
string comment; // Комментарий брокера к операции (по умолчанию заполняется расшифровкой кода возврата торгового сервера)
|
|
uint request_id; // Идентификатор запроса, устанавливается терминалом при отправке
|
|
int retcode_external; // Код ответа внешней торговой системы
|
|
};
|
|
|
|
struct MqlTradeCheckResult {
|
|
uint retcode; // Код ответа
|
|
double balance; // Баланс после совершения сделки
|
|
double equity; // Эквити после совершения сделки
|
|
double profit; // Плавающая прибыль
|
|
double margin; // Маржевые требования
|
|
double margin_free; // Свободная маржа
|
|
double margin_level; // Уровень маржи
|
|
string comment; // Комментарий к коду ответа (описание ошибки)
|
|
};
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Class CTrade. |
|
|
//| Appointment: Class trade operations. |
|
|
//| Derives from class CObject. |
|
|
//+------------------------------------------------------------------+
|
|
class CTrade : public CObject {
|
|
protected:
|
|
MqlTradeRequest m_request; // request data
|
|
MqlTradeResult m_result; // result data
|
|
MqlTradeCheckResult m_check_result; // result check data
|
|
bool m_async_mode; // trade mode
|
|
ulong m_magic; // expert magic number
|
|
ulong m_deviation; // deviation default
|
|
ENUM_ORDER_TYPE_FILLING m_type_filling;
|
|
ENUM_ACCOUNT_MARGIN_MODE m_margin_mode;
|
|
//---
|
|
ENUM_LOG_LEVELS m_log_level;
|
|
|
|
public:
|
|
CTrade(void);
|
|
~CTrade(void);
|
|
//--- methods of access to protected data
|
|
void LogLevel(const ENUM_LOG_LEVELS log_level) {
|
|
m_log_level = log_level;
|
|
}
|
|
|
|
void Request(MqlTradeRequest &request) const;
|
|
ENUM_TRADE_REQUEST_ACTIONS RequestAction(void) const {
|
|
return(m_request.action);
|
|
}
|
|
string RequestActionDescription(void) const;
|
|
ulong RequestMagic(void) const {
|
|
return(m_request.magic);
|
|
}
|
|
ulong RequestOrder(void) const {
|
|
return(m_request.order);
|
|
}
|
|
ulong RequestPosition(void) const {
|
|
return(m_request.position);
|
|
}
|
|
ulong RequestPositionBy(void) const {
|
|
return(m_request.position_by);
|
|
}
|
|
string RequestSymbol(void) const {
|
|
return(m_request.symbol);
|
|
}
|
|
double RequestVolume(void) const {
|
|
return(m_request.volume);
|
|
}
|
|
double RequestPrice(void) const {
|
|
return(m_request.price);
|
|
}
|
|
double RequestStopLimit(void) const {
|
|
return(m_request.stoplimit);
|
|
}
|
|
double RequestSL(void) const {
|
|
return(m_request.sl);
|
|
}
|
|
double RequestTP(void) const {
|
|
return(m_request.tp);
|
|
}
|
|
ulong RequestDeviation(void) const {
|
|
return(m_request.deviation);
|
|
}
|
|
ENUM_ORDER_TYPE RequestType(void) const {
|
|
return(m_request.type);
|
|
}
|
|
string RequestTypeDescription(void) const;
|
|
ENUM_ORDER_TYPE_FILLING RequestTypeFilling(void) const {
|
|
return(m_request.type_filling);
|
|
}
|
|
string RequestTypeFillingDescription(void) const;
|
|
ENUM_ORDER_TYPE_TIME RequestTypeTime(void) const {
|
|
return(m_request.type_time);
|
|
}
|
|
string RequestTypeTimeDescription(void) const;
|
|
datetime RequestExpiration(void) const {
|
|
return(m_request.expiration);
|
|
}
|
|
string RequestComment(void) const {
|
|
return(m_request.comment);
|
|
}
|
|
//---
|
|
void Result(MqlTradeResult &result) const;
|
|
uint ResultRetcode(void) const {
|
|
return(m_result.retcode);
|
|
}
|
|
string ResultRetcodeDescription(void) const;
|
|
int ResultRetcodeExternal(void) const {
|
|
return(m_result.retcode_external);
|
|
}
|
|
ulong ResultDeal(void) const {
|
|
return(m_result.deal);
|
|
}
|
|
ulong ResultOrder(void) const {
|
|
return(m_result.order);
|
|
}
|
|
double ResultVolume(void) const {
|
|
return(m_result.volume);
|
|
}
|
|
double ResultPrice(void) const {
|
|
return(m_result.price);
|
|
}
|
|
double ResultBid(void) const {
|
|
return(m_result.bid);
|
|
}
|
|
double ResultAsk(void) const {
|
|
return(m_result.ask);
|
|
}
|
|
string ResultComment(void) const {
|
|
return(m_result.comment);
|
|
}
|
|
//---
|
|
void CheckResult(MqlTradeCheckResult &check_result) const;
|
|
uint CheckResultRetcode(void) const {
|
|
return(m_check_result.retcode);
|
|
}
|
|
string CheckResultRetcodeDescription(void) const;
|
|
double CheckResultBalance(void) const {
|
|
return(m_check_result.balance);
|
|
}
|
|
double CheckResultEquity(void) const {
|
|
return(m_check_result.equity);
|
|
}
|
|
double CheckResultProfit(void) const {
|
|
return(m_check_result.profit);
|
|
}
|
|
double CheckResultMargin(void) const {
|
|
return(m_check_result.margin);
|
|
}
|
|
double CheckResultMarginFree(void) const {
|
|
return(m_check_result.margin_free);
|
|
}
|
|
double CheckResultMarginLevel(void) const {
|
|
return(m_check_result.margin_level);
|
|
}
|
|
string CheckResultComment(void) const {
|
|
return(m_check_result.comment);
|
|
}
|
|
//--- trade methods
|
|
void SetAsyncMode(const bool mode) {
|
|
m_async_mode = false;
|
|
}
|
|
void SetExpertMagicNumber(const ulong magic) {
|
|
m_magic = magic;
|
|
}
|
|
void SetDeviationInPoints(const ulong deviation) {
|
|
m_deviation = deviation;
|
|
}
|
|
void SetTypeFilling(const ENUM_ORDER_TYPE_FILLING filling) {
|
|
m_type_filling = filling;
|
|
}
|
|
bool SetTypeFillingBySymbol(const string symbol);
|
|
void SetMarginMode(void) {
|
|
m_margin_mode = ACCOUNT_MARGIN_MODE_RETAIL_HEDGING;
|
|
}
|
|
//--- methods for working with positions
|
|
bool PositionSelectByTicket(const ulong ticket);
|
|
bool PositionOpen(const string symbol, const ENUM_ORDER_TYPE order_type, const double volume,
|
|
const double price, const double sl, const double tp, const string comment = "");
|
|
bool PositionModify(const string symbol, const double sl, const double tp);
|
|
bool PositionModify(const ulong ticket, const double sl, const double tp);
|
|
bool PositionClose(const string symbol, const ulong deviation = ULONG_MAX);
|
|
bool PositionClose(const ulong ticket, const ulong deviation = ULONG_MAX);
|
|
bool PositionCloseBy(const ulong ticket, const ulong ticket_by);
|
|
bool PositionClosePartial(const string symbol, const double volume, const ulong deviation = ULONG_MAX);
|
|
bool PositionClosePartial(const ulong ticket, const double volume, const ulong deviation = ULONG_MAX);
|
|
//--- methods for working with pending orders
|
|
bool OrderOpen(const string symbol, const ENUM_ORDER_TYPE order_type, const double volume,
|
|
const double limit_price, const double price, const double sl, const double tp,
|
|
ENUM_ORDER_TYPE_TIME type_time = ORDER_TIME_GTC, const datetime expiration = 0,
|
|
const string comment = "");
|
|
bool OrderModify(const ulong ticket, const double price, const double sl, const double tp,
|
|
const ENUM_ORDER_TYPE_TIME type_time, const datetime expiration, const double stoplimit = 0.0);
|
|
bool OrderDelete(const ulong ticket);
|
|
//--- additions methods
|
|
bool Buy(const double volume, const string symbol = NULL, double price = 0.0, const double sl = 0.0, const double tp = 0.0, const string comment = "");
|
|
bool Sell(const double volume, const string symbol = NULL, double price = 0.0, const double sl = 0.0, const double tp = 0.0, const string comment = "");
|
|
bool BuyLimit(const double volume, const double price, const string symbol = NULL, const double sl = 0.0, const double tp = 0.0,
|
|
const ENUM_ORDER_TYPE_TIME type_time = ORDER_TIME_GTC, const datetime expiration = 0, const string comment = "");
|
|
bool BuyStop(const double volume, const double price, const string symbol = NULL, const double sl = 0.0, const double tp = 0.0,
|
|
const ENUM_ORDER_TYPE_TIME type_time = ORDER_TIME_GTC, const datetime expiration = 0, const string comment = "");
|
|
bool SellLimit(const double volume, const double price, const string symbol = NULL, const double sl = 0.0, const double tp = 0.0,
|
|
const ENUM_ORDER_TYPE_TIME type_time = ORDER_TIME_GTC, const datetime expiration = 0, const string comment = "");
|
|
bool SellStop(const double volume, const double price, const string symbol = NULL, const double sl = 0.0, const double tp = 0.0,
|
|
const ENUM_ORDER_TYPE_TIME type_time = ORDER_TIME_GTC, const datetime expiration = 0, const string comment = "");
|
|
//--- method check
|
|
virtual double CheckVolume(const string symbol, double volume, double price, ENUM_ORDER_TYPE order_type);
|
|
virtual bool OrderCheck(const MqlTradeRequest &request, MqlTradeCheckResult &check_result);
|
|
virtual bool OrderSend(const MqlTradeRequest &request, MqlTradeResult &result);
|
|
//--- info methods
|
|
void PrintRequest(void) const;
|
|
void PrintResult(void) const;
|
|
//--- positions
|
|
string FormatPositionType(string &str, const uint type) const;
|
|
//--- orders
|
|
string FormatOrderType(string &str, const uint type) const;
|
|
string FormatOrderStatus(string &str, const uint status) const;
|
|
string FormatOrderTypeFilling(string &str, const uint type) const;
|
|
string FormatOrderTypeTime(string &str, const uint type) const;
|
|
string FormatOrderPrice(string &str, const double price_order, const double price_trigger, const uint digits) const;
|
|
//--- trade request
|
|
string FormatRequest(string &str, const MqlTradeRequest &request) const;
|
|
string FormatRequestResult(string &str, const MqlTradeRequest &request, const MqlTradeResult &result) const;
|
|
|
|
protected:
|
|
bool FillingCheck(const string symbol);
|
|
bool ExpirationCheck(const string symbol);
|
|
bool OrderTypeCheck(const string symbol);
|
|
void ClearStructures(void);
|
|
bool IsStopped(const string function);
|
|
bool IsHedging(void) const {
|
|
return(m_margin_mode == ACCOUNT_MARGIN_MODE_RETAIL_HEDGING);
|
|
}
|
|
//--- position select depending on netting or hedging
|
|
bool SelectPosition(const string symbol);
|
|
ENUM_TRADE_RETCODE ErrorToRetcode(int lastError);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor |
|
|
//+------------------------------------------------------------------+
|
|
CTrade::CTrade(void) : m_async_mode(false),
|
|
m_magic(0),
|
|
m_deviation(10),
|
|
m_type_filling(ORDER_FILLING_FOK),
|
|
m_log_level(LOG_LEVEL_ERRORS) {
|
|
SetMarginMode();
|
|
//--- initialize protected data
|
|
ClearStructures();
|
|
//--- check programm mode
|
|
if(IsTesting())
|
|
m_log_level = LOG_LEVEL_ALL;
|
|
if(IsOptimization())
|
|
m_log_level = LOG_LEVEL_NO;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTrade::~CTrade(void) {
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get the request structure |
|
|
//+------------------------------------------------------------------+
|
|
void CTrade::Request(MqlTradeRequest &request) const {
|
|
request.action = m_request.action;
|
|
request.magic = m_request.magic;
|
|
request.order = m_request.order;
|
|
request.symbol = m_request.symbol;
|
|
request.volume = m_request.volume;
|
|
request.price = m_request.price;
|
|
request.stoplimit = m_request.stoplimit;
|
|
request.sl = m_request.sl;
|
|
request.tp = m_request.tp;
|
|
request.deviation = m_request.deviation;
|
|
request.type = m_request.type;
|
|
request.type_filling = m_request.type_filling;
|
|
request.type_time = m_request.type_time;
|
|
request.expiration = m_request.expiration;
|
|
request.comment = m_request.comment;
|
|
request.position = m_request.position;
|
|
request.position_by = m_request.position_by;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Get the trade action as string |
|
|
//+------------------------------------------------------------------+
|
|
string CTrade::RequestActionDescription(void) const {
|
|
string str;
|
|
//---
|
|
FormatRequest(str, m_request);
|
|
//---
|
|
return(str);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Get the order type as string |
|
|
//+------------------------------------------------------------------+
|
|
string CTrade::RequestTypeDescription(void) const {
|
|
string str;
|
|
//---
|
|
FormatOrderType(str, (uint)m_request.order);
|
|
//---
|
|
return(str);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Get the order type filling as string |
|
|
//+------------------------------------------------------------------+
|
|
string CTrade::RequestTypeFillingDescription(void) const {
|
|
string str;
|
|
//---
|
|
FormatOrderTypeFilling(str, m_request.type_filling);
|
|
//---
|
|
return(str);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Get the order type time as string |
|
|
//+------------------------------------------------------------------+
|
|
string CTrade::RequestTypeTimeDescription(void) const {
|
|
string str;
|
|
//---
|
|
FormatOrderTypeTime(str, m_request.type_time);
|
|
//---
|
|
return(str);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Get the result structure |
|
|
//+------------------------------------------------------------------+
|
|
void CTrade::Result(MqlTradeResult &result) const {
|
|
result.retcode = m_result.retcode;
|
|
result.deal = m_result.deal;
|
|
result.order = m_result.order;
|
|
result.volume = m_result.volume;
|
|
result.price = m_result.price;
|
|
result.bid = m_result.bid;
|
|
result.ask = m_result.ask;
|
|
result.comment = m_result.comment;
|
|
result.request_id = m_result.request_id;
|
|
result.retcode_external = m_result.retcode_external;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Get the retcode value as string |
|
|
//+------------------------------------------------------------------+
|
|
string CTrade::ResultRetcodeDescription(void) const {
|
|
string str;
|
|
//---
|
|
FormatRequestResult(str, m_request, m_result);
|
|
//---
|
|
return(str);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Get the check result structure |
|
|
//+------------------------------------------------------------------+
|
|
void CTrade::CheckResult(MqlTradeCheckResult &check_result) const {
|
|
//--- copy structure
|
|
check_result.retcode = m_check_result.retcode;
|
|
check_result.balance = m_check_result.balance;
|
|
check_result.equity = m_check_result.equity;
|
|
check_result.profit = m_check_result.profit;
|
|
check_result.margin = m_check_result.margin;
|
|
check_result.margin_free = m_check_result.margin_free;
|
|
check_result.margin_level = m_check_result.margin_level;
|
|
check_result.comment = m_check_result.comment;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Get the check retcode value as string |
|
|
//+------------------------------------------------------------------+
|
|
string CTrade::CheckResultRetcodeDescription(void) const {
|
|
string str;
|
|
MqlTradeResult result;
|
|
//---
|
|
result.retcode = m_check_result.retcode;
|
|
FormatRequestResult(str, m_request, result);
|
|
//---
|
|
return(str);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Open position |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::PositionOpen(const string symbol, const ENUM_ORDER_TYPE order_type, const double volume,
|
|
const double price, const double sl, const double tp, const string comment) {
|
|
//--- check stopped
|
|
if(IsStopped(__FUNCTION__))
|
|
return(false);
|
|
//--- clean
|
|
ClearStructures();
|
|
//--- check
|
|
if(order_type != ORDER_TYPE_BUY && order_type != ORDER_TYPE_SELL) {
|
|
m_result.retcode = TRADE_RETCODE_INVALID;
|
|
m_result.comment = "Invalid order type";
|
|
return(false);
|
|
}
|
|
//--- setting request
|
|
m_request.action = TRADE_ACTION_DEAL;
|
|
m_request.symbol = symbol;
|
|
m_request.magic = m_magic;
|
|
m_request.volume = volume;
|
|
m_request.type = order_type;
|
|
m_request.price = price;
|
|
m_request.sl = sl;
|
|
m_request.tp = tp;
|
|
m_request.deviation = m_deviation;
|
|
//--- check order type
|
|
if(!OrderTypeCheck(symbol))
|
|
return(false);
|
|
//--- check filling
|
|
if(!FillingCheck(symbol))
|
|
return(false);
|
|
m_request.comment = comment;
|
|
//--- action and return the result
|
|
return(this.OrderSend(m_request, m_result));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Modify specified opened position |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::PositionModify(const string symbol, const double sl, const double tp) {
|
|
//--- check stopped
|
|
if(IsStopped(__FUNCTION__))
|
|
return(false);
|
|
//--- check position existence
|
|
if(!SelectPosition(symbol))
|
|
return(false);
|
|
//--- clean
|
|
ClearStructures();
|
|
//--- setting request
|
|
m_request.action = TRADE_ACTION_SLTP;
|
|
m_request.symbol = symbol;
|
|
m_request.magic = m_magic;
|
|
m_request.sl = sl;
|
|
m_request.tp = tp;
|
|
m_request.position = OrderMagicNumber();
|
|
//--- action and return the result
|
|
return(OrderSend(m_request, m_result));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Modify specified opened position |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::PositionModify(const ulong ticket, const double sl, const double tp) {
|
|
//--- check stopped
|
|
if(IsStopped(__FUNCTION__))
|
|
return(false);
|
|
//--- check position existence
|
|
if(!PositionSelectByTicket(ticket))
|
|
return(false);
|
|
//--- clean
|
|
ClearStructures();
|
|
//--- setting request
|
|
m_request.action = TRADE_ACTION_SLTP;
|
|
m_request.price = OrderOpenPrice();
|
|
m_request.position = ticket;
|
|
m_request.symbol = OrderSymbol();
|
|
m_request.magic = m_magic;
|
|
m_request.sl = sl;
|
|
m_request.tp = tp;
|
|
//--- action and return the result
|
|
return(OrderSend(m_request, m_result));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Close specified opened position |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::PositionClose(const string symbol, const ulong deviation) {
|
|
bool partial_close = false;
|
|
int retry_count = 10;
|
|
uint retcode = TRADE_RETCODE_REJECT;
|
|
//--- check stopped
|
|
if(IsStopped(__FUNCTION__))
|
|
return(false);
|
|
//--- clean
|
|
ClearStructures();
|
|
//--- check filling
|
|
if(!FillingCheck(symbol))
|
|
return(false);
|
|
do {
|
|
//--- check
|
|
if(SelectPosition(symbol)) {
|
|
if(OrderType() == POSITION_TYPE_BUY) {
|
|
//--- prepare request for close BUY position
|
|
m_request.type = ORDER_TYPE_SELL;
|
|
m_request.price = SymbolInfoDouble(symbol, SYMBOL_BID);
|
|
} else {
|
|
//--- prepare request for close SELL position
|
|
m_request.type = ORDER_TYPE_BUY;
|
|
m_request.price = SymbolInfoDouble(symbol, SYMBOL_ASK);
|
|
}
|
|
} else {
|
|
//--- position not found
|
|
m_result.retcode = retcode;
|
|
return(false);
|
|
}
|
|
//--- setting request
|
|
m_request.action = TRADE_ACTION_DEAL;
|
|
m_request.symbol = symbol;
|
|
m_request.volume = OrderLots();
|
|
m_request.magic = m_magic;
|
|
m_request.deviation = (deviation == ULONG_MAX) ? m_deviation : deviation;
|
|
//--- check volume
|
|
double max_volume = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
|
|
if(m_request.volume > max_volume) {
|
|
m_request.volume = max_volume;
|
|
partial_close = true;
|
|
} else
|
|
partial_close = false;
|
|
//--- hedging? just send order
|
|
if(IsHedging()) {
|
|
m_request.position = OrderTicket();
|
|
return(OrderSend(m_request, m_result));
|
|
}
|
|
|
|
//--- WARNING. If position volume exceeds the maximum volume allowed for deal,
|
|
//--- and when the asynchronous trade mode is on, for safety reasons, position is closed not completely,
|
|
//--- but partially. It is decreased by the maximum volume allowed for deal.
|
|
if(m_async_mode)
|
|
break;
|
|
retcode = TRADE_RETCODE_DONE_PARTIAL;
|
|
if(partial_close)
|
|
Sleep(1000);
|
|
} while(partial_close);
|
|
//--- succeed
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Close specified opened position |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::PositionClose(const ulong ticket, const ulong deviation) {
|
|
//--- check stopped
|
|
if(IsStopped(__FUNCTION__))
|
|
return(false);
|
|
//--- check position existence
|
|
if(!OrderSelect((int)ticket, SELECT_BY_TICKET, MODE_TRADES))
|
|
return(false);
|
|
string symbol = OrderSymbol();
|
|
//--- clean
|
|
ClearStructures();
|
|
//--- check filling
|
|
if(!FillingCheck(symbol))
|
|
return(false);
|
|
//--- check
|
|
if((ENUM_POSITION_TYPE)OrderType() == POSITION_TYPE_BUY) {
|
|
//--- prepare request for close BUY position
|
|
m_request.type = ORDER_TYPE_SELL;
|
|
m_request.price = SymbolInfoDouble(symbol, SYMBOL_BID);
|
|
} else {
|
|
//--- prepare request for close SELL position
|
|
m_request.type = ORDER_TYPE_BUY;
|
|
m_request.price = SymbolInfoDouble(symbol, SYMBOL_ASK);
|
|
}
|
|
//--- setting request
|
|
m_request.action = TRADE_ACTION_CLOSE_BY;
|
|
m_request.position = ticket;
|
|
m_request.symbol = symbol;
|
|
m_request.volume = OrderLots();
|
|
m_request.magic = m_magic;
|
|
m_request.deviation = (deviation == ULONG_MAX) ? m_deviation : deviation;
|
|
//--- close position
|
|
return(OrderSend(m_request, m_result));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Close one position by other |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::PositionCloseBy(const ulong ticket, const ulong ticket_by) {
|
|
//--- check stopped
|
|
if(IsStopped(__FUNCTION__))
|
|
return(false);
|
|
//--- check hedging mode
|
|
if(!IsHedging())
|
|
return(false);
|
|
//--- check position existence
|
|
if(!PositionSelectByTicket(ticket))
|
|
return(false);
|
|
string symbol = OrderSymbol();
|
|
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE) OrderType();
|
|
if(!PositionSelectByTicket(ticket_by))
|
|
return(false);
|
|
string symbol_by = OrderSymbol();
|
|
ENUM_POSITION_TYPE type_by = (ENUM_POSITION_TYPE) OrderType();
|
|
//--- check positions
|
|
if(type == type_by)
|
|
return(false);
|
|
if(symbol != symbol_by)
|
|
return(false);
|
|
//--- clean
|
|
ClearStructures();
|
|
//--- check filling
|
|
if(!FillingCheck(symbol))
|
|
return(false);
|
|
//--- setting request
|
|
m_request.action = TRADE_ACTION_CLOSE_BY;
|
|
m_request.position = ticket;
|
|
m_request.position_by = ticket_by;
|
|
m_request.magic = m_magic;
|
|
//--- close position
|
|
return(OrderSend(m_request, m_result));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Partial close specified opened position (for hedging mode only) |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::PositionClosePartial(const string symbol, const double volume, const ulong deviation) {
|
|
uint retcode = TRADE_RETCODE_REJECT;
|
|
//--- check stopped
|
|
if(IsStopped(__FUNCTION__))
|
|
return(false);
|
|
//--- for hedging mode only
|
|
if(!IsHedging())
|
|
return(false);
|
|
//--- clean
|
|
ClearStructures();
|
|
//--- check filling
|
|
if(!FillingCheck(symbol))
|
|
return(false);
|
|
//--- check
|
|
if(SelectPosition(symbol)) {
|
|
if(OrderType() == POSITION_TYPE_BUY) {
|
|
//--- prepare request for close BUY position
|
|
m_request.type = ORDER_TYPE_SELL;
|
|
m_request.price = SymbolInfoDouble(symbol, SYMBOL_BID);
|
|
} else {
|
|
//--- prepare request for close SELL position
|
|
m_request.type = ORDER_TYPE_BUY;
|
|
m_request.price = SymbolInfoDouble(symbol, SYMBOL_ASK);
|
|
}
|
|
} else {
|
|
//--- position not found
|
|
m_result.retcode = retcode;
|
|
return(false);
|
|
}
|
|
//--- check volume
|
|
double position_volume = OrderLots();
|
|
if(position_volume > volume)
|
|
position_volume = volume;
|
|
//--- setting request
|
|
m_request.action = TRADE_ACTION_CLOSE_BY;
|
|
m_request.symbol = symbol;
|
|
m_request.volume = position_volume;
|
|
m_request.magic = m_magic;
|
|
m_request.deviation = (deviation == ULONG_MAX) ? m_deviation : deviation;
|
|
m_request.position = OrderTicket();
|
|
//--- hedging? just send order
|
|
return(OrderSend(m_request, m_result));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Partial close specified opened position (for hedging mode only) |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::PositionClosePartial(const ulong ticket, const double volume, const ulong deviation) {
|
|
//--- check stopped
|
|
if(IsStopped(__FUNCTION__))
|
|
return(false);
|
|
//--- for hedging mode only
|
|
if(!IsHedging())
|
|
return(false);
|
|
//--- check position existence
|
|
if(!PositionSelectByTicket(ticket))
|
|
return(false);
|
|
string symbol = OrderSymbol();
|
|
//--- clean
|
|
ClearStructures();
|
|
//--- check filling
|
|
if(!FillingCheck(symbol))
|
|
return(false);
|
|
//--- check
|
|
if(OrderType() == POSITION_TYPE_BUY) {
|
|
//--- prepare request for close BUY position
|
|
m_request.type = ORDER_TYPE_SELL;
|
|
m_request.price = SymbolInfoDouble(symbol, SYMBOL_BID);
|
|
} else {
|
|
//--- prepare request for close SELL position
|
|
m_request.type = ORDER_TYPE_BUY;
|
|
m_request.price = SymbolInfoDouble(symbol, SYMBOL_ASK);
|
|
}
|
|
//--- check volume
|
|
double position_volume = OrderLots();
|
|
if(position_volume > volume)
|
|
position_volume = volume;
|
|
//--- setting request
|
|
m_request.action = TRADE_ACTION_CLOSE_BY;
|
|
m_request.position = ticket;
|
|
m_request.symbol = symbol;
|
|
m_request.volume = position_volume;
|
|
m_request.magic = m_magic;
|
|
m_request.deviation = (deviation == ULONG_MAX) ? m_deviation : deviation;
|
|
//--- close position
|
|
return(OrderSend(m_request, m_result));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Installation pending order |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::OrderOpen(const string symbol, const ENUM_ORDER_TYPE order_type, const double volume, const double limit_price,
|
|
const double price, const double sl, const double tp,
|
|
ENUM_ORDER_TYPE_TIME type_time, const datetime expiration, const string comment) {
|
|
//--- check stopped
|
|
if(IsStopped(__FUNCTION__))
|
|
return(false);
|
|
//--- clean
|
|
ClearStructures();
|
|
//--- check filling
|
|
if(!FillingCheck(symbol))
|
|
return(false);
|
|
//--- check order type
|
|
if(order_type == ORDER_TYPE_BUY || order_type == ORDER_TYPE_SELL) {
|
|
m_result.retcode = TRADE_RETCODE_INVALID;
|
|
m_result.comment = "Invalid order type";
|
|
return(false);
|
|
}
|
|
/*
|
|
//--- check order expiration
|
|
if(type_time == ORDER_TIME_GTC && expiration == 0) {
|
|
|
|
int exp = (int)SymbolInfoInteger(symbol, SYMBOL_EXPIRATION_MODE);
|
|
if((exp & SYMBOL_EXPIRATION_GTC) != SYMBOL_EXPIRATION_GTC) {
|
|
//--- if you place order for an unlimited time and if placing of such orders is prohibited
|
|
//--- try to place order with expiration at the end of the day
|
|
if((exp & SYMBOL_EXPIRATION_DAY) != SYMBOL_EXPIRATION_DAY) {
|
|
//--- if even this is not possible - error
|
|
Print(__FUNCTION__, ": Error: Unable to place order without explicitly specified expiration time");
|
|
m_result.retcode = TRADE_RETCODE_INVALID_EXPIRATION;
|
|
m_result.comment = "Invalid expiration type";
|
|
return(false);
|
|
}
|
|
type_time = ORDER_TIME_DAY;
|
|
}
|
|
}
|
|
*/
|
|
|
|
//--- setting request
|
|
m_request.action = TRADE_ACTION_PENDING;
|
|
m_request.symbol = symbol;
|
|
m_request.magic = m_magic;
|
|
m_request.volume = volume;
|
|
m_request.type = order_type;
|
|
m_request.stoplimit = limit_price;
|
|
m_request.price = price;
|
|
m_request.sl = sl;
|
|
m_request.tp = tp;
|
|
m_request.type_time = type_time;
|
|
m_request.expiration = expiration;
|
|
//--- check order type
|
|
if(!OrderTypeCheck(symbol))
|
|
return(false);
|
|
//--- check filling
|
|
if(!FillingCheck(symbol)) {
|
|
m_result.retcode = TRADE_RETCODE_INVALID_FILL;
|
|
Print(__FUNCTION__ + ": Invalid filling type");
|
|
return(false);
|
|
}
|
|
//--- check expiration
|
|
if(!ExpirationCheck(symbol)) {
|
|
m_result.retcode = TRADE_RETCODE_INVALID_EXPIRATION;
|
|
Print(__FUNCTION__ + ": Invalid expiration type");
|
|
return(false);
|
|
}
|
|
m_request.comment = comment;
|
|
//--- action and return the result
|
|
return(OrderSend(m_request, m_result));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Modify specified pending order |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::OrderModify(const ulong ticket, const double price, const double sl, const double tp,
|
|
const ENUM_ORDER_TYPE_TIME type_time, const datetime expiration, const double stoplimit) {
|
|
//--- check stopped
|
|
if(IsStopped(__FUNCTION__))
|
|
return(false);
|
|
//--- check order existence
|
|
if(!OrderSelect((int)ticket, SELECT_BY_TICKET, MODE_TRADES))
|
|
return(false);
|
|
//--- clean
|
|
ClearStructures();
|
|
//--- setting request
|
|
m_request.symbol = OrderGetString(ORDER_SYMBOL);
|
|
m_request.action = TRADE_ACTION_MODIFY;
|
|
m_request.magic = m_magic;
|
|
m_request.order = ticket;
|
|
m_request.price = price;
|
|
m_request.stoplimit = stoplimit;
|
|
m_request.sl = sl;
|
|
m_request.tp = tp;
|
|
m_request.type_time = type_time;
|
|
m_request.expiration = expiration;
|
|
//--- action and return the result
|
|
return(OrderSend(m_request, m_result));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Delete specified pending order |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::OrderDelete(const ulong ticket) {
|
|
//--- check stopped
|
|
if(IsStopped(__FUNCTION__))
|
|
return(false);
|
|
//--- clean
|
|
ClearStructures();
|
|
//--- setting request
|
|
m_request.action = TRADE_ACTION_REMOVE;
|
|
m_request.magic = m_magic;
|
|
m_request.order = ticket;
|
|
//--- action and return the result
|
|
return(OrderSend(m_request, m_result));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Output full information of request to log |
|
|
//+------------------------------------------------------------------+
|
|
void CTrade::PrintRequest(void) const {
|
|
if(m_log_level < LOG_LEVEL_ALL)
|
|
return;
|
|
//---
|
|
string str;
|
|
PrintFormat("%s", FormatRequest(str, m_request));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Output full information of result to log |
|
|
//+------------------------------------------------------------------+
|
|
void CTrade::PrintResult(void) const {
|
|
if(m_log_level < LOG_LEVEL_ALL)
|
|
return;
|
|
//---
|
|
string str;
|
|
PrintFormat("%s", FormatRequestResult(str, m_request, m_result));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Clear structures m_request,m_result and m_check_result |
|
|
//+------------------------------------------------------------------+
|
|
void CTrade::ClearStructures(void) {
|
|
ZeroMemory(m_request);
|
|
ZeroMemory(m_result);
|
|
ZeroMemory(m_check_result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks forced shutdown of MQL5-program |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::IsStopped(const string function) {
|
|
if(!::IsStopped())
|
|
return(false);
|
|
//--- MQL5 program is stopped
|
|
PrintFormat("%s: MQL5 program is stopped. Trading is disabled", function);
|
|
m_result.retcode = TRADE_RETCODE_CLIENT_DISABLES_AT;
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Buy operation |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::Buy(const double volume, const string symbol = NULL, double price = 0.0, const double sl = 0.0, const double tp = 0.0, const string comment = "") {
|
|
//--- check volume
|
|
if(volume <= 0.0) {
|
|
m_result.retcode = TRADE_RETCODE_INVALID_VOLUME;
|
|
return(false);
|
|
}
|
|
//--- check symbol
|
|
string symbol_name = (symbol == NULL) ? _Symbol : symbol;
|
|
//--- check price
|
|
if(price == 0.0)
|
|
price = SymbolInfoDouble(symbol_name, SYMBOL_ASK);
|
|
//---
|
|
return(PositionOpen(symbol_name, ORDER_TYPE_BUY, volume, price, sl, tp, comment));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Sell operation |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::Sell(const double volume, const string symbol = NULL, double price = 0.0, const double sl = 0.0, const double tp = 0.0, const string comment = "") {
|
|
//--- check volume
|
|
if(volume <= 0.0) {
|
|
m_result.retcode = TRADE_RETCODE_INVALID_VOLUME;
|
|
return(false);
|
|
}
|
|
//--- check symbol
|
|
string symbol_name = (symbol == NULL) ? _Symbol : symbol;
|
|
//--- check price
|
|
if(price == 0.0)
|
|
price = SymbolInfoDouble(symbol_name, SYMBOL_BID);
|
|
//---
|
|
return(PositionOpen(symbol_name, ORDER_TYPE_SELL, volume, price, sl, tp, comment));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Send BUY_LIMIT order |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::BuyLimit(const double volume, const double price, const string symbol = NULL, const double sl = 0.0, const double tp = 0.0,
|
|
const ENUM_ORDER_TYPE_TIME type_time = ORDER_TIME_GTC, const datetime expiration = 0, const string comment = "") {
|
|
string symbol_name;
|
|
//--- check volume
|
|
if(volume <= 0.0) {
|
|
m_result.retcode = TRADE_RETCODE_INVALID_VOLUME;
|
|
return(false);
|
|
}
|
|
//--- check symbol
|
|
symbol_name = (symbol == NULL) ? Symbol() : symbol;
|
|
//--- send "BUY_LIMIT" order
|
|
return(OrderOpen(symbol_name, ORDER_TYPE_BUY_LIMIT, volume, 0.0, price, sl, tp, type_time, expiration, comment));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Send BUY_STOP order |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::BuyStop(const double volume, const double price, const string symbol = NULL, const double sl = 0.0, const double tp = 0.0,
|
|
const ENUM_ORDER_TYPE_TIME type_time = ORDER_TIME_GTC, const datetime expiration = 0, const string comment = "") {
|
|
string symbol_name;
|
|
//--- check volume
|
|
if(volume <= 0.0) {
|
|
m_result.retcode = TRADE_RETCODE_INVALID_VOLUME;
|
|
return(false);
|
|
}
|
|
//--- check symbol
|
|
symbol_name = (symbol == NULL) ? Symbol() : symbol;
|
|
//--- send "BUY_STOP" order
|
|
return(OrderOpen(symbol_name, ORDER_TYPE_BUY_STOP, volume, 0.0, price, sl, tp, type_time, expiration, comment));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Send SELL_LIMIT order |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::SellLimit(const double volume, const double price, const string symbol = NULL, const double sl = 0.0, const double tp = 0.0,
|
|
const ENUM_ORDER_TYPE_TIME type_time = ORDER_TIME_GTC, const datetime expiration = 0, const string comment = "") {
|
|
string symbol_name;
|
|
//--- check volume
|
|
if(volume <= 0.0) {
|
|
m_result.retcode = TRADE_RETCODE_INVALID_VOLUME;
|
|
return(false);
|
|
}
|
|
//--- check symbol
|
|
symbol_name = (symbol == NULL) ? Symbol() : symbol;
|
|
//--- send "SELL_LIMIT" order
|
|
return(OrderOpen(symbol_name, ORDER_TYPE_SELL_LIMIT, volume, 0.0, price, sl, tp, type_time, expiration, comment));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Send SELL_STOP order |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::SellStop(const double volume, const double price, const string symbol = NULL, const double sl = 0.0, const double tp = 0.0,
|
|
const ENUM_ORDER_TYPE_TIME type_time = ORDER_TIME_GTC, const datetime expiration = 0, const string comment = "") {
|
|
string symbol_name;
|
|
//--- check volume
|
|
if(volume <= 0.0) {
|
|
m_result.retcode = TRADE_RETCODE_INVALID_VOLUME;
|
|
return(false);
|
|
}
|
|
//--- check symbol
|
|
symbol_name = (symbol == NULL) ? Symbol() : symbol;
|
|
//--- send "SELL_STOP" order
|
|
return(OrderOpen(symbol_name, ORDER_TYPE_SELL_STOP, volume, 0.0, price, sl, tp, type_time, expiration, comment));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Converts the position type to text |
|
|
//+------------------------------------------------------------------+
|
|
string CTrade::FormatPositionType(string &str, const uint type) const {
|
|
//--- see the type
|
|
switch(type) {
|
|
case POSITION_TYPE_BUY:
|
|
str = "buy";
|
|
break;
|
|
case POSITION_TYPE_SELL:
|
|
str = "sell";
|
|
break;
|
|
default:
|
|
str = "unknown position type " + (string)type;
|
|
}
|
|
//--- return the result
|
|
return(str);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Converts the order type to text |
|
|
//+------------------------------------------------------------------+
|
|
string CTrade::FormatOrderType(string &str, const uint type) const {
|
|
//--- see the type
|
|
switch(type) {
|
|
case ORDER_TYPE_BUY:
|
|
str = "buy";
|
|
break;
|
|
case ORDER_TYPE_SELL:
|
|
str = "sell";
|
|
break;
|
|
case ORDER_TYPE_BUY_LIMIT:
|
|
str = "buy limit";
|
|
break;
|
|
case ORDER_TYPE_SELL_LIMIT:
|
|
str = "sell limit";
|
|
break;
|
|
case ORDER_TYPE_BUY_STOP:
|
|
str = "buy stop";
|
|
break;
|
|
case ORDER_TYPE_SELL_STOP:
|
|
str = "sell stop";
|
|
break;
|
|
default:
|
|
str = "unknown order type " + (string)type;
|
|
}
|
|
//--- return the result
|
|
return(str);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Converts the order filling type to text |
|
|
//+------------------------------------------------------------------+
|
|
string CTrade::FormatOrderTypeFilling(string &str, const uint type) const {
|
|
//--- see the type
|
|
switch(type) {
|
|
case ORDER_FILLING_RETURN:
|
|
str = "return remainder";
|
|
break;
|
|
case ORDER_FILLING_IOC:
|
|
str = "cancel remainder";
|
|
break;
|
|
case ORDER_FILLING_FOK:
|
|
str = "fill or kill";
|
|
break;
|
|
default:
|
|
str = "unknown type filling " + (string)type;
|
|
break;
|
|
}
|
|
//--- return the result
|
|
return(str);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Converts the type of order by expiration to text |
|
|
//+------------------------------------------------------------------+
|
|
string CTrade::FormatOrderTypeTime(string &str, const uint type) const {
|
|
//--- see the type
|
|
switch(type) {
|
|
case ORDER_TIME_GTC:
|
|
str = "gtc";
|
|
break;
|
|
case ORDER_TIME_DAY:
|
|
str = "day";
|
|
break;
|
|
case ORDER_TIME_SPECIFIED:
|
|
str = "specified";
|
|
break;
|
|
case ORDER_TIME_SPECIFIED_DAY:
|
|
str = "specified day";
|
|
break;
|
|
default:
|
|
str = "unknown type time " + (string)type;
|
|
}
|
|
//--- return the result
|
|
return(str);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Converts the order prices to text |
|
|
//+------------------------------------------------------------------+
|
|
string CTrade::FormatOrderPrice(string &str, const double price_order, const double price_trigger, const uint digits) const {
|
|
string price, trigger;
|
|
//--- Is there its trigger price?
|
|
if(price_trigger) {
|
|
price = DoubleToString(price_order, digits);
|
|
trigger = DoubleToString(price_trigger, digits);
|
|
str = StringFormat("%s (%s)", price, trigger);
|
|
} else
|
|
str = DoubleToString(price_order, digits);
|
|
//--- return the result
|
|
return(str);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Converts the parameters of a trade request to text |
|
|
//+------------------------------------------------------------------+
|
|
string CTrade::FormatRequest(string &str, const MqlTradeRequest &request) const {
|
|
string type, price, price_new;
|
|
string tmp_string;
|
|
long tmp_long;
|
|
//--- clean
|
|
str = "";
|
|
//--- set up
|
|
string symbol_name = (request.symbol == NULL) ? _Symbol : request.symbol;
|
|
int digits = _Digits;
|
|
ENUM_SYMBOL_TRADE_EXECUTION trade_execution = 0;
|
|
if(SymbolInfoInteger(symbol_name, SYMBOL_DIGITS, tmp_long))
|
|
digits = (int)tmp_long;
|
|
if(SymbolInfoInteger(symbol_name, SYMBOL_TRADE_EXEMODE, tmp_long))
|
|
trade_execution = (ENUM_SYMBOL_TRADE_EXECUTION)tmp_long;
|
|
//--- see what is wanted
|
|
switch(request.action) {
|
|
//--- instant execution of a deal
|
|
case TRADE_ACTION_DEAL:
|
|
switch(trade_execution) {
|
|
//--- request execution
|
|
case SYMBOL_TRADE_EXECUTION_REQUEST:
|
|
if(IsHedging() && request.position != 0)
|
|
str = StringFormat("request %s %s position #%I64u %s at %s",
|
|
FormatOrderType(type, request.type),
|
|
DoubleToString(request.volume, 2),
|
|
request.position,
|
|
request.symbol,
|
|
DoubleToString(request.price, digits));
|
|
else
|
|
str = StringFormat("request %s %s %s at %s",
|
|
FormatOrderType(type, request.type),
|
|
DoubleToString(request.volume, 2),
|
|
request.symbol,
|
|
DoubleToString(request.price, digits));
|
|
//--- Is there SL or TP?
|
|
if(request.sl != 0.0) {
|
|
tmp_string = StringFormat(" sl: %s", DoubleToString(request.sl, digits));
|
|
str += tmp_string;
|
|
}
|
|
if(request.tp != 0.0) {
|
|
tmp_string = StringFormat(" tp: %s", DoubleToString(request.tp, digits));
|
|
str += tmp_string;
|
|
}
|
|
break;
|
|
//--- instant execution
|
|
case SYMBOL_TRADE_EXECUTION_INSTANT:
|
|
if(IsHedging() && request.position != 0)
|
|
str = StringFormat("instant %s %s position #%I64u %s at %s",
|
|
FormatOrderType(type, request.type),
|
|
DoubleToString(request.volume, 2),
|
|
request.position,
|
|
request.symbol,
|
|
DoubleToString(request.price, digits));
|
|
else
|
|
str = StringFormat("instant %s %s %s at %s",
|
|
FormatOrderType(type, request.type),
|
|
DoubleToString(request.volume, 2),
|
|
request.symbol,
|
|
DoubleToString(request.price, digits));
|
|
//--- Is there SL or TP?
|
|
if(request.sl != 0.0) {
|
|
tmp_string = StringFormat(" sl: %s", DoubleToString(request.sl, digits));
|
|
str += tmp_string;
|
|
}
|
|
if(request.tp != 0.0) {
|
|
tmp_string = StringFormat(" tp: %s", DoubleToString(request.tp, digits));
|
|
str += tmp_string;
|
|
}
|
|
break;
|
|
//--- market execution
|
|
case SYMBOL_TRADE_EXECUTION_MARKET:
|
|
if(IsHedging() && request.position != 0)
|
|
str = StringFormat("market %s %s position #%I64u %s",
|
|
FormatOrderType(type, request.type),
|
|
DoubleToString(request.volume, 2),
|
|
request.position,
|
|
request.symbol);
|
|
else
|
|
str = StringFormat("market %s %s %s",
|
|
FormatOrderType(type, request.type),
|
|
DoubleToString(request.volume, 2),
|
|
request.symbol);
|
|
//--- Is there SL or TP?
|
|
if(request.sl != 0.0) {
|
|
tmp_string = StringFormat(" sl: %s", DoubleToString(request.sl, digits));
|
|
str += tmp_string;
|
|
}
|
|
if(request.tp != 0.0) {
|
|
tmp_string = StringFormat(" tp: %s", DoubleToString(request.tp, digits));
|
|
str += tmp_string;
|
|
}
|
|
break;
|
|
//--- exchange execution
|
|
case SYMBOL_TRADE_EXECUTION_EXCHANGE:
|
|
if(IsHedging() && request.position != 0)
|
|
str = StringFormat("exchange %s %s position #%I64u %s",
|
|
FormatOrderType(type, request.type),
|
|
DoubleToString(request.volume, 2),
|
|
request.position,
|
|
request.symbol);
|
|
else
|
|
str = StringFormat("exchange %s %s %s",
|
|
FormatOrderType(type, request.type),
|
|
DoubleToString(request.volume, 2),
|
|
request.symbol);
|
|
//--- Is there SL or TP?
|
|
if(request.sl != 0.0) {
|
|
tmp_string = StringFormat(" sl: %s", DoubleToString(request.sl, digits));
|
|
str += tmp_string;
|
|
}
|
|
if(request.tp != 0.0) {
|
|
tmp_string = StringFormat(" tp: %s", DoubleToString(request.tp, digits));
|
|
str += tmp_string;
|
|
}
|
|
break;
|
|
}
|
|
//--- end of TRADE_ACTION_DEAL processing
|
|
break;
|
|
|
|
//--- setting a pending order
|
|
case TRADE_ACTION_PENDING:
|
|
str = StringFormat("%s %s %s at %s",
|
|
FormatOrderType(type, request.type),
|
|
DoubleToString(request.volume, 2),
|
|
request.symbol,
|
|
FormatOrderPrice(price, request.price, request.stoplimit, digits));
|
|
//--- Is there SL or TP?
|
|
if(request.sl != 0.0) {
|
|
tmp_string = StringFormat(" sl: %s", DoubleToString(request.sl, digits));
|
|
str += tmp_string;
|
|
}
|
|
if(request.tp != 0.0) {
|
|
tmp_string = StringFormat(" tp: %s", DoubleToString(request.tp, digits));
|
|
str += tmp_string;
|
|
}
|
|
break;
|
|
|
|
//--- Setting SL/TP
|
|
case TRADE_ACTION_SLTP:
|
|
if(IsHedging() && request.position != 0)
|
|
str = StringFormat("modify position #%I64u %s (sl: %s, tp: %s)",
|
|
request.position,
|
|
request.symbol,
|
|
DoubleToString(request.sl, digits),
|
|
DoubleToString(request.tp, digits));
|
|
else
|
|
str = StringFormat("modify %s (sl: %s, tp: %s)",
|
|
request.symbol,
|
|
DoubleToString(request.sl, digits),
|
|
DoubleToString(request.tp, digits));
|
|
break;
|
|
|
|
//--- modifying a pending order
|
|
case TRADE_ACTION_MODIFY:
|
|
str = StringFormat("modify #%I64u at %s (sl: %s tp: %s)",
|
|
request.order,
|
|
FormatOrderPrice(price_new, request.price, request.stoplimit, digits),
|
|
DoubleToString(request.sl, digits),
|
|
DoubleToString(request.tp, digits));
|
|
break;
|
|
|
|
//--- deleting a pending order
|
|
case TRADE_ACTION_REMOVE:
|
|
str = StringFormat("cancel #%I64u", request.order);
|
|
break;
|
|
|
|
//--- close by
|
|
case TRADE_ACTION_CLOSE_BY:
|
|
if(IsHedging() && request.position != 0)
|
|
str = StringFormat("close position #%I64u by #%I64u", request.position, request.position_by);
|
|
else
|
|
str = StringFormat("wrong action close by (#%I64u by #%I64u)", request.position, request.position_by);
|
|
break;
|
|
|
|
default:
|
|
str = "unknown action " + (string)request.action;
|
|
break;
|
|
}
|
|
//--- return the result
|
|
return(str);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Converts the result of a request to text |
|
|
//+------------------------------------------------------------------+
|
|
string CTrade::FormatRequestResult(string &str, const MqlTradeRequest &request, const MqlTradeResult &result) const {
|
|
//--- set up
|
|
string symbol_name = (request.symbol == NULL) ? _Symbol : request.symbol;
|
|
int digits = _Digits;
|
|
long tmp_long;
|
|
ENUM_SYMBOL_TRADE_EXECUTION trade_execution = 0;
|
|
if(SymbolInfoInteger(symbol_name, SYMBOL_DIGITS, tmp_long))
|
|
digits = (int)tmp_long;
|
|
if(SymbolInfoInteger(symbol_name, SYMBOL_TRADE_EXEMODE, tmp_long))
|
|
trade_execution = (ENUM_SYMBOL_TRADE_EXECUTION)tmp_long;
|
|
//--- see the response code
|
|
switch(result.retcode) {
|
|
case TRADE_RETCODE_REQUOTE:
|
|
str = StringFormat("requote (%s/%s)",
|
|
DoubleToString(result.bid, digits),
|
|
DoubleToString(result.ask, digits));
|
|
break;
|
|
|
|
case TRADE_RETCODE_DONE:
|
|
if(request.action == TRADE_ACTION_DEAL &&
|
|
(trade_execution == SYMBOL_TRADE_EXECUTION_REQUEST ||
|
|
trade_execution == SYMBOL_TRADE_EXECUTION_INSTANT ||
|
|
trade_execution == SYMBOL_TRADE_EXECUTION_MARKET))
|
|
str = StringFormat("done at %s", DoubleToString(result.price, digits));
|
|
else
|
|
str = "done";
|
|
break;
|
|
|
|
case TRADE_RETCODE_DONE_PARTIAL:
|
|
if(request.action == TRADE_ACTION_DEAL &&
|
|
(trade_execution == SYMBOL_TRADE_EXECUTION_REQUEST ||
|
|
trade_execution == SYMBOL_TRADE_EXECUTION_INSTANT ||
|
|
trade_execution == SYMBOL_TRADE_EXECUTION_MARKET))
|
|
str = StringFormat("done partially %s at %s",
|
|
DoubleToString(result.volume, 2),
|
|
DoubleToString(result.price, digits));
|
|
else
|
|
str = StringFormat("done partially %s",
|
|
DoubleToString(result.volume, 2));
|
|
break;
|
|
|
|
case TRADE_RETCODE_REJECT:
|
|
str = "rejected";
|
|
break;
|
|
case TRADE_RETCODE_CANCEL:
|
|
str = "canceled";
|
|
break;
|
|
case TRADE_RETCODE_PLACED:
|
|
str = "placed";
|
|
break;
|
|
case TRADE_RETCODE_ERROR:
|
|
str = "common error";
|
|
break;
|
|
case TRADE_RETCODE_TIMEOUT:
|
|
str = "timeout";
|
|
break;
|
|
case TRADE_RETCODE_INVALID:
|
|
str = "invalid request";
|
|
break;
|
|
case TRADE_RETCODE_INVALID_VOLUME:
|
|
str = "invalid volume";
|
|
break;
|
|
case TRADE_RETCODE_INVALID_PRICE:
|
|
str = "invalid price";
|
|
break;
|
|
case TRADE_RETCODE_INVALID_STOPS:
|
|
str = "invalid stops";
|
|
break;
|
|
case TRADE_RETCODE_TRADE_DISABLED:
|
|
str = "trade disabled";
|
|
break;
|
|
case TRADE_RETCODE_MARKET_CLOSED:
|
|
str = "market closed";
|
|
break;
|
|
case TRADE_RETCODE_NO_MONEY:
|
|
str = "not enough money";
|
|
break;
|
|
case TRADE_RETCODE_PRICE_CHANGED:
|
|
str = "price changed";
|
|
break;
|
|
case TRADE_RETCODE_PRICE_OFF:
|
|
str = "off quotes";
|
|
break;
|
|
case TRADE_RETCODE_INVALID_EXPIRATION:
|
|
str = "invalid expiration";
|
|
break;
|
|
case TRADE_RETCODE_ORDER_CHANGED:
|
|
str = "order changed";
|
|
break;
|
|
case TRADE_RETCODE_TOO_MANY_REQUESTS:
|
|
str = "too many requests";
|
|
break;
|
|
case TRADE_RETCODE_NO_CHANGES:
|
|
str = "no changes";
|
|
break;
|
|
case TRADE_RETCODE_SERVER_DISABLES_AT:
|
|
str = "auto trading disabled by server";
|
|
break;
|
|
case TRADE_RETCODE_CLIENT_DISABLES_AT:
|
|
str = "auto trading disabled by client";
|
|
break;
|
|
case TRADE_RETCODE_LOCKED:
|
|
str = "locked";
|
|
break;
|
|
case TRADE_RETCODE_FROZEN:
|
|
str = "frozen";
|
|
break;
|
|
case TRADE_RETCODE_INVALID_FILL:
|
|
str = "invalid fill";
|
|
break;
|
|
case TRADE_RETCODE_CONNECTION:
|
|
str = "no connection";
|
|
break;
|
|
case TRADE_RETCODE_ONLY_REAL:
|
|
str = "only real";
|
|
break;
|
|
case TRADE_RETCODE_LIMIT_ORDERS:
|
|
str = "limit orders";
|
|
break;
|
|
case TRADE_RETCODE_LIMIT_VOLUME:
|
|
str = "limit volume";
|
|
break;
|
|
case TRADE_RETCODE_POSITION_CLOSED:
|
|
str = "position closed";
|
|
break;
|
|
case TRADE_RETCODE_INVALID_ORDER:
|
|
str = "invalid order";
|
|
break;
|
|
case TRADE_RETCODE_CLOSE_ORDER_EXIST:
|
|
str = "close order already exists";
|
|
break;
|
|
case TRADE_RETCODE_LIMIT_POSITIONS:
|
|
str = "limit positions";
|
|
break;
|
|
default:
|
|
str = "unknown retcode " + (string)result.retcode;
|
|
}
|
|
//--- return the result
|
|
return(str);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
double CTrade::CheckVolume(const string symbol, double volume, double price, ENUM_ORDER_TYPE order_type) {
|
|
//--- check
|
|
if(order_type != ORDER_TYPE_BUY && order_type != ORDER_TYPE_SELL)
|
|
return(0.0);
|
|
double free_margin = AccountInfoDouble(ACCOUNT_FREEMARGIN);
|
|
if(free_margin <= 0.0)
|
|
return(0.0);
|
|
//--- clean
|
|
ClearStructures();
|
|
//--- setting request
|
|
m_request.action = TRADE_ACTION_DEAL;
|
|
m_request.symbol = symbol;
|
|
m_request.volume = volume;
|
|
m_request.type = order_type;
|
|
m_request.price = price;
|
|
//--- action and return the result
|
|
if(!OrderCheck(m_request, m_check_result) && m_check_result.margin_free < 0.0) {
|
|
double coeff = free_margin / (free_margin - m_check_result.margin_free);
|
|
double lots = NormalizeDouble(volume * coeff, 2);
|
|
if(lots < volume) {
|
|
//--- normalize and check limits
|
|
double stepvol = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
|
|
if(stepvol > 0.0)
|
|
volume = stepvol * (MathFloor(lots / stepvol) - 1);
|
|
//---
|
|
double minvol = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
|
|
if(volume < minvol)
|
|
volume = 0.0;
|
|
}
|
|
}
|
|
return(volume);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks if the m_request structure is filled correctly |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::OrderCheck(const MqlTradeRequest &request, MqlTradeCheckResult &check_result) {
|
|
//--- action and return the result
|
|
return true;
|
|
//return(::OrderCheck(request, check_result));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Set order filling type according to symbol filling mode |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::SetTypeFillingBySymbol(const string symbol) {
|
|
//--- get possible filling policy types by symbol
|
|
uint filling = (uint)SymbolInfoInteger(symbol, SYMBOL_FILLING_MODE);
|
|
if((filling & SYMBOL_FILLING_FOK) == SYMBOL_FILLING_FOK) {
|
|
m_type_filling = ORDER_FILLING_FOK;
|
|
return(true);
|
|
}
|
|
if((filling & SYMBOL_FILLING_IOC) == SYMBOL_FILLING_IOC) {
|
|
m_type_filling = ORDER_FILLING_IOC;
|
|
return(true);
|
|
}
|
|
//---
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks and corrects type of filling policy |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::FillingCheck(const string symbol) {
|
|
//--- get execution mode of orders by symbol
|
|
ENUM_SYMBOL_TRADE_EXECUTION exec = (ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(symbol, SYMBOL_TRADE_EXEMODE);
|
|
//--- check execution mode
|
|
if(exec == SYMBOL_TRADE_EXECUTION_REQUEST || exec == SYMBOL_TRADE_EXECUTION_INSTANT) {
|
|
//--- neccessary filling type will be placed automatically
|
|
return(true);
|
|
}
|
|
//--- get possible filling policy types by symbol
|
|
// uint filling = (uint)SymbolInfoInteger(symbol, SYMBOL_FILLING_MODE);
|
|
//--- check execution mode again
|
|
if(exec == SYMBOL_TRADE_EXECUTION_MARKET) {
|
|
//--- for the MARKET execution mode
|
|
//--- analyze order
|
|
/*
|
|
if(m_request.action != TRADE_ACTION_PENDING) {
|
|
//--- in case of instant execution order
|
|
//--- if the required filling policy is supported, add it to the request
|
|
if((filling & SYMBOL_FILLING_FOK) == SYMBOL_FILLING_FOK) {
|
|
m_type_filling = ORDER_FILLING_FOK;
|
|
m_request.type_filling = m_type_filling;
|
|
return(true);
|
|
}
|
|
if((filling & SYMBOL_FILLING_IOC) == SYMBOL_FILLING_IOC) {
|
|
m_type_filling = ORDER_FILLING_IOC;
|
|
m_request.type_filling = m_type_filling;
|
|
return(true);
|
|
}
|
|
//--- wrong filling policy, set error code
|
|
m_result.retcode = TRADE_RETCODE_INVALID_FILL;
|
|
return(false);
|
|
}
|
|
*/
|
|
return(true);
|
|
}
|
|
/*
|
|
//--- EXCHANGE execution mode
|
|
switch(m_type_filling) {
|
|
case ORDER_FILLING_FOK:
|
|
//--- analyze order
|
|
if(m_request.action == TRADE_ACTION_PENDING) {
|
|
//--- in case of pending order
|
|
//--- add the expiration mode to the request
|
|
if(!ExpirationCheck(symbol))
|
|
m_request.type_time = ORDER_TIME_DAY;
|
|
//--- stop order?
|
|
if(m_request.type == ORDER_TYPE_BUY_STOP || m_request.type == ORDER_TYPE_SELL_STOP ||
|
|
m_request.type == ORDER_TYPE_BUY_LIMIT || m_request.type == ORDER_TYPE_SELL_LIMIT) {
|
|
//--- in case of stop order
|
|
//--- add the corresponding filling policy to the request
|
|
m_request.type_filling = ORDER_FILLING_RETURN;
|
|
return(true);
|
|
}
|
|
}
|
|
//--- in case of limit order or instant execution order
|
|
//--- if the required filling policy is supported, add it to the request
|
|
if((filling & SYMBOL_FILLING_FOK) == SYMBOL_FILLING_FOK) {
|
|
m_request.type_filling = m_type_filling;
|
|
return(true);
|
|
}
|
|
//--- wrong filling policy, set error code
|
|
m_result.retcode = TRADE_RETCODE_INVALID_FILL;
|
|
return(false);
|
|
case ORDER_FILLING_IOC:
|
|
//--- analyze order
|
|
if(m_request.action == TRADE_ACTION_PENDING) {
|
|
//--- in case of pending order
|
|
//--- add the expiration mode to the request
|
|
if(!ExpirationCheck(symbol))
|
|
m_request.type_time = ORDER_TIME_DAY;
|
|
//--- stop order?
|
|
if(m_request.type == ORDER_TYPE_BUY_STOP || m_request.type == ORDER_TYPE_SELL_STOP ||
|
|
m_request.type == ORDER_TYPE_BUY_LIMIT || m_request.type == ORDER_TYPE_SELL_LIMIT) {
|
|
//--- in case of stop order
|
|
//--- add the corresponding filling policy to the request
|
|
m_request.type_filling = ORDER_FILLING_RETURN;
|
|
return(true);
|
|
}
|
|
}
|
|
//--- in case of limit order or instant execution order
|
|
//--- if the required filling policy is supported, add it to the request
|
|
if((filling & SYMBOL_FILLING_IOC) == SYMBOL_FILLING_IOC) {
|
|
m_request.type_filling = m_type_filling;
|
|
return(true);
|
|
}
|
|
//--- wrong filling policy, set error code
|
|
m_result.retcode = TRADE_RETCODE_INVALID_FILL;
|
|
return(false);
|
|
case ORDER_FILLING_RETURN:
|
|
//--- add filling policy to the request
|
|
m_request.type_filling = m_type_filling;
|
|
return(true);
|
|
}
|
|
*/
|
|
//--- unknown execution mode, set error code
|
|
m_result.retcode = TRADE_RETCODE_ERROR;
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Check expiration type of pending order |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::ExpirationCheck(const string symbol) {
|
|
/*
|
|
//--- check symbol
|
|
string symbol_name = (symbol == NULL) ? _Symbol : symbol;
|
|
//--- get flags
|
|
long tmp_long;
|
|
int flags = 0;
|
|
if(SymbolInfoInteger(symbol_name, SYMBOL_EXPIRATION_MODE, tmp_long))
|
|
flags = (int)tmp_long;
|
|
//--- check type
|
|
switch(m_request.type_time) {
|
|
case ORDER_TIME_GTC:
|
|
if((flags & SYMBOL_EXPIRATION_GTC) != 0)
|
|
return(true);
|
|
break;
|
|
case ORDER_TIME_DAY:
|
|
if((flags & SYMBOL_EXPIRATION_DAY) != 0)
|
|
return(true);
|
|
break;
|
|
case ORDER_TIME_SPECIFIED:
|
|
if((flags & SYMBOL_EXPIRATION_SPECIFIED) != 0)
|
|
return(true);
|
|
break;
|
|
case ORDER_TIME_SPECIFIED_DAY:
|
|
if((flags & SYMBOL_EXPIRATION_SPECIFIED_DAY) != 0)
|
|
return(true);
|
|
break;
|
|
default:
|
|
Print(__FUNCTION__ + ": Unknown expiration type");
|
|
}
|
|
//--- failed
|
|
return(false);
|
|
*/
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks order |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::OrderTypeCheck(const string symbol) {
|
|
bool res = false;
|
|
//--- check symbol
|
|
string symbol_name = (symbol == NULL) ? _Symbol : symbol;
|
|
//--- get flags of allowed trade orders
|
|
long tmp_long;
|
|
int flags = 0;
|
|
ENUM_SYMBOL_TRADE_MODE trade_mode = SYMBOL_TRADE_MODE_DISABLED;
|
|
if(SymbolInfoInteger(symbol_name, SYMBOL_TRADE_MODE, tmp_long))
|
|
trade_mode = (ENUM_SYMBOL_TRADE_MODE) tmp_long;
|
|
//--- depending on the type of order in request
|
|
if(trade_mode == SYMBOL_TRADE_MODE_FULL) {
|
|
res = true;
|
|
} else {
|
|
switch(m_request.type) {
|
|
case ORDER_TYPE_SELL:
|
|
case ORDER_TYPE_SELL_LIMIT:
|
|
case ORDER_TYPE_BUY_STOP:
|
|
//--- check possibility of execution
|
|
if(trade_mode == SYMBOL_TRADE_MODE_LONGONLY) {
|
|
m_result.retcode = TRADE_RETCODE_INVALID_ORDER;
|
|
res = false;
|
|
}
|
|
break;
|
|
}
|
|
//--- check res
|
|
if(res) {
|
|
//--- trading order is valid
|
|
//--- check if we need and able to set protective orders
|
|
/*
|
|
if(m_request.sl != 0.0 || m_request.tp != 0.0) {
|
|
if((flags & SYMBOL_ORDER_SL) == 0)
|
|
m_request.sl = 0.0;
|
|
if((flags & SYMBOL_ORDER_TP) == 0)
|
|
m_request.tp = 0.0;
|
|
}
|
|
*/
|
|
} else {
|
|
//--- trading order is not valid
|
|
//--- set error
|
|
m_result.retcode = TRADE_RETCODE_INVALID_ORDER;
|
|
|
|
}
|
|
}
|
|
if(!res) {
|
|
Print(__FUNCTION__ + ": Invalid order type");
|
|
}
|
|
|
|
//--- result
|
|
return(res);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Send order |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::OrderSend(const MqlTradeRequest &request, MqlTradeResult &result) {
|
|
bool res = false;
|
|
int ticket;
|
|
string action = "";
|
|
string fmt = "";
|
|
ResetLastError();
|
|
|
|
result.retcode = TRADE_RETCODE_ERROR;
|
|
//--- action
|
|
switch(request.action) {
|
|
case TRADE_ACTION_DEAL:
|
|
ticket = ::OrderSend(request.symbol, request.type, request.volume, request.price, (int) request.deviation, request.sl, request.tp, request.comment, (int)request.magic, request.expiration);
|
|
if(ticket != -1) {
|
|
if(OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES)) {
|
|
result.retcode = TRADE_RETCODE_DONE;
|
|
result.deal = ticket;
|
|
result.price = OrderOpenPrice();
|
|
result.comment = OrderComment();
|
|
res = true;
|
|
}
|
|
}
|
|
break;
|
|
case TRADE_ACTION_PENDING:
|
|
ticket = ::OrderSend(request.symbol, request.type, request.volume, request.price, (int) request.deviation, request.sl, request.tp, request.comment, (int)request.magic, request.expiration);
|
|
if(ticket != -1) {
|
|
if(OrderSelect(ticket, SELECT_BY_TICKET, MODE_TRADES)) {
|
|
result.retcode = TRADE_RETCODE_PLACED;
|
|
result.order = ticket;
|
|
result.price = OrderOpenPrice();
|
|
result.comment = OrderComment();
|
|
res = true;
|
|
}
|
|
}
|
|
break;
|
|
case TRADE_ACTION_REMOVE:
|
|
res = ::OrderDelete((int) request.order);
|
|
break;
|
|
case TRADE_ACTION_CLOSE_BY:
|
|
res = ::OrderClose((int) m_request.position, m_request.volume, m_request.price, (int) m_request.deviation);
|
|
break;
|
|
case TRADE_ACTION_SLTP:
|
|
res = ::OrderModify((int) request.position, request.price, request.sl, request.tp, 0);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
//--- check
|
|
if(res) {
|
|
m_result.retcode = TRADE_RETCODE_DONE;
|
|
if(m_log_level > LOG_LEVEL_ERRORS)
|
|
PrintFormat(__FUNCTION__ + ": %s [%s]", FormatRequest(action, request), FormatRequestResult(fmt, request, result));
|
|
} else {
|
|
m_result.retcode = ErrorToRetcode(_LastError);
|
|
if(m_log_level > LOG_LEVEL_NO)
|
|
PrintFormat(__FUNCTION__ + ": %s [%s]", FormatRequest(action, request), FormatRequestResult(fmt, request, result));
|
|
}
|
|
//--- return the result
|
|
return(res);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Position select depending on netting or hedging |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::SelectPosition(const string symbol) {
|
|
bool res = false;
|
|
//---
|
|
if(IsHedging()) {
|
|
uint total = PositionsTotal();
|
|
for(uint i = 0; i < total; i++) {
|
|
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
|
|
if(OrderType() < 2) {
|
|
if(OrderSymbol() == symbol && m_magic == OrderMagicNumber()) {
|
|
res = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//---
|
|
return(res);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CTrade::PositionSelectByTicket(const ulong ticket) {
|
|
return OrderSelect((int)ticket, SELECT_BY_TICKET, MODE_TRADES);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
ENUM_TRADE_RETCODE CTrade::ErrorToRetcode(int lastError) {
|
|
switch(lastError) {
|
|
case ERR_NO_ERROR:
|
|
return TRADE_RETCODE_DONE;
|
|
case ERR_NO_RESULT:
|
|
return TRADE_RETCODE_DONE_PARTIAL;
|
|
case ERR_COMMON_ERROR:
|
|
return TRADE_RETCODE_ERROR;
|
|
case ERR_INVALID_TRADE_PARAMETERS:
|
|
return TRADE_RETCODE_INVALID;
|
|
case ERR_SERVER_BUSY:
|
|
return TRADE_RETCODE_REJECT;
|
|
case ERR_OLD_VERSION:
|
|
return TRADE_RETCODE_ERROR;
|
|
case ERR_NO_CONNECTION:
|
|
return TRADE_RETCODE_CONNECTION;
|
|
case ERR_NOT_ENOUGH_RIGHTS:
|
|
return TRADE_RETCODE_REJECT;
|
|
case ERR_TOO_FREQUENT_REQUESTS:
|
|
return TRADE_RETCODE_TOO_MANY_REQUESTS;
|
|
case ERR_MALFUNCTIONAL_TRADE:
|
|
return TRADE_RETCODE_REJECT;
|
|
case ERR_ACCOUNT_DISABLED:
|
|
return TRADE_RETCODE_ERROR;
|
|
case ERR_INVALID_ACCOUNT:
|
|
return TRADE_RETCODE_ERROR;
|
|
case ERR_TRADE_TIMEOUT:
|
|
return TRADE_RETCODE_TIMEOUT;
|
|
case ERR_INVALID_PRICE:
|
|
return TRADE_RETCODE_INVALID_PRICE;
|
|
case ERR_INVALID_STOPS:
|
|
return TRADE_RETCODE_INVALID_STOPS;
|
|
case ERR_INVALID_TRADE_VOLUME:
|
|
return TRADE_RETCODE_INVALID_VOLUME;
|
|
case ERR_MARKET_CLOSED:
|
|
return TRADE_RETCODE_MARKET_CLOSED;
|
|
case ERR_TRADE_DISABLED:
|
|
return TRADE_RETCODE_TRADE_DISABLED;
|
|
case ERR_NOT_ENOUGH_MONEY:
|
|
return TRADE_RETCODE_NO_MONEY;
|
|
case ERR_PRICE_CHANGED:
|
|
return TRADE_RETCODE_PRICE_CHANGED;
|
|
case ERR_OFF_QUOTES:
|
|
return TRADE_RETCODE_PRICE_OFF;
|
|
case ERR_BROKER_BUSY:
|
|
return TRADE_RETCODE_REJECT;
|
|
case ERR_REQUOTE:
|
|
return TRADE_RETCODE_REQUOTE;
|
|
case ERR_ORDER_LOCKED:
|
|
return TRADE_RETCODE_LOCKED;
|
|
case ERR_LONG_POSITIONS_ONLY_ALLOWED:
|
|
return TRADE_RETCODE_LONG_ONLY;
|
|
case ERR_TOO_MANY_REQUESTS:
|
|
return TRADE_RETCODE_TOO_MANY_REQUESTS;
|
|
case ERR_TRADE_MODIFY_DENIED:
|
|
return TRADE_RETCODE_FROZEN;
|
|
case ERR_TRADE_CONTEXT_BUSY:
|
|
return TRADE_RETCODE_REJECT;
|
|
case ERR_TRADE_EXPIRATION_DENIED:
|
|
return TRADE_RETCODE_INVALID_EXPIRATION;
|
|
case ERR_TRADE_HEDGE_PROHIBITED:
|
|
return TRADE_RETCODE_HEDGE_PROHIBITED;
|
|
case ERR_TRADE_PROHIBITED_BY_FIFO:
|
|
return TRADE_RETCODE_FIFO_CLOSE;
|
|
|
|
default:
|
|
return TRADE_RETCODE_DONE;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|