Progetto del consigliere - pagina 4

 
Vitaly Muzichenko:

Anch'io, ma sono arrivato alla conclusione molto tempo fa che il codice dovrebbe essere compatto in posti dove non viene mai guardato, non viene mai corretto e non verrà mai corretto.

Spargere il codice utente con tutti gli inludi è un ulteriore mal di testa, quando avete bisogno di trascinare e rilasciare un file in un altro terminale, o condividerlo, avrete bisogno di trascinare e rilasciare diversi file. Naturalmente, potete trasferire gli includenti a tutti i terminali, ma se cambiate o aggiungete qualcosa in un terminale, allora tutti devono essere sostituiti con uno nuovo.

Gli Expert Advisors e gli indicatori sono così piccoli che non ha senso distanziarli dal corpo del programma. Per essere più corretti, non sono piccoli, sono file singoli, non è come un sito con 10 000 pagine dove non si può fare a meno di classi e inludi. Inoltre, ci sono strutture ora, e sono abbastanza per scrivere codice compatto e funzionante al 100%.

Ecco qui.... Conosci i collegamenti simbolici alle cartelle?http://skesov.ru/sozdanie-simvolnoy-ssyilki-dlya-papki/

Ho tutte le mie librerie in una cartella, e in un mucchio di terminali, ce ne sono decine, nelle cartelle mql*\includes ci sono link simbolici a questa cartella reale. Niente deve essere trascinato da nessuna parte.

Inoltre, uso attivamente lo storage, se ci tengo tutto ciò che è importante, posso scaricarlo su un altro terminale in 5 secondi. Ma per le libs i link simbolici sono più convenienti, sempre la sincronizzazione completa.

Создание символьной ссылки для папки в Windows 8.1, 8, 7, Vista
Создание символьной ссылки для папки в Windows 8.1, 8, 7, Vista
  • 2013.07.24
  • skesov.ru
Доброго времени суток! Сегодня рассмотрим интересную тему под названием «Символьные ссылки». Вариантов использования данного инструмента не так уж много. К примеру, если вы используете часть оперативной памяти как RAM-диск, можно перенести какую-либо игру или её часть (скажем папки с графикой) и создать символьную ссылку. Это значительно...
 
Alexey Navoykov:
Raccomando di usare collegamenti simbolici o collegamenti di giunzione per la cartella MQL. Tutti i terminali cercheranno nella stessa cartella.

E condividerlo con qualcun altro?

 
Vitaly Muzichenko:

Condividere con qualcuno?

Beh, devi decidere cosa è più importante per te, il controllo o la guida). La facilità di condivisione con qualcuno è più importante per te della comodità della codifica? Se, diciamo, hai trovato un errore in qualche funzione che è usata da molti Expert Advisors, allora dovrai entrare nel codice di ognuno di loro e riscrivere questa funzione - non ti preoccupa come programmatore?

 
Grazie a tutti per aver discusso la mia domanda.
Ho deciso di guardare verso l'OOP in mqlx per cominciare, tenere le tue funzioni (ripetibili) in un file separato. E non essere pigro a commentare.

E un altro + per il link simbolico in Windows! L'ho usato in Linux ma l'ho dimenticato in Windows. Dovrò fare una prova...
 
Alexey Navoykov:

Dovete decidere cosa è più importante per voi, controllare o guidare). La semplicità del processo di condivisione con qualcuno è più importante per te della comodità della codifica? Se, per esempio, trovate un errore in una funzione che è usata da molti Expert Advisors, allora dovrete andare nel codice di ognuno di loro e riscrivere questa funzione - non vi preoccupa come programmatore?

Ho avuto un caso simile solo una volta, circa un mese fa, ma ho dovuto aggiungere il controllo dell'apertura del mercato lì, ci sono tutti i controlli tranne questo, ed è saltato fuori per la prima volta da quando lo uso.

Se c'è bisogno di aggiungere qualcosa, lo aggiungo al programma corrente, e poi uso il file come modello per il programma successivo. Come risultato, in pochi anni il modello ha tutto, beh, o quasi tutto, in modo che un bot di qualsiasi complessità può essere scritto in mezz'ora.

L'intero codice eseguibile sta in una sola schermata, anche se il file contiene poco più di 4.000 linee, ma ci guardo molto raramente, se non quello che ho bisogno di aggiungere. Da funzioni in loop rifiutati, utilizzati solo due, uno sul aperto raccoglie informazioni, la seconda sulla storia, e tutto questo nella struttura nella parte inferiore del codice. Tutto è molto semplice e vicino all'altro. Il codice principale è commentato. Il progetto si espande molto facilmente e rapidamente, senza perdite.

 
Alexey Volchanskiy:

Sembra buono, possiamo anche vedere TRACE_*** e ASSERT?

Bene... Per l'autore di una master class sulla seduzione delle donne, che invidio con un'invidia nera, sei il benvenuto.

La versione di debug è abilitata automaticamente nel mio codice, se la macro di sistema corrispondente è definita. Tuttavia, se non è abilitato, potete anche abilitare gli assert e il trace by defines:

#define _FORCETRACE 1
#define _FORCEASSERT 1

In questo caso, indipendentemente dalle impostazioni di sistema, vengono generate tracce di debug e asserzioni di debug.

Ho una direttiva per collegare queste macro:

#include <MyLib\DebugOrRelease\DebugSupport.mqh>

Questa direttiva aggancia tutti i file e le definizioni necessarie. Li ho tutti in una cartella separata DebugOrRelease. Li allego qui. (Il codice è stato scritto molto tempo fa, per lo più "frettolosamente", quindi non è bello come le interfacce e la classe di storia). Gli asserti e le tracce per la versione di debug sono nei file AssertD e TraceD, le vere funzioni sono PerformAssert() e PerformTrace().

Inoltre questi file e le macro usano il file di log globale (se l'output al file di log è impostato), l'ho già postato una volta, ma, ancora una volta. Il file di log è nella mia cartella "Common".

 
Andrey Kisselyov:

Buon lavoro, mi piace, ma non mi piace l'OOP e cerco di farne a meno. Non mi piacciono i processori con stream splitting (per esempio, 4 core e 8 thread). Dovrebbe essere chiaro che lo splitting e qualsiasi virtualizzazione è una perdita di prestazioni e perdita di tempo macchina per la sua implementazione, sia che si tratti di stream splitting nel kernel o di virtualizzazione di funzioni nel codice.

La brevità è la sorella del talento, penso che suoni meglio.

Ho imparato molto tempo fa che la manutenibilità e il riutilizzo del codice sono molto più importanti della riduzione delle prestazioni.

OOP - mi aiuta molto quando torno al codice dopo qualche tempo per modificarlo. Per non parlare del riutilizzo.

Ma, sono d'accordo, non è affatto necessario usare sempre OOP.

Diciamo che ho la classe CDataProvider:pulic CDataProviderI - fornitore di dati, che fornisce all'esperto serie temporali, indicatori, dati del terminale e dell'ambiente. In un Expert Advisor, ci possono essere molti TS - ognuno di loro riceverebbe i puntatori alle serie temporali e agli indicatori dal fornitore di dati (ogni TS non avrebbe bisogno di creare serie temporali - il fornitore di dati fornirebbe i puntatori alle serie temporali necessarie se esistono già, e creerebbe solo le serie temporali che non sono ancora state create).

Quando hai bisogno di ottenere un indicatore dal fornitore di dati - riempi la struttura di descrizione dell'indicatore, e poi richiedi un indicatore dal fornitore, puntando a questa struttura.

Di conseguenza, ogni indicatore all'interno del fornitore di dati dovrebbe essere in grado di identificare la sua struttura (il fornitore di dati "conosce" solo la classe base astratta della struttura) e in base ad essa creare l'oggetto indicatore pronto, che sarà creato dal fornitore di dati.

Ma è irragionevole fare tutta questa roba solo per verificare una nuova idea di un indicatore. Di conseguenza, per questi nuovi indicatori tutto è "fatto in casa", senza alcuna OOP. Tuttavia, se vedo che un indicatore è utile per Expert Advisors, è scritto "correttamente" - con pieno supporto OOP e creazione all'interno di un fornitore di dati.

P.S.

A proposito, nel caso degli indicatori e del fornitore di dati vediamo il vantaggio dell'ereditarietà della virtualizzazione. Abbiamo l'interfaccia di base dei parametri degli indicatori CIndicatorParametersI, il successore di questa interfaccia sono i parametri reali dell'indicatore necessario. Quando richiediamo l'indicatore, dichiariamo questi parametri e passiamo al fornitore di dati un puntatore all'interfaccia astratta. Così lo stesso fornitore di dati non sa nemmeno quale indicatore è richiesto - è definito in una funzione, in cui l'indicatore è creato secondo il nuovo tipo. E solo questo indicatore creato sa quali parametri sono stati passati, recupera i parametri richiesti dall'oggetto passato.

Il trucco è che quasi ovunque all'interno del fornitore di dati c'è una semplice classe base di parametri (o indicatori) - solo le funzioni più semplici e comuni delle interfacce di base sono disponibili al fornitore di dati. Questo semplifica la modifica del codice (quando è necessario), e non crea la tentazione di "manomettere" il codice degli indicatori del fornitore di dati. Se volete cambiare un indicatore, si fa solo all'interno dell'indicatore, il fornitore di dati è solo un magazzino di indicatori, il massimo che può fare è creare un nuovo indicatore.

 
George Merts:

A proposito, mi rende molto nervoso quando l'annidamento è più di due livelli. Cerco di non scriverlo mai in questo modo, distribuendo il codice sulle funzioni.

E anche quando ci sono due livelli di annidamento - dopo ogni parentesi di chiusura devo scrivere un commento, quale blocco chiude (per esempio, l'intestazione del ciclo duplicato).

Per quanto riguarda lo stile, ecco il mio codice per selezionareuna posizione storica per MT5 (per mago specificato, simbolo, con intervallo di date specificato):

La classe storia stessa è una discendente dell'interfaccia astratta CTradeHistoryI:

Selezionando la storia richiesta - è possibile ricalcolare i suoi componenti (posizioni per MT5 o ordini per MT4), e ottenere un'interfaccia a qualsiasi componente come interfaccia astratta:

Per MT4 ci sono classi di storia corrispondenti che ereditano anche da quelle interfacce - così la natura multipiattaforma è fornita allo stesso tempo - un EA non ha bisogno di scoprire dove lavora, tutto il lavoro con la storia è fatto attraverso interfacce astratte.


Non è una grande critica:

class CTradePosComponentI: public CMyObject
{
...
}

Perché reinventare la ruota sotto forma di CMyObject, quando c'è un CObject standard che tutti capiscono?

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

La funzionalità di CObject e CArrayObj è chiaramente copiata qui. Perché? L'ordinamento rapido è costruito in contenitori di dati standard. Dovreste usarli.

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

Se la classe ha un'interfaccia - è meglio nascondere il suo costruttore nella sezione protetta. Allora il suo oggetto non può essere creato direttamente.

Definire un costruttore vuoto? Non lo so. Io non lo farei. È meglio non menzionare il distruttore se non ne hai bisogno.

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

Incremento non standard iI, iterazione non standard ++iI, iHistoryDealsTotal - definito da qualche parte là fuori, molto prima del ciclo. Meglio più semplice:

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

È veloce come la versione precedente, ma molto più evidente.

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); };

Gli stessi programmatori sembrano essere contrari a questi testi, ma loro stessi li scrivono da qualche parte. Nessuno vuole indagare su queste sciocchezze. Cosa ha impedito loro di scriverlo in quel modo:

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);
};

E ';' alla fine delle parentesi graffe - è obsoleto, non dovresti farlo ora.

Un metodo Select gigante consiste in un ciclo for gigante:

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;
         };
      ...
}

Ovviamente, tutti i controlli per la conformità della transazione con l'Expert Advisor corrente dovrebbero essere implementati in un metodo separato, per esempio come questo:

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

E in generale, select dovrebbe essere diviso in 3-4 metodi in più per rendere chiaro cosa sta succedendo in esso.

George Merts
L'Expert Advisor stesso consiste di cinque linee. In questo file, si dichiara l'oggetto della fabbrica di parti dell'EA e si includono le inclusioni.

Factory è un modello molto controverso. È bene usarlo, ma non consiglio di fare tutto attraverso una fabbrica.

George Merts
E anche quando ci sono due livelli di annidamento, è obbligatorio scrivere commenti dopo ogni parentesi di chiusura, quale blocco seppellisce (per esempio, l'intestazione di un ciclo duplicato).

Beh, è per questo che si scrive tra parentesi nel brutto modo di MQL. Se l'hai scritto così:

if(OrderSelect())
{
   ...
}

Si vedrebbe sempre quale staffa chiude quale blocco di codice.

Ci sarebbe una dozzina di altri avvertimenti nel tuo codice. Certo, il codice non è perfetto, ma si può sentire il gusto dell'autore per la bellezza :))

 
Vasiliy Sokolov:

Un po' di critica:

О. Questo è il tipo di discussione che amo. Quindi.

Perché reinventare la ruota sotto forma di CMyObject, quando c'è un CObject standard che tutti capiscono?

La funzionalità di CObject e CArrayObj è ovviamente copiata qui. Perché? L'ordinamento rapido è costruito in contenitori di dati standard. Usateli.

CMyObject è un erede del CObject standard, tutte le liste e gli array nel mio codice sono discendenti di CArray (e altri array della libreria standard). Non uso quasi mai gli array standard array[].

E, naturalmente, ordinare e lavorare con le liste usa le funzionalità di base di CObject.

E la differenza tra loro è questa: un CObject standard è "un oggetto lista o array ordinato". Un CMyObject è un CObject che ha un certo tipo e contiene un valore dato quando è stato creato. Avevo bisogno di questo oggetto a causa della diffusa riduzione degli oggetti alla classe astratta di base - per capire da puntatore a quale oggetto "effettivamente" punta. Il tipo CMyObject è impostato da quella stessa funzione SetMyObjectType (). Questa funzione deve necessariamente essere chiamata nei costruttori di qualsiasi derivato da CMyObject, per assegnare un identificatore alla classe a cui l'oggetto appartiene.

Ha anche SetUDCreationValue() - che imposta un valore definito dall'utente al momento della creazione. Raramente usato. È necessario per distinguere diversi oggetti della stessa classe.

Se l'interfaccia di classe - il suo costruttore è meglio nascondere nella sezione protetta. Allora il suo oggetto non può essere creato direttamente.

Il costruttore protetto non è un'opzione. Sì, credo che sia ragionevole per le interfacce, non sapevo che fosse possibile.

Definire un costruttore vuoto? Non lo so. Io non lo farei. È meglio non menzionare il distruttore se non ne avete bisogno.

È una "eredità maledetta del passato". Una volta abbiamo scritto un progetto abbastanza grande e lì, se non definivamo un distruttore vuoto, ci voleva molto tempo per rimuovere gli oggetti per qualche motivo. Così, da allora lo faccio al volo. In generale, anche il distruttore deve essere virtuale.

Incremento non standard iI, iterazione non standard ++iI, iHistoryDealsTotal - definito da qualche parte là fuori, molto prima del ciclo. È meglio mantenerlo più semplice:

Non sono d'accordo. L'incremento è perfettamente normale, - i, solo notazione standardizzata - prima con una lettera piccola il suo tipo è intero, e poi con una lettera maiuscola il suo nome è I.

Sembra che tu sia contrario a questi fogli, ma lo scrivi da qualche parte. Nessuno vuole analizzare queste sciocchezze. Cosa ti ha impedito di scriverlo così:

In questo caso ho dovuto scegliere tra la "visibilità" della classe e la bellezza della funzione. Ho scelto "visibilità". La bellezza ha sofferto.

Il metodo Select gigante consiste in un ciclo for gigante:

Ovviamente, tutti i controlli per la conformità della transazione con l'Expert Advisor corrente dovrebbero essere in un metodo separato, per esempio come questo:

E in generale, select ha bisogno di essere diviso in 3-4 metodi in più per rendere chiaro cosa sta succedendo in esso.

Sono d'accordo. Qui, in principio, questo stesso ciclo è "cresciuto", all'inizio non era così grande.

Anche se non è sempre conveniente implementare controlli minori in funzioni private, poiché questi controlli non sono sempre rintracciabili nel codice.

Factory è un modello molto controverso. L'uso va bene, ma non consiglio di fare tutto attraverso una fabbrica.

Ora non ricordo, c'erano diverse varianti di costruzione dell'Expert Advisor. Mi sono fermato alla "fabbrica di parti Expert Advisor". In linea di principio, non è più un puro modello classico di "fabbrica". Originariamente, doveva essere un modello "classico", ma ora è piuttosto un "costruttore-concentratore" di parti di Expert Advisor. Ed è anche responsabile della rimozione di quelle parti, cosa che non è caratteristica di una fabbrica. Ma il nome è rimasto.

Ecco perché lo si scrive tra parentesi nel brutto modo di MQL. Se l'hai scritto così:

Si vedrebbe sempre quale staffa chiude quale blocco di codice.

Perché "il modo brutto"?

L'intestazione del ciclo, poi la parentesi di apertura con indentazione, poi l'intero blocco con la stessa indentazione, e infine la parentesi di chiusura - anche questa con indentazione.

Cosa pensate sia meglio?

 
Gregory Kovalenko:

Ho bisogno di ottenere profitti su 2 ordini aperti. L'ultimo ordine aperto, lo chiamoOrderProfit2, e l'ordine successivo -OrderProfit1.

Il primo ordine viene aperto per primo, poi il 2° ordine, quindi il 1° ordine nel ciclo si chiama 2)

Dov'è l'errore?

Si sta solo passando attraverso gli ordini. Da nessuna parte controlla quale sia il primo e quale il secondo.

È necessario inserire un controllo sull'orario di apertura. Poi si può distinguere tra l'ordine che si è aperto prima e l'ordine che si è aperto dopo. O forse possono aprirsi allo stesso tempo.

Motivazione: