Organiser le cycle de commande - page 9

 
fxsaber:

Modifié

Maintenant sans alertes.

La réponse est évidente - si Ticket == PrevTicket ---> return WRONG_VALUE.

Si la fonction renvoie une valeur inférieure à zéro, elle doit être appelée à nouveau.

 
Artyom Trishkin:

La réponse est évidente - si Ticket == PrevTicket ---> return WRONG_VALUE.

Il s'agit donc de la solution d'un cas particulier du problème que j'ai décrit pour plus de clarté sous la forme d'une égalité des billets voisins.

Le problème est en fait le même - le tremblement de l'indexation pendant la boucle. Cela peut conduire à une situation où un ticket est ignoré, ou un ticket est répété dans un autre, etc.

Je n'ai pas trouvé d'alternative à IsChange.

 
fxsaber:

Il s'agit donc d'une solution à un cas particulier du problème, que j'ai décrit pour plus de clarté sous la forme d'une égalité des billets voisins.

Le problème est en fait toujours le même : l'ébranlement de l'indexation pendant le cycle. Cela peut conduire à une situation où un ticket est ignoré, ou un ticket est répété dans un autre, etc.

Je n'ai pas trouvé d'alternative à IsChange.

Nous créons une liste dans le minuteur et nous travaillons avec elle.

 
Artyom Trishkin:

Créez une liste dans le minuteur et travaillez avec elle.

Mieux dans le code.

 
fxsaber:

Mieux dans le code.

Il est très difficile de le montrer en code - il y a toute une bibliothèque de classes interdépendantes.

L'idée est la suivante - nous passons tous les ordres et positions sur le compte et remplissons la liste CArrayObj dans le timer. Nous le mettons constamment à jour pour obtenir les informations actuelles en un seul passage.

S'il est nécessaire de fermer des positions ou de supprimer des ordres, nous obtenons cette liste et y sélectionnons les objets d'ordre et leurs tickets nécessaires pour les fonctions de fermeture (modification). Si cet objet d'ordre est physiquement absent (fermé ou supprimé pendant ces actions), nous passons simplement à l'objet suivant de la liste, puisque celui-ci a déjà été fermé, et la liste sera mise à jour à la prochaine itération du minuteur. Le premier problème qui vient à l'esprit est que la liste n'est pas pertinente lorsque l'environnement commercial change pendant la période où les actions sont effectuées sur la liste. Mais à mon avis, l'absence physique d'un ordre dans la liste ne devrait pas trop nous déranger - nous obtenons simplement une erreur et passons au suivant dans la liste - cette liste n'est pas mélangée comme dans un environnement de négociation et le saut est impossible - seulement une déclaration du fait de l'absence d'un ordre correspondant à une entrée dans la liste.

C'est ce que je pense jusqu'à présent, puisque je ne l'ai pas encore implémenté dans mon code. Je me prépare à la mise en œuvre, mais je dois d'abord terminer d'autres cours (j'ai posé une question sur la substitution des fonctions dans la branche Features). Lorsque je commencerai la mise en œuvre, les problèmes éventuels y seront visibles, et je déciderai comment les résoudre et les corriger.

 
Artyom Trishkin:

La réponse est évidente - si Ticket == PrevTicket ---> return WRONG_VALUE.

Si la fonction renvoie une valeur inférieure à zéro, nous devons la rappeler.

Vous n'avez rien à faire du tout. Il suffit de suivre la logique en ayant l'air d'un idiot.

Nous avons 6 commandes

  • 0 ticket 100
  • 1 ticket 101
  • 2 tickets 102
  • 3 tickets 103
  • 4 tickets 104
  • 5 tickets 105

Nous commençons à passer en revue les commandes pour, disons, une modification,

Nous choisissons 5 tickets 105, vérifions si nous voulons les modifier et les modifier.

À ce moment-là, nos mains impures ont supprimé le ticket 102 de l'ordre 2, et la liste a changé. L'ordre avec le ticket 105 est maintenant le quatrième, 4, dans la liste. Et il est de nouveau sélectionné pour la modification. Mais nous ne travaillons pas sans vérifier... nous avons vérifié s'il devait être modifié, MAIS !!! Ce n'est pas... Et alors ? Qui a été blessé parce que l'ordre a été re-sélectionné ?

 
Artyom Trishkin:

Il est très difficile de le montrer en code - il y a toute une bibliothèque de classes interdépendantes.

L'idée est la suivante - nous passons tous les ordres et positions sur le compte et remplissons la liste CArrayObj dans le timer. Nous le mettons constamment à jour pour obtenir les informations actuelles en un seul passage.

S'il est nécessaire de fermer des positions ou de supprimer des ordres, nous obtenons cette liste et y sélectionnons les objets d'ordre et leurs tickets nécessaires pour les fonctions de fermeture (modification). Si cet objet-ordre est physiquement absent (fermé ou supprimé pendant ces actions), nous passons simplement à l'objet suivant dans la liste, puisque celui-ci a déjà été fermé, et la liste sera mise à jour à la prochaine itération de la minuterie. Le premier problème qui vient à l'esprit est que la liste n'est pas pertinente lorsque l'environnement commercial change pendant la durée des actions figurant sur la liste. Mais à mon avis, l'absence physique d'un ordre dans la liste ne devrait pas trop nous déranger - nous obtenons simplement une erreur et passons au suivant dans la liste - cette liste n'est pas mélangée comme dans un environnement de négociation et le saut est impossible - seulement une déclaration du fait de l'absence d'un ordre correspondant à une entrée dans la liste.

C'est ce que je pense jusqu'à présent, puisque je ne l'ai pas encore implémenté dans mon code. Je me prépare à la mise en œuvre, mais je dois d'abord finaliser d'autres classes (j'ai posé une question sur la substitution des fonctions dans la branche des fonctionnalités). Lorsque je commencerai la mise en œuvre, les problèmes éventuels seront visibles et je déciderai de la manière de les résoudre et de les corriger.

Voici une mise en œuvre de ce principe

Forum sur le trading, les systèmes de trading automatisés et les tests de stratégies de trading

Organiser une boucle de commande

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; // вместо строки выше лучше делать такой вызов (переполнения стека от рекурсивных вызовов быть не должно)
    }


Après l'envoi d'un ordre de transaction, l'environnement commercial change, il est donc conseillé d'exécuter toute la logique commerciale du TS à partir de zéro immédiatement après la réponse du serveur commercial.

Seulement, cela nécessite toujours IsChange. La minuterie n'est pas du tout une option. Même Sleep(1) gâche l'ensemble du tableau.

 
Alexey Viktorov:

Il n'y a absolument rien à faire. Il suffit de traverser la logique avec l'air d'un idiot.

Taki, oui, pas de problème si la fermeture/activation d'une commande est cochée.
Et si son propre tableau de ticks, alors l'ordre manquera tout simplement.

ps. Permettez-moi d'ajouter un autre cas - si un ordre en attente est configuré pour arrêter un ordre réel (pour un roulement), alors lorsque l'arrêt est déclenché, l'ouverture de l'ordre en attente peut être retardée pendant une durée indéterminée. C'est-à-dire qu'un ordre sera déclenché par le marché, tandis que l'autre restera en suspens pendant un certain nombre de ticks...

 
fxsaber:

Voici une mise en œuvre de ce principe

Seulement, cela nécessite toujours IsChange. La minuterie n'est pas du tout une option. Même Sleep(1) gâche l'ensemble du tableau.

IsChange() est exactement implémenté dans le timer (la version de test n'est pas encore terminée) :

//+------------------------------------------------------------------+
//| Обновляет список ордеров                                         |
//+------------------------------------------------------------------+
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 classe de contrôle peut capter le changement soit par le nombre retourné par Refresh() des deux classes (dans le testeur) :

//+------------------------------------------------------------------+
//| Таймер                                                           |
//+------------------------------------------------------------------+
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();
  }
//+------------------------------------------------------------------+

ou dans un événement utilisateur, qui ne sont pas encore implémentés (démo, réel).

 
Artyom Trishkin:

IsChange() est exactement ce qui est implémenté dans le timer (version de test non encore terminée) :

Pourquoi, si IsChange est de cinq lignes ?