//+------------------------------------------------------------------+ //| Deal.mqh | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #include //--- Перечисление целочисленных свойств сделки enum ENUM_DEAL_PROPERTY_INT { DEAL_PROP_TICKET = 0, // Тикет сделки DEAL_PROP_ORDER, // Ордер, на основание которого выполнена сделка DEAL_PROP_TIME, // Время совершения сделки DEAL_PROP_TIME_MSC, // Время совершения сделки в миллисекундах DEAL_PROP_TYPE, // Тип сделки DEAL_PROP_ENTRY, // Направление сделки DEAL_PROP_MAGIC, // Magic number сделки DEAL_PROP_REASON, // Причина или источник проведения сделки DEAL_PROP_POSITION_ID, // Идентификатор позиции DEAL_PROP_SPREAD, // Spread при совершении сделки }; //--- Перечисление вещественных свойств сделки enum ENUM_DEAL_PROPERTY_DBL { DEAL_PROP_VOLUME = DEAL_PROP_SPREAD+1,// Объем сделки DEAL_PROP_PRICE, // Цена сделки DEAL_PROP_COMMISSION, // Комиссия DEAL_PROP_SWAP, // Накопленный своп при закрытии DEAL_PROP_PROFIT, // Финансовый результат сделки DEAL_PROP_FEE, // Оплата за проведение сделки DEAL_PROP_SL, // Уровень Stop Loss DEAL_PROP_TP, // Уровень Take Profit }; //--- Перечисление строковых свойств сделки enum ENUM_DEAL_PROPERTY_STR { DEAL_PROP_SYMBOL = DEAL_PROP_TP+1, // Символ, по которому произведена сделка DEAL_PROP_COMMENT, // Комментарий к сделке DEAL_PROP_EXTERNAL_ID, // Идентификатор сделки во внешней торговой системе }; //+------------------------------------------------------------------+ //| Класс сделки | //+------------------------------------------------------------------+ class CDeal : public CObject { private: MqlTick m_tick; // Структура тика сделки long m_lprop[DEAL_PROP_SPREAD+1]; // Массив для хранения целочисленных свойств double m_dprop[DEAL_PROP_TP-DEAL_PROP_SPREAD]; // Массив для хранения вещественных свойств string m_sprop[DEAL_PROP_EXTERNAL_ID-DEAL_PROP_TP]; // Массив для хранения строковых свойств //--- Возвращает индекс массива, по которому фактически расположено (1) double-свойство и (2) string-свойство сделки int IndexProp(ENUM_DEAL_PROPERTY_DBL property) const { return(int)property-DEAL_PROP_SPREAD-1; } int IndexProp(ENUM_DEAL_PROPERTY_STR property) const { return(int)property-DEAL_PROP_TP-1; } //--- Получает (1) тик сделки, (2) спред минутного бара сделки bool GetDealTick(const int amount=20); int GetSpreadM1(void); //--- Возвращает время с миллисекундами string TimeMscToString(const long time_msc,int flags=TIME_DATE|TIME_MINUTES|TIME_SECONDS) const; protected: //--- Дополнительные свойства int m_digits; // Digits символа double m_point; // Point символа double m_bid; // Bid при совершении сделки double m_ask; // Ask при совершении сделки public: //--- Установка свойств //--- Устанавливает (1) целочисленное, (2) вещественное и (3) строковое свойство сделки void SetProperty(ENUM_DEAL_PROPERTY_INT property,long value){ this.m_lprop[property]=value; } void SetProperty(ENUM_DEAL_PROPERTY_DBL property,double value){ this.m_dprop[this.IndexProp(property)]=value; } void SetProperty(ENUM_DEAL_PROPERTY_STR property,string value){ this.m_sprop[this.IndexProp(property)]=value; } //--- Целочисленные свойства void SetTicket(const long ticket) { this.SetProperty(DEAL_PROP_TICKET, ticket); } // Тикет void SetOrder(const long order) { this.SetProperty(DEAL_PROP_ORDER, order); } // Ордер void SetTime(const datetime time) { this.SetProperty(DEAL_PROP_TIME, time); } // Время void SetTimeMsc(const long value) { this.SetProperty(DEAL_PROP_TIME_MSC, value); } // Время в миллисекундах void SetTypeDeal(const ENUM_DEAL_TYPE type) { this.SetProperty(DEAL_PROP_TYPE, type); } // Тип void SetEntry(const ENUM_DEAL_ENTRY entry) { this.SetProperty(DEAL_PROP_ENTRY, entry); } // Направление void SetMagic(const long magic) { this.SetProperty(DEAL_PROP_MAGIC, magic); } // Magic number void SetReason(const ENUM_DEAL_REASON reason) { this.SetProperty(DEAL_PROP_REASON, reason); } // Причина или источник проведения сделки void SetPositionID(const long id) { this.SetProperty(DEAL_PROP_POSITION_ID, id); } // Идентификатор позиции //--- Вещественные свойства void SetVolume(const double volume) { this.SetProperty(DEAL_PROP_VOLUME, volume); } // Объем void SetPrice(const double price) { this.SetProperty(DEAL_PROP_PRICE, price); } // Цена void SetCommission(const double value) { this.SetProperty(DEAL_PROP_COMMISSION, value); } // Комиссия void SetSwap(const double value) { this.SetProperty(DEAL_PROP_SWAP, value); } // Накопленный своп при закрытии void SetProfit(const double value) { this.SetProperty(DEAL_PROP_PROFIT, value); } // Финансовый результат void SetFee(const double value) { this.SetProperty(DEAL_PROP_FEE, value); } // Оплата за проведение сделки void SetSL(const double value) { this.SetProperty(DEAL_PROP_SL, value); } // Уровень Stop Loss void SetTP(const double value) { this.SetProperty(DEAL_PROP_TP, value); } // Уровень Take Profit //--- Строковые свойства void SetSymbol(const string symbol) { this.SetProperty(DEAL_PROP_SYMBOL,symbol); } // Имя символа void SetComment(const string comment) { this.SetProperty(DEAL_PROP_COMMENT,comment); } // Комментарий void SetExternalID(const string ext_id) { this.SetProperty(DEAL_PROP_EXTERNAL_ID,ext_id); } // Идентификатор сделки во внешней торговой системе //--- Получение свойств //--- Возвращает из массива свойств (1) целочисленное, (2) вещественное и (3) строковое свойство сделки long GetProperty(ENUM_DEAL_PROPERTY_INT property) const { return this.m_lprop[property]; } double GetProperty(ENUM_DEAL_PROPERTY_DBL property) const { return this.m_dprop[this.IndexProp(property)]; } string GetProperty(ENUM_DEAL_PROPERTY_STR property) const { return this.m_sprop[this.IndexProp(property)]; } //--- Целочисленные свойства long Ticket(void) const { return this.GetProperty(DEAL_PROP_TICKET); } // Тикет long Order(void) const { return this.GetProperty(DEAL_PROP_ORDER); } // Ордер datetime Time(void) const { return (datetime)this.GetProperty(DEAL_PROP_TIME); } // Время long TimeMsc(void) const { return this.GetProperty(DEAL_PROP_TIME_MSC); } // Время в миллисекундах ENUM_DEAL_TYPE TypeDeal(void) const { return (ENUM_DEAL_TYPE)this.GetProperty(DEAL_PROP_TYPE); } // Тип ENUM_DEAL_ENTRY Entry(void) const { return (ENUM_DEAL_ENTRY)this.GetProperty(DEAL_PROP_ENTRY); } // Направление long Magic(void) const { return this.GetProperty(DEAL_PROP_MAGIC); } // Magic number ENUM_DEAL_REASON Reason(void) const { return (ENUM_DEAL_REASON)this.GetProperty(DEAL_PROP_REASON); } // Причина или источник проведения сделки long PositionID(void) const { return this.GetProperty(DEAL_PROP_POSITION_ID); } // Идентификатор позиции //--- Вещественные свойства double Volume(void) const { return this.GetProperty(DEAL_PROP_VOLUME); } // Объем double Price(void) const { return this.GetProperty(DEAL_PROP_PRICE); } // Цена double Commission(void) const { return this.GetProperty(DEAL_PROP_COMMISSION); } // Комиссия double Swap(void) const { return this.GetProperty(DEAL_PROP_SWAP); } // Накопленный своп при закрытии double Profit(void) const { return this.GetProperty(DEAL_PROP_PROFIT); } // Финансовый результат double Fee(void) const { return this.GetProperty(DEAL_PROP_FEE); } // Оплата за проведение сделки double SL(void) const { return this.GetProperty(DEAL_PROP_SL); } // Уровень Stop Loss double TP(void) const { return this.GetProperty(DEAL_PROP_TP); } // Уровень Take Profit //--- Строковые свойства string Symbol(void) const { return this.GetProperty(DEAL_PROP_SYMBOL); } // Имя символа string Comment(void) const { return this.GetProperty(DEAL_PROP_COMMENT); } // Комментарий string ExternalID(void) const { return this.GetProperty(DEAL_PROP_EXTERNAL_ID); } // Идентификатор сделки во внешней торговой системе //--- Дополнительные свойства double Bid(void) const { return this.m_bid; } // Bid при совершении сделки double Ask(void) const { return this.m_ask; } // Ask при совершении сделки int Spread(void) const { return (int)this.GetProperty(DEAL_PROP_SPREAD); } // Spread при совершении сделки //--- Возвращает описание (1) типа сделки, (2) способа изменения позиции, (3) причины проведения сделки string TypeDescription(void) const; string EntryDescription(void) const; string ReasonDescription(void) const; //--- Возвращает описание сделки string Description(void); //--- Распечатывает в журнал свойства сделки void Print(void); //--- Сравнивает два объекта между собой по указанному в mode свойству virtual int Compare(const CObject *node, const int mode=0) const; //--- Конструкторы/деструктор CDeal(void){} CDeal(const ulong ticket); ~CDeal(); }; //+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ CDeal::CDeal(const ulong ticket) { //--- Сохранение свойств //--- Целочисленные свойства this.SetTicket((long)ticket); // Тикет сделки this.SetOrder(::HistoryDealGetInteger(ticket, DEAL_ORDER)); // Ордер this.SetTime((datetime)::HistoryDealGetInteger(ticket, DEAL_TIME)); // Время совершения сделки this.SetTimeMsc(::HistoryDealGetInteger(ticket, DEAL_TIME_MSC)); // Время совершения сделки в миллисекундах this.SetTypeDeal((ENUM_DEAL_TYPE)::HistoryDealGetInteger(ticket, DEAL_TYPE)); // Тип this.SetEntry((ENUM_DEAL_ENTRY)::HistoryDealGetInteger(ticket, DEAL_ENTRY)); // Направление this.SetMagic(::HistoryDealGetInteger(ticket, DEAL_MAGIC)); // Magic number this.SetReason((ENUM_DEAL_REASON)::HistoryDealGetInteger(ticket, DEAL_REASON)); // Причина или источник проведения сделки this.SetPositionID(::HistoryDealGetInteger(ticket, DEAL_POSITION_ID)); // Идентификатор позиции //--- Вещественные свойства this.SetVolume(::HistoryDealGetDouble(ticket, DEAL_VOLUME)); // Объем this.SetPrice(::HistoryDealGetDouble(ticket, DEAL_PRICE)); // Цена this.SetCommission(::HistoryDealGetDouble(ticket, DEAL_COMMISSION)); // Комиссия this.SetSwap(::HistoryDealGetDouble(ticket, DEAL_SWAP)); // Накопленный своп при закрытии this.SetProfit(::HistoryDealGetDouble(ticket, DEAL_PROFIT)); // Финансовый результат this.SetFee(::HistoryDealGetDouble(ticket, DEAL_FEE)); // Оплата за проведение сделки this.SetSL(::HistoryDealGetDouble(ticket, DEAL_SL)); // Уровень Stop Loss this.SetTP(::HistoryDealGetDouble(ticket, DEAL_TP)); // Уровень Take Profit //--- Строковые свойства this.SetSymbol(::HistoryDealGetString(ticket, DEAL_SYMBOL)); // Имя символа this.SetComment(::HistoryDealGetString(ticket, DEAL_COMMENT)); // Комментарий this.SetExternalID(::HistoryDealGetString(ticket, DEAL_EXTERNAL_ID)); // Идентификатор сделки во внешней торговой системе //--- Дополнительные параметры this.m_digits = (int)::SymbolInfoInteger(this.Symbol(), SYMBOL_DIGITS); this.m_point = ::SymbolInfoDouble(this.Symbol(), SYMBOL_POINT); //--- Параметры для расчёта спреда this.m_bid = 0; this.m_ask = 0; this.SetProperty(DEAL_PROP_SPREAD, 0); //--- Если исторический тик и значение Point символа удалось получить if(this.GetDealTick() && this.m_point!=0) { //--- запишем значения цен Bid и Ask и рассчитаем и сохраним значение спреда this.m_bid=this.m_tick.bid; this.m_ask=this.m_tick.ask; int spread=(int)::fabs((this.m_ask-this.m_bid)/this.m_point); this.SetProperty(DEAL_PROP_SPREAD, spread); } //--- Если исторический тик получить не удалось, возьмём значение спреда минутного бара, на котором была сделка else this.SetProperty(DEAL_PROP_SPREAD, this.GetSpreadM1()); } //+------------------------------------------------------------------+ //| Деструктор | //+------------------------------------------------------------------+ CDeal::~CDeal() { } //+------------------------------------------------------------------+ //| Сравнивает два объекта между собой по указанному свойству | //+------------------------------------------------------------------+ int CDeal::Compare(const CObject *node,const int mode=0) const { const CDeal * obj = node; switch(mode) { case DEAL_PROP_TICKET : return(this.Ticket() > obj.Ticket() ? 1 : this.Ticket() < obj.Ticket() ? -1 : 0); case DEAL_PROP_ORDER : return(this.Order() > obj.Order() ? 1 : this.Order() < obj.Order() ? -1 : 0); case DEAL_PROP_TIME : return(this.Time() > obj.Time() ? 1 : this.Time() < obj.Time() ? -1 : 0); case DEAL_PROP_TIME_MSC : return(this.TimeMsc() > obj.TimeMsc() ? 1 : this.TimeMsc() < obj.TimeMsc() ? -1 : 0); case DEAL_PROP_TYPE : return(this.TypeDeal() > obj.TypeDeal() ? 1 : this.TypeDeal() < obj.TypeDeal() ? -1 : 0); case DEAL_PROP_ENTRY : return(this.Entry() > obj.Entry() ? 1 : this.Entry() < obj.Entry() ? -1 : 0); case DEAL_PROP_MAGIC : return(this.Magic() > obj.Magic() ? 1 : this.Magic() < obj.Magic() ? -1 : 0); case DEAL_PROP_REASON : return(this.Reason() > obj.Reason() ? 1 : this.Reason() < obj.Reason() ? -1 : 0); case DEAL_PROP_POSITION_ID : return(this.PositionID() > obj.PositionID() ? 1 : this.PositionID() < obj.PositionID() ? -1 : 0); case DEAL_PROP_SPREAD : return(this.Spread() > obj.Spread() ? 1 : this.Spread() < obj.Spread() ? -1 : 0); case DEAL_PROP_VOLUME : return(this.Volume() > obj.Volume() ? 1 : this.Volume() < obj.Volume() ? -1 : 0); case DEAL_PROP_PRICE : return(this.Price() > obj.Price() ? 1 : this.Price() < obj.Price() ? -1 : 0); case DEAL_PROP_COMMISSION : return(this.Commission() > obj.Commission() ? 1 : this.Commission() < obj.Commission() ? -1 : 0); case DEAL_PROP_SWAP : return(this.Swap() > obj.Swap() ? 1 : this.Swap() < obj.Swap() ? -1 : 0); case DEAL_PROP_PROFIT : return(this.Profit() > obj.Profit() ? 1 : this.Profit() < obj.Profit() ? -1 : 0); case DEAL_PROP_FEE : return(this.Fee() > obj.Fee() ? 1 : this.Fee() < obj.Fee() ? -1 : 0); case DEAL_PROP_SL : return(this.SL() > obj.SL() ? 1 : this.SL() < obj.SL() ? -1 : 0); case DEAL_PROP_TP : return(this.TP() > obj.TP() ? 1 : this.TP() < obj.TP() ? -1 : 0); case DEAL_PROP_SYMBOL : return(this.Symbol() > obj.Symbol() ? 1 : this.Symbol() < obj.Symbol() ? -1 : 0); case DEAL_PROP_COMMENT : return(this.Comment() > obj.Comment() ? 1 : this.Comment() < obj.Comment() ? -1 : 0); case DEAL_PROP_EXTERNAL_ID : return(this.ExternalID() > obj.ExternalID() ? 1 : this.ExternalID() < obj.ExternalID() ? -1 : 0); default : return(-1); } } //+------------------------------------------------------------------+ //| Возвращает описание типа сделки | //+------------------------------------------------------------------+ string CDeal::TypeDescription(void) const { switch(this.TypeDeal()) { case DEAL_TYPE_BUY : return "Buy"; case DEAL_TYPE_SELL : return "Sell"; case DEAL_TYPE_BALANCE : return "Balance"; case DEAL_TYPE_CREDIT : return "Credit"; case DEAL_TYPE_CHARGE : return "Additional charge"; case DEAL_TYPE_CORRECTION : return "Correction"; case DEAL_TYPE_BONUS : return "Bonus"; case DEAL_TYPE_COMMISSION : return "Additional commission"; case DEAL_TYPE_COMMISSION_DAILY : return "Daily commission"; case DEAL_TYPE_COMMISSION_MONTHLY : return "Monthly commission"; case DEAL_TYPE_COMMISSION_AGENT_DAILY : return "Daily agent commission"; case DEAL_TYPE_COMMISSION_AGENT_MONTHLY: return "Monthly agent commission"; case DEAL_TYPE_INTEREST : return "Interest rate"; case DEAL_TYPE_BUY_CANCELED : return "Canceled buy deal"; case DEAL_TYPE_SELL_CANCELED : return "Canceled sell deal"; case DEAL_DIVIDEND : return "Dividend operations"; case DEAL_DIVIDEND_FRANKED : return "Franked (non-taxable) dividend operations"; case DEAL_TAX : return "Tax charges"; default : return "Unknown: "+(string)this.TypeDeal(); } } //+------------------------------------------------------------------+ //| Возвращает описание способа изменения позиции | //+------------------------------------------------------------------+ string CDeal::EntryDescription(void) const { switch(this.Entry()) { case DEAL_ENTRY_IN : return "Entry In"; case DEAL_ENTRY_OUT : return "Entry Out"; case DEAL_ENTRY_INOUT : return "Reverse"; case DEAL_ENTRY_OUT_BY : return "Close a position by an opposite one"; default : return "Unknown: "+(string)this.Entry(); } } //+------------------------------------------------------------------+ //| Возвращает описание причины проведения сделки | //+------------------------------------------------------------------+ string CDeal::ReasonDescription(void) const { switch(this.Reason()) { case DEAL_REASON_CLIENT : return "Terminal"; case DEAL_REASON_MOBILE : return "Mobile"; case DEAL_REASON_WEB : return "Web"; case DEAL_REASON_EXPERT : return "EA"; case DEAL_REASON_SL : return "SL"; case DEAL_REASON_TP : return "TP"; case DEAL_REASON_SO : return "SO"; case DEAL_REASON_ROLLOVER : return "Rollover"; case DEAL_REASON_VMARGIN : return "Var. Margin"; case DEAL_REASON_SPLIT : return "Split"; case DEAL_REASON_CORPORATE_ACTION: return "Corp. Action"; default : return "Unknown reason "+(string)this.Reason(); } } //+------------------------------------------------------------------+ //| Возвращает описание сделки | //+------------------------------------------------------------------+ string CDeal::Description(void) { return(::StringFormat("Deal: %-9s %.2f %-4s #%I64d at %s", this.EntryDescription(), this.Volume(), this.TypeDescription(), this.Ticket(), this.TimeMscToString(this.TimeMsc()))); } //+------------------------------------------------------------------+ //| Распечатывает в журнал свойства сделки | //+------------------------------------------------------------------+ void CDeal::Print(void) { ::Print(this.Description()); } //+------------------------------------------------------------------+ //| Возвращает время с миллисекундами | //+------------------------------------------------------------------+ string CDeal::TimeMscToString(const long time_msc, int flags=TIME_DATE|TIME_MINUTES|TIME_SECONDS) const { return(::TimeToString(time_msc/1000, flags) + "." + ::IntegerToString(time_msc %1000, 3, '0')); } //+------------------------------------------------------------------+ //| Получает тик сделки | //| https://www.mql5.com/ru/forum/42122/page47#comment_37205238 | //+------------------------------------------------------------------+ bool CDeal::GetDealTick(const int amount=20) { MqlTick ticks[]; // Сюда будем получать тики int attempts = amount; // Количество попыток получения тиков int offset = 500; // Начальное смещение времени для попытки int copied = 0; // Количество скопированных тиков //--- До тех пор, пока не скопирован тик и не закончилось количество попыток копирования //--- пытаемся получить тик, на каждой итерации увеличивая вдвое начальное смещение времени (расширяем диапазон времени "from_msc") while(!::IsStopped() && (copied<=0) && (attempts--)!=0) copied = ::CopyTicksRange(this.Symbol(), ticks, COPY_TICKS_INFO, this.TimeMsc()-(offset <<=1), this.TimeMsc()); //--- Если тик скопировать удалось (он последний в массиве тиков) - записываем его в переменную m_tick if(copied>0) this.m_tick=ticks[copied-1]; //--- Возвращаем флаг того, что тик скопирован return(copied>0); } //+------------------------------------------------------------------+ //| Получает спред минутного бара сделки | //+------------------------------------------------------------------+ int CDeal::GetSpreadM1(void) { int array[1]={}; int bar=::iBarShift(this.Symbol(), PERIOD_M1, this.Time()); if(bar==WRONG_VALUE) return 0; return(::CopySpread(this.Symbol(), PERIOD_M1, bar, 1, array)==1 ? array[0] : 0); } //+------------------------------------------------------------------+