Organizar el ciclo de pedidos - página 9

 
fxsaber:

Cambiado

Ahora sin alertas.

La respuesta es obvia - si Ticket == PrevTicket ---> devolver WRONG_VALUE.

Si la función devuelve un valor menor que cero, entonces debe ser llamada de nuevo.

 
Artyom Trishkin:

La respuesta es obvia - si Ticket == PrevTicket ---> devolver WRONG_VALUE.

Por lo tanto, se trata de la solución de un caso especial del problema que he descrito para mayor claridad en forma de igualdad de billetes vecinos.

En realidad, el problema es el mismo: la agitación de la indexación durante el bucle. Esto puede llevar a una situación en la que se salte algún billete, o se repita un billete en uno, etc.

No he encontrado una alternativa a IsChange.

 
fxsaber:

Se trata, pues, de una solución a un caso particular del problema, que he descrito, para mayor claridad, en forma de igualdad de billetes vecinos.

En realidad, el problema es el mismo: el temblor de la indexación durante el ciclo. Esto puede llevar a una situación en la que se salte algún billete, o se repita un billete en uno, etc.

No he encontrado una alternativa a IsChange.

Creamos una lista en el temporizador y trabajamos con ella.

 
Artyom Trishkin:

Crea una lista en el temporizador y trabaja con ella.

Mejor en código.

 
fxsaber:

Mejor en código.

Es muy difícil de mostrar en el código - hay toda una biblioteca de clases interrelacionadas.

La idea es la siguiente - pasamos todas las órdenes y posiciones en la cuenta y llenamos la lista CArrayObj en el temporizador. Lo actualizamos constantemente para obtener información real en una sola pasada.

Si es necesario cerrar posiciones o eliminar órdenes, obtenemos esta lista y seleccionamos de ella los objetos de orden necesarios y sus tickets que se necesitan para las funciones de cierre (modificación). Si este objeto-orden está físicamente ausente (cerrado o eliminado durante estas acciones), simplemente pasamos al siguiente objeto de la lista, puesto que éste ya ha sido cerrado, y la lista se actualizará en la siguiente iteración del temporizador. El primer problema que se me ocurre es que la lista es irrelevante cuando el entorno comercial cambia durante el tiempo en que se realizan las acciones en la lista. Pero tal y como yo lo veo, la ausencia física de una orden en la lista no debería molestarnos demasiado -simplemente obtenemos un error y pasamos a la siguiente de la lista- esta lista no se baraja como en un entorno comercial y es imposible saltársela -sólo se constata el hecho de la ausencia de una orden correspondiente a una entrada de la lista.

Esto es lo que pienso hasta ahora, ya que aún no lo he implementado en mi código. Me estoy preparando para la implementación, pero inicialmente necesito terminar algunas otras clases (hice una pregunta sobre la sustitución de funciones en la rama de Características). Cuando empiece la implementación, los posibles problemas serán visibles allí, y decidiré cómo resolverlos y arreglarlos.

 
Artyom Trishkin:

La respuesta es obvia - si Ticket == PrevTicket ---> devolver WRONG_VALUE.

Si la función devuelve un valor inferior a cero, debemos llamarla de nuevo.

No tienes que hacer nada en absoluto. Basta con seguir la lógica pareciendo un idiota.

Tenemos 6 pedidos

  • 0 billete 100
  • 1 billete 101
  • 2 boletos 102
  • 3 boletos 103
  • 4 boletos 104
  • 5 boleto 105

Empezamos a revisar los pedidos para, digamos, modificarlos,

Elegimos el billete 5 105, comprobamos si queremos modificarlo y lo modificamos.

En ese momento, nuestras sucias manos borraron la orden 2 del billete 102, y la lista cambió. La orden con el billete 105 es ahora la cuarta, la 4, de la lista. Y vuelve a estar seleccionada para su modificación. Pero no trabajamos sin comprobar... ¡¡¡comprobamos si se debe modificar, PERO!!! No lo hace... ¿Y qué? ¿Quién se ha visto perjudicado por la reordenación del orden?

 
Artyom Trishkin:

Es muy difícil de mostrar en el código - hay toda una biblioteca de clases interrelacionadas.

La idea es la siguiente - pasamos todas las órdenes y posiciones en la cuenta y llenamos la lista CArrayObj en el temporizador. Lo actualizamos constantemente para obtener información real en una sola pasada.

Si es necesario cerrar posiciones o eliminar órdenes, obtenemos esta lista y seleccionamos de ella los objetos de orden necesarios y sus tickets que se necesitan para las funciones de cierre (modificación). Si este objeto-orden está físicamente ausente (cerrado o eliminado durante estas acciones), simplemente pasamos al siguiente objeto de la lista, puesto que éste ya ha sido cerrado, y la lista se actualizará en la siguiente iteración del temporizador. El primer problema que se me ocurre es que la lista es irrelevante cuando el entorno comercial cambia durante el tiempo en que se realizan las acciones en la lista. Pero tal y como yo lo veo, la ausencia física de una orden en la lista no debería molestarnos demasiado -simplemente obtenemos un error y pasamos a la siguiente de la lista- esta lista no se baraja como en un entorno comercial y es imposible saltársela -sólo se constata el hecho de la ausencia de una orden correspondiente a una entrada de la lista.

Esto es lo que he imaginado hasta ahora, ya que aún no lo he implementado en mi código. Me estoy preparando para la implementación, pero inicialmente necesito terminar algunas otras clases (hice una pregunta sobre la sustitución de funciones en la rama de Características). Cuando empiece la aplicación, los posibles problemas serán visibles y decidiré cómo resolverlos y arreglarlos.

He aquí una aplicación de esto

Foro sobre trading, sistemas de trading automatizados y pruebas de estrategias de trading

Organizar un bucle de pedidos

fxsaber, 2017.09.11 20:29

// Редкий, но правильный костяк модификации ордеров
for (int i = OrdersTotal() - 1; i >= 0; i--)
  if (OrderSelect(i, SELECT_BY_POS))
    if (OrderModify(OrderTicket(), Price, SL, TP, OrderExpiration()))     
    {
      i = OrdersTotal(); // Хотя бы так
      
      // А лучше так
//      OnTick(); break; // вместо строки выше лучше делать такой вызов (переполнения стека от рекурсивных вызовов быть не должно)
    }


Después de enviar una orden comercial, el entorno comercial cambia, por lo que es aconsejable ejecutar toda la lógica comercial del ST desde cero inmediatamente después de la respuesta del servidor comercial.

Sólo que todavía requiere IsChange. El temporizador no es una opción en absoluto. Incluso Sleep(1) estropea todo el panorama.

 
Alexey Viktorov:

No hay que hacer absolutamente nada. Basta con pasar por la lógica con la mirada de un idiota.

Taki, sí, no hay problema si se comprueba el cierre/activación de una orden.
Bueno y si su propia matriz de garrapatas, entonces la orden sólo se perderá.

ps. Permítanme añadir otro caso: si una orden pendiente está configurada para detener una orden real (para un rollover), entonces cuando se activa el stop, la orden pendiente puede retrasarse en la apertura por un tiempo indefinido. Es decir, una orden será activada por el mercado, mientras que la otra se mantendrá durante un cierto número de ticks...

 
fxsaber:

He aquí una aplicación de esto

Sólo que todavía requiere IsChange. El temporizador no es una opción en absoluto. Incluso Sleep(1) estropea todo el panorama.

IsChange() está implementado exactamente en el temporizador (la versión de prueba aún no está terminada):

//+------------------------------------------------------------------+
//| Обновляет список ордеров                                         |
//+------------------------------------------------------------------+
int CMarketCollection::Refresh(void)
  {
   ::ZeroMemory(m_struct_market);
   int number_new=0, total=::OrdersTotal();
   m_list_all_orders.Clear();
   for(int i=0; i<total; i++){
      if(!::OrderSelect(i,SELECT_BY_POS)) continue;
      long ticket=::OrderTicket();
      m_struct_market.hash_sum_acc+=ticket;
      m_struct_market.total_volumes+=::OrderLots();
      ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)::OrderType();
      if(type==ORDER_TYPE_BUY || type==ORDER_TYPE_SELL){
         CMarketOrder* order=new CMarketOrder();
         if(order==NULL) continue;
         m_list_all_orders.InsertSort(order);
         m_struct_market.total_positions++;
         }
      else{
         CMarketPending* order=new CMarketPending();
         if(order==NULL) continue;
         m_list_all_orders.InsertSort(order);
         m_struct_market.total_pending++;
         }
      }
   //--- Первый запуск
   if(m_hash_sum_acc_prev==WRONG_VALUE){
      m_hash_sum_acc_prev=m_struct_market.hash_sum_acc;
      m_total_positions_prev=m_struct_market.total_positions;
      m_total_pending_prev=m_struct_market.total_pending;
      m_total_volume_prev=m_struct_market.total_volumes;
      }
   //---
   if(m_struct_market.hash_sum_acc!=m_hash_sum_acc_prev){
      number_new=(m_struct_market.total_pending+m_struct_market.total_positions)-(m_total_positions_prev+m_total_pending_prev);
      Print(FUNC,"Хэш-сумма всех ордеров и позиций изменилась");
      //--- Увеличисля общий объём
      if(::NormalizeDouble(m_struct_market.total_volumes-m_total_volume_prev,3)>0){
         Print(FUNC,"Общий объём увеличился");
         if(m_struct_market.total_positions>m_total_positions_prev) Print(FUNC,"Количество позиций увеличилось");
         if(m_struct_market.total_pending>m_total_pending_prev) Print(FUNC,"Количество ордеров увеличилось");
         //--- Отправка EVENT в управляющий класс CEngine
         // сделать!
         }
      //--- Уменьшился общий объём
      else if(::NormalizeDouble(m_struct_market.total_volumes-m_total_volume_prev,3)<0){
         Print(FUNC,"Общий объём уменьшился");
         if(m_struct_market.total_positions<m_total_positions_prev) Print(FUNC,"Количество позиций уменьшилось");
         if(m_struct_market.total_pending<m_total_pending_prev) Print(FUNC,"Количество ордеров уменьшилось");
         //--- Отправка EVENT в управляющий класс CEngine
         // сделать!
         }
      else{
         // что-то ещё, не пойму пока что именно - не было претендентов ещё
         }
      //---
      m_hash_sum_acc_prev=m_struct_market.hash_sum_acc;
      m_total_positions_prev=m_struct_market.total_positions;
      m_total_pending_prev=m_struct_market.total_pending;
      m_total_volume_prev=m_struct_market.total_volumes;
      }
   //---
   //---
   return number_new;
  }
//+------------------------------------------------------------------+

La clase de control puede captar el cambio ya sea por el número devuelto por Refresh() de ambas clases (en el probador):

//+------------------------------------------------------------------+
//| Таймер                                                           |
//+------------------------------------------------------------------+
void CEngine::OnTimer(void)
  {
   //--- Обновление списка исторических ордеров и позиций
   m_new_history=History.Refresh();
   if(m_new_history>0){
      Print(FUNC,"Изменение в исторических на ",m_new_history);
      //--- реакция
      }
   //--- Обновление списка рыночных ордеров и позиций
   m_new_market=Market.Refresh();
   if(m_new_market!=0){
      Print(FUNC,"Изменение в активных на ",m_new_market);
      //--- реакция
      }
   //---
   Sym.OnTimer();
  }
//+------------------------------------------------------------------+

o en un evento de usuario, que aún no están implementados (demo, real).

 
Artyom Trishkin:

IsChange() es exactamente lo que se implementa en el temporizador (versión de prueba aún no terminada):

¿Por qué, si IsChange es de cinco líneas?