Article-16952-MQL5-Visual-T.../SymbolTrade.mqh
2026-03-24 13:06:02 +07:00

438 行
55 KiB
MQL5

//+------------------------------------------------------------------+
//| SymbolTrade.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"
#define DIRECTORY "TradingByHistoryDeals"
#define FILE_NAME "HistoryDealsData.bin"
#define PATH DIRECTORY+"\\"+FILE_NAME
#include <Arrays\ArrayObj.mqh>
#include <Trade\Trade.mqh>
//+------------------------------------------------------------------+
//| Структура сделки. Используется для создания файла истории сделок|
//+------------------------------------------------------------------+
struct SDeal
{
ulong ticket; // Тикет сделки
long order; // Ордер, на основании которого была открыта сделка
long pos_id; // Идентификатор позиции
long time_msc; // Время в миллисекундах
datetime time; // Время
double volume; // Объём
double price; // Цена
double profit; // Прибыль
double commission; // Комиссия по сделке
double swap; // Накопленный своп при закрытии
double fee; // Оплата за проведение сделки, начисляется сразу после совершения сделки
double sl; // Уровень Stop Loss
double tp; // Уровень Take Profit
ENUM_DEAL_TYPE type; // Тип
ENUM_DEAL_ENTRY entry; // Способ изменения позиции
ENUM_DEAL_REASON reason; // Причина или источник проведения сделки
long magic; // Идентификатор эксперта
int digits; // Digits символа
ushort symbol[16]; // Символ
ushort comment[64]; // Комментарий к сделке
ushort external_id[256]; // Идентификатор сделки во внешней торговой системе (на бирже)
//--- Установка строковых свойств
bool SetSymbol(const string deal_symbol) { return(::StringToShortArray(deal_symbol, symbol)==deal_symbol.Length()); }
bool SetComment(const string deal_comment) { return(::StringToShortArray(deal_comment, comment)==deal_comment.Length()); }
bool SetExternalID(const string deal_external_id) { return(::StringToShortArray(deal_external_id, external_id)==deal_external_id.Length()); }
//--- Возврат строковых свойств
string Symbol(void) { return(::ShortArrayToString(symbol)); }
string Comment(void) { return(::ShortArrayToString(comment)); }
string ExternalID(void) { return(::ShortArrayToString(external_id)); }
};
//--- Типы сортировки сделок
enum ENUM_DEAL_SORT_MODE
{
SORT_MODE_DEAL_TICKET = 0, // Режим сравнения/сортировки по тикету сделки
SORT_MODE_DEAL_ORDER, // Режим сравнения/сортировки по ордеру, на основание которого выполнена сделка
SORT_MODE_DEAL_TIME, // Режим сравнения/сортировки по времени совершения сделки
SORT_MODE_DEAL_TIME_MSC, // Режим сравнения/сортировки по времени совершения сделки в миллисекундах
SORT_MODE_DEAL_TYPE, // Режим сравнения/сортировки по типу сделки
SORT_MODE_DEAL_ENTRY, // Режим сравнения/сортировки по направлению сделки
SORT_MODE_DEAL_MAGIC, // Режим сравнения/сортировки по Magic number сделки
SORT_MODE_DEAL_REASON, // Режим сравнения/сортировки по причине или источнику проведения сделки
SORT_MODE_DEAL_POSITION_ID, // Режим сравнения/сортировки по идентификатору позиции
SORT_MODE_DEAL_VOLUME, // Режим сравнения/сортировки по объему сделки
SORT_MODE_DEAL_PRICE, // Режим сравнения/сортировки по цене сделки
SORT_MODE_DEAL_COMMISSION, // Режим сравнения/сортировки по комиссии
SORT_MODE_DEAL_SWAP, // Режим сравнения/сортировки по накопленному свопу при закрытии
SORT_MODE_DEAL_PROFIT, // Режим сравнения/сортировки по финансовому результату сделки
SORT_MODE_DEAL_FEE, // Режим сравнения/сортировки по оплате за проведение сделки
SORT_MODE_DEAL_SL, // Режим сравнения/сортировки по уровню Stop Loss
SORT_MODE_DEAL_TP, // Режим сравнения/сортировки по уровню Take Profit
SORT_MODE_DEAL_SYMBOL, // Режим сравнения/сортировки по имени символа, по которому произведена сделка
SORT_MODE_DEAL_COMMENT, // Режим сравнения/сортировки по комментарию к сделке
SORT_MODE_DEAL_EXTERNAL_ID, // Режим сравнения/сортировки по идентификатору сделки во внешней торговой системе
SORT_MODE_DEAL_TICKET_TESTER, // Режим сравнения/сортировки по тикету сделки в тестере
SORT_MODE_DEAL_POS_ID_TESTER, // Режим сравнения/сортировки по идентификатору позиции в тестере
};
//+------------------------------------------------------------------+
//| Класс сделки. Используется для торговли в тестере стратегий |
//+------------------------------------------------------------------+
class CDeal : public CObject
{
protected:
//--- Целочисленные свойства
ulong m_ticket; // Тикет сделки. Уникальное число, которое присваивается каждой сделке
long m_order; // Ордер, на основание которого выполнена сделка
datetime m_time; // Время совершения сделки
long m_time_msc; // Время совершения сделки в миллисекундах с 01.01.1970
ENUM_DEAL_TYPE m_type; // Тип сделки
ENUM_DEAL_ENTRY m_entry; // Направление сделки – вход в рынок, выход из рынка или разворот
long m_magic; // Magic number для сделки (смотри ORDER_MAGIC)
ENUM_DEAL_REASON m_reason; // Причина или источник проведения сделки
long m_pos_id; // Идентификатор позиции, в открытии, изменении или закрытии которой участвовала эта сделка
//--- Вещественные свойства
double m_volume; // Объем сделки
double m_price; // Цена сделки
double m_commission; // Комиссия по сделке
double m_swap; // Накопленный своп при закрытии
double m_profit; // Финансовый результат сделки
double m_fee; // Оплата за проведение сделки, начисляется сразу после совершения сделки
double m_sl; // Уровень Stop Loss
double m_tp; // Уровень Take Profit
//--- Строковые свойства
string m_symbol; // Имя символа, по которому произведена сделка
string m_comment; // Комментарий к сделке
string m_external_id; // Идентификатор сделки во внешней торговой системе (на бирже)
//--- Дополнительные свойства
int m_digits; // Digits символа
double m_point; // Point символа
ulong m_ticket_tester; // Тикет позиции в тестере
long m_pos_id_tester; // Идентификатор позиции в тестере
public:
//--- Установка свойств сделки
void SetTicket(const ulong ticket) { this.m_ticket=ticket; }
void SetOrder(const long order) { this.m_order=order; }
void SetTime(const datetime time) { this.m_time=time; }
void SetTimeMsc(const long value) { this.m_time_msc=value; }
void SetType(const ENUM_DEAL_TYPE type) { this.m_type=type; }
void SetEntry(const ENUM_DEAL_ENTRY entry) { this.m_entry=entry; }
void SetMagic(const long magic) { this.m_magic=magic; }
void SetReason(const ENUM_DEAL_REASON reason) { this.m_reason=reason; }
void SetPositionID(const long id) { this.m_pos_id=id; }
void SetVolume(const double volume) { this.m_volume=volume; }
void SetPrice(const double price) { this.m_price=price; }
void SetCommission(const double commission) { this.m_commission=commission; }
void SetSwap(const double swap) { this.m_swap=swap; }
void SetProfit(const double profit) { this.m_profit=profit; }
void SetFee(const double fee) { this.m_fee=fee; }
void SetSL(const double sl) { this.m_sl=sl; }
void SetTP(const double tp) { this.m_tp=tp; }
void SetSymbol(const string symbol) { this.m_symbol=symbol; }
void SetComment(const string comment) { this.m_comment=comment; }
void SetExternalID(const string ext_id) { this.m_external_id=ext_id; }
void SetTicketTester(const ulong ticket) { this.m_ticket_tester=ticket; }
void SetPosIDTester(const long pos_id) { this.m_pos_id_tester=pos_id; }
//--- Возврат свойств сделки
ulong Ticket(void) const { return this.m_ticket; }
long Order(void) const { return this.m_order; }
datetime Time(void) const { return this.m_time; }
long TimeMsc(void) const { return this.m_time_msc; }
ENUM_DEAL_TYPE TypeDeal(void) const { return this.m_type; }
ENUM_DEAL_ENTRY Entry(void) const { return this.m_entry; }
long Magic(void) const { return this.m_magic; }
ENUM_DEAL_REASON Reason(void) const { return this.m_reason; }
long PositionID(void) const { return this.m_pos_id; }
double Volume(void) const { return this.m_volume; }
double Price(void) const { return this.m_price; }
double Commission(void) const { return this.m_commission; }
double Swap(void) const { return this.m_swap; }
double Profit(void) const { return this.m_profit; }
double Fee(void) const { return this.m_fee; }
double SL(void) const { return this.m_sl; }
double TP(void) const { return this.m_tp; }
string Symbol(void) const { return this.m_symbol; }
string Comment(void) const { return this.m_comment; }
string ExternalID(void) const { return this.m_external_id; }
int Digits(void) const { return this.m_digits; }
double Point(void) const { return this.m_point; }
ulong TicketTester(void) const { return this.m_ticket_tester; }
long PosIDTester(void) const { return this.m_pos_id_tester; }
//--- Сравнивает два объекта между собой по указанному в mode свойству
virtual int Compare(const CObject *node, const int mode=0) const
{
const CDeal *obj=node;
switch(mode)
{
case SORT_MODE_DEAL_TICKET : return(this.Ticket() > obj.Ticket() ? 1 : this.Ticket() < obj.Ticket() ? -1 : 0);
case SORT_MODE_DEAL_ORDER : return(this.Order() > obj.Order() ? 1 : this.Order() < obj.Order() ? -1 : 0);
case SORT_MODE_DEAL_TIME : return(this.Time() > obj.Time() ? 1 : this.Time() < obj.Time() ? -1 : 0);
case SORT_MODE_DEAL_TIME_MSC : return(this.TimeMsc() > obj.TimeMsc() ? 1 : this.TimeMsc() < obj.TimeMsc() ? -1 : 0);
case SORT_MODE_DEAL_TYPE : return(this.TypeDeal() > obj.TypeDeal() ? 1 : this.TypeDeal() < obj.TypeDeal() ? -1 : 0);
case SORT_MODE_DEAL_ENTRY : return(this.Entry() > obj.Entry() ? 1 : this.Entry() < obj.Entry() ? -1 : 0);
case SORT_MODE_DEAL_MAGIC : return(this.Magic() > obj.Magic() ? 1 : this.Magic() < obj.Magic() ? -1 : 0);
case SORT_MODE_DEAL_REASON : return(this.Reason() > obj.Reason() ? 1 : this.Reason() < obj.Reason() ? -1 : 0);
case SORT_MODE_DEAL_POSITION_ID : return(this.PositionID() > obj.PositionID() ? 1 : this.PositionID() < obj.PositionID() ? -1 : 0);
case SORT_MODE_DEAL_VOLUME : return(this.Volume() > obj.Volume() ? 1 : this.Volume() < obj.Volume() ? -1 : 0);
case SORT_MODE_DEAL_PRICE : return(this.Price() > obj.Price() ? 1 : this.Price() < obj.Price() ? -1 : 0);
case SORT_MODE_DEAL_COMMISSION : return(this.Commission() > obj.Commission() ? 1 : this.Commission() < obj.Commission() ? -1 : 0);
case SORT_MODE_DEAL_SWAP : return(this.Swap() > obj.Swap() ? 1 : this.Swap() < obj.Swap() ? -1 : 0);
case SORT_MODE_DEAL_PROFIT : return(this.Profit() > obj.Profit() ? 1 : this.Profit() < obj.Profit() ? -1 : 0);
case SORT_MODE_DEAL_FEE : return(this.Fee() > obj.Fee() ? 1 : this.Fee() < obj.Fee() ? -1 : 0);
case SORT_MODE_DEAL_SL : return(this.SL() > obj.SL() ? 1 : this.SL() < obj.SL() ? -1 : 0);
case SORT_MODE_DEAL_TP : return(this.TP() > obj.TP() ? 1 : this.TP() < obj.TP() ? -1 : 0);
case SORT_MODE_DEAL_SYMBOL : return(this.Symbol() > obj.Symbol() ? 1 : this.Symbol() < obj.Symbol() ? -1 : 0);
case SORT_MODE_DEAL_COMMENT : return(this.Comment() > obj.Comment() ? 1 : this.Comment() < obj.Comment() ? -1 : 0);
case SORT_MODE_DEAL_EXTERNAL_ID : return(this.ExternalID() >obj.ExternalID() ? 1 : this.ExternalID() <obj.ExternalID() ? -1 : 0);
case SORT_MODE_DEAL_TICKET_TESTER : return(this.TicketTester()>obj.TicketTester()? 1 : this.TicketTester()<obj.TicketTester() ? -1 : 0);
case SORT_MODE_DEAL_POS_ID_TESTER : return(this.PosIDTester() >obj.PosIDTester() ? 1 : this.PosIDTester() <obj.PosIDTester() ? -1 : 0);
default : return(WRONG_VALUE);
}
}
//--- Конструкторы/деструктор
CDeal(const ulong ticket, const string symbol) : m_ticket(ticket), m_symbol(symbol), m_ticket_tester(0), m_pos_id_tester(0)
{ this.m_digits=(int)::SymbolInfoInteger(symbol, SYMBOL_DIGITS); this.m_point=::SymbolInfoDouble(symbol, SYMBOL_POINT); }
CDeal(void) {}
~CDeal(void) {}
};
//+------------------------------------------------------------------+
//| Класс торговли по символу |
//+------------------------------------------------------------------+
CDeal DealTmp; // Временный объект сделки для поиска по свойствам
class CSymbolTrade : public CObject
{
private:
int m_index_next_deal; // Индекс очередной ещё не обработанной сделки
int m_deals_processed; // Количество обработанных сделок
protected:
MqlTick m_tick; // Структура тика
CArrayObj m_list_deals; // Список сделок, проведённых по символу
CTrade m_trade; // Торговый класс
string m_symbol; // Наименование символа
public:
//--- Возвращает список сделок
CArrayObj *GetListDeals(void) { return(&this.m_list_deals); }
//--- Устанавливает символ
void SetSymbol(const string symbol) { this.m_symbol=symbol; }
//--- (1) Устанавливает, (2) возвращает количество обработанных сделок
void SetNumProcessedDeals(const int num) { this.m_deals_processed=num; }
int NumProcessedDeals(void) const { return this.m_deals_processed; }
//--- Добавляет сделку в массив сделок
bool AddDeal(CDeal *deal);
//--- Возвращает сделку (1) по времени в секундах, (2) по индексу в списке,
//--- (3) сделку открытия по идентификатору позиции, (4) текущую сделку в списке
CDeal *GetDealByTime(const datetime time);
CDeal *GetDealByIndex(const int index);
CDeal *GetDealInByPosID(const long pos_id);
CDeal *GetDealCurrent(void);
//--- Возвращает (1) количество сделок в списке, (2) индекс текущей сделки в списке
int DealsTotal(void) const { return this.m_list_deals.Total(); }
int DealCurrentIndex(void) const { return this.m_index_next_deal; }
//--- Устанавливает индекс следующей сделки
void SetNextDealIndex(void) { this.m_index_next_deal++; }
//--- Возвращает (1) символ, (2) описание объекта
string Symbol(void) const { return this.m_symbol; }
string Description(void) const
{
return ::StringFormat("%s trade object. Total deals: %d", this.Symbol(), this.DealsTotal() );
}
//--- Возвращает текущую цену (1) Bid, (2) Ask, время в (3) секундах, (4) миллисекундах
double Bid(void);
double Ask(void);
datetime Time(void);
long TimeMsc(void);
//--- Открывает (1) длинную, (2) короткую позицию, (3) закрывает позицию по тикету
ulong Buy(const double volume, const ulong magic, const double sl, const double tp, const string comment);
ulong Sell(const double volume, const ulong magic, const double sl, const double tp, const string comment);
bool ClosePos(const ulong ticket);
//--- Возвращает результат сравнения текущего времени с указанным
bool CheckTime(const datetime time) { return(this.Time()>=time); }
//--- Обработчик OnTester. Возвращает количество обработанных тестером сделок
double OnTester(void)
{
::PrintFormat("Symbol %s: Total deals: %d, number of processed deals: %d", this.Symbol(), this.DealsTotal(), this.NumProcessedDeals());
return this.m_deals_processed;
}
//--- Сравнивает два объекта между собой (сравнение только по символу)
virtual int Compare(const CObject *node, const int mode=0) const
{
const CSymbolTrade *obj=node;
return(this.Symbol()>obj.Symbol() ? 1 : this.Symbol()<obj.Symbol() ? -1 : 0);
}
//--- Конструкторы/деструктор
CSymbolTrade(void) : m_index_next_deal(0), m_deals_processed(0) {}
CSymbolTrade(const string symbol) : m_symbol(symbol), m_index_next_deal(0), m_deals_processed(0)
{
this.m_trade.SetMarginMode();
this.m_trade.SetTypeFillingBySymbol(this.m_symbol);
}
~CSymbolTrade(void) {}
};
//+------------------------------------------------------------------+
//| CSymbolTrade::Добавляет сделку в массив сделок |
//+------------------------------------------------------------------+
bool CSymbolTrade::AddDeal(CDeal *deal)
{
//--- Если в списке уже есть сделка с тикетом сделки, переданной в метод - возвращаем true
this.m_list_deals.Sort(SORT_MODE_DEAL_TICKET);
if(this.m_list_deals.Search(deal)>WRONG_VALUE)
return true;
//--- Добавляем указатель на сделку в список в порядке сортировки по времени в миллисекундах
this.m_list_deals.Sort(SORT_MODE_DEAL_TIME_MSC);
if(!this.m_list_deals.InsertSort(deal))
{
::PrintFormat("%s: Failed to add deal", __FUNCTION__);
return false;
}
//--- Всё успешно
return true;
}
//+------------------------------------------------------------------+
//| CSymbolTrade::Возвращает объект сделки по времени в секундах |
//+------------------------------------------------------------------+
CDeal* CSymbolTrade::GetDealByTime(const datetime time)
{
DealTmp.SetTime(time);
this.m_list_deals.Sort(SORT_MODE_DEAL_TIME_MSC);
int index=this.m_list_deals.Search(&DealTmp);
return this.m_list_deals.At(index);
}
//+------------------------------------------------------------------+
//|CSymbolTrade::Возвращает сделку открытия по идентификатору позиции|
//+------------------------------------------------------------------+
CDeal *CSymbolTrade::GetDealInByPosID(const long pos_id)
{
int total=this.m_list_deals.Total();
for(int i=0; i<total; i++)
{
CDeal *deal=this.m_list_deals.At(i);
if(deal==NULL || deal.PositionID()!=pos_id)
continue;
if(deal.Entry()==DEAL_ENTRY_IN)
return deal;
}
return NULL;
}
//+------------------------------------------------------------------+
//| CSymbolTrade::Возвращает объект сделки по индексу в списке |
//+------------------------------------------------------------------+
CDeal *CSymbolTrade::GetDealByIndex(const int index)
{
return this.m_list_deals.At(index);
}
//+------------------------------------------------------------------+
//| Возвращает сделку, на которую указывает индекс текущей сделки |
//+------------------------------------------------------------------+
CDeal *CSymbolTrade::GetDealCurrent(void)
{
this.m_list_deals.Sort(SORT_MODE_DEAL_TIME_MSC);
return this.GetDealByIndex(this.m_index_next_deal);
}
//+------------------------------------------------------------------+
//| CSymbolTrade::Возвращает текущую цену Bid |
//+------------------------------------------------------------------+
double CSymbolTrade::Bid(void)
{
::ResetLastError();
if(!::SymbolInfoTick(this.m_symbol, this.m_tick))
{
::PrintFormat("%s: SymbolInfoTick() failed. Error %d",__FUNCTION__, ::GetLastError());
return 0;
}
return this.m_tick.bid;
}
//+------------------------------------------------------------------+
//| CSymbolTrade::Возвращает текущую цену Ask |
//+------------------------------------------------------------------+
double CSymbolTrade::Ask(void)
{
::ResetLastError();
if(!::SymbolInfoTick(this.m_symbol, this.m_tick))
{
::PrintFormat("%s: SymbolInfoTick() failed. Error %d",__FUNCTION__, ::GetLastError());
return 0;
}
return this.m_tick.ask;
}
//+------------------------------------------------------------------+
//| CSymbolTrade::Возвращает текущее время в секундах |
//+------------------------------------------------------------------+
datetime CSymbolTrade::Time(void)
{
::ResetLastError();
if(!::SymbolInfoTick(this.m_symbol, this.m_tick))
{
::PrintFormat("%s: SymbolInfoTick() failed. Error %d",__FUNCTION__, ::GetLastError());
return 0;
}
return this.m_tick.time;
}
//+------------------------------------------------------------------+
//| CSymbolTrade::Возвращает текущее время в миллисекундах |
//+------------------------------------------------------------------+
long CSymbolTrade::TimeMsc(void)
{
::ResetLastError();
if(!::SymbolInfoTick(this.m_symbol, this.m_tick))
{
::PrintFormat("%s: SymbolInfoTick() failed. Error %d",__FUNCTION__, ::GetLastError());
return 0;
}
return this.m_tick.time_msc;
}
//+------------------------------------------------------------------+
//| CSymbolTrade::Открывает длинную позицию |
//+------------------------------------------------------------------+
ulong CSymbolTrade::Buy(const double volume, const ulong magic, const double sl, const double tp, const string comment)
{
this.m_trade.SetExpertMagicNumber(magic);
if(!this.m_trade.Buy(volume, this.m_symbol, 0, sl, tp, comment))
{
return 0;
}
return this.m_trade.ResultOrder();
}
//+------------------------------------------------------------------+
//| CSymbolTrade::Открывает короткую позицию |
//+------------------------------------------------------------------+
ulong CSymbolTrade::Sell(const double volume, const ulong magic, const double sl, const double tp, const string comment)
{
this.m_trade.SetExpertMagicNumber(magic);
if(!this.m_trade.Sell(volume, this.m_symbol, 0, sl, tp, comment))
{
return 0;
}
return this.m_trade.ResultOrder();
}
//+------------------------------------------------------------------+
//| CSymbolTrade::Закрывает позицию по тикету |
//+------------------------------------------------------------------+
bool CSymbolTrade::ClosePos(const ulong ticket)
{
return this.m_trade.PositionClose(ticket);
}
//+------------------------------------------------------------------+