Autoapprendimento del linguaggio MQL5 da zero - pagina 52

 

Fate attenzione al filtro per simbolo e per procedura guidata nel ciclo di posizione. Se non c'è un filtro, ma si tracciano tutte le posizioni aperte su tutti i simboli, e questo è male.

Così, a prima vista, tutto sembra andare bene.

Совершение сделок - Торговые операции - Справка по MetaTrader 5
Совершение сделок - Торговые операции - Справка по MetaTrader 5
  • www.metatrader5.com
Торговая деятельность в платформе связана с формированием и отсылкой рыночных и отложенных ордеров для исполнения брокером, а также с управлением текущими позициями путем их модификации или закрытия. Платформа позволяет удобно просматривать торговую историю на счете, настраивать оповещения о событиях на рынке и многое другое. Открытие позиций...
 
Andrei Novichkov:

Fate attenzione al filtro per simbolo e per procedura guidata nel ciclo di posizione. Se non c'è un filtro, ma si tracciano tutte le posizioni aperte su tutti i simboli, e questo è male.

Così, tutto sembra essere a posto a prima vista.

Grazie mille, Andrey, ho capito tutto di Magic perché diverse posizioni possono essere aperte per un simbolo, ma è sorta un'altra domanda. L'Expert Advisor passerà attraverso le posizioni aperte per tutti i simboli in una volta sola, se non è esplicitamente puntato sul simbolo corrente? E questo nonostante il fatto che sia impostato per una certa coppia di valute, per esempio, EURUSD? Onestamente, non capisco bene questo punto.

Saluti, Vladimir.

 
MrBrooklin:

Grazie mille, Andrey, ho capito tutto di Magic, perché diverse posizioni possono essere aperte su un simbolo, ma ho un'altra domanda. L'EA guarderà attraverso le posizioni aperte per tutti i simboli in una volta, se non è esplicitamente puntato sul simbolo corrente? E questo nonostante il fatto che sia impostato per una certa coppia di valute, per esempio, EURUSD? Onestamente, non capisco bene questo punto.

Sinceramente, Vladimir.


Sì. È impostato in tutte le posizioni aperte per tutti i simboli.
Coerentemente per tutte le posizioni aperte.
Ecco una semplice ricerca nel libro di testo.

https://book.mql4.com/ru/build/trading
 
MrBrooklin:

Così, sulla base della letteratura letta, ho scritto un breve algoritmo per creare un Expert Advisor con la funzione trailing stop:

  1. Creiamo un Expert Advisor per automatizzare il livello di trailing stop Loss di una posizione già aperta con livelli di Take Profit e Stopspecificati Loss.
  2. Nell'Expert Advisor, crea un blocco di parametri di input con due parametri: imposta "livello di trailing" e imposta "passo di trailing".
  3. Quando arrivano nuove quotazioni, elaborale con la funzione OnTick( ). Il trailing funziona solo quando arriva un nuovo tick per il simbolo corrente.
  4. Creiamo ed eseguiamo un ciclo per cercare tutte le posizioni.
  5. Se improvvisamente non troviamo posizioni aperte, torniamo al ciclo
  6. Aggiorniamo le citazioni.
  7. Se c'è una posizione aperta, si continua.
  8. Definiamo il tipo di posizione aperta: Comprare o Vendere.
  9. Se c'è una posizione diacquistoaperta , definiamo dove si trova il prezzo corrente rispetto alla posizione aperta .
  10. Se il prezzo attuale è superiore al prezzo al quale è stata aperta la posizione, controlliamo a quale livello è salito.
  11. Se il prezzo corrente ha raggiunto il "trailing level" definito nei parametri di input, spostiamo loStop Loss al livello senza perdita che equivale al prezzo di apertura della posizioneBuy. Altrimenti non facciamo nulla.
  12. Se il prezzo corrente supera il livello di Trailing Stop del valore uguale al livello di Trailing Stop, loStop Loss viene spostato dal livello del prezzo di apertura della posizione Buy del valore uguale al livello di Trailing Stop e così via fino a quando il prezzo raggiunge il livello Take Profit specificato per questa posizione .
  13. Se il prezzo gira e raggiunge il livello diStop Lossgià spostato , la posizione viene chiusa .
  14. Se la posizione èSell, definiamo dove si trova il prezzo corrente rispetto alla posizione aperta .
  15. Se il prezzo attuale è inferiore al prezzo della posizione aperta, controlliamo a quale livello è sceso.
  16. Se il prezzo corrente ha raggiunto il livello di trailing specificato nei parametri di input, spostiamo Stop Loss al livello senza perdita pari al prezzo di apertura della posizioneSell. Altrimenti non facciamo nulla.
  17. Se il prezzo corrente ha superato il livello di Trailing Stop del valore uguale al livello diTrailing Stop, loStop Loss viene spostato dal livello di apertura della posizione Sell del valore uguale al livello di Trailing Stop e così via fino a quando il prezzo raggiunge il livello Take Profit specificato per quella posizione .
  18. Se il prezzo gira e raggiunge il livello diStop Loss, la posizione viene chiusa .

Per favore, rivedete l'algoritmo e datemi qualche suggerimento su quali punti sono stati mancati.

Sinceramente, Vladimir.

La teoria non è male, ora concentriamoci sulla pratica. Funzionerà?

 
Aliaksandr Hryshyn:

Buona la teoria, ora la pratica. Puoi farlo?

Ci proverò. Ma lei capisce che questo richiede un livello di conoscenza completamente diverso, e io non ce l'ho ancora.

Saluti, Vladimir.

 
Aleksey Masterov:

Sì. Su tutte le pose aperte su tutti i simboli...
Coerentemente in tutte le pose aperte.
Ecco una semplice ricerca già nel libro di testo.

https://book.mql4.com/ru/build/trading

Sì, Alexey, ho già visto questo codice. È sotto forma di un file di inclusione. Ad essere onesti, non ho trovato nulla sul simbolo in esso, anche se l'ho visto diverse volte. Forse ho capito male qualcosa o sto solo cercando male.

Sinceramente, Vladimir.

 

Per ora, continuiamo con le funzioni.

Come ho scritto prima, le funzioni sono ovunque, bisogna amarle e saperle scrivere. Le funzioni, sono i nostri piccoli combattenti per risolvere i problemi globali. Se fossimo generali in un esercito, che tipo di combattenti vorremmo controllare? Ecco una lista approssimativa:

  • Un combattente deve eseguire chiaramente un ordine. Il livello medio di intelligenza di un soldato di fanteria non è grande. Quindi è meglio fissare obiettivi chiari e semplici per questi combattenti: "prendere un bunker", "prendere una lingua", "minare un ponte".
  • Se il compito è difficile non cercare un combattente super intelligente per realizzarlo. È meglio dividere il compito in più sottocompiti e prendere due o tre combattenti più stupidi ma più efficienti. Lascia che tutti risolvano i loro sottocompiti senza domande, o meglio ancora lascia che siano ignoranti del concetto e del compito nel suo insieme. Allora se qualcuno viene fatto prigioniero non sarà un problema, l'intero piano non sarà rivelato.
  • Un soldato deve eseguire l'ordine indipendentemente dall'ambiente circostante: neve, pioggia, Parigi e donne - se tali ambienti non hanno alcun effetto sull'attuazione dell'ordine, allora quelle condizioni e l'ambiente esterno devono essere ignorati.
  • Succede che i compiti possono essere difficili. Richiedono molti combattenti per risolverli. Un generale non può essere assegnato ad ogni combattente. Invece, è necessario assegnare un soldato più intelligente per guidare diversi combattenti. Questo gruppo a sua volta si unisce con lo stesso in una compagnia di soldati e nomina un ufficiale superiore.

Ma siamo stati sviati, passiamo di nuovo alle funzioni.

Se una funzione risolve troppi problemi in generale - seguendo l'analogia, è un combattente molto intelligente che, se qualcosa va storto con essa, potrebbe rovinare l'intera impresa. Se si chiede cosa fa una tale funzione, la risposta potrebbe essere lunga. Se il risultato di questa funzione smette improvvisamente di essere corretto, sarà molto difficile scoprire cosa causa un errore in essa (perché ci sono molti compiti, molto codice, molte chiamate a sottoprocedure e dove esattamente l'errore è difficile da capire).

Se una funzione calcola risultati corretti il lunedì, il mercoledì e la domenica e nei giorni di riposo a seconda del nostro "umore", possiamo fare affidamento su questa funzione? Immaginate che la funzione OrderSend, per esempio, apra le posizioni solo il giovedì e che venga definito un parametro magico 13. E questo non è affatto un'assurdità o una fantasia. Questo comportamento può essere organizzato con un click delle dita - è sufficiente rendere la funzione dipendente da alcuni parametri dell'ambiente esterno.

Supponiamo che la funzione:

double sum(double a, double b)
{
   return a+b;
}

restituirà sempre la somma di due valori, indipendentemente dall'ambiente esterno. Ciò significa che anche se copiamo questa funzione in un altro script o Expert Advisor, funzionerà perfettamente in esso. Questa funzione può essere scritta una volta sola e utilizzata in molti dei nostri programmi semplicemente copiando in modo ottuso. Potremo sempre contare sul suo risultato sapendo che il suo funzionamento non dipende da nulla. Tali funzioni, il cui risultato non dipende dal loro ambiente, sono chiamate funzioni senza effetti collaterali o pure. Se ci sforziamo di scrivere funzioni pure, presto ne avremo molte. Questo significa che potete combinarli in un file e includerli nei vostri nuovi progetti. Questo è chiamato riutilizzo del codice. Non facciamo il lavoro due volte. Invece, usiamo funzioni già scritte che conosciamo e la cui affidabilità è stata testata più di una volta.

Guardiamo ora l'anti-esempio:

double c = 0.0;
double sum(double a, double b)
{
   return a+b+c;
}

Il risultato sembra essere lo stesso, perché c è sempre zero. O non è sempre così? E se qualcuno cambia c da qualche parte? Che cosa allora? Cosa succede se qualcuno da qualche parte usa anche la variabile esterna c, ma per i suoi scopi, e ha una variabile c di tipo diverso, diciamo stringa? Combinare queste due funzioni non è più possibile (il compilatore non permette di dichiarare due variabili con lo stesso nome). Le loro dipendenze comuni sono anche difficili da risolvere. Non so proprio cosa farci. Per esempio, non conosco ancora un modo affidabile e facile per far lavorare insieme queste funzioni.

Anche se non ci sono altre funzioni e solo una funzione legge una variabile esterna, non è così facile copiarla altrove. Dobbiamo copiare sia questa funzione che la sua dipendenza. Ma cosa succede se copiamo queste funzioni in un file comune? Abbiamo 50 o 100 di queste funzioni lì. E ognuno di essi copia con sé un mucchio di variabili dipendenti. Otteniamo un groviglio di variabili correlate con funzioni poco chiare. Ma a cosa serve tutto questo? Quali problemi risolve? Perché creare dipendenze inutili quando si può farne a meno nella maggior parte dei casi?

Le funzioni hanno un'altra caratteristica sorprendente. Le funzioni sono autodescrittive. In altre parole, non è necessario disegnare uno schema, basta scegliere buoni nomi e dividere l'algoritmo generale in funzioni. Ecco un esempio:

void OnTick()
{
   if(SelectFirstPendingOrder(ORDER_TYPE_BUY))
       CancelSelectPendingOrder();
}

Non so cosa faccia questo codice, perché le funzioni non sono nemmeno scritte. Ma se dovessi leggerlo, probabilmente significherebbe che se il primo <primo> ordine pendente con direzione ORDER_TYPE_BUY è selezionato con successo, verrebbe annullato (la prima funzione seleziona, la seconda annulla). Poiché il codice verrebbe eseguito ogni tick, non importa quanti ordini pendenti ci fossero, ognuno di essi verrebbe cancellato prima o poi. Questo significa anche che qualsiasi tentativo di piazzare un ordine di acquisto in sospeso verrebbe soppresso - l'ordine verrebbe immediatamente rimosso. Allo stesso tempo, gli ordini di vendita saranno effettuati senza problemi.

Ci sono solo due righe di codice e due funzioni. E l'algoritmo non è banale, e ciò che è più importante, è affidabile.

Quando si parla di MCL, dovremmo parlare un po' di più delle funzioni pure. Essendo un linguaggio applicativo, è difficile scrivere qualcosa senza basarsi sui dati forniti dal terminale. Dopo tutto, questo è il compito principale: interagire correttamente con l'ambiente di trading. Formalmente, qualsiasi ambiente di trading è modificabile: prezzi, numero di ordini, cambiamenti di equilibrio, ecc. Pertanto, qualsiasi funzione che interagisce con un ambiente commerciale così mutevole non è chiara. Perché l'ambiente commerciale esterno può anche essere considerato come una variabile globale, che cambia costantemente. Ma quando scriviamo OrdersTotal(), non ci aspettiamo che questa funzione restituisca sempre lo stesso valore. Invece, ci aspettiamo che restituisca il numero di ordini pendenti che varierà naturalmente. Pertanto, in MQL, considereremo le funzioni come pulite e riutilizzabili, anche nel caso in cui chiamino funzioni di API esterne, come OrdersTotal(). Sarà la nostra ragionevole indulgenza.

 
Vasiliy Sokolov:

Continuiamo con le funzioni...

Grazie mille, Vasily, per l'inestimabile conoscenza che condividi non solo con me, ma anche con i programmatori principianti che leggono o leggeranno questo argomento!

Con lo stesso grande rispetto, Vladimir.

 

Continuo a studiare il linguaggio di programmazione MQL5. Mentre non ci sono state osservazioni serie sull'algoritmo di scrittura del codice di Trailing_Stop Expert Advisor (mi ricordo del simbolo e di Magic, lo aggiungerò all'algoritmo più tardi!), ho creato i parametri di input per l'EA e ho scritto il codice del ciclo che avvia la ricerca delle posizioni aperte.

Quando ho eseguito l'EA ho visto un problema - nella scheda "Experts" del terminale di trading appaiono 2 messaggi identici "A loop has started" ad ogni tick, nonostante il fatto che il terminale di trading ha solo un grafico della coppia di valute EURUSD e solo una posizione è aperta su di esso. E questi messaggi hanno lo stesso tempo esatto di uscita.

Ho lottato fino a mezzanotte ma non ho potuto vincere. Non riesco a capire quale sia il problema.


Il codice dell'Expert Advisor è scritto in inglese, mentre i commenti sono in russo per facilitare il processo. In questo EA, ho cercato di descrivere tutto, come ho promesso prima, in un modo comprensibile per uno studente di 1° grado di una scuola di programmazione.

Saluti, Vladimir.

//+------------------------------------------------------------------+
//|                                                Trailing_Stop.mq5 |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

input ushort TrailingLevel=100; //Уровень трейлинга (для включения)
input ushort TrailingStep=10;   //Шаг трейлинга (для перемещения)
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   /* Для цикла for создаем локальную переменную i и присваиваем ей значение "торговая функция PositionsTotal",
      которая возвращает нам количество открытых позиций*/
   int i=PositionsTotal();
   /* Разберемся, что такое оператор for.
      Оператор for состоит из трех Выражений и выполняемого Оператора:
      for(Выражение_1; Выражение_2; Выражение_3)
         Оператор;
      Выражение_1 описывает инициализацию цикла. За инициализацию цикла будет отвечать "Торговая функция PositionsTotal".
      Выражение_2 проверяет условия завершения цикла. Если оно истинно, то выполняется Оператор в теле цикла for.
      Все повторяется до тех пор, пока Выражение_2 не станет ложным. Если оно ложно, цикл заканчивается
      и управление передается следующему оператору.
      Выражение_З вычисляется после каждой итерации (т.е. после каждого повторения действия).
   */
   for(i; i>=0; i--) //запускаем цикл перебора открытых позиций (i) от максимума до нуля (i>=0) с шагом минус 1 (i--)
      Print("Запущен цикл");
  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
//---

  }
//+------------------------------------------------------------------+
 
MrBrooklin:


i è uguale al numero di posizioni aperte, quindi molti cicli saranno con la stampa

Print("Запущен цикл");
è necessario rimuovere il segno "=" in
   for(i; i>=0; i--)
perché avete bisogno di passare attraverso il ciclo quando il numero di posizioni aperte è 0. questa chiamata a zero è da dove proviene la seconda stampa