class MT4_ORDER
Структура для хранения данных ордера или позиции: тикет, тип, объём, символ, цены открытия и закрытия, стоп-лосс, тейк-профит, комиссия, своп, прибыль и сопутствующая информация.
Сообщите нам, если считаете, что материал размещён без разрешения.
Обращение по товару «tradefunctions.mqh» отправится на рассмотрение. Укажите контакт для ответа и опишите причину.
Библиотека исходного кода для MetaTrader 5, обеспечивающая совместимость с торговыми функциями MetaTrader 4. Предоставляет структуры и классы для хранения данных позиций и ордеров, доступа к истории сделок, а также обёртки над торговыми API MQL5 для выполнения стандартных торговых операций — открытия, закрытия, модификации и удаления позиций и отложенных ордеров. Библиотека работает как include-файл и подключается к экспертам, индикаторам или скриптам для упрощения миграции MT4-логики на платформу MQL5.
Классов: 3 · методов: 25 · конструкторов: 0 · деструкторов: 0 · отдельных функций: 0
class MT4_ORDER
Структура для хранения данных ордера или позиции: тикет, тип, объём, символ, цены открытия и закрытия, стоп-лосс, тейк-профит, комиссия, своп, прибыль и сопутствующая информация.
class MT4HISTORY
Класс для управления доступом к истории сделок и ордеров, включая кэширование и поиск связанных тикетов.
class MT4ORDERS
Класс, предоставляющий набор методов для выполнения торговых операций, совместимых с интерфейсом MetaTrader 4, поверх API MQL5.
int GetDigits(double Price)
Определяет количество знаков после запятой для указанной цены.
string DoubleToString(const double Num, const int digits)
Преобразует числовое значение в строку с учётом количества десятичных знаков цены инструмента.
string TimeToString(const long time)
Форматирует временную метку в миллисекундах в читаемую строку представления времени.
MT4_ORDER GetPositionData(void)
Заполняет структуру MT4_ORDER актуальными данными текущей открытой позиции.
MT4_ORDER GetOrderData(void)
Заполняет структуру MT4_ORDER данными текущего выбранного отложенного ордера.
string ToString(void)
Формирует строковое представление данных ордера или позиции для вывода в журнал.
bool IsMT4Deal(const ulong &Ticket)
Проверяет, является ли указанный тикет тикетом сделки закрытия позиции (выходная или закрывающая сделка).
bool IsMT4Order(const ulong &Ticket)
Проверяет, является ли указанный тикет тикетом ордера, который ещё не исполнен полностью или представляет собой позицию.
ulong GetPositionDealIn(const ulong PositionIdentifier)
Возвращает тикет сделки входа (открытия) для указанной позиции или текущей позиции.
ulong GetPositionDealOut(const ulong PositionIdentifier)
Возвращает тикет сделки выхода (закрытия) для указанной позиции.
int GetAmount(void)
Возвращает общее количество сохранённых в истории тикетов ордеров и сделок.
long operator[](const uint &Pos)
Предоставляет доступ к тикету истории по индексу, автоматически обновляя кэш при необходимости.
bool OrderClose(const long Ticket, const double dLots, const double Price, const int SlipPage, const color Arrow_Color, const string comment)
Закрывает позицию по указанному тикету, объёму, цене и с допустимым проскальзыванием.
bool OrderModify(const long Ticket, const double Price, const double SLxx, const double TPxx, const datetime Expiration, const color Arrow_Color)
Изменяет цену, стоп-лосс, тейк-профит и время истечения отложенного ордера или позиции.
bool OrderCloseBy(const long Ticket, const long Opposite, const color Arrow_Color)
Закрывает позицию встречной противоположной позицией через механизм CloseBy.
bool OrderDelete(const long Ticket, const color Arrow_Color)
Удаляет отложенный ордер по указанному тикету.
void OrderPrint(void)
Выводит данные текущего выбранного ордера или позиции в журнал терминала.
double OrderLots(const bool Value)
Возвращает объём текущей позиции с учётом частично исполненных отложенных ордеров.
bool OrderSelect(const long Index, const int Select, const int Pool)
Выбирает ордер или позицию по индексу, типу выбора и пулу для последующего доступа к данным.
TICKET_TYPE OrderSend(const string Symb, const int Type, const double dVolume, const double Price, const int SlipPage, const double SLxx, const double TPxx, const string comment, const MAGIC_TYPE magic, const datetime dExpiration, color arrow_color)
Отправляет торговый запрос на открытие или размещение отложенного ордера и возвращает тикет ордера или позиции.
uint OrderCloseAsync(const long Ticket, const double dLots, const double Price, const int SlipPage, const color Arrow_Color)
Асинхронно закрывает позицию и возвращает идентификатор асинхронного запроса.
uint OrderModifyAsync(const long Ticket, const double Price, const double SLxx, const double TPxx, const datetime Expiration, const color Arrow_Color)
Асинхронно модифицирует ордер или позицию и возвращает идентификатор асинхронного запроса.
uint OrderDeleteAsync(const long Ticket, const color Arrow_Color)
Асинхронно удаляет отложенный ордер и возвращает идентификатор асинхронного запроса.
uint OrderSendAsync(const string Symb, const int Type, const double dVolume, const double Price, const int SlipPage, const double SLxx, const double TPxx, const string comment, const MAGIC_TYPE magic, const datetime dExpiration, color arrow_color)
Асинхронно отправляет торговый запрос на открытие или размещение ордера и возвращает идентификатор запроса.
TICKET_TYPE OrderTicketID(void)
Возвращает тикет текущей позиции или ордера, выбранного через OrderSelect.
#ifdef __MQL5__
#ifndef __MT4ORDERS__
#ifdef MT4ORDERS_BYPASS_MAXTIME
#include <fxsaber\TradesID\ByPass.mqh> //
#endif // #ifdef MT4ORDERS_BYPASS_MAXTIME
#ifdef MT4ORDERS_BENCHMARK_MINTIME
#include <fxsaber\Benchmark\Benchmark.mqh> //
#define _B2(A) _B(A, MT4ORDERS_BENCHMARK_MINTIME)
#define _B3(A) _B(A, 1)
#define _BV2(A) _BV(A, MT4ORDERS_BENCHMARK_MINTIME)
#else // MT4ORDERS_BENCHMARK_MINTIME
#define _B2(A) (A)
#define _B3(A) (A)
#define _BV2(A) { A; }
#endif // MT4ORDERS_BENCHMARK_MINTIME
#define __MT4ORDERS__ "2021.06.01"
#define MT4ORDERS_SLTP_OLD // Enabling the old mechanism of identifying the SL/TP of closed positions via OrderClose
#ifdef MT4_TICKET_TYPE
#define TICKET_TYPE int
#define MAGIC_TYPE int
#undef MT4_TICKET_TYPE
#else // MT4_TICKET_TYPE
#define TICKET_TYPE long // Negative values are also needed for OrderSelectByTicket.
#define MAGIC_TYPE long
#endif // MT4_TICKET_TYPE
struct MT4_ORDER
{
long Ticket;
int Type;
long TicketOpen;
long TicketID;
double Lots;
string Symbol;
string Comment;
double OpenPriceRequest;
double OpenPrice;
long OpenTimeMsc;
datetime OpenTime;
ENUM_DEAL_REASON OpenReason;
double StopLoss;
double TakeProfit;
double ClosePriceRequest;
double ClosePrice;
long CloseTimeMsc;
datetime CloseTime;
ENUM_DEAL_REASON CloseReason;
ENUM_ORDER_STATE State;
datetime Expiration;
long MagicNumber;
double Profit;
double Commission;
double Swap;
#define POSITION_SELECT (-1)
#define ORDER_SELECT (-2)
static int GetDigits( double Price )
{
int Res = 0;
while ((bool)(Price = ::NormalizeDouble(Price - (int)Price, 8)))
{
Price *= 10;
Res++;
}
return(Res);
}
static string DoubleToString( const double Num, const int digits )
{
return(::DoubleToString(Num, ::MathMax(digits, MT4_ORDER::GetDigits(Num))));
}
static string TimeToString( const long time )
{
return((string)(datetime)(time / 1000) + "." + ::IntegerToString(time % 1000, 3, '0'));
}
static const MT4_ORDER GetPositionData( void )
{
MT4_ORDER Res = {};
Res.Ticket = ::PositionGetInteger(POSITION_TICKET);
Res.Type = (int)::PositionGetInteger(POSITION_TYPE);
Res.Lots = ::PositionGetDouble(POSITION_VOLUME);
Res.Symbol = ::PositionGetString(POSITION_SYMBOL);
Res.OpenPrice = ::PositionGetDouble(POSITION_PRICE_OPEN);
Res.OpenTimeMsc = (datetime)::PositionGetInteger(POSITION_TIME_MSC);
Res.StopLoss = ::PositionGetDouble(POSITION_SL);
Res.TakeProfit = ::PositionGetDouble(POSITION_TP);
Res.ClosePrice = ::PositionGetDouble(POSITION_PRICE_CURRENT);
Res.CloseTimeMsc = 0;
Res.Expiration = 0;
Res.MagicNumber = ::PositionGetInteger(POSITION_MAGIC);
Res.Profit = ::PositionGetDouble(POSITION_PROFIT);
Res.Swap = ::PositionGetDouble(POSITION_SWAP);
return(Res);
}
static const MT4_ORDER GetOrderData( void )
{
MT4_ORDER Res = {};
Res.Ticket = ::OrderGetInteger(ORDER_TICKET);
Res.Type = (int)::OrderGetInteger(ORDER_TYPE);
Res.Lots = ::OrderGetDouble(ORDER_VOLUME_CURRENT);
Res.Symbol = ::OrderGetString(ORDER_SYMBOL);
Res.Comment = ::OrderGetString(ORDER_COMMENT);
Res.OpenPrice = ::OrderGetDouble(ORDER_PRICE_OPEN);
Res.OpenTimeMsc = (datetime)::OrderGetInteger(ORDER_TIME_SETUP_MSC);
Res.StopLoss = ::OrderGetDouble(ORDER_SL);
Res.TakeProfit = ::OrderGetDouble(ORDER_TP);
Res.ClosePrice = ::OrderGetDouble(ORDER_PRICE_CURRENT);
Res.CloseTimeMsc = 0; // (datetime)::OrderGetInteger(ORDER_TIME_DONE)
Res.Expiration = (datetime)::OrderGetInteger(ORDER_TIME_EXPIRATION);
Res.MagicNumber = ::OrderGetInteger(ORDER_MAGIC);
Res.Profit = 0;
Res.Commission = 0;
Res.Swap = 0;
if (!Res.OpenPrice)
Res.OpenPrice = Res.ClosePrice;
return(Res);
}
string ToString( void ) const
{
static const string Types[] = {"buy", "sell", "buy limit", "sell limit", "buy stop", "sell stop", "balance"};
const int digits = (int)::SymbolInfoInteger(this.Symbol, SYMBOL_DIGITS);
MT4_ORDER TmpOrder = {};
if (this.Ticket == POSITION_SELECT)
{
TmpOrder = MT4_ORDER::GetPositionData();
TmpOrder.Comment = this.Comment;
TmpOrder.Commission = this.Commission;
}
else if (this.Ticket == ORDER_SELECT)
TmpOrder = MT4_ORDER::GetOrderData();
return(((this.Ticket == POSITION_SELECT) || (this.Ticket == ORDER_SELECT)) ? TmpOrder.ToString() :
("#" + (string)this.Ticket + " " +
MT4_ORDER::TimeToString(this.OpenTimeMsc) + " " +
((this.Type < ::ArraySize(Types)) ? Types[this.Type] : "unknown") + " " +
MT4_ORDER::DoubleToString(this.Lots, 2) + " " +
(::StringLen(this.Symbol) ? this.Symbol + " " : NULL) +
MT4_ORDER::DoubleToString(this.OpenPrice, digits) + " " +
MT4_ORDER::DoubleToString(this.StopLoss, digits) + " " +
MT4_ORDER::DoubleToString(this.TakeProfit, digits) + " " +
((this.CloseTimeMsc > 0) ? (MT4_ORDER::TimeToString(this.CloseTimeMsc) + " ") : "") +
MT4_ORDER::DoubleToString(this.ClosePrice, digits) + " " +
MT4_ORDER::DoubleToString(::NormalizeDouble(this.Commission, 3), 2) + " " + // Больше трех цифр после запятой не выводим.
MT4_ORDER::DoubleToString(this.Swap, 2) + " " +
MT4_ORDER::DoubleToString(this.Profit, 2) + " " +
((this.Comment == "") ? "" : (this.Comment + " ")) +
(string)this.MagicNumber +
(((this.Expiration > 0) ? (" expiration " + (string)this.Expiration): ""))));
}
};
#define RESERVE_SIZE 1000
#define DAY (24 * 3600)
#define HISTORY_PAUSE (MT4HISTORY::IsTester ? 0 : 5)
#define END_TIME D'31.12.3000 23:59:59'
#define THOUSAND 1000
#define LASTTIME(A) \
if (Time##A >= LastTimeMsc) \
{ \
const datetime TmpTime = (datetime)(Time##A / THOUSAND); \
\
if (TmpTime > this.LastTime) \
{ \
this.LastTotalOrders = 0; \
this.LastTotalDeals = 0; \
\
this.LastTime = TmpTime; \
LastTimeMsc = this.LastTime * THOUSAND; \
} \
\
this.LastTotal##A##s++; \
}
#ifndef MT4ORDERS_FASTHISTORY_OFF
#include <Generic\HashMap.mqh>
#endif // MT4ORDERS_FASTHISTORY_OFF
class MT4HISTORY
{
private:
static const bool MT4HISTORY::IsTester;
#ifndef MT4ORDERS_FASTHISTORY_OFF
CHashMap<ulong, ulong> DealsIn; // By PositionID, it returns DealIn.
CHashMap<ulong, ulong> DealsOut; // By PositionID, it returns DealOut.
#endif // MT4ORDERS_FASTHISTORY_OFF
long Tickets[];
uint Amount;
int LastTotalDeals;
int LastTotalOrders;
#ifdef MT4ORDERS_HISTORY_OLD
datetime LastTime;
datetime LastInitTime;
int PrevDealsTotal;
int PrevOrdersTotal;
bool IsChangeHistory( void )
{
bool Res = !_B2(::HistorySelect(0, INT_MAX));
if (!Res)
{
const int iDealsTotal = ::HistoryDealsTotal();
const int iOrdersTotal = ::HistoryOrdersTotal();
if (Res = (iDealsTotal != this.PrevDealsTotal) || (iOrdersTotal != this.PrevOrdersTotal))
{
this.PrevDealsTotal = iDealsTotal;
this.PrevOrdersTotal = iOrdersTotal;
}
}
return(Res);
}
bool RefreshHistory( void )
{
bool Res = !MT4HISTORY::IsChangeHistory();
if (!Res)
{
const datetime LastTimeCurrent = ::TimeCurrent();
if (!MT4HISTORY::IsTester && ((LastTimeCurrent >= this.LastInitTime + DAY)/* || (MT4HISTORY::AccountNumber != ::AccountInfoInteger(ACCOUNT_LOGIN))*/))
{
this.LastTime = 0;
this.LastTotalOrders = 0;
this.LastTotalDeals = 0;
this.Amount = 0;
::ArrayResize(this.Tickets, this.Amount, RESERVE_SIZE);
this.LastInitTime = LastTimeCurrent;
#ifndef MT4ORDERS_FASTHISTORY_OFF
this.DealsIn.Clear();
this.DealsOut.Clear();
#endif // MT4ORDERS_FASTHISTORY_OFF
}
const datetime LastTimeCurrentLeft = LastTimeCurrent - HISTORY_PAUSE;
if (!this.LastTime || _B2(::HistorySelect(this.LastTime, END_TIME))) //
{
const int TotalOrders = ::HistoryOrdersTotal();
const int TotalDeals = ::HistoryDealsTotal();
Res = ((TotalOrders > this.LastTotalOrders) || (TotalDeals > this.LastTotalDeals));
if (Res)
{
int iOrder = this.LastTotalOrders;
int iDeal = this.LastTotalDeals;
ulong TicketOrder = 0;
ulong TicketDeal = 0;
long TimeOrder = (iOrder < TotalOrders) ? ::HistoryOrderGetInteger((TicketOrder = ::HistoryOrderGetTicket(iOrder)), ORDER_TIME_DONE_MSC) : LONG_MAX;
long TimeDeal = (iDeal < TotalDeals) ? ::HistoryDealGetInteger((TicketDeal = ::HistoryDealGetTicket(iDeal)), DEAL_TIME_MSC) : LONG_MAX;
if (this.LastTime < LastTimeCurrentLeft)
{
this.LastTotalOrders = 0;
this.LastTotalDeals = 0;
this.LastTime = LastTimeCurrentLeft;
}
long LastTimeMsc = this.LastTime * THOUSAND;
while ((iDeal < TotalDeals) || (iOrder < TotalOrders))
if (TimeOrder < TimeDeal)
{
LASTTIME(Order)
if (MT4HISTORY::IsMT4Order(TicketOrder))
{
this.Amount = ::ArrayResize(this.Tickets, this.Amount + 1, RESERVE_SIZE);
this.Tickets[this.Amount - 1] = -(long)TicketOrder;
}
iOrder++;
TimeOrder = (iOrder < TotalOrders) ? ::HistoryOrderGetInteger((TicketOrder = ::HistoryOrderGetTicket(iOrder)), ORDER_TIME_DONE_MSC) : LONG_MAX;
}
else
{
LASTTIME(Deal)
if (MT4HISTORY::IsMT4Deal(TicketDeal))
{
this.Amount = ::ArrayResize(this.Tickets, this.Amount + 1, RESERVE_SIZE);
this.Tickets[this.Amount - 1] = (long)TicketDeal;
#ifndef MT4ORDERS_FASTHISTORY_OFF
_B2(this.DealsOut.Add(::HistoryDealGetInteger(TicketDeal, DEAL_POSITION_ID), TicketDeal));
#endif // MT4ORDERS_FASTHISTORY_OFF
}
#ifndef MT4ORDERS_FASTHISTORY_OFF
else if ((ENUM_DEAL_ENTRY)::HistoryDealGetInteger(TicketDeal, DEAL_ENTRY) == DEAL_ENTRY_IN)
_B2(this.DealsIn.Add(::HistoryDealGetInteger(TicketDeal, DEAL_POSITION_ID), TicketDeal));
#endif // MT4ORDERS_FASTHISTORY_OFF
iDeal++;
TimeDeal = (iDeal < TotalDeals) ? ::HistoryDealGetInteger((TicketDeal = ::HistoryDealGetTicket(iDeal)), DEAL_TIME_MSC) : LONG_MAX;
}
}
else if (LastTimeCurrentLeft > this.LastTime)
{
this.LastTime = LastTimeCurrentLeft;
this.LastTotalOrders = 0;
this.LastTotalDeals = 0;
}
}
}
return(Res);
}
#else // #ifdef MT4ORDERS_HISTORY_OLD
bool RefreshHistory( void )
{
if (_B2(::HistorySelect(0, INT_MAX)))
{
const int TotalOrders = ::HistoryOrdersTotal();
const int TotalDeals = ::HistoryDealsTotal();
if ((TotalOrders > this.LastTotalOrders) || (TotalDeals > this.LastTotalDeals))
{
ulong TicketOrder = 0;
ulong TicketDeal = 0;
long TimeOrder = (this.LastTotalOrders < TotalOrders) ?
::HistoryOrderGetInteger((TicketOrder = ::HistoryOrderGetTicket(this.LastTotalOrders)), ORDER_TIME_DONE_MSC) : LONG_MAX;
long TimeDeal = (this.LastTotalDeals < TotalDeals) ?
::HistoryDealGetInteger((TicketDeal = ::HistoryDealGetTicket(this.LastTotalDeals)), DEAL_TIME_MSC) : LONG_MAX;
while ((this.LastTotalDeals < TotalDeals) || (this.LastTotalOrders < TotalOrders))
if (TimeOrder < TimeDeal)
{
if (MT4HISTORY::IsMT4Order(TicketOrder))
{
this.Amount = ::ArrayResize(this.Tickets, this.Amount + 1, RESERVE_SIZE);
this.Tickets[this.Amount - 1] = -(long)TicketOrder;
}
this.LastTotalOrders++;
TimeOrder = (this.LastTotalOrders < TotalOrders) ?
::HistoryOrderGetInteger((TicketOrder = ::HistoryOrderGetTicket(this.LastTotalOrders)), ORDER_TIME_DONE_MSC) : LONG_MAX;
}
else
{
if (MT4HISTORY::IsMT4Deal(TicketDeal))
{
this.Amount = ::ArrayResize(this.Tickets, this.Amount + 1, RESERVE_SIZE);
this.Tickets[this.Amount - 1] = (long)TicketDeal;
_B2(this.DealsOut.Add(::HistoryDealGetInteger(TicketDeal, DEAL_POSITION_ID), TicketDeal));
}
else if ((ENUM_DEAL_ENTRY)::HistoryDealGetInteger(TicketDeal, DEAL_ENTRY) == DEAL_ENTRY_IN)
_B2(this.DealsIn.Add(::HistoryDealGetInteger(TicketDeal, DEAL_POSITION_ID), TicketDeal));
this.LastTotalDeals++;
TimeDeal = (this.LastTotalDeals < TotalDeals) ?
::HistoryDealGetInteger((TicketDeal = ::HistoryDealGetTicket(this.LastTotalDeals)), DEAL_TIME_MSC) : LONG_MAX;
}
}
}
return(true);
}
#endif // #ifdef MT4ORDERS_HISTORY_OLD #else
public:
static bool IsMT4Deal( const ulong &Ticket )
{
const ENUM_DEAL_TYPE DealType = (ENUM_DEAL_TYPE)::HistoryDealGetInteger(Ticket, DEAL_TYPE);
const ENUM_DEAL_ENTRY DealEntry = (ENUM_DEAL_ENTRY)::HistoryDealGetInteger(Ticket, DEAL_ENTRY);
return(((DealType != DEAL_TYPE_BUY) && (DealType != DEAL_TYPE_SELL)) || // non trading deal
((DealEntry == DEAL_ENTRY_OUT) || (DealEntry == DEAL_ENTRY_OUT_BY))); // trading
}
static bool IsMT4Order( const ulong &Ticket )
{
return(!::HistoryOrderGetInteger(Ticket, ORDER_POSITION_ID) || (::HistoryOrderGetDouble(Ticket, ORDER_VOLUME_CURRENT) &&
::HistoryOrderGetInteger(Ticket, ORDER_TYPE) > ORDER_TYPE_SELL));
}
MT4HISTORY( void ) : Amount(::ArrayResize(this.Tickets, 0, RESERVE_SIZE)),
LastTotalDeals(0), LastTotalOrders(0)
#ifdef MT4ORDERS_HISTORY_OLD
, LastTime(0), LastInitTime(0), PrevDealsTotal(0), PrevOrdersTotal(0)
#endif // #ifdef MT4ORDERS_HISTORY_OLD
{
}
ulong GetPositionDealIn( const ulong PositionIdentifier = -1 ) // 0 is not available, since the tester's balance trade is zero
{
ulong Ticket = 0;
if (PositionIdentifier == -1)
{
const ulong MyPositionIdentifier = ::PositionGetInteger(POSITION_IDENTIFIER);
#ifndef MT4ORDERS_FASTHISTORY_OFF
if (!_B2(this.DealsIn.TryGetValue(MyPositionIdentifier, Ticket))
#ifndef MT4ORDERS_HISTORY_OLD
&& !_B2(this.RefreshHistory() && this.DealsIn.TryGetValue(MyPositionIdentifier, Ticket))
#endif // #ifndef MT4ORDERS_HISTORY_OLD
)
#endif // MT4ORDERS_FASTHISTORY_OFF
{
const datetime PosTime = (datetime)::PositionGetInteger(POSITION_TIME);
if (_B3(::HistorySelect(PosTime, PosTime)))
{
const int Total = ::HistoryDealsTotal();
for (int i = 0; i < Total; i++)
{
const ulong TicketDeal = ::HistoryDealGetTicket(i);
if ((::HistoryDealGetInteger(TicketDeal, DEAL_POSITION_ID) == MyPositionIdentifier) /*&&
((ENUM_DEAL_ENTRY)::HistoryDealGetInteger(TicketDeal, DEAL_ENTRY) == DEAL_ENTRY_IN) */) // First mention will be DEAL_ENTRY_IN anyway
{
Ticket = TicketDeal;
#ifndef MT4ORDERS_FASTHISTORY_OFF
_B2(this.DealsIn.Add(MyPositionIdentifier, Ticket));
#endif // MT4ORDERS_FASTHISTORY_OFF
break;
}
}
}
}
}
else if (PositionIdentifier && // PositionIdentifier of balance trades is zero
#ifndef MT4ORDERS_FASTHISTORY_OFF
!_B2(this.DealsIn.TryGetValue(PositionIdentifier, Ticket)) &&
#ifndef MT4ORDERS_HISTORY_OLD
!_B2(this.RefreshHistory() && this.DealsIn.TryGetValue(PositionIdentifier, Ticket)) &&
#endif // #ifndef MT4ORDERS_HISTORY_OLD
#endif // MT4ORDERS_FASTHISTORY_OFF
_B3(::HistorySelectByPosition(PositionIdentifier)) && (::HistoryDealsTotal() > 1)) // Why > 1, not > 0 ?!
{
Ticket = _B2(::HistoryDealGetTicket(0)); // First mention will be DEAL_ENTRY_IN anyway
/*
const int Total = ::HistoryDealsTotal();
for (int i = 0; i < Total; i++)
{
const ulong TicketDeal = ::HistoryDealGetTicket(i);
if (TicketDeal > 0)
if ((ENUM_DEAL_ENTRY)::HistoryDealGetInteger(TicketDeal, DEAL_ENTRY) == DEAL_ENTRY_IN)
{
Ticket = TicketDeal;
break;
}
} */
#ifndef MT4ORDERS_FASTHISTORY_OFF
_B2(this.DealsIn.Add(PositionIdentifier, Ticket));
#endif // MT4ORDERS_FASTHISTORY_OFF
}
return(Ticket);
}
ulong GetPositionDealOut( const ulong PositionIdentifier )
{
ulong Ticket = 0;
#ifndef MT4ORDERS_FASTHISTORY_OFF
if (!_B2(this.DealsOut.TryGetValue(PositionIdentifier, Ticket)) && _B2(this.RefreshHistory()))
_B2(this.DealsOut.TryGetValue(PositionIdentifier, Ticket));
#endif // MT4ORDERS_FASTHISTORY_OFF
return(Ticket);
}
int GetAmount( void )
{
_B2(this.RefreshHistory());
return((int)this.Amount);
}
long operator []( const uint &Pos )
{
long Res = 0;
if ((Pos >= this.Amount)/* || (!MT4HISTORY::IsTester && (MT4HISTORY::AccountNumber != ::AccountInfoInteger(ACCOUNT_LOGIN)))*/)
{
_B2(this.RefreshHistory());
if (Pos < this.Amount)
Res = this.Tickets[Pos];
}
else
Res = this.Tickets[Pos];
return(Res);
}
};
static const bool MT4HISTORY::IsTester = ::MQLInfoInteger(MQL_TESTER);
#undef LASTTIME
#undef THOUSAND
#undef END_TIME
#undef HISTORY_PAUSE
#undef DAY
#undef RESERVE_SIZE
#define OP_BUY ORDER_TYPE_BUY
#define OP_SELL ORDER_TYPE_SELL
#define OP_BUYLIMIT ORDER_TYPE_BUY_LIMIT
#define OP_SELLLIMIT ORDER_TYPE_SELL_LIMIT
#define OP_BUYSTOP ORDER_TYPE_BUY_STOP
#define OP_SELLSTOP ORDER_TYPE_SELL_STOP
#define OP_BALANCE 6
#define SELECT_BY_POS 0
#define SELECT_BY_TICKET 1
#define MODE_TRADES 0
#define MODE_HISTORY 1
class MT4ORDERS
{
private:
static MT4_ORDER Order;
static MT4HISTORY History;
static const bool MT4ORDERS::IsTester;
static const bool MT4ORDERS::IsHedging;
static int OrderSendBug;
static bool HistorySelectOrder( const ulong Ticket )
{
return(Ticket && ((::HistoryOrderGetInteger(Ticket, ORDER_TICKET) == Ticket) ||
(_B2(::HistorySelect(0, INT_MAX)) && (::HistoryOrderGetInteger(Ticket, ORDER_TICKET) == Ticket))));
}
static bool HistorySelectDeal( const ulong &Ticket )
{
return(Ticket && ((::HistoryDealGetInteger(Ticket, DEAL_TICKET) == Ticket) ||
(_B2(::HistorySelect(0, INT_MAX)) && (::HistoryDealGetInteger(Ticket, DEAL_TICKET) == Ticket))));
}
#define UNKNOWN_COMMISSION DBL_MIN
#define UNKNOWN_REQUEST_PRICE DBL_MIN
#define UNKNOWN_TICKET 0
static bool CheckNewTicket( void )
{
return(false); // This functionality is useless - there is INT_MIN/INT_MAX with SELECT_BY_POS + MODE_TRADES
static long PrevPosTimeUpdate = 0;
static long PrevPosTicket = 0;
const long PosTimeUpdate = ::PositionGetInteger(POSITION_TIME_UPDATE_MSC);
const long PosTicket = ::PositionGetInteger(POSITION_TICKET);
const bool Res = ((PosTimeUpdate != PrevPosTimeUpdate) || (PosTicket != PrevPosTicket));
if (Res)
{
MT4ORDERS::GetPositionData();
PrevPosTimeUpdate = PosTimeUpdate;
PrevPosTicket = PosTicket;
}
return(Res);
}
static bool CheckPositionTicketOpen( void )
{
if ((MT4ORDERS::Order.TicketOpen == UNKNOWN_TICKET) || MT4ORDERS::CheckNewTicket())
MT4ORDERS::Order.TicketOpen = (long)_B2(MT4ORDERS::History.GetPositionDealIn()); // All because of this very expensive function
return(true);
}
static bool CheckPositionCommissionComment( void )
{
if ((MT4ORDERS::Order.Commission == UNKNOWN_COMMISSION) || MT4ORDERS::CheckNewTicket())
{
MT4ORDERS::Order.Commission = 0; // ::PositionGetDouble(POSITION_COMMISSION); // Always zero
MT4ORDERS::Order.Comment = ::PositionGetString(POSITION_COMMENT);
if (!MT4ORDERS::Order.Commission || (MT4ORDERS::Order.Comment == ""))
{
MT4ORDERS::CheckPositionTicketOpen();
const ulong Ticket = MT4ORDERS::Order.TicketOpen;
if ((Ticket > 0) && _B2(MT4ORDERS::HistorySelectDeal(Ticket)))
{
if (!MT4ORDERS::Order.Commission)
{
const double LotsIn = ::HistoryDealGetDouble(Ticket, DEAL_VOLUME);
if (LotsIn > 0)
MT4ORDERS::Order.Commission = ::HistoryDealGetDouble(Ticket, DEAL_COMMISSION) * ::PositionGetDouble(POSITION_VOLUME) / LotsIn;
}
if (MT4ORDERS::Order.Comment == "")
MT4ORDERS::Order.Comment = ::HistoryDealGetString(Ticket, DEAL_COMMENT);
}
}
}
return(true);
}
/*
static bool CheckPositionOpenReason( void )
{
if ((MT4ORDERS::Order.OpenReason == UNKNOWN_REASON) || MT4ORDERS::CheckNewTicket())
{
MT4ORDERS::CheckPositionTicketOpen();
const ulong Ticket = MT4ORDERS::Order.TicketOpen;
if ((Ticket > 0) && (MT4ORDERS::IsTester || MT4ORDERS::HistorySelectDeal(Ticket)))
MT4ORDERS::Order.OpenReason = (ENUM_DEAL_REASON)::HistoryDealGetInteger(Ticket, DEAL_REASON);
}
return(true);
}
*/
static bool CheckPositionOpenPriceRequest( void )
{
const long PosTicket = ::PositionGetInteger(POSITION_TICKET);
if (((MT4ORDERS::Order.OpenPriceRequest == UNKNOWN_REQUEST_PRICE) || MT4ORDERS::CheckNewTicket()) &&
!(MT4ORDERS::Order.OpenPriceRequest = (_B2(MT4ORDERS::HistorySelectOrder(PosTicket)) &&
(MT4ORDERS::IsTester || (::PositionGetInteger(POSITION_TIME_MSC) ==
::HistoryOrderGetInteger(PosTicket, ORDER_TIME_DONE_MSC)))) // Is this check necessary?
? ::HistoryOrderGetDouble(PosTicket, ORDER_PRICE_OPEN)
: ::PositionGetDouble(POSITION_PRICE_OPEN)))
MT4ORDERS::Order.OpenPriceRequest = ::PositionGetDouble(POSITION_PRICE_OPEN); // In case the order price is zero
return(true);
}
static void GetPositionData( void )
{
MT4ORDERS::Order.Ticket = POSITION_SELECT;
MT4ORDERS::Order.Commission = UNKNOWN_COMMISSION; // MT4ORDERS::CheckPositionCommissionComment();
MT4ORDERS::Order.OpenPriceRequest = UNKNOWN_REQUEST_PRICE; // MT4ORDERS::CheckPositionOpenPriceRequest()
MT4ORDERS::Order.TicketOpen = UNKNOWN_TICKET;
return;
}
#undef UNKNOWN_TICKET
#undef UNKNOWN_REQUEST_PRICE
#undef UNKNOWN_COMMISSION
static void GetOrderData( void )
{
MT4ORDERS::Order.Ticket = ORDER_SELECT;
return;
}
static void GetHistoryOrderData( const ulong Ticket )
{
MT4ORDERS::Order.Ticket = ::HistoryOrderGetInteger(Ticket, ORDER_TICKET);
MT4ORDERS::Order.Type = (int)::HistoryOrderGetInteger(Ticket, ORDER_TYPE);
MT4ORDERS::Order.TicketOpen = MT4ORDERS::Order.Ticket;
MT4ORDERS::Order.TicketID = MT4ORDERS::Order.Ticket; // A deleted pending order can have a non-zero POSITION_ID.
MT4ORDERS::Order.Lots = ::HistoryOrderGetDouble(Ticket, ORDER_VOLUME_CURRENT);
if (!MT4ORDERS::Order.Lots)
MT4ORDERS::Order.Lots = ::HistoryOrderGetDouble(Ticket, ORDER_VOLUME_INITIAL);
MT4ORDERS::Order.Symbol = ::HistoryOrderGetString(Ticket, ORDER_SYMBOL);
MT4ORDERS::Order.Comment = ::HistoryOrderGetString(Ticket, ORDER_COMMENT);
MT4ORDERS::Order.OpenTimeMsc = ::HistoryOrderGetInteger(Ticket, ORDER_TIME_SETUP_MSC);
MT4ORDERS::Order.OpenTime = (datetime)(MT4ORDERS::Order.OpenTimeMsc / 1000);
MT4ORDERS::Order.OpenPrice = ::HistoryOrderGetDouble(Ticket, ORDER_PRICE_OPEN);
MT4ORDERS::Order.OpenPriceRequest = MT4ORDERS::Order.OpenPrice;
MT4ORDERS::Order.OpenReason = (ENUM_DEAL_REASON)::HistoryOrderGetInteger(Ticket, ORDER_REASON);
MT4ORDERS::Order.StopLoss = ::HistoryOrderGetDouble(Ticket, ORDER_SL);
MT4ORDERS::Order.TakeProfit = ::HistoryOrderGetDouble(Ticket, ORDER_TP);
MT4ORDERS::Order.CloseTimeMsc = ::HistoryOrderGetInteger(Ticket, ORDER_TIME_DONE_MSC);
MT4ORDERS::Order.CloseTime = (datetime)(MT4ORDERS::Order.CloseTimeMsc / 1000);
MT4ORDERS::Order.ClosePrice = ::HistoryOrderGetDouble(Ticket, ORDER_PRICE_CURRENT);
MT4ORDERS::Order.ClosePriceRequest = MT4ORDERS::Order.ClosePrice;
MT4ORDERS::Order.CloseReason = MT4ORDERS::Order.OpenReason;
MT4ORDERS::Order.State = (ENUM_ORDER_STATE)::HistoryOrderGetInteger(Ticket, ORDER_STATE);
MT4ORDERS::Order.Expiration = (datetime)::HistoryOrderGetInteger(Ticket, ORDER_TIME_EXPIRATION);
MT4ORDERS::Order.MagicNumber = ::HistoryOrderGetInteger(Ticket, ORDER_MAGIC);
MT4ORDERS::Order.Profit = 0;
MT4ORDERS::Order.Commission = 0;
MT4ORDERS::Order.Swap = 0;
return;
}
static string GetTickFlag( uint tickflag )
{
string flag = " " + (string)tickflag;
#define TICKFLAG_MACRO(A) flag += ((bool)(tickflag & TICK_FLAG_##A)) ? " TICK_FLAG_" + #A : ""; \
tickflag -= tickflag & TICK_FLAG_##A;
TICKFLAG_MACRO(BID)
TICKFLAG_MACRO(ASK)
TICKFLAG_MACRO(LAST)
TICKFLAG_MACRO(VOLUME)
TICKFLAG_MACRO(BUY)
TICKFLAG_MACRO(SELL)
#undef TICKFLAG_MACRO
if (tickflag)
flag += " FLAG_UNKNOWN (" + (string)tickflag + ")";
return(flag);
}
#define TOSTR(A) " " + #A + " = " + (string)Tick.A
#define TOSTR2(A) " " + #A + " = " + ::DoubleToString(Tick.A, digits)
#define TOSTR3(A) " " + #A + " = " + (string)(A)
static string TickToString( const string &Symb, const MqlTick &Tick )
{
const int digits = (int)::SymbolInfoInteger(Symb, SYMBOL_DIGITS);
return(TOSTR3(Symb) + TOSTR(time) + "." + ::IntegerToString(Tick.time_msc % 1000, 3, '0') +
TOSTR2(bid) + TOSTR2(ask) + TOSTR2(last)+ TOSTR(volume) + MT4ORDERS::GetTickFlag(Tick.flags));
}
static string TickToString( const string &Symb )
{
MqlTick Tick = {};
return(TOSTR3(::SymbolInfoTick(Symb, Tick)) + MT4ORDERS::TickToString(Symb, Tick));
}
#undef TOSTR3
#undef TOSTR2
#undef TOSTR
static void AlertLog( void )
{
string Str = ::TimeToString(::TimeLocal(), TIME_DATE);
::StringReplace(Str, ".", NULL);
return;
}
static long GetTimeCurrent( void )
{
long Res = 0;
MqlTick Tick = {};
for (int i = ::SymbolsTotal(true) - 1; i >= 0; i--)
{
const string SymbName = ::SymbolName(i, true);
if (!::SymbolInfoInteger(SymbName, SYMBOL_CUSTOM) && ::SymbolInfoTick(SymbName, Tick) && (Tick.time_msc > Res))
Res = Tick.time_msc;
}
return(Res);
}
static string TimeToString( const long time )
{
return((string)(datetime)(time / 1000) + "." + ::IntegerToString(time % 1000, 3, '0'));
}
#define WHILE(A) while ((!(Res = (A))) && MT4ORDERS::Waiting())
#define TOSTR(A) #A + " = " + (string)(A) + "\n"
#define TOSTR2(A) #A + " = " + ::EnumToString(A) + " (" + (string)(A) + ")\n"
static void GetHistoryPositionData( const ulong Ticket )
{
MT4ORDERS::Order.Ticket = (long)Ticket;
MT4ORDERS::Order.TicketID = ::HistoryDealGetInteger(MT4ORDERS::Order.Ticket, DEAL_POSITION_ID);
MT4ORDERS::Order.Type = (int)::HistoryDealGetInteger(Ticket, DEAL_TYPE);
if ((MT4ORDERS::Order.Type > OP_SELL))
MT4ORDERS::Order.Type += (OP_BALANCE - OP_SELL - 1);
else
MT4ORDERS::Order.Type = 1 - MT4ORDERS::Order.Type;
MT4ORDERS::Order.Lots = ::HistoryDealGetDouble(Ticket, DEAL_VOLUME);
MT4ORDERS::Order.Symbol = ::HistoryDealGetString(Ticket, DEAL_SYMBOL);
MT4ORDERS::Order.Comment = ::HistoryDealGetString(Ticket, DEAL_COMMENT);
MT4ORDERS::Order.CloseTimeMsc = ::HistoryDealGetInteger(Ticket, DEAL_TIME_MSC);
MT4ORDERS::Order.CloseTime = (datetime)(MT4ORDERS::Order.CloseTimeMsc / 1000); // (datetime)::HistoryDealGetInteger(Ticket, DEAL_TIME);
MT4ORDERS::Order.ClosePrice = ::HistoryDealGetDouble(Ticket, DEAL_PRICE);
MT4ORDERS::Order.CloseReason = (ENUM_DEAL_REASON)::HistoryDealGetInteger(Ticket, DEAL_REASON);
MT4ORDERS::Order.Expiration = 0;
MT4ORDERS::Order.MagicNumber = ::HistoryDealGetInteger(Ticket, DEAL_MAGIC);
MT4ORDERS::Order.Profit = ::HistoryDealGetDouble(Ticket, DEAL_PROFIT);
MT4ORDERS::Order.Commission = ::HistoryDealGetDouble(Ticket, DEAL_COMMISSION);
MT4ORDERS::Order.Swap = ::HistoryDealGetDouble(Ticket, DEAL_SWAP);
#ifndef MT4ORDERS_SLTP_OLD
MT4ORDERS::Order.StopLoss = ::HistoryDealGetDouble(Ticket, DEAL_SL);
MT4ORDERS::Order.TakeProfit = ::HistoryDealGetDouble(Ticket, DEAL_TP);
#else // MT4ORDERS_SLTP_OLD
MT4ORDERS::Order.StopLoss = 0;
MT4ORDERS::Order.TakeProfit = 0;
#endif // MT4ORDERS_SLTP_OLD
const ulong OrderTicket = (MT4ORDERS::Order.Type < OP_BALANCE) ? ::HistoryDealGetInteger(Ticket, DEAL_ORDER) : 0;
const ulong PosTicket = MT4ORDERS::Order.TicketID;
const ulong OpenTicket = (OrderTicket > 0) ? _B2(MT4ORDERS::History.GetPositionDealIn(PosTicket)) : 0;
if (OpenTicket > 0)
{
const ENUM_DEAL_REASON Reason = (ENUM_DEAL_REASON)HistoryDealGetInteger(Ticket, DEAL_REASON);
const ENUM_DEAL_ENTRY DealEntry = (ENUM_DEAL_ENTRY)::HistoryDealGetInteger(Ticket, DEAL_ENTRY);
#ifdef MT4ORDERS_FASTHISTORY_OFF
const bool Res = true;
#else // MT4ORDERS_FASTHISTORY_OFF
bool Res = MT4ORDERS::IsTester ? MT4ORDERS::HistorySelectOrder(OrderTicket) : MT4ORDERS::Waiting(true);
if (!Res)
WHILE(_B2(MT4ORDERS::HistorySelectOrder(OrderTicket))) //
;
if (_B2(MT4ORDERS::HistorySelectDeal(OpenTicket))) // It will surely work, since OpenTicket is reliably in history.
#endif // MT4ORDERS_FASTHISTORY_OFF
{
MT4ORDERS::Order.TicketOpen = (long)OpenTicket;
MT4ORDERS::Order.OpenReason = (ENUM_DEAL_REASON)HistoryDealGetInteger(OpenTicket, DEAL_REASON);
MT4ORDERS::Order.OpenPrice = ::HistoryDealGetDouble(OpenTicket, DEAL_PRICE);
MT4ORDERS::Order.OpenTimeMsc = ::HistoryDealGetInteger(OpenTicket, DEAL_TIME_MSC);
MT4ORDERS::Order.OpenTime = (datetime)(MT4ORDERS::Order.OpenTimeMsc / 1000);
const double OpenLots = ::HistoryDealGetDouble(OpenTicket, DEAL_VOLUME);
if (OpenLots > 0)
MT4ORDERS::Order.Commission += ::HistoryDealGetDouble(OpenTicket, DEAL_COMMISSION) * MT4ORDERS::Order.Lots / OpenLots;
const long Magic = ::HistoryDealGetInteger(OpenTicket, DEAL_MAGIC);
if (Magic)
MT4ORDERS::Order.MagicNumber = Magic;
const string StrComment = ::HistoryDealGetString(OpenTicket, DEAL_COMMENT);
if (Res) // OrderTicket may be absent in history, but it may be found among those still alive. It is probably reasonable to wheedle info out of there.
{
double OrderPriceOpen = ::HistoryOrderGetDouble(OrderTicket, ORDER_PRICE_OPEN);
#ifdef MT4ORDERS_SLTP_OLD
if (Reason == DEAL_REASON_TP)
{
if (!OrderPriceOpen)
OrderPriceOpen = (double)::StringSubstr(MT4ORDERS::Order.Comment, MT4ORDERS::IsTester ? 3 : (::StringFind(MT4ORDERS::Order.Comment, "tp ") + 3));
MT4ORDERS::Order.TakeProfit = OrderPriceOpen;
MT4ORDERS::Order.StopLoss = ::HistoryOrderGetDouble(OrderTicket, ORDER_TP);
}
else if (Reason == DEAL_REASON_SL)
{
if (!OrderPriceOpen)
OrderPriceOpen = (double)::StringSubstr(MT4ORDERS::Order.Comment, MT4ORDERS::IsTester ? 3 : (::StringFind(MT4ORDERS::Order.Comment, "sl ") + 3));
MT4ORDERS::Order.StopLoss = OrderPriceOpen;
MT4ORDERS::Order.TakeProfit = ::HistoryOrderGetDouble(OrderTicket, ORDER_SL);
}
else if (!MT4ORDERS::IsTester &&::StringLen(MT4ORDERS::Order.Comment) > 3)
{
const string PartComment = ::StringSubstr(MT4ORDERS::Order.Comment, 0, 3);
if (PartComment == "[tp")
{
MT4ORDERS::Order.CloseReason = DEAL_REASON_TP;
if (!OrderPriceOpen)
OrderPriceOpen = (double)::StringSubstr(MT4ORDERS::Order.Comment, MT4ORDERS::IsTester ? 3 : (::StringFind(MT4ORDERS::Order.Comment, "tp ") + 3));
MT4ORDERS::Order.TakeProfit = OrderPriceOpen;
MT4ORDERS::Order.StopLoss = ::HistoryOrderGetDouble(OrderTicket, ORDER_TP);
}
else if (PartComment == "[sl")
{
MT4ORDERS::Order.CloseReason = DEAL_REASON_SL;
if (!OrderPriceOpen)
OrderPriceOpen = (double)::StringSubstr(MT4ORDERS::Order.Comment, MT4ORDERS::IsTester ? 3 : (::StringFind(MT4ORDERS::Order.Comment, "sl ") + 3));
MT4ORDERS::Order.StopLoss = OrderPriceOpen;
MT4ORDERS::Order.TakeProfit = ::HistoryOrderGetDouble(OrderTicket, ORDER_SL);
}
else
{
MT4ORDERS::Order.StopLoss = ::HistoryOrderGetDouble(OrderTicket, ORDER_TP);
MT4ORDERS::Order.TakeProfit = ::HistoryOrderGetDouble(OrderTicket, ORDER_SL);
}
}
else
{
MT4ORDERS::Order.StopLoss = ::HistoryOrderGetDouble(OrderTicket, ORDER_TP);
MT4ORDERS::Order.TakeProfit = ::HistoryOrderGetDouble(OrderTicket, ORDER_SL);
}
#endif // MT4ORDERS_SLTP_OLD
MT4ORDERS::Order.State = (ENUM_ORDER_STATE)::HistoryOrderGetInteger(OrderTicket, ORDER_STATE);
if (!(MT4ORDERS::Order.ClosePriceRequest = (DealEntry == DEAL_ENTRY_OUT_BY) ? MT4ORDERS::Order.ClosePrice : OrderPriceOpen))
MT4ORDERS::Order.ClosePriceRequest = MT4ORDERS::Order.ClosePrice;
if (!(MT4ORDERS::Order.OpenPriceRequest = _B2(MT4ORDERS::HistorySelectOrder(PosTicket) &&
(MT4ORDERS::IsTester || (::HistoryDealGetInteger(OpenTicket, DEAL_TIME_MSC) == ::HistoryOrderGetInteger(PosTicket, ORDER_TIME_DONE_MSC)))) ?
::HistoryOrderGetDouble(PosTicket, ORDER_PRICE_OPEN) : MT4ORDERS::Order.OpenPrice))
MT4ORDERS::Order.OpenPriceRequest = MT4ORDERS::Order.OpenPrice;
}
else
{
MT4ORDERS::Order.State = ORDER_STATE_FILLED;
MT4ORDERS::Order.ClosePriceRequest = MT4ORDERS::Order.ClosePrice;
MT4ORDERS::Order.OpenPriceRequest = MT4ORDERS::Order.OpenPrice;
}
if (StrComment != "")
MT4ORDERS::Order.Comment = StrComment;
}
if (!Res)
{
MT4ORDERS::AlertLog();
}
}
else
{
MT4ORDERS::Order.TicketOpen = MT4ORDERS::Order.Ticket;
if (!MT4ORDERS::Order.TicketID && (MT4ORDERS::Order.Type <= OP_SELL)) // ID of balance deals must remain zero.
MT4ORDERS::Order.TicketID = MT4ORDERS::Order.Ticket;
MT4ORDERS::Order.OpenPrice = MT4ORDERS::Order.ClosePrice; // ::HistoryDealGetDouble(Ticket, DEAL_PRICE);
MT4ORDERS::Order.OpenTimeMsc = MT4ORDERS::Order.CloseTimeMsc;
MT4ORDERS::Order.OpenTime = MT4ORDERS::Order.CloseTime; // (datetime)::HistoryDealGetInteger(Ticket, DEAL_TIME);
MT4ORDERS::Order.OpenReason = MT4ORDERS::Order.CloseReason;
MT4ORDERS::Order.State = ORDER_STATE_FILLED;
MT4ORDERS::Order.ClosePriceRequest = MT4ORDERS::Order.ClosePrice;
MT4ORDERS::Order.OpenPriceRequest = MT4ORDERS::Order.OpenPrice;
}
if (OrderTicket)
{
bool Res = MT4ORDERS::IsTester ? MT4ORDERS::HistorySelectOrder(OrderTicket) : MT4ORDERS::Waiting(true);
if (!Res)
WHILE(_B2(MT4ORDERS::HistorySelectOrder(OrderTicket))) //
;
if ((ENUM_ORDER_TYPE)::HistoryOrderGetInteger(OrderTicket, ORDER_TYPE) == ORDER_TYPE_CLOSE_BY)
{
const ulong PosTicketBy = ::HistoryOrderGetInteger(OrderTicket, ORDER_POSITION_BY_ID);
if (PosTicketBy == PosTicket) // CloseBy-Slave should not affect the total trade.
{
MT4ORDERS::Order.Lots = 0;
MT4ORDERS::Order.Commission = 0;
MT4ORDERS::Order.ClosePrice = MT4ORDERS::Order.OpenPrice;
MT4ORDERS::Order.ClosePriceRequest = MT4ORDERS::Order.ClosePrice;
}
else // CloseBy-Master must receive a commission from CloseBy-Slave.
{
const ulong OpenTicketBy = (OrderTicket > 0) ? _B2(MT4ORDERS::History.GetPositionDealIn(PosTicketBy)) : 0;
if ((OpenTicketBy > 0) && _B2(MT4ORDERS::HistorySelectDeal(OpenTicketBy)))
{
const double OpenLots = ::HistoryDealGetDouble(OpenTicketBy, DEAL_VOLUME);
if (OpenLots > 0)
MT4ORDERS::Order.Commission += ::HistoryDealGetDouble(OpenTicketBy, DEAL_COMMISSION) * MT4ORDERS::Order.Lots / OpenLots;
}
}
}
}
return;
}
static bool Waiting( const bool FlagInit = false )
{
static ulong StartTimexx = 0;
const bool Res = FlagInit ? false : (::GetMicrosecondCount() - StartTimexx < MT4ORDERS::OrderSend_MaxPause);
if (FlagInit)
{
StartTimexx = ::GetMicrosecondCount();
MT4ORDERS::OrderSendBug = 0;
}
else if (Res)
{
MT4ORDERS::OrderSendBug++;
}
return(Res);
}
static bool EqualPrices( const double Price1, const double &Price2, const int &digits)
{
return(!::NormalizeDouble(Price1 - Price2, digits));
}
static bool HistoryDealSelect2( MqlTradeResult &Result ) // At the end of the name there is a digit for greater compatibility with macros.
{
#ifdef MT4ORDERS_HISTORY_OLD
if (!Result.deal && Result.order && _B3(::HistorySelectByPosition(::HistoryOrderGetInteger(Result.order, ORDER_POSITION_ID))))
{
#else // #ifdef MT4ORDERS_HISTORY_OLD
if (!Result.deal && Result.order && _B2(MT4ORDERS::HistorySelectOrder(Result.order)))
{
const long OrderTimeFill = ::HistoryOrderGetInteger(Result.order, ORDER_TIME_DONE_MSC);
#endif // #ifdef MT4ORDERS_HISTORY_OLD #else
if (::HistorySelect(0, INT_MAX)) // Without it, the deal detection may fail.
for (int i = ::HistoryDealsTotal() - 1; i >= 0; i--)
{
const ulong DealTicket = ::HistoryDealGetTicket(i);
if (Result.order == ::HistoryDealGetInteger(DealTicket, DEAL_ORDER))
{
Result.deal = DealTicket;
Result.price = ::HistoryDealGetDouble(DealTicket, DEAL_PRICE);
break;
}
#ifndef MT4ORDERS_HISTORY_OLD
else if (::HistoryDealGetInteger(DealTicket, DEAL_TIME_MSC) < OrderTimeFill)
break;
#endif // #ifndef MT4ORDERS_HISTORY_OLD
}
}
return(_B2(MT4ORDERS::HistorySelectDeal(Result.deal)));
}
/*
#define MT4ORDERS_BENCHMARK Alert(MT4ORDERS::LastTradeRequest.symbol + " " + \
(string)MT4ORDERS::LastTradeResult.order + " " + \
MT4ORDERS::LastTradeResult.comment); \
Print(ToStПоказано первые 50% кода (символов: 61288)
Купите тариф с пакетом открытий или войдите, если тариф уже есть. Открытый файл останется в вашем кабинете навсегда.
На этой странице откроется полный исходный код файла вместо ознакомительного фрагмента.
Зачем нужен email? После подтверждения оплаты файл сохранится в кабинете на этот email. Если аккаунта ещё нет, мы создадим его и пришлём ссылку для задания пароля. Если аккаунт уже есть, покупка добавится в него. Укажите адрес, к которому у вас есть доступ.
Чтобы оформить заказ, введите email и перейдите к оплате.