Библиотеки: MT4Orders - страница 79

 

Реал. Использую MT4Orders, виртуальное окружение через Virtual, виртуал синхронизирую с реалом. Для этого на каждом тике прохожусь по открытым в реале ордерам несколько раз. Для этого на первом проходе формирую список интересующих ордеров, на остальных проходах уже работаю с этим списком.

Раньше был такой код:

long OrderTickets[];
for(int i=OrdersTotal()-1;i>=0;--i)
{
  if(!OrderSelect(i,SELECT_BY_POS,MODE_TRADES))
    continue;
  AddToArray(OrderTickets,OrderTicket());
}
...
for(int i=ArraySize(OrderTickets)-1;i>=0;--i)
{
  if(!OrderSelect(OrderTickets[i],SELECT_BY_TICKET,MODE_TRADES))
    continue;
  do_something();
}

Практика показала, что так делать не стоит. Частично залитый лимитник породит 2 ордера с одинаковым тикетом, и на втором проходе будет печаль.

Сейчас перешёл на такой код

int Orders[];
for(int i=OrdersTotal()-1;i>=0;--i)
{
  if(!OrderSelect(i,SELECT_BY_POS,MODE_TRADES))
    continue;
  AddToArray(Orders,i);
}
...
for(int i=0;i!=ArraySize(Orders);++i)
{
  if(!OrderSelect(Orders[i],SELECT_BY_POS,MODE_TRADES))
    continue;
  do_something();
}

Дополнительно делается снапшот перед работой. Так будет работать корректно или может ещё что-то вылезти?

 
traveller00 #:

Реал. Использую MT4Orders, виртуальное окружение через Virtual, виртуал синхронизирую с реалом. Для этого на каждом тике прохожусь по открытым в реале ордерам несколько раз. Для этого на первом проходе формирую список интересующих ордеров, на остальных проходах уже работаю с этим списком.

Раньше был такой код:

Практика показала, что так делать не стоит. Частично залитый лимитник породит 2 ордера с одинаковым тикетом, и на втором проходе будет печаль.

Печали не будет, если делать так.

AddToArray(OrderTickets, (OrderType() <= OP_SELL) ? OrderTicket() : -OrderTicket());

Это фича MT4Orders. Однако, SELECT_BY_TICKET - это небыстрый механизм в MT4/5 и виртуалке. Одна из причин - если не найдет среди живых, полезет в историю (MODE_TRADES-флаг - только указание приоритета, где сначала искать).


Сейчас перешёл на такой код

Дополнительно делается снапшот перед работой. Так будет работать корректно или может ещё что-то вылезти?

Здесь уже речь о дополнении от Virtual.mqh. Безусловно, снепшоты очень круто ускорят работу, если торгуется много символов/мэджиков.

Снепшот сводит работу с внешним для советника окружением (торговый API) к минимуму. Т.е. самые тормозные функции вызываются единожды для изначального считывания, а далее вся работа с торговым окружением идет внутри советника незаметно для программиста, т.к. все тот же MT4-style.


Имеет смысл использовать один из этих вариантов снепшота.

#define VIRTUAL_SNAPSHOT_REFRESHTIME 1000 // Время жизни снепшота для обновления. В MT5 требует подключенной MT4Orders.mqh
#define VIRTUAL_SNAPSHOT_WITHOUT_HISTORY // Отказ от снепшота истории для повышения производительности
#include <fxsaber\Virtual\Virtual.mqh>

VIRTUAL::Snapshot(); // 1: один (_Symbol) символ, все мэджики.
VIRTUAL::Snapshot(VIRTUAL_SNAPSHOT_REFRESHTIME, MyMagic); // 2: один (_Symbol), один мэджик.
VIRTUAL::Snapshot(VIRTUAL_SNAPSHOT_REFRESHTIME, -1, false, ""); // 3: все символы и мэджики.


  1. Использую его. У меня в моновалютном советнике может быть несколько подТС (каждая со своим мэджиком). И даже если несколько советников на одном символе, все равно вижу для себя целесообразным использовать именно этот вариант. Т.е. один раз заснепшотил, а дальше все подТС работают с результатом.
  2. Если моновалютный советник работает только с одним мэджиком, то самый быстрый вариант.
  3. Мультивалютный советник, где можно задать мэджик или все мэджики.


Можно тысячу раз подряд вызвать VIRTUAL::Snapshot(), но он не будет взаимодействать с внешним торговым API чаще, чем задано в VIRTUAL_SNAPSHOT_REFRESHTIME (в примере это одна миллисекунда - мне хватает). Поэтому ничего не требуется запоминать, код, грубо говоря, становится таким.

VIRTUAL::Snapshot();

for (uint i = OrdersTotal(); (bool)i--;)
{
  VIRTUAL::Snapshot();
  
  do_something();
}


Вот мой код синхронизации (вызывается для каждой подТС) с виртуальными окружениями.

  // Синхронизирует VirtualPointerTo-окружение на основе текущего (выбранного) и VirtualPointerOut (для закрытия позиций).
  static bool SyncChannel( const string &Symb, const MAGIC_TYPE Magic, const double Lots,
                           const VIRTUAL_POINTER &VirtualPointerTo, const VIRTUAL_POINTER &VirtualPointerOut, const int FlagChange = false )
  {      
    // Нужно для механизма IsNull.
    if (!VirtualPointerTo.GetHandle() || !VIRTUAL::GetHandle())
      _B2(VIRTUAL::Snapshot());    
    
    const bool IsNull = !OrdersTotal() && !_VP(VirtualPointerTo, OrdersTotal());
    bool Res = IsNull || !FlagChange || _B(SYNCCHANNEL::IsNotChange(true), 500);
  
    if (!IsNull)
    {
      Res &= SYNCCHANNEL::IsCloseBy(/*Symb*/) && MACRO_ISCHANGE(SYNCCHANNEL::SyncCloseBy(Symb, Magic, VirtualPointerTo/*, (inMinLotCorrection <= 0)*/));
      
      Res &= MACRO_ISCHANGE(SYNCCHANNEL::SyncExistOrders(Symb, Magic, VirtualPointerTo, VirtualPointerOut));

      if (Lots && (inMinLotCorrection > Lots))
        ::Alert("inMinLotCorrection(" + (string)inMinLotCorrection + ") > Lots(" + (string)Lots + ")");
      
      Res &= MACRO_ISCHANGE(SYNCCHANNEL::SetNewLimit(Symb, Magic, Lots, OP_BUYLIMIT, VirtualPointerTo, inMinLotCorrection));
      Res &= MACRO_ISCHANGE(SYNCCHANNEL::SetNewLimit(Symb, Magic, Lots, OP_SELLLIMIT, VirtualPointerTo, inMinLotCorrection));
    }    

    return(Res);    
  }

Из выделенного видно создание снепшота. Его следует вызывать после тормозных функций. Например, OrderSend. Если лишний раз вызвать - ничего страшного, как показал в коде выше.

Однако, исходник привел по другой причине. Все потенциально тормозные функции при синхронизации облачаю в макрос, который полностью останавливает синхронизацию, если во время нее пришел новый тик (SymbolInfoTick или CopyTicks), т.к. требуется сначала новые тики пробросить через все виртуалки, а только потом синхронизировать с реалом. Все точно также, как должно работать у торговых копиров и на внешних крипто-биржах.

 
Словил OrderBug22 В чем может бить проблема? RP 0 19:00:01.733 Pip Funnels 1.0 (AUDJPY,H2) Line = 1537 NF 0 19:00:01.733 Pip Funnels 1.0 (AUDJPY,H2) Before MT4ORDERS::HistoryDealSelect(Result): DP 0 19:00:01.733 Pip Funnels 1.0 (AUDJPY,H2) MT4ORDERS::OrderSendBug = 22 CL 0 19:00:01.733 Pip Funnels 1.0 (AUDJPY,H2) Result.deal = 0
 
Sviatoslav Tyshchyk #:
Словил OrderBug22 В чем может бить проблема? RP 0 19:00:01.733 Pip Funnels 1.0 (AUDJPY,H2) Line = 1537 NF 0 19:00:01.733 Pip Funnels 1.0 (AUDJPY,H2) Before MT4ORDERS::HistoryDealSelect(Result): DP 0 19:00:01.733 Pip Funnels 1.0 (AUDJPY,H2) MT4ORDERS::OrderSendBug = 22 CL 0 19:00:01.733 Pip Funnels 1.0 (AUDJPY,H2) Result.deal = 0

Нет проблемы.

 
Такой прием позволяет убрать из истории торгов отложенные ордера.
int HistoryOrdersTotalTmp( void ) { return(-1); }

#define HistoryOrdersTotal HistoryOrdersTotalTmp
  #include <MT4Orders.mqh> // https://www.mql5.com/ru/code/16006
#undef HistoryOrdersTotal
 
Line = 1773
Before ::HistoryOrderSelect(Result.order):
MT4ORDERS::OrderSendBug = 96
Result.deal = 0
Line = 1796
Before MT4ORDERS::HistoryDealSelect(Result):
MT4ORDERS::OrderSendBug = 96
Result.deal = 0
Alert: OrderSend(1505369998) - BUG!
Alert: Please send the logs to the coauthor - https://www.mql5.com/en/users/fxsaber
Alert: C:\Program Files\MetaTrader 5 5\MQL5\Logs\20240402.log

какой-то баг

 
leonerd #:

какой-то баг

Это норма.