
Multibot in MetaTrader: Avvio di più robot da un singolo grafico
Contenuto
- Introduzione
- Definizione del problema e limiti di applicabilità
- Differenze tra i terminali MetaTrader 4 e MetaTrader 5 in termini di utilizzo di un multibot
- Le sfumature della costruzione di un modello universale
- Scrivere un modello universale
- Conclusione
Introduzione
Nel mondo dei mercati finanziari, i sistemi di trading automatizzati sono diventati parte integrante del processo decisionale. Questi sistemi possono essere configurati per analizzare il mercato, prendere decisioni di entrata e di uscita ed eseguire operazioni utilizzando regole e algoritmi predefiniti. Tuttavia, l'impostazione e l'esecuzione di robot su più grafici può essere un compito che richiede molto tempo. Ogni robot deve essere configurato individualmente per ogni grafico, il che richiede uno sforzo supplementare.
In questo articolo, vi mostrerò l'implementazione di un semplice modello che consente di creare un robot universale per più grafici in MetaTrader 4 e 5. Il nostro modello vi consentirà di allegare il robot ad un grafico, mentre il resto dei grafici verrà elaborato all'interno dell'EA. Pertanto, il nostro modello semplifica notevolmente il processo di impostazione e di esecuzione dei robot su più grafici, facendo risparmiare tempo e fatica ai trader. In questo articolo, considererò in dettaglio il processo di creazione di un robot di questo tipo in MQL5, dall'idea al test.
Definizione del problema e limiti di applicabilità
Questa idea mi è venuta non molto tempo fa, anche se da tempo osservo decisioni simili da parte di venditori professionisti. In altre parole, non sono né il primo né l'ultimo ad avere un'idea in questo campo, ma come sempre devono verificarsi alcune condizioni perché il programmatore possa iniziare a prendere tali decisioni. La ragione principale dello sviluppo di tali Expert Advisor nel negozio MQL5 è il desiderio di comodità per l'utente. Tuttavia, nel mio caso la motivazione era leggermente differente. La motivazione era che dovevo prima testare diverse strategie contemporaneamente per diversi strumenti, oppure la stessa strategia, ma per vedere le sue caratteristiche multi valuta.
Inoltre, un fattore molto importante quando si testa una strategia nel tester, soprattutto in modalità multi valuta, è la curva generale di redditività , che è alla base di qualsiasi valutazione dei sistemi di trading automatici durante il backtesting sui dati storici. Quando si testano i sistemi di trading separatamente su uno strumento, è piuttosto difficile combinare tali rapporti in un secondo momento. Non sono a conoscenza di strumenti del genere, almeno per MetaTrader 5. Per quanto riguarda la quarta versione del terminale, esiste uno strumento non ufficiale per tali manipolazioni. L'ho usato in almeno un articolo, ma ovviamente questo approccio non è preferibile.
Oltre al processo di test, è altrettanto importante il processo di trading automatico stesso e la sincronizzazione di EA simili che lavorano in modo indipendente, ciascuno sul proprio grafico. Se i grafici di questo tipo sono troppi, possono richiedere risorse informatiche aggiuntive , rallentando o peggiorando le prestazioni del trading e causando errori imprevisti e altri spiacevoli incidenti che possono avere un effetto negativo sul risultato finale del trading. Per ogni EA di questo tipo, dobbiamo escogitare ID ordine unici, protezione contro le richieste di server ad alta frequenza e molte altre cose che non sono ovvie a prima vista.
L'elaborazione della parte grafica dell'EA è un problema separato e molto delicato. Ora tutti i creatori di EA, più o meno abili, fanno almeno una versione minima con qualche indicazione sul grafico a cui l'EA è allegato. In questo modo l'EA appare più serio e ispira maggiore fiducia; infine, quasi sempre, la visualizzazione di alcune informazioni sul grafico per l'utente consente talvolta un controllo più efficace sul processo di trading dell'EA. Inoltre, se necessario, è possibile aggiungere elementi per il controllo manuale. Tutto questo si chiama interfaccia utente. La distribuzione di questi EA tramite grafici comporta un aumento esponenziale del carico dell’aggiornamento delle informazioni grafiche, testuali e numeriche nelle interfacce. Naturalmente, quando si utilizza un multi-template, si ha un'interfaccia che richiede la minima quantità di risorse per il terminale.
Naturalmente, tale modello non risolve tutti i problemi, ma comunque mi aiuta molto nei miei progetti. Io uso diversi robot e in generale, tutti gli approcci hanno il diritto di esistere, ma credo che molti programmatori alle prime armi possano trovare utile questo schema. Non è necessario copiarlo completamente, ma se lo desiderate potete facilmente adattarlo alle vostre esigenze. Il mio obiettivo non è quello di darvi qualcosa di straordinario, ma di mostrare e spiegare una delle opzioni per risolvere un problema del genere.
Differenze tra i terminali MetaTrader 4 e MetaTrader 5 in termini di utilizzo di un multibot
Ciò che mi piace dell'ultima MetaTrader 5 è la potenza del suo tester, che offre tutte le funzionalità necessarie per eseguire test su più strumenti contemporaneamente, purché si utilizzi l'approccio allo sviluppo degli EA descritto sopra. Il tester sincronizza automaticamente le quotazioni per tempo, fornendo una curva di profittabilità chiaramente sincronizzata su scala temporale. MetaTrader 4 non dispone di questa funzionalità. Credo che questo sia il suo più grande svantaggio. Tuttavia, vale la pena notare che MetaQuotes sta facendo del suo meglio per supportare il quarto terminale e la sua popolarità è ancora alta. Come utente attivo di MetaTrader 4, posso dire che queste carenze non sono così significative come potrebbero sembrare.
Il linguaggio MQL4 è stato recentemente aggiornato a MQL5. Questo significa che quando si scrivono modelli simili al nostro, le differenze nel codice saranno minime. È mia buona consuetudine cercare di implementare le cose per entrambi i terminali, quindi riceverete un modello per entrambi i terminali. Tali miglioramenti al vecchio terminale, tra l'altro, ci permettono di utilizzare le seguenti funzioni di cui abbiamo veramente bisogno:
- CopyClose - richiesta dei prezzi di chiusura delle barre
- CopyOpen - richiesta di prezzi di apertura delle barre
- CopyHigh - richiesta dei massimi delle barre
- CopyLow - richiesta dei minimi delle barre
- CopyTime - richiesta dell'orario di apertura della barra
- SymbolInfoTick - richiesta dell'ultimo tick in entrata per il simbolo richiesto
- SymbolInfoInteger - richiesta di dati del simbolo, che possono essere descritti da numeri interi ed elenchi numerati.
- SymbolInfo******* - altre funzioni necessarie
Queste caratteristiche sono presenti sia in MQL4 che in MQL5. Queste funzioni consentono di ottenere i dati delle barre per qualsiasi simbolo e periodo. Pertanto, l'unica differenza spiacevole tra il tester della quarta e della quinta versione è il fatto che queste funzioni nel quarto terminale funzioneranno solo per il grafico corrente su cui si sta effettuando il test, mentre il resto delle richieste informeranno semplicemente che non ci sono dati a causa delle peculiarità del tester MetaTrader 4. Perciò, quando si testa il nostro modello, si otterrà solo il trading sul simbolo selezionato e solo una delle curve di profitto per un singolo robot.
Nel quinto terminale, riceverete già il trading per tutti i simboli richiesti e la linea comune di profittabilità. Per quanto riguarda l'applicazione nel trading, quando si opera direttamente con tale robot in entrambi i terminali, si ricevono le prestazioni complete di tale modello. In altre parole, la differenza è solo nel tester. Ma anche in questi casi, si può fare a meno del fatto che, quando si crea un EA, è meglio iniziare con la versione per MetaTrader 5. Dopo aver effettuato tutti i test necessari, è possibile realizzare rapidamente la versione per MetaTrader 4.
Naturalmente, ci sono numerose differenze che non ho trattato. Voglio solo sottolineare l'importanza di alcune di esse, poiché queste sfumature devono essere conosciute quando si costruisce una struttura elaborata per un modello di questo tipo. MetaTrader 5 è sicuramente migliore del suo predecessore, ma tuttavia non ho alcun desiderio di sbarazzarmi del quarto terminale, perché in molte situazioni la sua richiesta di risorse computazionali non è così grande rispetto al quinto. Entrambi gli strumenti sono ancora validi.
Le sfumature della costruzione di un modello universale
Per costruire un modello di questo tipo, è necessario comprendere come funziona il terminale, cos'è un Expert Advisor e cos'è un grafico MetaTrader. Inoltre, è necessario comprendere che ogni grafico è un oggetto separato. Ogni grafico di questo tipo può essere associato a diversi indicatori e ad un solo EA. Potrebbero esserci svariati grafici identici. Di solito vengono creati molti grafici per eseguire diversi EA su un unico simbolo e periodo o per eseguire copie multiple di un EA con impostazioni differenti. Comprendendo queste sottigliezze, dovremmo giungere alla conclusione che per abbandonare i grafici multipli a favore del nostro modello, dovremo implementare tutto questo all'interno del nostro modello. Questo può essere rappresentato sotto forma di diagramma:
Un discorso a parte va fatto per i tick. Lo svantaggio di questo approccio è che non saremo in grado di sottoscrivere il gestore per la comparsa di un nuovo tick per ogni grafico. Dovremo applicare i tick dal grafico su cui lavora il nostro robot o utilizzare il timer. In definitiva, ciò comporterà momenti spiacevoli per i robot che lavorano con i tick, che sono i seguenti:
- Dovremo scrivere dei gestori OnTick personalizzati
- Questi gestori dovranno essere implementati come un derivato di OnTimer
- I tick non saranno perfetti perché OnTimer funziona con un ritardo (il valore del ritardo non è importante, ma lo è la sua presenza).
- Per ottenere i tick, è necessario utilizzare la funzione SymbolInfoTick
Per chi conta ogni millisecondo, questo può essere un momento irresistibile, soprattutto per chi ama l'arbitraggio. Tuttavia, non lo enfatizzo nel mio modello. Nel corso degli anni, dopo aver costruito diversi sistemi, sono arrivato al paradigma della barra di trading. Ciò significa che le operazioni di trading e gli altri calcoli avvengono per lo più quando compare una nuova barra. Questo approccio presenta una serie di evidenti vantaggi:
- L'imprecisione nel determinare l'inizio di una nuova barra non influisce significativamente sul trading.
- Più lungo è il periodo della barra, minore è questa influenza.
- La discretizzazione sotto forma di barre fornisce un aumento nella velocità di test attraverso ordini di grandezza
- La stessa qualità dei test sia su tick veri che su quelli artificiali
L'approccio insegna un certo paradigma di costruzione degli EA. Questo paradigma elimina molti problemi associati ai tick degli EA, velocizza il processo di test, fornisce una maggiore aspettativa matematica di profitto, che è l'ostacolo principale, e fa risparmiare molto tempo e potenza di calcolo. Penso che si possano trovare molti altri vantaggi, ma credo che questo sia sufficiente nel contesto di questo articolo.
Per implementare il nostro modello, non è necessario implementare l'intera struttura dell'area di lavoro del terminale di trading all'interno del nostro modello, ma è sufficiente implementare un grafico separato per ogni robot. Questa non è la struttura più ottimale, ma se siamo d'accordo che ogni singolo strumento sarà presente solo una volta nell'elenco degli strumenti, allora questa ottimizzazione non è necessaria. Il risultato sarà:
Abbiamo realizzato la struttura più semplice per l'implementazione dei grafici. Ora è il momento di pensare agli input di un tale modello e soprattutto, a come tenere conto del numero dinamico di grafici e di EA per ogni situazione nell'ambito delle possibilità consentite dal linguaggio MQL5. L'unico modo per risolvere questo problema è utilizzare variabili di input di tipo stringa. Una stringa ci permette di memorizzare una grande quantità di dati. Infatti, per descrivere tutti i parametri necessari per un modello di questo tipo, avremo bisogno di array dinamici nei dati di input. Naturalmente, nessuno implementerà queste cose semplicemente perché poche persone sfrutterebbero tali opportunità. La stringa è il nostro array dinamico, in cui possiamo inserire tutto ciò che vogliamo. Quindi usiamola. Per il mio modello più semplice, ho deciso di introdurre tre variabili in questo modo:
- Charts - i nostri grafici (elenco)
- Charts Lots - lotti per il trading (elenco)
- Charts Timeframes - periodi dei grafici (elenco)
In generale, possiamo combinare tutti questi dati in un'unica stringa, ma la sua struttura sarà complessa e sarà difficile per un potenziale utente capire come descrivere correttamente i dati. Inoltre, sarà molto facile commettere errori nella compilazione e si potranno verificare molte cose spiacevoli durante l'utilizzo, per non parlare dell'incredibile complessità della funzione di conversione, che estrarrà questi dati dalle stringhe. Ho visto soluzioni simili tra i venditori e in generale hanno fatto tutto bene. Tutti i dati sono semplicemente elencati separati da virgole. All'inizio dell'EA, questi dati vengono estratti da una stringa utilizzando funzioni speciali e inseriti negli array dinamici corrispondenti, che vengono poi utilizzati nel codice. Anche noi seguiremo questo percorso. Possiamo aggiungere altre stringhe simili con regole di enumerazione identiche. Ho deciso di utilizzare ":" come separatore. Se si utilizza una virgola, non è chiaro come trattare gli array double come Chart Lots. È possibile aggiungere altre variabili stringa e, in generale, è possibile costruire un modello ancora più completo e versatile, ma il mio compito qui è solo quello di mostrare come implementare tutto ciò e fornire una prima versione del modello modificabile in modo semplice e veloce.
Non è sufficiente implementare tali array, è necessario anche implementare variabili comuni, ad esempio:
- Work Timeframe For Unsigned - un periodo grafico quando non specificato.
- Fix Lot For Unsigned - un lottaggio quando non specificato
L'elenco Charts dovrebbe essere riempito. La stessa azione è facoltativa per Chart Lots e Charts Timeframes. Ad esempio, si possono prendere singoli lotti per tutti i grafici e lo stesso periodo per tutti i grafici. Una funzionalità simile sarà implementata nel nostro modello. È auspicabile applicare queste regole di implementazione ogni qualvolta sia possibile per garantire brevità e chiarezza nell'impostazione dei parametri di input di un EA costruito sulla base di tali modelli.
Definiamo ora alcune variabili più importanti per un'implementazione minima di questo modello:
- Last Bars Count - il numero delle ultime barre di un grafico che memorizziamo per ogni grafico.
- Deposit For Lot - deposito per l'utilizzo di un determinato lotto.
- First Magic - ID univoco per la separazione delle operazioni di un EA
Credo che la prima variabile sia abbastanza chiara. La seconda variabile è molto più difficile da comprendere. Questo è il modo in cui regolo il lotto automatico nei miei EA. Se lo imposto a "0", comunico all'algoritmo che deve negoziare solo un lotto fisso specificato nella stringa corrispondente o in una variabile condivisa che abbiamo considerato in precedenza. Altrimenti, imposto il deposito richiesto in modo da poter applicare il lotto specificato nelle impostazioni. È facile capire che con un deposito minore o maggiore, questo lotto cambia il suo valore in base all'equazione:
- Lotto = Lotto in input * ( Deposito Corrente / Deposito Per Lotto )
Penso che ora tutto dovrebbe essere chiaro. Se vogliamo un lotto fisso, impostiamo zero, mentre negli altri casi adattiamo il deposito nelle impostazioni di input ai rischi. Penso che sia economico e allegro. Se necessario, è possibile modificare l'approccio alla valutazione del rischio per il lotto automatico, ma personalmente preferisco questa opzione, non ha senso pensarci troppo.
Vale la pena menzionare la sincronizzazione e in particolare sul problema dell'impostazione del Expert Magic Number. Quando si fa trading con gli EA o anche in forma mista, tutti i programmatori che si rispettino prestano particolare attenzione a questa particolare variabile. Quando si utilizzano più EA, è molto importante assicurarsi che ognuno di questi EA abbia un ID univoco. Altrimenti, quando si lavora con ordini, transazioni o posizioni, si creerà una confusione totale e le strategie smetteranno di funzionare correttamente, e nella maggior parte dei casi smetteranno di funzionare completamente. Spero di non dover spiegare il perché. Ogni volta che un EA viene inserito sul grafico, è necessario configurare questi ID e assicurarsi che non si ripetano. Anche un singolo errore può portare a conseguenze disastrose. Inoltre, se si chiude accidentalmente il grafico con l'EA, è necessario riconfigurarlo nuovamente. Di conseguenza, la probabilità di errore aumenta notevolmente. Inoltre, è molto sgradevole sotto molti altri aspetti. Ad esempio, se si chiude il grafico e si dimentica l'ID utilizzato. In questo caso, dovrete scavare nella cronologia di trading per cercarla. Senza l'ID, l'EA appena riavviato potrebbe funzionare in modo errato e potrebbero accadere molte altre cose spiacevoli.
L'uso di un modello come il mio ci libera da questo controllo e minimizza i possibili errori, poiché dobbiamo impostare solo l'ID iniziale nelle impostazioni dell'EA, mentre gli altri ID saranno generati automaticamente utilizzando un incremento e assegnati alle copie corrispondenti degli EA. Questo processo avverrà automaticamente ad ogni riavvio. In ogni caso, ricordare un solo ID di partenza è molto più facile che ricordare un ID a caso nel bel mezzo della strada.
Scrivere un modello universale
È il momento di implementare il modello. Cercherò di omettere gli elementi eccessivi, in modo che chiunque abbia bisogno di questo modello possa scaricare e vedere il resto nel codice sorgente. Qui mostrerò solo le cose che sono direttamente collegate alle nostre idee. I livelli di stop e altri parametri sono definiti dagli utenti. La mia implementazione è disponibile nel codice sorgente. Per prima cosa, definiamo le nostre variabili di input, che ci serviranno sicuramente:
//+------------------------------------------------------------------+ //| Inputs | //+------------------------------------------------------------------+ input string SymbolsE="EURUSD:GBPUSD:USDCHF:USDJPY:NZDUSD:AUDUSD:USDCAD";//Charts input string LotsE="0.01:0.01:0.01:0.01:0.01:0.01:0.01";//Chart Lots input string TimeframesE="H1:H1:H1:H1:H1:H1:H1";//Chart Timeframes input int LastBars=10;//Last Bars Count input ENUM_TIMEFRAMES TimeframeE=PERIOD_M1;//Work Timeframe For Unsigned input double RepurchaseLotE=0.01;//Fix Lot For Unsigned input double DepositForRepurchaseLotE=0.00;//Deposit For Lot (if "0" then fix) input int MagicE=156;//First Magic
Qui si può vedere un esempio di compilazione di variabili stringa che riflettono i nostri array dinamici, proprio come nell'esempio delle variabili condivise. A proposito, questo codice sarà lo stesso sia in MQL4 che in MQL5. Ho cercato di rendere tutto il più simile possibile.
Ora decidiamo come ottenere i nostri dati delle stringhe. Questo verrà fatto dalla funzione corrispondente, ma prima creeremo degli array in cui la nostra funzione aggiungerà i dati ottenuti dalle stringhe:
//+------------------------------------------------------------------+ //|Arrays | //+------------------------------------------------------------------+ string S[];// Symbols array double L[];//Lots array ENUM_TIMEFRAMES T[];//Timeframes array
La funzione seguente riempie questi array:
//+------------------------------------------------------------------+ //| Fill arrays | //+------------------------------------------------------------------+ void ConstructArrays() { int SCount=1; for (int i = 0; i < StringLen(SymbolsE); i++)//calculation of the number of tools { if (SymbolsE[i] == ':') { SCount++; } } ArrayResize(S,SCount);//set the size of the character array ArrayResize(CN,SCount);//set the size of the array to use bars for each character int Hc=0;//found instrument index for (int i = 0; i < StringLen(SymbolsE); i++)//building an array of tools { if (i == 0)//if we just started { int LastIndex=-1; for (int j = i; j < StringLen(SymbolsE); j++) { if (StringGetCharacter(SymbolsE,j) == ':') { LastIndex=j; break; } } if (LastIndex != -1)//if no separating colon was found { S[Hc]=StringSubstr(SymbolsE,i,LastIndex); Hc++; } else { S[Hc]=SymbolsE; Hc++; } } if (SymbolsE[i] == ':') { int LastIndex=-1; for (int j = i+1; j < StringLen(SymbolsE); j++) { if (StringGetCharacter(SymbolsE,j) == ':') { LastIndex=j; break; } } if (LastIndex != -1)//if no separating colon was found { S[Hc]=StringSubstr(SymbolsE,i+1,LastIndex-(i+1)); Hc++; } else { S[Hc]=StringSubstr(SymbolsE,i+1,StringLen(SymbolsE)-(i+1)); Hc++; } } } for (int i = 0; i < ArraySize(S); i++)//assignment of the requested number of bars { CN[i]=LastBars; } ConstructLots(); ConstructTimeframe(); }
In breve, qui viene calcolata la quantità di dati in una stringa grazie ai separatori. In base al primo array, viene impostata la dimensione di tutti gli altri array in modo simile ad un array con i simboli, dopodiché i simboli vengono riempiti per primi, seguiti da funzioni come ConstructLots e ConstructTimeframe. La loro implementazione è simile a quella di questa funzione, con alcune differenze. È possibile vedere la loro implementazione nel codice sorgente. Non li ho aggiunti all'articolo per non mostrare il codice duplicato.
Ora dobbiamo creare le classi appropriate per il grafico virtuale e il robot virtuale ad esso collegato. Iniziamo definendo che i grafici virtuali e gli EA saranno memorizzati in array:
//+------------------------------------------------------------------+
//| Charts & experts pointers |
//+------------------------------------------------------------------+
Chart *Charts[];
BotInstance *Bots[];
Partiamo dalla classe del grafico:
//+------------------------------------------------------------------+ //| Chart class | //+------------------------------------------------------------------+ class Chart { public: datetime TimeI[]; double CloseI[]; double OpenI[]; double HighI[]; double LowI[]; string BasicSymbol;//the base instrument that was extracted from the substring double ChartPoint;//point size of the current chart double ChartAsk;//Ask double ChartBid;//Bid datetime tTimeI[];//auxiliary array to control the appearance of a new bar static int TCN;//tcn string CurrentSymbol;//symbol ENUM_TIMEFRAMES Timeframe;//timeframe int copied;//how much data is copied int lastcopied;//last amount of data copied datetime LastCloseTime;//last bar time MqlTick LastTick;//last tick fos this instrument Chart() { ArrayResize(tTimeI,2); } void ChartTick()//this chart tick { SymbolInfoTick(CurrentSymbol,LastTick); ArraySetAsSeries(tTimeI,false); copied=CopyTime(CurrentSymbol,Timeframe,0,2,tTimeI); ArraySetAsSeries(tTimeI,true); if ( copied == 2 && tTimeI[1] > LastCloseTime ) { ArraySetAsSeries(CloseI,false); ArraySetAsSeries(OpenI,false); ArraySetAsSeries(HighI,false); ArraySetAsSeries(LowI,false); ArraySetAsSeries(TimeI,false); lastcopied=CopyClose(CurrentSymbol,Timeframe,0,Chart::TCN+2,CloseI); lastcopied=CopyOpen(CurrentSymbol,Timeframe,0,Chart::TCN+2,OpenI); lastcopied=CopyHigh(CurrentSymbol,Timeframe,0,Chart::TCN+2,HighI); lastcopied=CopyLow(CurrentSymbol,Timeframe,0,Chart::TCN+2,LowI); lastcopied=CopyTime(CurrentSymbol,Timeframe,0,Chart::TCN+2,TimeI); ArraySetAsSeries(CloseI,true); ArraySetAsSeries(OpenI,true); ArraySetAsSeries(HighI,true); ArraySetAsSeries(LowI,true); ArraySetAsSeries(TimeI,true); LastCloseTime=tTimeI[1]; } ChartBid=LastTick.bid; ChartAsk=LastTick.ask; ChartPoint=SymbolInfoDouble(CurrentSymbol,SYMBOL_POINT); } }; int Chart::TCN = 0;
La classe ha una sola funzione, che controlla l'aggiornamento dei tick e delle barre, oltre ai campi necessari per identificare alcuni parametri necessari di un particolare grafico. Mancano alcuni parametri. Se lo si desidera, è possibile aggiungere quelli mancanti aggiungendo il loro aggiornamento, ad esempio, come l'aggiornamento di ChartPoint. Gli array delle barre sono realizzate in stile MQL4. Trovo ancora molto comodo lavorare con array predeterminati in MQL4. È molto comodo quando si sa che la barra zero è la barra corrente. Comunque, questa è solo la mia visione. Siete liberi di seguire quello che preferite.
Ora dobbiamo descrivere la classe di un EA virtuale separato:
//+------------------------------------------------------------------+ //| Bot instance class | //+------------------------------------------------------------------+ class BotInstance//expert advisor object { public: CPositionInfo m_position;// trade position object CTrade m_trade;// trading object ///-------------------this robot settings---------------------- int MagicF;//Magic string CurrentSymbol;//Symbol double CurrentLot;//Start Lot int chartindex;//Chart Index ///------------------------------------------------------------ ///constructor BotInstance(int index,int chartindex0)//load all data from hat using index, + chart index { chartindex=chartindex0; MagicF=MagicE+index; CurrentSymbol=Charts[chartindex].CurrentSymbol; CurrentLot=L[index]; m_trade.SetExpertMagicNumber(MagicF); } /// void InstanceTick()//bot tick { if ( bNewBar() ) Trade(); } private: datetime Time0; bool bNewBar()//new bar { if ( Time0 < Charts[chartindex].TimeI[1] && Charts[chartindex].ChartPoint != 0.0 ) { if (Time0 != 0) { Time0=Charts[chartindex].TimeI[1]; return true; } else { Time0=Charts[chartindex].TimeI[1]; return false; } } else return false; } //////************************************Main Logic******************************************************************** void Trade()//main trade function { //Close[0] --> Charts[chartindex].CloseI[0] - example of access to data arrays of bars of the corresponding chart //Open[0] --> Charts[chartindex].OpenI[0] ----------------------------------------------------------------------- //High[0] --> Charts[chartindex].HighI[0] ----------------------------------------------------------------------- //Low[0] --> Charts[chartindex].LowI[0] ------------------------------------------------------------------------- //Time[0] --> Charts[chartindex].TimeI[0] ----------------------------------------------------------------------- if ( true ) { CloseBuyF(); //CloseSellF(); } if ( true ) { BuyF(); //SellF(); } } double OptimalLot()//optimal lot calculation { if (DepositForRepurchaseLotE != 0.0) return CurrentLot * (AccountInfoDouble(ACCOUNT_BALANCE)/DepositForRepurchaseLotE); else return CurrentLot; } //here you can add functionality or variables if the trading function turns out to be too complicated //////******************************************************************************************************************* ///trade functions int OrdersG()//the number of open positions / orders of this virtual robot { ulong ticket; bool ord; int OrdersG=0; for ( int i=0; i<PositionsTotal(); i++ ) { ticket=PositionGetTicket(i); ord=PositionSelectByTicket(ticket); if ( ord && PositionGetInteger(POSITION_MAGIC) == MagicF && PositionGetString(POSITION_SYMBOL) == CurrentSymbol ) { OrdersG++; } } return OrdersG; } /////////********/////////********//////////***********/////////trade function code block void BuyF()//buy market { double DtA; double CorrectedLot; DtA=double(TimeCurrent())-GlobalVariableGet("TimeStart161_"+IntegerToString(MagicF));//unique bot marker last try datetime if ( (DtA > 0 || DtA < 0) ) { CorrectedLot=OptimalLot(Charts[chartindex]); if ( CorrectedLot > 0.0 ) { //try buy logic } } } void SellF()//sell market { //Same logic } void CloseSellF()//close sell position { ulong ticket; bool ord; for ( int i=0; i<PositionsTotal(); i++ ) { ticket=PositionGetTicket(i); ord=PositionSelectByTicket(ticket); if ( ord && PositionGetInteger(POSITION_MAGIC) == MagicF && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && PositionGetString(POSITION_SYMBOL) == Charts[chartindex].CurrentSymbol ) { //Close Sell logic } } } void CloseBuyF()//close buy position { //same logic } bool bOurMagic(ulong ticket,int magiccount)//whether the magic of the current deal matches one of the possible magics of our robot { int MagicT[]; ArrayResize(MagicT,magiccount); for ( int i=0; i<magiccount; i++ ) { MagicT[i]=MagicE+i; } for ( int i=0; i<ArraySize(MagicT); i++ ) { if ( HistoryDealGetInteger(ticket,DEAL_MAGIC) == MagicT[i] ) return true; } return false; } /////////********/////////********//////////***********/////////end trade function code block };
Ho eliminato alcune logiche ripetitive per ridurre la quantità di codice. Questa è la classe in cui deve essere implementato l'intero algoritmo del vostro EA. Le principali funzionalità presenti in questa classe:
- Trade() - funzione di trading principale che viene richiamata nel gestore delle barre per il grafico corrispondente
- BuyF() - funzione di acquisto a mercato
- SellF() - funzione di vendita a mercato
- CloseBuyF() - funzione di chiusura delle posizioni buy a mercato
- CloseSellF() - funzione di chiusura delle posizioni di vendita a mercato
Questo è l'insieme minimo di funzioni per dimostrare il trading a barre. Per questa dimostrazione, dobbiamo solo aprire una posizione qualsiasi e chiuderla nella barra successiva. Ciò è sufficiente nell'ambito di questo articolo. Questa classe offre alcune funzionalità aggiuntive che dovrebbero completare la comprensione:
- OrdersG() - conteggio delle posizioni aperte su un simbolo specifico collegato al grafico
- OptimalLot() - prepara un lotto prima di inviarlo alla funzione di trading (selezionando un lotto fisso o calcolando un lotto automatico).
- bOurMagic() - verifica la conformità delle transazioni della cronologia con l'elenco di quelle consentite (solo per l'ordinamento di una cronologia personalizzata)
Queste funzioni possono essere necessarie per implementare la logica di trading. Sarebbe anche ragionevole ricordare il nuovo gestore della barra:
- InstanceTick() - simulazione di tick su un'istanza dell’EA separata
- bNewBar() - attributo per verificare la comparsa di una nuova barra (utilizzato all'interno di InstanceTick)
Se l’attributo mostra una nuova barra, viene attivata la funzione Trade. Questa è la funzione in cui deve essere impostata la logica di trading principale. La connessione con il grafico corrispondente viene effettuata utilizzando la variabile chartindex assegnata al momento della creazione dell'istanza. In questo modo ogni istanza di EA conosce il grafico da cui deve prendere una quotazione.
Consideriamo ora il processo di creazione dei grafici virtuali e degli EA stessi. I grafici virtuali vengono creati per primi:
//+------------------------------------------------------------------+ //| Creation of graph objects | //+------------------------------------------------------------------+ void CreateCharts() { bool bAlready; int num=0; string TempSymbols[]; string Symbols[]; ConstructArrays();//array preparation int tempcnum=CN[0]; Chart::TCN=tempcnum;//required number of stored bars for all instruments for (int j = 0; j < ArraySize(Charts); j++)//fill in all the names and set the dimensions of all time series, each graph { Charts[j] = new Chart(); Charts[j].lastcopied=0; ArrayResize(Charts[j].CloseI,tempcnum+2);//assign size to character arrays ArrayResize(Charts[j].OpenI,tempcnum+2);//---------------------------------- ArrayResize(Charts[j].HighI,tempcnum+2);//---------------------------------- ArrayResize(Charts[j].LowI,tempcnum+2);//----------------------------------- ArrayResize(Charts[j].TimeI,tempcnum+2);//---------------------------------- Charts[j].CurrentSymbol = S[j];//symbol Charts[j].Timeframe = T[j];//timeframe } ArrayResize(Bots,ArraySize(S));//assign a size to the array of bots }
Dopo aver creato i grafici e impostato la dimensione dell'array con gli EA virtuali, è necessario creare le istanze degli EA stessi e implementare la connessione degli EA virtuali con i grafici:
//+------------------------------------------------------------------+ //| create and hang all virtual robots on charts | //+------------------------------------------------------------------+ void CreateInstances() { for (int i = 0; i < ArraySize(S); i++) { for (int j = 0; j < ArraySize(Charts); j++) { if ( Charts[j].CurrentSymbol == S[i] ) { Bots[i] = new BotInstance(i,j); break; } } } }
La connessione viene effettuata utilizzando l'indice "j" impostato in ogni istanza dell'EA virtuale al momento della sua creazione. La corrispondente variabile mostrata sopra è evidenziata. Naturalmente, tutto questo può essere fatto in molti modi e in modo molto più elegante, ma credo che la cosa principale sia che l'idea generale sia chiara.
Non resta che mostrare come vengono simulati i tick su ogni grafico e sull'EA ad esso associato:
//+------------------------------------------------------------------+ //| All bcharts & all bots tick imitation | //+------------------------------------------------------------------+ void AllChartsTick() { for (int i = 0; i < ArraySize(Charts); i++) { Charts[i].ChartTick(); } } void AllBotsTick() { for (int i = 0; i < ArraySize(S); i++) { if ( Charts[Bots[i].chartindex].lastcopied >= Chart::TCN+1 ) Bots[i].InstanceTick(); } }
L'unica cosa che voglio far notare è che questo modello è stato ottenuto rielaborando il mio modello più complesso, che era destinato a scopi molto più seri, quindi potrebbero esserci elementi eccessivi qua e là. Penso che si possano facilmente rimuovere e rendere il codice più ordinato, se si vuole.
Oltre al modello, c'è un'interfaccia semplice che, a mio avviso, può tornare utile, ad esempio, quando si scrive un ordine in qualità di freelance o per altri scopi:
Ho lasciato dello spazio libero in questa interfaccia, che sarà sufficiente per tre voci nel caso in cui non abbiate abbastanza spazio. Se necessario, è possibile espandere o modificare completamente la struttura. Se vogliamo aggiungere i tre campi mancanti in questo particolare esempio, dobbiamo trovare i seguenti punti nel codice:
//+------------------------------------------------------------------+ //| Reserved elements | //+------------------------------------------------------------------+ "template-UNSIGNED1",//UNSIGNED1 "template-UNSIGNED2",//UNSIGNED2 "template-UNSIGNED3",//UNSIGNED3 //LabelCreate(0,OwnObjectNames[13],0,x+Border+2,y+17+Border+20*5+20*5+23,corner,"","Arial",11,clrWhite,0.0,ANCHOR_LEFT);//UNSIGNED1 //LabelCreate(0,OwnObjectNames[14],0,x+Border+2,y+17+Border+20*5+20*5+23+20*1,corner,"","Arial",11,clrWhite,0.0,ANCHOR_LEFT);//UNSIGNED2 //LabelCreate(0,OwnObjectNames[15],0,x+Border+2,y+17+Border+20*5+20*5+23+20*2,corner,"","Arial",11,clrWhite,0.0,ANCHOR_LEFT);//UNSIGNED3 //////////////////////////// //TempText="UNSIGNED1 : "; //TempText+=DoubleToString(NormalizeDouble(0.0),3); //ObjectSetString(0,OwnObjectNames[13],OBJPROP_TEXT,TempText); //TempText="UNSIGNED2 : "; //TempText+=DoubleToString(NormalizeDouble(0.0),3); //ObjectSetString(0,OwnObjectNames[14],OBJPROP_TEXT,TempText); //TempText="UNSIGNED3 : "; //TempText+=DoubleToString(NormalizeDouble(0.0),3); //ObjectSetString(0,OwnObjectNames[15],OBJPROP_TEXT,TempText); ///////////////////////////
Le prime tre voci assegnano i nomi dei nuovi elementi dell'interfaccia, le seconde tre sono usate quando si crea l'interfaccia all'inizio dell'EA, mentre le ultime tre sono usate nella funzione per aggiornare le informazioni sull'interfaccia. Ora è il momento di testare le prestazioni di entrambi i modelli. Il visualizzatore del tester sarà sufficiente per una dimostrazione visiva. Mostrerò solo l'opzione per MetaTrader 5, perché il suo visualizzatore è migliore. Inoltre, il risultato del lavoro mostrerà chiaramente tutto ciò che è necessario per confermare l'efficienza:
Come potete vedere, abbiamo caricato tutti e sette i grafici delle principali coppie Forex. Il registro di visualizzazione mostra che il trading è in corso per tutti i simboli elencati. Il trading viene svolto in modo indipendente, come richiesto. In altre parole, gli EA operano ognuno sul proprio grafico e non interagiscono affatto.
Conclusione
In questo articolo abbiamo esaminato le principali sfumature della costruzione di modelli universali per i terminali MetaTrader 4 e MetaTrader 5, abbiamo realizzato un modello semplice ma funzionante, analizzato i punti più importanti del suo funzionamento e anche confermato la sua fattibilità utilizzando il visualizzatore del tester di MetaTrader 5. Penso che ormai sia abbastanza ovvio che un modello come questo non sia così complicato. In generale, è possibile realizzare diverse implementazioni di tali modelli, ma è ovvio che tali modelli possono essere completamente diversi pur rimanendo applicabili. La cosa principale è comprendere le sfumature di base della costruzione di queste strutture. Se necessario, è possibile rielaborare i modelli per uso personale.
Tradotto dal russo da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/ru/articles/12434





- 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