Bibliotecas: OnTickMulti - página 5

 
Stanislav Korotky #:

Para um caso de teste completo, eu precisaria entender a tarefa prática: negociamos apenas com base em ticks cujo tempo coincide com precisão de milissegundos?

Não. O objetivo é sempre manter um ambiente atualizado para todos os símbolos. No meu exemplo, a condição de atualidade é sempre satisfeita no OnTimer. A abertura da posição é apenas uma demonstração.
 
fxsaber #:
Não. O objetivo é sempre manter um ambiente atualizado em relação a todos os símbolos. No meu exemplo, no OnTimer, a condição de atualização é sempre satisfeita. A abertura da posição é apenas uma demonstração.

Tudo bem.

Ainda não está claro onde a implementação no evento OnTickMulti “perde” o tick ou, em outras palavras, como é que o SymbolInfoTick retorna preços diferentes dos esperados (eu havia suposto anteriormente que provavelmente há um atraso devido à dispachação de eventos personalizados do indicador) — eu testaria sem o indicador-espião para manter a pureza do experimento — apenas SymbolInfoTick/CopyTicks em todos os símbolos a partir do OnTick comum, pelo menos para o mesmo timestamp.

Quanto ao OnTimer, ele me levanta algumas dúvidas (corrijam-me se eu estiver errado):

void OnTimer()
{
  if (TimeMsc++ == 1759280400081) // 01/10/2025 01:00:00.081
    PositionOpen();
}

Não temos garantia de que o manipulador será executado em menos de um milissegundo; portanto, um simples incremento do contador não garante a manutenção da sincronização inicial, ou seja, a sincronização com a hora do servidor precisa ser feita corretamente em tempo real a cada vez (após a abertura de uma posição ou outros cálculos mais complexos do que um simples incremento, se houver). Em outras palavras, a abordagem refinada apresentada não vai funcionar quando for preciso abrir muitas posições.

Além disso, a sincronização inicial (inicialização do contador) também não é 100% correta, na minha opinião.

ulong TimeMsc = TimeTradeServer() * 1000 + (inTimer && EventSetMillisecondTimer(1));

Digamos que, no testador, a hora do servidor realmente comece sem milissegundos, mas como esse código funcionaria online? E por que adicionamos 1 milissegundo? Eu, no entanto, obteria a hora do servidor a partir dos ticks.

Não se trata de picuinhas, mas simplesmente de dúvidas quanto ao “cumprimento constante da condição de atualidade”.

 
Stanislav Korotky #:

O simples incremento do contador não garante a manutenção da sincronização inicial

No Testador, isso é garantido.

Mas como esse código funcionaria online?

Online, esse problema não existe, pois todos os dados que chegam ao terminal são indicativos: desatualizados devido a atrasos.

E por que adicionamos 1 milissegundo?

Porque o primeiro OnTimer será chamado após um intervalo definido, e não imediatamente.

 
Stanislav Korotky #:

Ainda não está claro onde a execução no evento OnTickMulti “perde” o tick ou, em outras palavras, como é que o SymbolInfoTick retorna preços diferentes dos esperados

O OnTickMulti certamente não está envolvido, já que o próprio SymbolInfoTick padrão de um dos símbolos retorna o tick correto, enquanto para outros símbolos isso não ocorre.


A razão está apenas na sequência de envio dos ticks.

Fórum sobre trading, sistemas de negociação automatizados e testes de estratégias de trading

Bibliotecas: OnTickMulti

fxsaber, 30/09/2025 09:24

Os ticks com o mesmo horário não chegam simultaneamente. Todos chegam sequencialmente. E se um tick do EURUSD com um horário maior chegar primeiro, nesse momento não se sabe nada sobre o tick com o mesmo horário do GBPUSD, que chegará em segundo lugar. Portanto, no momento em que o primeiro tick do EURUSD chega, o segundo tick do GBPUSD simplesmente não existe, mas há os dados do tick anterior do GBPUSD.
 
fxsaber #:

Porque o primeiro OnTimer será acionado após um intervalo definido, e não imediatamente.

Criei uma maneira rápida de atualizar todos os dados.

#include <fxsaber\OnTickMulti\OnTickMulti.mqh> // https://www.mql5.com/pt/code/47647

#include <MT4Orders.mqh> // https://www.mql5.com/pt/code/16006

void PositionOpen()
{
  for (uint i = ArraySize(OnTickMultiObject.Symbols); (bool)i--;)
  {
    const string SymbName = OnTickMultiObject.Symbols[i];
    
    OrderSend(SymbName, OP_BUY, 1, SymbolInfoDouble(SymbName, SYMBOL_ASK), 0, 0, 0);
  }
}

long CurrentTime = 0;

// OnTick com vários caracteres.
void OnTickMulti( const string &Symb, const uint &Index )
{  
  MqlTick Tick;
  
  if (SymbolInfoTick(Symb, Tick) && (Tick.time_msc > CurrentTime))
  {
    CurrentTime = Tick.time_msc;
    
    EventSetMillisecondTimer(1);
  }  
}

void OnTimer()
{
  if (CurrentTime == 1759280400081) // 01/10/2025 01:00:00.081
    PositionOpen();
    
  EventKillTimer();
}

Esse mecanismo permite que, mesmo no modo normal (mono-moeda sem OnTickMulti), se trabalhe apenas com dados atualizados. Por exemplo, no EURUSD, há vários ticks com o mesmo horário. A atualização permite trabalhar com o tick mais recente — o último dessa sequência.


P.S. Essa é mais uma razão para criar símbolos personalizados: registrar no histórico apenas o último tick das sequências com horário idêntico. Nesse caso, no modo de moeda única, a atualização será sempre respeitada.

Библиотеки: TicksShort
Библиотеки: TicksShort
  • 2025.09.26
  • www.mql5.com
Статьи и техническая библиотека по автоматическому трейдингу: Библиотеки: TicksShort
 
Rorschach #:

Mas, afinal, os preços de abertura também são os mesmos, embora o horário de abertura seja o mesmo para todos os símbolos.

E, devido a esse comportamento, não é possível testar corretamente uma série de sistemas.

Refresquei a memória sobre o modo de teste com preços de abertura. O detalhe é que, apesar do nome, o testador gera, nesse modo, 4 ticks OHLC, e não 1, como seria intuitivamente esperado pelo nome. Desses 4 pontos de controle, para os experts, é considerada apenas a primeira O e o OnTick é chamado; já para os indicadores, além disso, para HLC ou LHC (dependendo da direção da barra), o OnCalculate é chamado 3 vezes com ticks para os preços correspondentes. Os tempos para esses três pontos adicionais são definidos artificialmente como iguais às últimas três segundos da barra. Isso significa que o indicador-espião envia vários eventos para os símbolos, em vez de apenas um. Provavelmente, isso deve ser levado em consideração por quem usa o modo com preços de abertura.

Além disso, observei um artefato (ao adicionar depuração ao meu indicador-espião semelhante, baseado no livro) de que o evento relativo ao tick de um símbolo adicional da barra anterior, por algum motivo, se repete na nova barra, só que, em seguida, o OnCalculate do indicador-espião é chamado e chega o evento de um novo tick do símbolo adicional com o preço atualizado. Como resultado, para ter o preço atualizado dos símbolos adicionais, é preciso ter um carimbo de data/hora nos próprios eventos e não processar repetidamente os eventos já processados. Eu faço isso no meu código ao enviar da seguinte forma:

int OnCalculate(const int rates_total, const int prev_calculated, const int, const double &price[])
{
   MqlTick tick;
   SymbolInfoTick(_Symbol, tick);
   EventChartCustom(Chart, Message, Index, (double)tick.time_msc, NULL);
  
   return rates_total;
}

E na recepção (é mostrado o caso de um único símbolo adicional; para muitos, é necessário um array timestamp[]!):

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
   if(id == CHARTEVENT_CUSTOM + Message)
   {
      static long timestamp;
      long event = (long)dparam;
      if(event > timestamp)
      {
         OnSymbolTick((int)lparam);
         timestamp = event;
      }
   }
}

Vale ressaltar que, na presença de ticks com milissegundos iguais, o primeiro deles é executado, e não o último.

 
Rorschach #:
A maneira mais simples de sincronização
Achei que a sincronização de séries temporais fosse um problema bem estudado e que já existissem algoritmos prontos, mas quando procurei na internet, não encontrei nada