Projet du conseiller - page 4

 
Vitaly Muzichenko:

Moi aussi, mais je suis arrivé à la conclusion il y a longtemps que le code devrait être compact dans des endroits où il n'est jamais regardé, où il n'est jamais corrigé et où il ne sera jamais corrigé.

La dispersion du code utilisateur dans tous les inludes est un casse-tête supplémentaire. Lorsque vous devez glisser et déposer un fichier dans un autre terminal, ou le partager, vous devrez glisser et déposer plusieurs fichiers. Bien sûr, vous pouvez transférer les includniks à tous les terminaux, mais si vous modifiez ou ajoutez quelque chose dans un terminal, alors tous les includniks doivent être remplacés par un nouveau.

Les conseillers experts et les indicateurs sont si petits qu'il n'y a aucun intérêt à les éloigner du corps du programme. Pour être plus correct, ils ne sont pas petits, ils sont en fichier unique, ce n'est pas comme un site de 10 000 pages où vous ne pouvez pas vous passer des classes et des inludes. De plus, il existe maintenant des structures, et elles sont tout à fait suffisantes pour écrire un code compact et 100% exploitable.

Nous y voilà.... Connaissez-vous les liens symboliques vers les dossiers ?http://skesov.ru/sozdanie-simvolnoy-ssyilki-dlya-papki/

J'ai toutes mes bibliothèques dans un dossier, et dans un tas de terminaux, il y en a des dizaines, dans les dossiers mql*\includes il y a des liens symboliques vers ce vrai dossier. Rien ne doit être traîné nulle part.

De plus, j'utilise activement le stockage, si je garde là tout ce qui est important, je peux le télécharger en 5 secondes sur un autre terminal. Mais pour les librairies, les liens symboliques sont plus pratiques et permettent toujours une synchronisation complète.

Создание символьной ссылки для папки в Windows 8.1, 8, 7, Vista
Создание символьной ссылки для папки в Windows 8.1, 8, 7, Vista
  • 2013.07.24
  • skesov.ru
Доброго времени суток! Сегодня рассмотрим интересную тему под названием «Символьные ссылки». Вариантов использования данного инструмента не так уж много. К примеру, если вы используете часть оперативной памяти как RAM-диск, можно перенести какую-либо игру или её часть (скажем папки с графикой) и создать символьную ссылку. Это значительно...
 
Alexey Navoykov:
Je recommande d'utiliser des liens symboliques ou des liens de jonction pour le dossier MQL. Tous les terminaux se trouveront dans le même dossier.

Et le partager avec quelqu'un d'autre ?

 
Vitaly Muzichenko:

Vous partagez avec quelqu'un ?

Eh bien, vous devez décider ce qui est le plus important pour vous, vérifier ou conduire.) La facilité de partager avec quelqu'un est-elle plus importante pour vous que le confort du codage ? Si, par exemple, vous trouvez une erreur dans une fonction utilisée par de nombreux conseillers experts, vous devrez entrer dans le code de chacun d'entre eux et réécrire cette fonction - cela ne vous gêne-t-il pas en tant que programmeur ?

 
Merci à tous d'avoir discuté de ma question.
J'ai décidé de me tourner vers la POO dans mqlx pour commencer, en gardant vos fonctions (répétables) dans un ou plusieurs fichiers séparés. Et ne soyez pas paresseux pour commenter.

Et un autre + pour le lien symbolique dans Windows ! Je l'ai utilisé sous Linux, mais je l'ai oublié sous Windows. Je vais devoir l'essayer...
 
Alexey Navoykov:

Vous devez décider ce qui est le plus important pour vous, vérifier ou conduire). La simplicité du processus de partage avec quelqu'un est-elle plus importante pour vous que le confort du codage ? Si, par exemple, vous trouvez une erreur dans une fonction, qui est utilisée par de nombreux conseillers experts, vous devrez alors entrer dans le code de chacun d'entre eux et réécrire cette fonction - cela ne vous gêne-t-il pas en tant que programmeur ?

Je n'ai eu un tel cas qu'une seule fois, il y a environ un mois, mais j'ai dû ajouter la vérification de l'ouverture du marché à cet endroit, il y a toutes les vérifications sauf celle-ci, et elle est apparue pour la première fois depuis que je l'utilise.

Si quelque chose doit être ajouté, je l'ajoute au programme en cours, et ensuite j'utilise le fichier comme modèle pour le programme suivant. En conséquence, en quelques années, le modèle dispose de tout, ou presque, de sorte qu'un bot de n'importe quelle complexité peut être écrit en une demi-heure.

L'ensemble du code exécutable tient dans un seul écran, bien que le fichier contienne un peu plus de 4 000 lignes, mais je n'y regarde que très rarement, ne serait-ce que ce que je dois ajouter. Des fonctions dans les boucles refusées, utilisé seulement deux, l'un sur l'ouverture recueille des informations, le second sur l'histoire, et tout cela dans la structure au bas du code. Tout est très simple et proche les uns des autres. Le code principal est commenté. Le projet s'étend très facilement et rapidement, sans aucune perte.

 
Alexey Volchanskiy:

Cela semble bon, pouvons-nous également voir TRACE_*** et ASSERT ?

Eh bien... Pour l'auteur d'une master class sur la séduction des femmes, que j'envie d'une envie noire, de rien.

La version debug est automatiquement activée dans mon code, si la macro système correspondante est définie. Toutefois, s'il n'est pas activé, vous pouvez également activer les assertions et la traçabilité par définitions :

#define _FORCETRACE 1
#define _FORCEASSERT 1

Dans ce cas - indépendamment des paramètres du système - des traces de débogage et des assertions de débogage sont générées.

J'ai une directive pour connecter ces macros :

#include <MyLib\DebugOrRelease\DebugSupport.mqh>

Cette directive relie tous les fichiers et définitions nécessaires. Je les ai tous dans un dossier séparé DebugOrRelease. Je les joins ici. (Le code a été écrit il y a longtemps, principalement "à la hâte", donc il n'est pas aussi joli que les interfaces et la classe d'histoire). Les assertions et les traces elles-mêmes pour la version de débogage se trouvent dans les fichiers AssertD et TraceD, les véritables fonctions sont PerformAssert() et PerformTrace().

En outre, ces fichiers et macros utilisent le fichier journal global (si la sortie vers le fichier journal est définie), je l'ai déjà affiché une fois, mais, encore une fois. Le fichier journal se trouve dans mon dossier "Common".

Dossiers :
 
Andrey Kisselyov:

Bon travail, j'aime bien, mais je n'aime pas la POO et j'essaie de m'en passer. Je n'aime pas les processeurs avec le fractionnement des flux (par exemple, 4 cœurs et 8 threads). Il devrait être clair que le fractionnement et toute virtualisation est une perte de performance et une perte de temps machine pour son implémentation, que ce soit le fractionnement des flux dans le noyau ou la virtualisation des fonctions dans le code.

la brièveté est la sœur du talent, je pense que ça sonne mieux.

J'ai appris il y a longtemps que la maintenabilité et la réutilisation du code sont bien plus importantes que la réduction des performances.

La POO - elle m'aide beaucoup lorsque je retourne au code après un certain temps pour le modifier. Sans parler de la réutilisation.

Mais, je suis d'accord, il est loin d'être toujours nécessaire d'utiliser la POO.

Disons que j'ai la classe CDataProvider:pulic CDataProviderI - fournisseur de données, qui fournit à l'expert des séries chronologiques, des indicateurs, des données terminales et environnementales. Dans un conseiller expert, il peut y avoir de nombreux TS - chacun d'entre eux recevrait des pointeurs vers des séries temporelles et des indicateurs du fournisseur de données (chaque TS n'aurait pas besoin de créer des séries temporelles - le fournisseur de données fournirait des pointeurs vers les séries temporelles nécessaires si elles existent déjà, et ne créerait que les séries temporelles qui n'ont pas encore été créées).

Lorsque vous devez obtenir un indicateur auprès du fournisseur de données, remplissez la structure de description de l'indicateur, puis demandez au fournisseur un indicateur qui pointe vers cette structure.

Par conséquent, chaque indicateur à l'intérieur du fournisseur de données doit être capable d'identifier sa structure (le fournisseur de données ne "connaît" que la classe de base abstraite de la structure) et, en fonction de celle-ci, de créer l'objet indicateur prêt, qui sera créé par le fournisseur de données.

Mais il est déraisonnable de fabriquer tout cela dans le seul but de vérifier une nouvelle idée d'indicateur. Par conséquent, pour ces nouveaux indicateurs, tout est "fait maison", sans aucune POO. Cependant, si je vois qu'un indicateur est utile pour les conseillers experts, il est écrit "correctement" - avec un support OOP complet et une création dans un fournisseur de données.

P.S.

A propos, dans le cas des indicateurs et du fournisseur de données, nous voyons l'avantage de l'héritage de virtualisation. Nous avons l'interface de base des paramètres de l'indicateur CIndicatorParametersI, le successeur de cette interface sont les paramètres réels de l'indicateur nécessaire. Lors de la demande de l'indicateur, nous déclarons ces paramètres et transmettons au fournisseur de données un pointeur vers l'interface abstraite. Ainsi, le fournisseur de données lui-même ne sait même pas quel indicateur est demandé - il est défini dans une fonction, dans laquelle l'indicateur est créé en fonction du nouveau type. Et seul cet indicateur créé sait quels paramètres ont été passés, il récupère les paramètres requis de l'objet passé.

L'astuce est que presque partout dans le fournisseur de données, il y a une simple classe de base de paramètres (ou d'indicateurs) - seules les fonctions les plus simples et les plus courantes des interfaces de base sont disponibles pour le fournisseur de données. Cela simplifie la modification du code (lorsqu'elle est nécessaire), et ne crée pas la tentation de "trafiquer" le code des indicateurs du fournisseur de données. Si vous voulez changer un indicateur, cela se fait uniquement à l'intérieur de l'indicateur, le fournisseur de données n'est qu'un stockage d'indicateurs, le maximum qu'il puisse faire est de créer un nouvel indicateur.

 
George Merts:

D'ailleurs, cela me rend très nerveux lorsque l'imbrication se fait sur plus de deux niveaux. J'essaie de ne jamais l'écrire de cette façon, en répartissant le code sur les fonctions.

Et même lorsqu'il y a deux niveaux d'imbrication - après chaque parenthèse fermante, je dois écrire un commentaire indiquant le bloc qu'elle ferme (par exemple, l'en-tête de boucle dupliquée).

En ce qui concerne le style, voici mon code pour sélectionnerune position historique pour MT5 (par magik spécifié, symbole, avec une plage de dates spécifiée) :

La classe historique elle-même est un descendant de l'interface abstraite CTradeHistoryI :

En sélectionnant l'historique requis - vous pouvez recalculer ses composants (positions pour MT5 ou ordres pour MT4), et obtenir une interface à n'importe quel composant comme une interface abstraite :

Pour MT4, il existe des classes d'historique correspondantes qui héritent également de ces interfaces - ainsi, la nature multiplateforme est assurée en même temps - un EA n'a pas besoin de savoir où il travaille, tout le travail avec l'historique est effectué par le biais d'interfaces abstraites.


Ce n'est pas vraiment une critique :

class CTradePosComponentI: public CMyObject
{
...
}

Pourquoi réinventer la roue sous la forme de CMyObject, alors qu'il existe un CObject standard que tout le monde comprend ?

class class CTradeHistoryI: public CMyObject
{
// Расширенный интерфейс
   virtual void Sort(ESortTPCMode stmMode = STM_BY_OPEN_TIME_A) = 0;
}

La fonctionnalité de CObject et CArrayObj est clairement copiée ici. Pourquoi ? Le tri rapide est intégré dans les conteneurs de données standard. Vous devriez les utiliser.

class CTradePosComponentI: public CMyObject
{
public:
   void CTradePosComponentI() {    SetMyObjectType(MOT_TRADEPOS_COMPONENT_I); };
   virtual void ~CTradePosComponentI() {};
}

Si la classe possède une interface, il est préférable de cacher son constructeur dans la section protégée. Son objet ne peut donc pas être créé directement.

Définir un constructeur vide ? Je ne sais pas. Je ne le ferais pas. Il vaut mieux ne pas mentionner le destructeur si vous n'en avez pas besoin.

for(iI=0;iI<iHistoryDealsTotal; ++iI)
...

Incrément non standard iI, itération non standard ++iI, iHistoryDealsTotal est défini quelque part, bien avant la boucle. Plus simple :

for(int i = 0; i < HistoryDealsTotal();i++)

C'est aussi rapide que l'option précédente, mais beaucoup plus évident.

virtual bool               IsTPCInUnloss() const { if(GetTPCStopLoss() <= 0 || GetTPCStopLoss() == EMPTY_VALUE) return(false); if(GetTPCType() == POSITION_TYPE_BUY) { if(GetTPCStopLoss() >= GetTPCOpenPrice()) return(true); } else { if(GetTPCStopLoss() <= GetTPCOpenPrice())return(true); }; return (false); };

Les programmeurs eux-mêmes semblent être contre de tels textes, mais ils les écrivent eux-mêmes quelque part. Personne ne veut examiner de telles absurdités. Ce qui les a empêchés de l'écrire de cette façon :

virtual bool IsTPCInUnloss() const
{
   if(GetTPCStopLoss() <= 0 || GetTPCStopLoss() == EMPTY_VALUE)
      return(false);
   if(GetTPCType() == POSITION_TYPE_BUY)
   { 
      if(GetTPCStopLoss() >= GetTPCOpenPrice())
         return(true);
   } 
   else
   {
     if(GetTPCStopLoss() <= GetTPCOpenPrice())
        return(true);
   }; 
   return (false);
};

Et " ;" à la fin des accolades - c'est obsolète, vous ne devriez plus le faire maintenant.

Une méthode Select géante consiste en une boucle for géante :

for(iI=0;iI<iHistoryDealsTotal; ++iI)
      {
      ulCurTicket = HistoryDealGetTicket(iI);
      
      if(ulCurTicket == 0)
         return(WRONG_VALUE);
      
      // Получим направление сделки   
      if(HistoryDealGetInteger(ulCurTicket,DEAL_ENTRY,lCurEntry)!=true)
         {
         TRACE_INTEGER("Не удалось получить направление сделки ! Тикет: ",ulCurTicket);
         continue;
         };
      
      // Проверим направление сделки
      if(lCurEntry != DEAL_ENTRY_OUT)
         continue;
      
      // Получим магик сделки
      if(HistoryDealGetInteger(ulCurTicket,DEAL_MAGIC,lCurMagic)!=true)
         {
         TRACE_INTEGER("Не удалось получить магик сделки ! Тикет: ",ulCurTicket);
         continue;
         };
         
      // Проверим магик
      if(ulMagic != NULL && lCurMagic != ulMagic)
         {
         //TRACE_INTEGER("Сделка не подходит ! Имеет неверный магик ! Magic сделки: ",lCurMagic);
         //TRACE_INTEGER("Требуемый Magic : ",ulMagic);
         continue;
         };
      ...
}

Évidemment, toutes les vérifications de la conformité de la transaction avec le conseiller expert actuel doivent être mises en œuvre dans une méthode distincte, par exemple comme ceci :

for(iI=0;iI<iHistoryDealsTotal; ++iI)
{
   if(!CheckDeal(iI))
      continue;
   ...
}

Et en général, la sélection devrait être divisée en 3 ou 4 méthodes supplémentaires afin de rendre plus clair ce qui s'y passe.

George Merts
Le conseiller expert lui-même se compose de cinq lignes. Dans ce fichier, l'objet de l'usine de pièces de l'EA lui-même est déclaré et les inclusions sont incluses.

Factory est un modèle très controversé. C'est bien de l'utiliser, mais je ne recommande pas de tout faire à travers une usine.

George Merts
Et même lorsqu'il y a deux niveaux d'imbrication, il est obligatoire d'écrire des commentaires après chaque parenthèse fermante, le bloc qu'elle enterre (disons, l'en-tête de boucle dupliquée).

Eh bien, c'est pourquoi vous l'écrivez entre parenthèses dans la manière laide de MQL. Si tu l'as écrit comme ça :

if(OrderSelect())
{
   ...
}

Vous verrez toujours quel crochet ferme quel bloc de code.

Vous auriez pu trouver plus d'une douzaine d'avertissements dans votre code. Bien sûr, le code n'est pas parfait, mais on sent le goût de l'auteur pour la beauté :))

 
Vasiliy Sokolov:

C'est un peu une critique :

О. C'est le genre de discussion que j'aime. Donc.

Pourquoi réinventer la roue sous la forme de CMyObject, alors qu'il existe un CObject standard que tout le monde comprend ?

La fonctionnalité de CObject et CArrayObj est évidemment copiée ici. Pourquoi ? Le tri rapide est intégré dans les conteneurs de données standard. Utilisez-les.

CMyObject est l'héritier du CObject standard, toutes les listes et tableaux de mon code sont des descendants de CArray (et d'autres tableaux de la bibliothèque standard). Je n'utilise pratiquement jamais les tableaux standards array[].

Et, bien sûr, le tri et le travail avec les listes utilisent les fonctionnalités de base de CObject.

Et la différence entre eux est la suivante : un CObject standard est "un objet de type liste ou tableau trié". Un CMyObject est un CObject qui a un certain type, et contient une certaine valeur donnée lors de sa création. J'avais besoin de cet objet en raison de la réduction généralisée des objets à une classe abstraite de base - pour comprendre par un pointeur vers quel objet on pointe "réellement". Le type CMyObject est défini par cette même fonction SetMyObjectType (). Cette fonction doit nécessairement être appelée dans les constructeurs de tout dérivé de CMyObject, pour attribuer un identifiant à la classe à laquelle l'objet appartient.

Il dispose également de SetUDCreationValue() - qui définit une valeur définie par l'utilisateur lors de la création. Rarement utilisé. Il est nécessaire pour distinguer les différents objets d'une même classe.

Si l'interface de la classe - son constructeur est mieux de cacher dans la section protégée. Son objet ne peut donc pas être créé directement.

Constructeur protégé ? ?? Oui, je suppose que c'est raisonnable pour les interfaces, je ne savais pas que c'était possible.

Définir un constructeur vide ? Je ne sais pas. Je ne ferais pas ça. Il est préférable de ne pas mentionner le destructeur si vous n'en avez pas besoin.

C'est un "héritage maudit du passé". Il était une fois un projet assez important et si nous ne définissions pas un destructeur vide, la suppression des objets prenait beaucoup de temps pour une raison quelconque. Donc, je l'ai fait à la volée depuis. De manière générale, le destructeur doit également être virtuel.

Incrément non standard iI, itération non standard ++iI, iHistoryDealsTotal - défini quelque part, bien avant la boucle. Il est préférable de faire plus simple :

Pas d'accord. L'incrément est parfaitement normal, - i, juste une notation standardisée - d'abord avec une petite lettre son type est entier, et ensuite avec une majuscule son nom est I.

Vous semblez être contre de telles feuilles, mais vous l'écrivez quelque part. Personne ne veut analyser de telles absurdités. Ce qui vous a empêché de l'écrire de cette façon :

Dans ce cas, j'ai dû choisir entre la "visibilité" de la classe et la beauté de la fonction. J'ai choisi "visibilité". La beauté a souffert.

La méthode Select géante consiste en une boucle for géante :

Évidemment, toutes les vérifications de la conformité de la transaction avec le conseiller expert actuel devraient être dans une méthode séparée, par exemple comme ceci :

Et en général, select doit être divisé en 3-4 méthodes supplémentaires pour que ce qui s'y passe soit clair.

Je suis d'accord. Ici, en principe, ce même cycle a "grandi", au début il n'était pas si grand.

Bien qu'il ne soit pas toujours pratique d'implémenter des contrôles mineurs dans les fonctions privées, car ces contrôles ne sont pas toujours traçables dans le code.

Factory est un modèle très controversé. L'utilisation est bien, mais je ne recommande pas de tout faire passer par une usine.

Maintenant, je ne me souviens plus, il y avait plusieurs variantes de la construction du conseiller expert. Je me suis arrêté à l'"usine de pièces détachées pour conseillers experts". En principe, il ne s'agit plus d'un pur modèle classique de "fabrique". À l'origine, il devait être un modèle "classique", mais aujourd'hui, il s'agit plutôt d'un "constructeur-concentrateur" de parties d'Expert Advisor. Et il est également responsable de l'enlèvement de ces pièces, ce qui n'est pas caractéristique d'une usine. Mais le nom est resté.

C'est pourquoi vous l'écrivez entre parenthèses, à la manière peu élégante de MQL. Si tu l'as écrit comme ça :

On voit toujours quel crochet ferme quel bloc de code.

Pourquoi "l'horrible façon" ?

L'en-tête de la boucle, puis la parenthèse ouvrante avec retrait, puis le bloc entier avec le même retrait, et enfin la parenthèse fermante - également avec retrait.

Qu'est-ce qui est mieux, selon vous ?

 
Gregory Kovalenko:

J'ai besoin d'obtenir des profits sur 2 ordres ouverts. Le dernier ordre ouvert, je l'appelleOrderProfit2, et l'ordre suivant -OrderProfit1.

Le premier ordre est ouvert en premier, puis le 2ème ordre, donc le 1er ordre dans la boucle est appelé 2)

Où se trouve l'erreur ?

Vous ne faites que passer les commandes. Il ne vérifie nulle part lequel est le premier et lequel est le second.

Vous devez effectuer un contrôle sur l'heure d'ouverture. Vous pouvez alors faire la distinction entre l'ordre qui s'est ouvert plus tôt et l'ordre qui s'est ouvert plus tard. Ou peut-être peuvent-ils s'ouvrir en même temps.

Raison: