Creazione di un Expert Advisor, che fa Trading su una Serie di Strumenti
Introduzione
Il lato tecnico dell'implementazione del codice di programma in modo che un singolo Expert Advisor, lanciato su un singolo grafico, sia in grado di negoziare con diverse attività finanziarie contemporaneamente. In generale, questo non era un problema nemmeno in MQL4. Ma solo con l'avvento del terminale client MetaTrader 5, i trader hanno finalmente avuto l'opportunità di eseguire un'analisi completa del lavoro di tali automatizzati, utilizzando strategy tester.
Quindi, ora gli automi multi-valuta diventeranno più popolari che mai e possiamo prevedere un'ondata di interesse nella costruzione di tali sistemi di trading. Ma il problema principale dell'implementazione di tali robot è nel fatto che le loro dimensioni nel codice del programma si espandono, nella migliore delle ipotesi, in una progressione aritmetica, e questo non è facile da comprendere per un tipico programmatore.
In questo articolo scriveremo un semplice Expert Advisor multi-valuta, in cui i difetti della struttura sono, se non assenti, almeno ridotti al minimo.
1. Implementazione di un semplice sistema trend-following
In effetti, potremmo iniziare con un sistema di trading massimamente semplice, seguendo l'andamento sulla base di un terminale integrato di un indicatore tecnico Triple Exponential Moving Average. Questo è un algoritmo molto semplice, che non richiede commenti speciali e che ora introdurremo nel codice del programma.
Ma prima di tutto, vorrei trarre le conclusioni più generali sull'Expert Advisor. Ha senso iniziare con il blocco dei parametri di Expert Advisor in arrivo, dichiarati a livello globale.
Quindi, prima di tutto dobbiamo scegliere le attività finanziarie con cui lavoreremo. Ciò può essere fatto utilizzando variabili di input di linea, in cui è possibile memorizzare i simboli delle risorse. Ora sarebbe bello avere un interruttore di divieto commerciale per ogni attività finanziaria, che consentirebbe di disabilitare le operazioni di trading da parte dell'asset.
Naturalmente, ogni asset dovrebbe essere associato ai propri parametri di trading individuali di Stop Loss, Take Profit, volume della posizione aperta e slippage. E per ovvie ragioni, i parametri di input dell'indicatore Triple Exponential Moving Average per ogni chip di trading dovrebbero essere individuali.
Ecco un ultimo blocco di variabili di input per un solo chip, eseguito in conformità con questi argomenti. I blocchi rimanenti differiscono solo per i numeri nei nomi dei parametri di input dell'Expert Advisor. Per questo esempio mi sono limitato a sole dodici asset finanziari, anche se idealmente non ci sono limitazioni software per il numero di tali blocchi.
Abbiamo solo bisogno di qualcosa su cui fare trading! E, soprattutto, il nostro PC deve avere risorse sufficienti per risolvere questo problema.
input string Symb0 = "EURUSD"; input bool Trade0 = true; input int Per0 = 15; input ENUM_APPLIED_PRICE ApPrice0 = PRICE_CLOSE; input int StLoss0 = 1000; input int TkProfit0 = 2000; input double Lots0 = 0.1; input int Slippage0 = 30;
Ora che abbiamo capito le variabili a livello globale, possiamo procedere alla costruzione del codice all'interno della funzione OnTick(). L'opzione più razionale qui sarebbe la divisione dell'algoritmo per la ricezione dei segnali di trading e la parte di trading effettiva dell'Expert Advisor in due funzioni personalizzate.
E poiché l'Expert Advisor lavora con dodici asset finanziari contemporaneamente, ci devono essere anche dodici chiamate di queste funzioni all'interno del blocco OnTick().
Naturalmente, il primo parametro di input di queste funzioni dovrebbe essere un numero univoco, sotto il quale verranno elencate queste attività di trading. Il secondo parametro di input, per ovvi motivi, sarà il nome della linea dell'asset finanziario di trading.
Per il ruolo del terzo parametro, impostiamo una variabile logica per risolvere il trade. Successivamente, per l'algoritmo che determina i segnali di trading, segui i segnali dell'indicatore di input e per una funzione di trading - la distanza dagli ordini in sospeso, il volume di posizione e lo slippage (slittamento consentito del prezzo della posizione aperta).
Per trasferire i segnali di trading da una funzione all'altra, gli array statici devono essere impostati come parametri della funzione, che derivano i loro valori attraverso un riferimento. Questa è la versione finale del codice proposto per la funzione OnTick().
void OnTick() { //--- declare variables arrays for trade signals static bool UpSignal[12], DnSignal[12], UpStop[12], DnStop[12]; //--- get trade signals TradeSignalCounter( 0, Symb0, Trade0, Per0, ApPrice0, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 1, Symb1, Trade1, Per1, ApPrice1, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 2, Symb2, Trade2, Per2, ApPrice2, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 3, Symb3, Trade3, Per3, ApPrice3, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 4, Symb4, Trade4, Per4, ApPrice4, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 5, Symb5, Trade5, Per5, ApPrice5, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 6, Symb6, Trade6, Per6, ApPrice6, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 7, Symb7, Trade7, Per7, ApPrice7, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 8, Symb8, Trade8, Per8, ApPrice8, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 9, Symb9, Trade9, Per9, ApPrice9, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter(10, Symb10, Trade10, Per10, ApPrice10, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter(11, Symb11, Trade11, Per11, ApPrice11, UpSignal, DnSignal, UpStop, DnStop); //--- perform trade operations TradePerformer( 0, Symb0, Trade0, StLoss0, TkProfit0, Lots0, Slippage0, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 1, Symb1, Trade1, StLoss1, TkProfit1, Lots1, Slippage1, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 2, Symb2, Trade2, StLoss2, TkProfit2, Lots2, Slippage2, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 3, Symb3, Trade3, StLoss3, TkProfit3, Lots3, Slippage3, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 4, Symb4, Trade4, StLoss4, TkProfit4, Lots4, Slippage4, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 5, Symb5, Trade5, StLoss5, TkProfit5, Lots5, Slippage5, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 6, Symb6, Trade6, StLoss6, TkProfit6, Lots6, Slippage6, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 7, Symb7, Trade7, StLoss7, TkProfit7, Lots7, Slippage7, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 8, Symb8, Trade8, StLoss8, TkProfit8, Lots8, Slippage8, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 9, Symb9, Trade9, StLoss9, TkProfit9, Lots9, Slippage9, UpSignal, DnSignal, UpStop, DnStop); TradePerformer(10, Symb10, Trade10, StLoss10, TkProfit10, Lots10, Slippage10, UpSignal, DnSignal, UpStop, DnStop); TradePerformer(11, Symb11, Trade11, StLoss11, TkProfit11, Lots11, Slippage11, UpSignal, DnSignal, UpStop, DnStop); //--- }
All'interno della funzione TradeSignalCounter(), è necessario ottenere l’handle dell'indicatore tecnico Triple Exponential Moving Average una volta all'inizio di ogni chip, e poi ad ogni cambio della barra per calcolare i segnali di trading.
Questo schema relativamente semplice con l'implementazione nel codice sta iniziando ad essere pieno di piccoli dettagli.
bool TradeSignalCounter(int Number, string Symbol_, bool Trade, int period, ENUM_APPLIED_PRICE ApPrice, bool &UpSignal[], bool &DnSignal[], bool &UpStop[], bool &DnStop[]) { //--- check if trade is prohibited if(!Trade)return(true); //--- declare variable to store final size of variables arrays static int Size_=0; //--- declare array to store handles of indicators as static variable static int Handle[]; static int Recount[],MinBars[]; double TEMA[4],dtema1,dtema2; //--- initialization if(Number+1>Size_) // Entering the initialization block only on first start { Size_=Number+1; // For this number entering the block is prohibited //--- change size of variables arrays ArrayResize(Handle,Size_); ArrayResize(Recount,Size_); ArrayResize(MinBars,Size_); //--- determine minimum number of bars, sufficient for calculation MinBars[Number]=3*period; //--- setting array elements to 0 DnSignal[Number] = false; UpSignal[Number] = false; DnStop [Number] = false; UpStop [Number] = false; //--- use array as timeseries ArraySetAsSeries(TEMA,true); //--- get indicator's handle Handle[Number]=iTEMA(Symbol_,0,period,0,ApPrice); } //--- check if number of bars is sufficient for calculation if(Bars(Symbol_,0)<MinBars[Number])return(true); //--- get trade signals if(IsNewBar(Number,Symbol_,0) || Recount[Number]) // Entering the block on bar change or on failed copying of data { DnSignal[Number] = false; UpSignal[Number] = false; DnStop [Number] = false; UpStop [Number] = false; //--- using indicator's handles, copy values of indicator's //--- buffers into static array, specially prepared for this purpose if(CopyBuffer(Handle[Number],0,0,4,TEMA)<0) { Recount[Number]=true; // As data were not received, we should return // into this block (where trade signals are received) on next tick! return(false); // Exiting the TradeSignalCounter() function without receiving trade signals } //--- all copy operations from indicator buffer are successfully completed Recount[Number]=false; // We may not return to this block until next change of bar int Digits_ = int(SymbolInfoInteger(Symbol_,SYMBOL_DIGITS)+4); dtema2 = NormalizeDouble(TEMA[2] - TEMA[3], Digits_); dtema1 = NormalizeDouble(TEMA[1] - TEMA[2], Digits_); //---- determining the input signals if(dtema2 > 0 && dtema1 < 0) DnSignal[Number] = true; if(dtema2 < 0 && dtema1 > 0) UpSignal[Number] = true; //---- determining the output signals if(dtema1 > 0) DnStop[Number] = true; if(dtema1 < 0) UpStop[Number] = true; } //----+ return(true); }
A questo proposito, il codice della funzione TradePerformer() risulta essere silenzioso e semplice:
bool TradePerformer(int Number, string Symbol_, bool Trade, int StLoss, int TkProfit, double Lots, int Slippage, bool &UpSignal[], bool &DnSignal[], bool &UpStop[], bool &DnStop[]) { //--- check if trade is prohibited if(!Trade)return(true); //--- close opened positions if(UpStop[Number])BuyPositionClose(Symbol_,Slippage); if(DnStop[Number])SellPositionClose(Symbol_,Slippage); //--- open new positions if(UpSignal[Number]) if(BuyPositionOpen(Symbol_,Slippage,Lots,StLoss,TkProfit)) UpSignal[Number]=false; //This trade signal will be no more on this bar! //--- if(DnSignal[Number]) if(SellPositionOpen(Symbol_,Slippage,Lots,StLoss,TkProfit)) DnSignal[Number]=false; //This trade signal will be no more on this bar! //--- return(true); }Ma questo è solo perché i comandi effettivi per l'esecuzione delle operazioni di trading sono racchiusi in quattro funzioni aggiuntive:
BuyPositionClose(); SellPositionClose(); BuyPositionOpen(); SellPositionOpen();
Tutte e quattro le funzioni funzionano in modo del tutto analogo, quindi possiamo limitarci all'esame di una sola di esse:
bool BuyPositionClose(const string symbol,ulong deviation) { //--- declare structures of trade request and result of trade request MqlTradeRequest request; MqlTradeResult result; ZeroMemory(request); ZeroMemory(result); //--- check if there is BUY position if(PositionSelect(symbol)) { if(PositionGetInteger(POSITION_TYPE)!=POSITION_TYPE_BUY) return(false); } else return(false); //--- initializing structure of the MqlTradeRequest to close BUY position request.type = ORDER_TYPE_SELL; request.price = SymbolInfoDouble(symbol, SYMBOL_BID); request.action = TRADE_ACTION_DEAL; request.symbol = symbol; request.volume = PositionGetDouble(POSITION_VOLUME); request.sl = 0.0; request.tp = 0.0; request.deviation=(deviation==ULONG_MAX) ? deviation : deviation; request.type_filling=ORDER_FILLING_FOK; //--- string word=""; StringConcatenate(word, "<<< ============ BuyPositionClose(): Close Buy position at ", symbol," ============ >>>"); Print(word); //--- send order to close position to trade server if(!OrderSend(request,result)) { Print(ResultRetcodeDescription(result.retcode)); return(false); } //----+ return(true); }
Fondamentalmente, questo è praticamente l'intero Expert Advisor multi-valuta (Exp_TEMA.mq5)!
Oltre alle funzioni considerate, contiene due funzioni utente aggiuntive:
bool IsNewBar(int Number, string symbol, ENUM_TIMEFRAMES timeframe); string ResultRetcodeDescription(int retcode);
La prima di queste funzioni restituisce il valore reale al momento della modifica della barra, in base al simbolo e all'intervallo di tempo selezionati, e la seconda, restituisce la riga dal codice risultato della transazione di trading, derivato dal campo retcode della struttura della richiesta di trading MqlTradeResult.
L'Expert Advisor è pronto, è tempo di iniziare con i test! Non ci sono differenze visibili e serie nei test dell'Expert Advisor multi-valuta rispetto al suo collega Expert Advisor a moneta unica.
Determinare le configurazioni nel tab "Parametri" dello Strategy Tester:
Figura 1. Tab "Impostazioni" dello Strategy tester
Se necessario, regolare i valori dei parametri di input nella scheda "Parametri di input:
Figura 2. Tab "Parametri" dello Strategy tester
e quindi fare click sul pulsante "Start" nello Strategy Tester nel tab "Impostazioni":
Figura 3. Esecuzione del test di Expert Advisor
Il tempo di passaggio della prima prova dell'Expert Advisor può rivelarsi molto significativo, a causa del caricamento della cronologia per tutti e dodici i simboli. Dopo aver completato il test nello strategy tester, apri il tab "Risultati":
Figura 4. Risultati dei test
e fare un'analisi dei dati, utilizzando il contenuto del tab "Chart":
Figura 5. Grafico delle dinamiche del saldo e del capitale
e il "Journal":
Figura 6. Journal dello Strategy tester
Naturalmente, l'essenza stessa degli ingressi e delle uscite dell'algoritmo dal market di questo Expert Advisor è troppo semplice, e sarebbe ingenuo aspettarsi risultati molto significativi, quando si utilizzano i primi parametri casuali. Ma il nostro obiettivo qui è dimostrare l'idea fondamentale di costruire un Expert Advisor multi-valuta nel modo più semplice possibile.
Con l'ottimizzazione di questo Expert Advisor possono sorgere alcuni inconvenienti dovuti a troppi parametri di input. L'algoritmo genetico di ottimizzazione richiede una quantità molto minore di questi parametri, quindi l'Expert Advisor dovrebbe essere ottimizzato su ciascun chip individualmente, disabilitando i restanti chip dei parametri di input TradeN.
Ora, quando l'essenza stessa dell'approccio è stata delineata, puoi iniziare a lavorare con l'algoritmo più interessante del processo decisionale per il robot multi-valuta.
2. Risonanze sui mercati finanziari e loro applicazione nei sistemi di trading
L'idea di tenere conto delle correlazioni tra i diversi asset finanziari, in generale, non è nuova, e sarebbe interessante implementare l'algoritmo, che si baserebbe proprio sull'analisi di tali tendenze. In questo articolo implementerò un'automazione multi-valuta, basata sull'articolo di Vasily Yakimkin "Resonances - a New Class of Technical Indicators" pubblicato sulla rivista "Currency Speculator" (in russo) 04, 05, 2001.
L'essenza stessa di questo approccio in poche parole sembra la seguente. Ad esempio, per la ricerca su EUR / USD, utilizziamo non solo i risultati di alcuni indicatori sull'attività finanziaria, ma anche i risultati dello stesso indicatore relativi alle attività EUR / USD - EUR / JPY e USD / JPY. È meglio usare l'indicatore, i cui valori sono normalizzati nello stesso intervallo di modifiche per la semplicità e la facilità delle misurazioni e dei calcoli.
Considerando questi requisiti, l'indicatore stocastico si presta bene per questo classico. Anche se, in realtà, non vi è alcuna differenza nell'uso di altri indicatori. Come direzione di tendenza considereremo il segno di differenza tra il valore dello Stoh stocastico e il suo segno della linea Sign .
Figura 7. Determinare la direzione della tendenza
Per il simbolo variabile dStoh esiste un'intera tabella delle possibili combinazioni e delle loro interpretazioni per la direzione del trend attuale:
Figura 8. Combinazioni del simbolo variabile dStoh e della direzione di tendenza
Nel caso in cui due segnali degli asset EUR / JPY e USD / JPY hanno valori opposti, dovremmo determinare la loro somma e, se questa somma è maggiore di zero, considerare entrambi i segnali come positivi, altrimenti - come negativi.
Pertanto, per l'apertura di Longs, utilizzare la situazione in cui il trend è in crescita, e per uscire, utilizzare una tendenza al ribasso, o una tendenza quando i segnali dell'indicatore dell'asset principale EUR / USD sono negativi. Inoltre, esci dal lungo se l'asset principale non ha segnali e se la somma della variabile dStoh per le attività rimanenti è inferiore a zero. Per i pantaloncini, tutto è assolutamente analogo, solo la situazione è opposta.
La soluzione più razionale sarebbe quella di posizionare l'intera parte analitica dell'Expert Advisor nell'indicatore multi-valuta, e per l'Expert Advisor dai buffer degli indicatori, prendere solo i segnali pronti per il controllo dei trade. La versione di questo tipo di indicatore è presentata dall'indicatore MultiStochastic.mq5, che fornisce un'analisi visiva delle condizioni di mercato.
Figura 9. Indicatore MultiStochastic
La barra verde segnala l'apertura e il mantenimento dei longs e le barre rosse - degli shorts, rispettivamente. I punti rosa e verde chiaro sul bordo superiore del grafico rappresentano i segnali di uscita dalle posizioni lunghe e corte.
Questo indicatore può essere utilizzato direttamente per ricevere segnali nell'Expert Advisor, ma sarebbe comunque meglio facilitare il suo lavoro e rimuovere tutti i buffer e gli elementi di visualizzazione non necessari, lasciando solo ciò che è direttamente coinvolto nella fornitura di segnali di trading. Questo è esattamente ciò che è stato fatto nell'indicatore MultiStochastic_Exp.mq5.
In questo Expert Advisor, ho scambiato solo tre chip, quindi il codice della funzione OnTick() è diventato estremamente semplice:
void OnTick() { //--- declare variables arrays for trade signals static bool UpSignal[], DnSignal[], UpStop[], DnStop[]; //--- get trade signals TradeSignalCounter(0, Trade0, Kperiod0, Dperiod0, slowing0, ma_method0, price_0, SymbolA0, SymbolB0, SymbolC0, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter(1, Trade1, Kperiod1, Dperiod1, slowing1, ma_method1, price_1, SymbolA1, SymbolB1, SymbolC1, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter(2, Trade2, Kperiod2, Dperiod2, slowing2, ma_method2, price_2, SymbolA2, SymbolB2, SymbolC2, UpSignal, DnSignal, UpStop, DnStop); //--- perform trade operations TradePerformer( 0, SymbolA0, Trade0, StopLoss0, 0, Lots0, Slippage0, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 1, SymbolA1, Trade1, StopLoss1, 0, Lots1, Slippage1, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 2, SymbolA2, Trade2, StopLoss2, 0, Lots2, Slippage2, UpSignal, DnSignal, UpStop, DnStop); //--- }
Tuttavia, il codice della funzione TradeSignalCounter() è un po' più complesso: Il fatto è che un indicatore multi-valuta funziona direttamente con tre timeserie di diversi asset finanziari e, pertanto, implementiamo una verifica più sottile delle barre per l'adeguatezza del numero minimo di esse in una delle tre timeserie, utilizzando la funzione Rates_Total ().
Inoltre, viene effettuata un'ulteriore verifica sulla sincronizzazione delle timeserie, utilizzando la funzione SynchroCheck(), per garantire l'accuratezza nel determinare il momento in cui si verifica un cambio di barra in tutte le timeserie contemporaneamente.
bool TradeSignalCounter(int Number, bool Trade, int Kperiod, int Dperiod, int slowing, ENUM_MA_METHOD ma_method, ENUM_STO_PRICE price_, string SymbolA, string SymbolB, string SymbolC, bool &UpSignal[], bool &DnSignal[], bool &UpStop[], bool &DnStop[]) { //--- check if trade is prohibited if(!Trade)return(true); //--- declare variable to store sizes of variables arrays static int Size_=0; //--- declare arrays to store handles of indicators as static variables static int Handle[]; static int Recount[],MinBars[]; //--- double dUpSignal_[1],dDnSignal_[1],dUpStop_[1],dDnStop_[1]; //--- change size of variables arrays if(Number+1>Size_) { uint size=Number+1; //---- if(ArrayResize(Handle,size)==-1 || ArrayResize(Recount,size)==-1 || ArrayResize(UpSignal, size) == -1 || ArrayResize(DnSignal, size) == -1 || ArrayResize(UpStop, size) == -1 || ArrayResize(DnStop, size) == -1 || ArrayResize(MinBars,size) == -1) { string word=""; StringConcatenate(word,"TradeSignalCounter( ",Number, " ): Error!!! Unable to change sizes of variables arrays!!!"); int error=GetLastError(); ResetLastError(); //--- if(error>4000) { StringConcatenate(word,"TradeSignalCounter( ",Number," ): Error code ",error); Print(word); } Size_=-2; return(false); } Size_=int(size); Recount[Number] = false; MinBars[Number] = Kperiod + Dperiod + slowing; //--- get indicator's handle Handle[Number]=iCustom(SymbolA,0,"MultiStochastic_Exp", Kperiod,Dperiod,slowing,ma_method,price_, SymbolA,SymbolB,SymbolC); } //--- check if number of bars is sufficient for calculation if(Rates_Total(SymbolA,SymbolB,SymbolC)<MinBars[Number])return(true); //--- check timeseries synchronization if(!SynchroCheck(SymbolA,SymbolB,SymbolC))return(true); //--- get trade signals if(IsNewBar(Number,SymbolA,0) || Recount[Number]) { DnSignal[Number] = false; UpSignal[Number] = false; DnStop [Number] = false; UpStop [Number] = false; //--- using indicators' handles, copy values of indicator's //--- buffers into static arrays, specially prepared for this purpose if(CopyBuffer(Handle[Number], 1, 1, 1, dDnSignal_) < 0){Recount[Number] = true; return(false);} if(CopyBuffer(Handle[Number], 2, 1, 1, dUpSignal_) < 0){Recount[Number] = true; return(false);} if(CopyBuffer(Handle[Number], 3, 1, 1, dDnStop_ ) < 0){Recount[Number] = true; return(false);} if(CopyBuffer(Handle[Number], 4, 1, 1, dUpStop_ ) < 0){Recount[Number] = true; return(false);} //--- convert obtained values into values of logic variables of trade commands if(dDnSignal_[0] == 300)DnSignal[Number] = true; if(dUpSignal_[0] == 300)UpSignal[Number] = true; if(dDnStop_ [0] == 300)DnStop [Number] = true; if(dUpStop_ [0] == 300)UpStop [Number] = true; //--- all copy operations from indicator's buffers completed successfully //--- unnecessary to return into this block until next bar change Recount[Number]=false; } //----+ return(true); }
Non ci sono altre differenze ideologiche radicali del codice di questo Expert Advisor (Exp_ResonanceHunter.mq5) dovute al fatto che è compilato sulla base delle stesse componenti funzionali. Quindi non credo che sarà necessario dedicare altro tempo alla sua struttura interna.
Conclusione
A mio parere, il codice dell'Expert Advisor multi-valuta in MQL5 è assolutamente analogo al codice di un normale Expert Advisor.
Tradotto dal russo da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/ru/articles/105
- App di trading gratuite
- Oltre 8.000 segnali per il copy trading
- Notizie economiche per esplorare i mercati finanziari
Accetti la politica del sito e le condizioni d’uso