Убил 2 дня на поиск правильного решения по перебору ордеров истории. Без "костылей" осуществить по-прежнему не удаётся.
Задача была извлечь цену открытия и цену закрытия каждой транзакции в истории сделок. Нашёл решение (но это "костыль"):
Как извелчь PriceOpen и PriceClose каждой транзакции, которая уже в истории? Извлечь без костылей - просто средствами языка.
Лучше не ордера перебирать, а сделки. Если позиция открыта по рынку, то в ордере цены нет. При закрытии, точно не знаю есть-ли…
зы: Вот в документации есть такой пример
void OnStart() { color BuyColor =clrBlue; color SellColor=clrRed; //--- request trade history HistorySelect(0,TimeCurrent()); //--- create objects string name; uint total=HistoryDealsTotal(); ulong ticket=0; double price; double profit; datetime time; string symbol; long type; long entry; //--- for all deals for(uint i=0;i<total;i++) { //--- try to get deals ticket if((ticket=HistoryDealGetTicket(i))>0) { //--- get deals properties price =HistoryDealGetDouble(ticket,DEAL_PRICE); time =(datetime)HistoryDealGetInteger(ticket,DEAL_TIME); symbol=HistoryDealGetString(ticket,DEAL_SYMBOL); type =HistoryDealGetInteger(ticket,DEAL_TYPE); entry =HistoryDealGetInteger(ticket,DEAL_ENTRY); profit=HistoryDealGetDouble(ticket,DEAL_PROFIT); //--- only for current symbol if(price && time && symbol==Symbol()) { //--- create price object name="TradeHistory_Deal_"+string(ticket); if(entry) ObjectCreate(0,name,OBJ_ARROW_RIGHT_PRICE,0,time,price,0,0); else ObjectCreate(0,name,OBJ_ARROW_LEFT_PRICE,0,time,price,0,0); //--- set object properties ObjectSetInteger(0,name,OBJPROP_SELECTABLE,0); ObjectSetInteger(0,name,OBJPROP_BACK,0); ObjectSetInteger(0,name,OBJPROP_COLOR,type?BuyColor:SellColor); if(profit!=0) ObjectSetString(0,name,OBJPROP_TEXT,"Profit: "+string(profit)); } } } //--- apply on chart ChartRedraw(); }
его достаточно просто переделать под ваши нужды.
Только надо добавить
DEAL_POSITION_ID | Идентификатор позиции, в открытии, изменении или закрытии которой участвовала эта сделка. Каждая позиция имеет уникальный идентификатор, который присваивается всем сделкам, совершенным на инструменте в течение всей жизни позиции. | long |
POSITION_IDENTIFIER | Идентификатор позиции - это уникальное число, которое присваивается каждой вновь открытой позиции и не изменяется в течение всей ее жизни. Соответствует тикету ордера, которым была открыта позиция. Идентификатор позиции указывается в каждом ордере (ORDER_POSITION_ID) и сделке (DEAL_POSITION_ID), которая ее открыла, изменила или закрыла. Используйте это свойство для поиска ордеров и сделок, связанных с позицией. При развороте позиции в режиме неттинга (единой сделкой in/out) идентификатор позиции POSITION_IDENTIFIER не изменяется. Однако при этом POSITION_TICKET изменяется на тикет ордера, в результате которого произошел разворот. В режиме хеджинга разворот позиции не предусмотрен. | long |
для того, чтобы не получить открытие одной позиции и закрытие другой.
Некоторые костыли с открытым исходным кодом, поэтому можно посмотреть...
Ticket.
Сначала нужно определиться с идентификацией закрытой позиции - тикет. Это можно сделать через тикеты соответствующих ордеров, сделок или позиций. Для каждого варианта MT5 предалагает по ПКМ нужное представление истории торговли. Поэтому, наверное, надо делать такой код.
#property script_show_inputs input long inDealTicket = 0; // Deal Ticket input long inOrderTicket = 0; // Order Ticket input long inPositionTicket = 0; // Position Ticket #define TOSTRING(A) #A + " = " + (string)(A) + " " void OnStart() { Print(TOSTRING(inDealTicket) + TOSTRING(PriceOpenByDeal(inDealTicket))); Print(TOSTRING(inOrderTicket) + TOSTRING(PriceOpenByOrder(inOrderTicket))); Print(TOSTRING(inPositionTicket) + TOSTRING(PriceOpenByPosition(inPositionTicket))); Print(TOSTRING(inDealTicket) + TOSTRING(PriceCloseByDeal(inDealTicket))); Print(TOSTRING(inOrderTicket) + TOSTRING(PriceCloseByOrder(inOrderTicket))); Print(TOSTRING(inPositionTicket) + TOSTRING(PriceCloseByPosition(inPositionTicket))); }
Дальше возникает проблема с определением цены открытия и закрытия.
PriceOpen.
Хорошо бы понимать, что такое цена открытия в следующих ситуациях.
- Позиция открылась одной сделкой и закрылась.
- Позиция открылась несколькими сделками и закрылась.
- Отложенный ордер частично исполнился и открыл позицию. Затем модицифировался и долил позицию. Затем позиция была закрыта, а остаток отложки был модифицирован. Затем остаток исполнился, открыв позицию, и она закрылась.
double PriceOpenByDeal( const long Deal ) { return((HistoryDealSelect(Deal) && HistorySelectByPosition(HistoryDealGetInteger(Deal, DEAL_POSITION_ID))) ? HistoryDealGetDouble(HistoryDealGetTicket(0), DEAL_PRICE) : 0); } double PriceOpenByOrder( const long Order ) { return((HistoryOrderSelect(Order) && HistorySelectByPosition(HistoryOrderGetInteger(Order, ORDER_POSITION_ID))) ? HistoryDealGetDouble(HistoryDealGetTicket(0), DEAL_PRICE) : 0); } double PriceOpenByPosition( const long Position ) { return(HistorySelectByPosition(Position) ? HistoryDealGetDouble(HistoryDealGetTicket(0), DEAL_PRICE) : 0); }
Для остальных пунктов писать не готов. Возможно, этого будет достаточно для большинства.
PriceClose.
- Позиция закрылась одной сделкой.
- Позиция частично закрывалась несколькими сделками.
- п.3 выше.
Для п.1. такой код.
double PriceCloseByDeal( const long Deal ) { return((HistoryDealSelect(Deal) && HistorySelectByPosition(HistoryDealGetInteger(Deal, DEAL_POSITION_ID)) ? HistoryDealGetDouble(HistoryDealGetTicket(HistoryDealsTotal() - 1), DEAL_PRICE) : 0)); } double PriceCloseByOrder( const long Order ) { return((HistoryOrderSelect(Order) && HistorySelectByPosition(HistoryOrderGetInteger(Order, ORDER_POSITION_ID))) ? HistoryDealGetDouble(HistoryDealGetTicket(HistoryDealsTotal() - 1), DEAL_PRICE) : 0); } double PriceCloseByPosition( const long Position ) { return(HistorySelectByPosition(Position) ? HistoryDealGetDouble(HistoryDealGetTicket(HistoryDealsTotal() - 1), DEAL_PRICE) : 0); }
Для остальных пунктов писать не готов. Возможно, этого будет достаточно для большинства.
Производительность.
Если нужно перебрать историю торговли, где много сделок/ордеров, то цикл с телом из кода выше может очень долго выполняться, т.к. такой код никуда не годится для массовых операций. Чтобы было быстро, нужно погружаться в архитектуру. Чтобы каждый этого не делал, существуют костыли.
ЗЫ Код не проверял - не запускал. Знаю архитектуру, поэтому почти уверен, что ошибок нет.
@Alexey Viktorov спасибо. Пример этот видел, по нему и пытался разобраться. Не понял зачем было так усложнять язык? Было же хорошо OrderClosePrice() и всё. Который раз убеждаюсь: лучшее - враг хорошего. Чем лучше стало - не понимаю. А чем хуже - понятно: хрен разберёшься. Это в духе метаквотесов - дать крупицу инфы и ни какой ясности :(
@fxsaber спасибо, что откликнулись. Тип счёта - хеджинговый и только хеджинговый. Преимущества неттингового не вижу совсем, поэтому не использую. Отложенные ордера в цикле перебора не участвуют. Честно говря, Ваш код не добавил ясности. Хорошо, попробуем иначе.
P.S.
Меня тут знакомый спросил, зачем я создал новую тему, а не написал в "Ошибки, баги, вопросы"? Сначала я так и хотел сделать. Но покопав конкретон информацию, понял что ясности в этом вопросе в инете нет вообще. Поэтому решил вынести в отдельную тему. Во-первых, чтоб обсуждение не перемешивалось в кашу с ответами, темы этой не скасающимися. Во-вторых, чтоб другой ищущий в этом вопросе ясности (а он 100% будет) мог найти результат этого обсуждения.
Чуть позже я покажу циклы, которыми пытался вывести информацию на экран. От их использования вопросов только добавляется...
Вот такое нужно?
HistorySelect(0,TimeCurrent()); int historyDealsTotal = HistoryDealsTotal(); for(int i=0; i < historyDealsTotal && !IsStopped(); i++) { ulong ticketIn=HistoryDealGetTicket(i); if(ticketIn > 0 && HistoryDealGetInteger(ticketIn,DEAL_ENTRY) == DEAL_ENTRY_IN) { double dealPriceOpen = HistoryDealGetDouble(ticketIn,DEAL_PRICE); // цена открытия ulong positionId = HistoryDealGetInteger(ticketIn,DEAL_POSITION_ID); for(int q=0; q < historyDealsTotal && !IsStopped(); q++) { ulong ticketOut=HistoryDealGetTicket(q); if(ticketOut > 0 && HistoryDealGetInteger(ticketOut,DEAL_ENTRY) == DEAL_ENTRY_OUT) { if(HistoryDealGetInteger(ticketOut,DEAL_POSITION_ID) == positionId) { double dealPriceClose = HistoryDealGetDouble(ticketOut,DEAL_PRICE); // цена закрытия break; } } } } }
upd. Если правильно понимаю, при программном извлечении данных по историческим сделкам, все они (сделки) сортированы от самой новой к самой старой. Тогда, наверное, лучше искать от закрытия к открытию. И открытие искать с индекса закрытия + 1.
Производительность.
Если нужно перебрать историю торговли, где много сделок/ордеров, то цикл с телом из кода выше может очень долго выполняться, т.к. такой код никуда не годится для массовых операций. Чтобы было быстро, нужно погружаться в архитектуру. Чтобы каждый этого не делал, существуют костыли.
Замер производительности.
#define TOSTRING(A) #A + " = " + (string)(A) + " " #define BENCH(A) \ { \ const ulong StartTime = GetMicrosecondCount(); \ Print(TOSTRING(A) + "- " + \ (string)(GetMicrosecondCount() - StartTime) + " mcs."); \ } double CalcDeals1( const ulong &Deals[] ) { double Res = 0; for (uint i = ArraySize(Deals); (bool)i--;) Res += PriceOpenByDeal(Deals[i]); // https://www.mql5.com/ru/forum/457706#comment_50623924 return(Res); } void OnStart() { HistorySelect(0, INT_MAX); Print(TOSTRING(HistoryDealsTotal()) + TOSTRING(HistoryOrdersTotal())); ulong Deals[]; // Собрали все сделки в массив, чтобы хоть как-то оптимизировать. for (uint i = ArrayResize(Deals, HistoryDealsTotal()); (bool)i--;) Deals[i] = HistoryDealGetTicket(i); BENCH(CalcDeals1(Deals)) // BENCH(CalcDeals2(Deals)) }
Результат.
HistoryDealsTotal() = 31383 HistoryOrdersTotal() = 65969 CalcDeals1(Deals) = 48559.13890000004 - 21997433 mcs. CalcDeals2(Deals) = 48559.13890000004 - 81357 mcs.
Почти в 300 раз медленнее не самого быстрого варианта. В общем, теория подтвердилась практикой.
По-ходу я вообще еду мимо. Раз ясности нет, будем вычислять. Пишем простой скрипт.
void OnStart(){ //--- request trade history HistorySelect(0,TimeCurrent()); // Нахрена это вызывать, я так и не понял uint TotalOrder=HistoryOrdersTotal(); uint TotalDeal=HistoryDealsTotal(); Print("TotalOrder = ",TotalOrder); Print("HTotalDeal = ",TotalDeal); return; }
Кидаем на график, получаем в журнале экспертов результат:
Опа, а сделок-то всего 4!
Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий
fxsaber, 2023.11.20 22:28
HistoryDealsTotal() = 31383 HistoryOrdersTotal() = 65969 CalcDeals1(Deals) = 48559.13890000004 - 21997433 mcs. CalcDeals2(Deals) = 48559.13890000004 - 81357 mcs.
Почти в 300 раз медленнее не самого быстрого варианта.
CalcDeals1(Deals) = 48559.13890000004 - 19674 mcs.
Если понимать архитектуру, можно добиваться ускорений на порядки по сравнению с рекомендациями для начинающих.
Но небольшую стороннюю библиотеку (не торговую) для этого все же пришлось использовать.
#include <fxsaber\TradesID\TradesID.mqh> // https://www.mql5.com/ru/code/34173 double PriceOpenByDeal( const long Deal ) { static TRADESID TradesID; ulong Deals[]; return(TradesID.GetDealsByID(HistoryDealGetInteger(Deal, DEAL_POSITION_ID), Deals) ? HistoryDealGetDouble(Deals[0], DEAL_PRICE) : 0); }
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Убил 2 дня на поиск правильного решения по перебору ордеров истории. Без "костылей" осуществить по-прежнему не удаётся.
Задача была извлечь цену открытия и цену закрытия каждой транзакции в истории сделок. Нашёл решение (но это "костыль"):
#include <MT4Orders.mqh>
Как извелчь PriceOpen и PriceClose каждой транзакции, которая уже в истории? Извлечь без костылей - просто средствами языка.