BeazCode/Include/MQL4/Trade/Trade.mqh
super.admin a992b4c1cb convert
2025-05-30 14:43:23 +02:00

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;
}
}
//+------------------------------------------------------------------+