
L'Uso di ORDER_MAGIC per il Trading con Diversi Expert Advisor su un Singolo Strumento
Introduzione
In MQL5 abbiamo la capacità di assegnare un numero magico a ciascun ordine in sospeso, al fine di utilizzare queste informazioni per identificare l'ordine. Ciò apre vaste possibilità di interazione tra diversi Expert Advisor e lo sviluppo di sistemi ancora più complessi. In questo articolo, vorrei informare il pubblico sulle opportunità sottovalutate del numero Magico.
Ma prima di procedere all'essenza dell'argomento di questo articolo, dobbiamo acquisire una migliore comprensione di ciò che costituisce il numero Magico. Cosa potrebbe essere magico in un numero che determina quale Expert Advisor lo ha impostato? I "miracoli" iniziano con le opportunità che gli sviluppatori stabiliscono nel tipo ulong, che è dichiarato dal numero Magico.
Il tipo ulong è il più lungo
Se osserviamo nel dettaglio il tipo intero long, vediamo che il valore massimo di questo tipo è semplicemente fenomenale:
Tipo | Dimensione in byte | Valore Minimo | Valore Massimo | Analogico nel linguaggio C + + |
long | 8 | -9 223 372 036 854 775 808 | 9 223 372 036 854 775 807 | __int64 |
ulong | 8 | 0 | 18 446 744 073 709 551 615 | unsigned __int64 |
Tabella 1 Proprietà dei tipi di dati long e ulong
ma il tipo ulong lo ha superato combinando la mantissa positiva e negativa.
Quindi, la lunghezza specificata è enorme, ma come è stata usata prima?
Nella mia esperienza di lavoro in mql 4, ho spesso notato l'insensatezza della codifica del numero Magico da parte di molti sviluppatori. Il numero Magico è stato usato in modo sensato, ma la sua codifica sembrava abbastanza sciocca. Cosa si può dire dell'individualità del numero Magico 12345, tale Magico è utilizzato da quasi la metà della fraternità in via di sviluppo. La seconda metà utilizza il numero Magico 55555, 33333 e 77777, e questo è praticamente il set completo. Voglio attirare l'attenzione del lettore sul fatto che è improbabile che il suo computer abbia più di 1.000 Expert Advisor, quindi il numero 1000 sarà sufficiente per codificare il nome individuale di tutti i tuoi Expert Advisor.
1000 - sono solo 3 categorie complete, quindi cosa dovremmo fare con le restanti 15 che sono disponibili nel tipo ulong? La risposta è semplice: codificarli.
Cosa dice Wikipidia sulla parola codice:
The Code - regola (algoritmo), il confronto per ogni singolo messaggio di una combinazione strettamente particolare di simboli (caratteri) (o segnali).
Pertanto, stabiliremo le regole. Propongo di prescrivere nel codice del numero Magico, non solo l'ID dell'Expert Advisor, ma anche lo strumento su cui è in esecuzione. Il fatto che l'Expert Advisor sia in esecuzione, ad esempio, su EURUSD, non significa che l'Expert Advisor visualizzerà un ordine solo su tale strumento. Inoltre, penso che sarà utile scrivere il codice di interazione degli Expert Advisor, qualcosa come "tuo/straniero", in modo che l'Expert Advisor, quando controlla le posizioni, possa capire che l'ordine corrente è costruito da un Expert Advisor amichevole. Penso che questo sarà sufficiente per creare un sistema molto complesso.
E quindi riassumiamo ciò che abbiamo: quali opportunità stiamo mettendo nel sistema:
- La possibilità di due o più Expert Advisor di lavorare su un unico strumento e non interferire.
- La possibilità di due o più Expert Advisor di lavorare su strumenti diversi e completarsi a vicenda.
- La capacità di identificare l'ordine dallo strumento, lavorando con l'Expert Advisor.
E così, il compito è impostato, iniziamo ora la sua attuazione.
Semplice Expert Advisor
Redigere il codice del semplice Expert Advisor, ad esempio, tenere la posizione nella direzione del Moving. Penso che il lettore che ha deciso di analizzare il numero Magico, abbia già letto l'articolo Guida Passo Dopo Passo alla Scrittura di un Expert Advisor in MQL5 per Principianti, in caso contrario, consiglio vivamente di farlo, poiché non entrerò nei dettagli della creazione dell'Expert Advisor. Fondamentalmente, l'Expert Advisor aprirà la posizione una volta e la girerà per tutte le altre volte. Pertanto, avremo bisogno della funzione per aprire la posizione, cioè per inserire la richiesta di trading (ordine di trading).
Crea una classe ausiliaria, che calcolerà i parametri per noi per riempire i campi della struttura della richiesta di trading.
//+------------------------------------------------------------------+ //| The class provides auxiliary trading calculations | //+------------------------------------------------------------------+ class CProvision { protected: MqlTradeRequest trades; // pointer to the request structure of OrderSend public: int TYPE(const double &v[]); // determines the type, in respect to the readings of the moving double pricetype(int type); // calculates the level of the opening, in respect to the type double SLtype(int type); // calculates the level of the stop-loss in respect to the type double TPtype(int type); // calculates the level of the take-profit, in respect to the type long spread(); // returns the spread of the current instrument int SendOrder(ENUM_ORDER_TYPE type,double volume); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int CProvision::SendOrder(ENUM_ORDER_TYPE type,double volume) { trades.action =TRADE_ACTION_DEAL; // Type of the implemented actions trades.magic =magic; // Stamp of the Expert Advisor (identifier of the magic number) trades.symbol =_Symbol; // Name of the trading instrument trades.volume =volume; // Request the volume of the trade in lots trades.price =pricetype((int)type); // Price trades.sl =SLtype((int)type); // Level of Stop Loss order trades.tp =TPtype((int)type); // Level of Take Profit order trades.deviation=(int)spread(); // Maximum acceptable deviation from the requested price trades.type=type; // Order type trades.type_filling=ORDER_FILLING_FOK; if(OrderSend(trades,res)){return(res.retcode);} return(-1); } //+------------------------------------------------------------------+ //| Determines the type, in respect to the reading of the moving | //+------------------------------------------------------------------+ int CProvision::TYPE(const double &v[]) { double t=v[0]-v[1]; if(t==0.0)t=1.0; return((int)(0.5*t/fabs(t)+0.5)); } //+------------------------------------------------------------------+ //| Calculates the level of opening in respect to the type | //+------------------------------------------------------------------+ double CProvision::pricetype(int type) { if(SymbolInfoTick(_Symbol,tick)) { if(type==0)return(tick.ask); if(type==1)return(tick.bid); } return(-1); } //+------------------------------------------------------------------+ //| Calculates the level of stop-loss in respect to the type | //+------------------------------------------------------------------+ double CProvision::SLtype(int type) { if(SymbolInfoTick(_Symbol,tick)) { if(type==0)return(tick.bid-SL*SymbolInfoDouble(Symbol(),SYMBOL_POINT)); if(type==1)return(tick.ask+SL*SymbolInfoDouble(Symbol(),SYMBOL_POINT)); } return(0); } //+------------------------------------------------------------------+ //| Calculates the level of timeframe in respect to the type | //+------------------------------------------------------------------+ double CProvision::TPtype(int type) { if(SymbolInfoTick(_Symbol,tick)) { if(type==0)return(tick.bid+TP*SymbolInfoDouble(Symbol(),SYMBOL_POINT)); if(type==1)return(tick.ask-TP*SymbolInfoDouble(Symbol(),SYMBOL_POINT)); } return(0); } //+------------------------------------------------------------------+ //| Returns the spread | //+------------------------------------------------------------------+ long CProvision::spread() { return(SymbolInfoInteger(_Symbol,SYMBOL_SPREAD)); }
Avendo una classe del genere, possiamo scrivere un codice per un semplice Expert Advisor senza problemi:
//+------------------------------------------------------------------+ //| Code of the Expert Advisor | //+------------------------------------------------------------------+ //--- Input parameters input ulong magic =1; // magic input int SL =300; // Stop Loss input int TP =1000; // Take Profit input int MA_Period =25; // MA period input double lot =0.1; // Volume of position input int MA_shift =0; // Shift of indicator input ENUM_MA_METHOD MA_smooth =MODE_SMA; // Smoothing type input ENUM_APPLIED_PRICE price =PRICE_OPEN; // Price type //--- We will store the indicator's handle int MA_handle, // Handle of the indicator type_MA, // Type that specify the direction of MA rezult; // The variable takes the value of the result of the OrderSend operation double v[2]; // Buffer for receiving values of MA MqlTradeResult res; // Pointer to the structure of responding by OrderSend MqlTick tick; // Pointer to the structure of last market information CProvision prov; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Create the indicator's handle MA_handle=iMA(Symbol(),0,MA_Period,MA_shift,MA_smooth,price); return(0); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { if(CopyBuffer(MA_handle,0,0,2,v)<=0) {Print("#",magic,"Error of copying");return;} type_MA=prov.TYPE(v); // Determine type depending on MA indication if(PositionSelect(_Symbol))// If there is an open position { if(PositionGetInteger(POSITION_TYPE)!=type_MA)// Check if its time to close { Print("#",magic,"Position by magic number has volume ",PositionGetDouble(POSITION_VOLUME), " reverse position of type ",PositionGetInteger(POSITION_TYPE)," by ",type_MA); rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,PositionGetDouble(POSITION_VOLUME)+lot); // reverse the position if(rezult!=-1)Print("#",magic," Code of the operation result ",rezult," volume ",res.volume); else{Print("#",magic,"Error",GetLastError()); return;} } } else // If there is no open position then open { Print("#",magic,"Position by magic number has volume ",PositionGetDouble(POSITION_VOLUME), " open position of type ",type_MA); rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,lot); // open position if(rezult!=-1)Print("#",magic," Code of operation result ",rezult," volume ",res.volume); else{Print("#",magic,"Error",GetLastError()); return;} } }
Eseguilo e assicurati che l’Expert Advisor non differisca in termini di redditività, ma scambi esattamente secondo la logica specificata, che è esattamente ciò di cui abbiamo bisogno da esso.
Figura 1. Il lavoro di un Expert Advisor su un singolo strumento
Ora proveremo a eseguire questo EA, ma su diversi time frame di uno strumento (per gli esperimenti abbiamo scelto uno strumento casuale, che è EURUSD: o)
Figura 2. Il conflitto di due Expert Advisor sullo stesso strumento su timeframe diversi
Poiché entrambi gli Expert Advisor sono in esecuzione su un singolo strumento e il codice non specifica la condivisione delle posizioni, entrambi gli Expert Advisor stanno cercando di correggere la posizione di trading, a seconda delle letture dei loro indicatori, e come conseguenza di ciò, - sorge un conflitto. L'Expert Advisor che è in esecuzione su M1 sta cercando di trasformare la posizione nella cella, mentre il suo rivale cerca di fermarlo. E' ovvio che abbiamo bisogno di un calcolo separato delle posizioni, che è ciò che faremo ora.
Posizione o posizione virtuale?
Poiché in MetaTrader 5 gli sviluppatori sono passati dagli ordini alla considerazione delle posizioni, ha senso considerare in modo più dettagliato le funzioni associate alla registrazione delle posizioni.
// Returns the number of open positions. int PositionsTotal(); // Returns the symbol of the open position by the number in the list of positions. string PositionGetSymbol(int index); // Selects the open position for further working with it. bool PositionSelect(string symbol, uint timeout=0); // Function returns the requested property of the open position. double PositionGetDouble(ENUM_POSITION_PROPERTY property_id); // The function returns the requested property of the open position. long PositionGetInteger(ENUM_POSITION_PROPERTY property_id); // The function returns the requested property of the open position. string PositionGetString(ENUM_POSITION_PROPERTY property_id);
Identificatori delle enumerazioni per le funzioni di ottenimento della richiesta delle proprietà di posizione conformi PositionGetDouble , PositionGetInteger, e PositionGetString fornite nelle tabelle 2-4.
Identificatore | Descrizione | Tipo |
POSITION_VOLUME | Volume della posizione | double |
POSITION_PRICE_OPEN | Prezzo della posizione | double |
POSITION_SL | Livello di Stop Loss per la posizione aperta | double |
POSITION_TP | Livello di Take Profit per la posizione aperta | double |
POSITION_PRICE_CURRENT | Prezzo corrente dal simbolo | double |
POSITION_COMMISSION | Commissione | double |
POSITION_SWAP | Swap accumulato | double |
POSITION_PROFIT | Profitto corrente | double |
Tabella 2 Valore di enumerazione ENUM_POSITION_PROPERTY_DOUBLE
Identificatore | Descrizione | Tipo |
POSITION_TIME | Tempo di apertura delle posizioni | datetime |
POSITION_TYPE | Tipo di posizione | |
POSITION_MAGIC | Numero Magic per la posizione (vedi ORDER_MAGIC ) | long |
POSITION_IDENTIFIER | Identificazione della posizione - questo è un numero univoco che viene assegnato a ciascuna posizione riaperta e che non cambia durante il suo ciclo di vita. Il fatturato di una posizione non cambia il suo ID. | long |
Tabella 3 Valori di enumerazione ENUM_POSITION_PROPERTY_INTEGER
Identificatore | Descrizione | Tipo |
POSITION_SYMBOL | Simbolo, per il quale viene aperta la posizione | string |
POSITION_COMMENT | Commento alla posizione | string |
Tabella 4 Valori di enumerazione ENUM_POSITION_PROPERTY_STRING
Dalle funzioni, possiamo vedere chiaramente che il linguaggio non contiene la divisione delle posizioni, basata sul principio di "chi ha fatto l'ordine", ma la possibilità di tali record è disponibile poiché il ORDER_MAGIC, il POSITION_MAGIC e il DEAL_MAGIC sono lo stesso numero esatto, e sono presi dal numero magico, indicato dall'utente. Il POSITION_MAGIC è preso dal DEAL_MAGIC, che apre la posizione, e il DEAL_MAGIC è a sua volta preso dal ORDER_MAGIC dell'ordine effettuato.
L'identificazione di un ordine, una transazione o una posizione possono essere eseguiti senza problemi, ma è impossibile condurre una posizione con un particolare numero Magic. Ora cercheremo di eliminare questa carenza . Creiamo analoghi di funzioni integrate, ma con l'identificazione dal numero Magico. Dichiara una classe per aver lavorato con una posizione virtuale sul numero Magico.
Dal momento che abbiamo l'opportunità di lavorare con l'OOP, dichiariamo anche la nostra struttura (acquisendo ulteriore pratica di scrittura oggettiva).
//+------------------------------------------------------------------+ //| Structure of the CPositionVirtualMagic class | //+------------------------------------------------------------------+ struct SPositionVirtualMagic { double volume; // volume of virt. position ENUM_POSITION_TYPE type; // type of virt. position }; //+--------------------------------------------------------------------------------+ //| The class calculates the virtual position of an Expert Advisor by magic number | //+--------------------------------------------------------------------------------+ class CPositionVirtualMagic { protected: SPositionVirtualMagic pvm; public: double cVOLUME(){return(pvm.volume);} // Returns the volume of virtual position of an Expert Advisor ENUM_POSITION_TYPE cTYPE(){return(pvm.type);} // Returns the type of virtual position of an Expert Advisor bool PositionVirtualMagic(ulong Magic, string symbol, datetime CurrentTime ); // the method of calculation virt. position returns the presence or absence of virt. position private: void prHistory_Deals(ulong &buf[],int HTD); // Fills the array of tickets }; //+-------------------------------------------------------------------------------------+ //| Method of calculation of virt. position, returns true if there is a virt. position | //+-------------------------------------------------------------------------------------+ bool CPositionVirtualMagic::PositionVirtualMagic(ulong Magic, string symbol, datetime CurrentTime ) { int DIGITS=(int)-log10(SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP)); if(DIGITS<0)DIGITS=0; ulong Dticket=0; int History_Total_Deals=-1; double volume=0,volume_BUY=0,volume_SELL=0; ulong DTicketbuf[]; do { if(HistorySelect(0,TimeCurrent())) { History_Total_Deals=HistoryDealsTotal(); prHistory_Deals(DTicketbuf,History_Total_Deals); } HistorySelect(0,TimeCurrent()); } while(History_Total_Deals!=HistoryDealsTotal()); for(int t=0;t<History_Total_Deals;t++) { Dticket=DTicketbuf[t]; if(HistoryDealSelect(Dticket)) { if(HistoryDealGetInteger(Dticket,DEAL_TIME)>=CurrentTime) { if(HistoryDealGetInteger(Dticket,DEAL_MAGIC)==Magic) { if(HistoryDealGetInteger(Dticket,DEAL_TYPE)==DEAL_TYPE_BUY) { volume_BUY+=HistoryDealGetDouble(Dticket,DEAL_VOLUME); } else { if(HistoryDealGetInteger(Dticket,DEAL_TYPE)==DEAL_TYPE_SELL) { volume_SELL+=HistoryDealGetDouble(Dticket,DEAL_VOLUME); } } } } } else{HistorySelect(0,TimeCurrent());t--;} // if there is a fault, load history data and pass the step again } volume=NormalizeDouble(volume_BUY-volume_SELL,DIGITS); if(volume<0)pvm.type=POSITION_TYPE_SELL; else { if(volume>0)pvm.type=POSITION_TYPE_BUY; } pvm.volume=fabs(volume); if(pvm.volume==0)return(false); else return(true); }
Nel testo sopra (dove viene fornito il codice della classe CProvision), non è stato spiegato da dove proviene tutto e dove va oltre, poiché lo sviluppo dell'Expert Advisor non è l'argomento di questo articolo.
Ma considereremo in dettaglio la classe CPositionVirtualMagic.
Alla classe viene assegnata la struttura:
struct SPositionVirtualMagic
che viene utilizzata per accettare i risultati dei calcoli, tale dichiarazione globale all'interno della classe, grazie a pvm (variabile della struttura), questa struttura sarà disponibile ovunque, in qualsiasi metodo della classe.
Segui quindi i due metodi della classe:
double cVOLUME(){return(pvm.volume);} // Returns the volume of the virtual position of the EA ENUM_POSITION_TYPE cTYPE() {return(pvm.type);} // Returns the type of the virtual position of the EA
Questi metodi sono dichiarati pubblici e, pertanto, saranno disponibili tramite la variabile class chiamata, in qualsiasi punto del programma, e sono progettati per l'output dei valori della struttura nella posizione richiesta.
Questa è anche la sezione in cui viene dichiarato il seguente metodo:
bool PositionVirtualMagic(ulong Magic,string symbol,datetime CurrentTime);
Questa è la funzione principale della classe, e ci concentreremo ulteriormente sulle sue analisi dettagliate, e nel frattempo, mi supererò e descriverò la funzione sotto l’indicatore dell'accesso privato:
void prHistory_Deals(ulong &buf[],int HTD);
Questo metodo produce un ticket di record di transazioni nell'array, che è fondamentalmente un ciclo, e potrebbe essere descritto nella funzione chiamata, ma volevo ridurre la dimensione (al fine di aumentare la leggibilità del codice) della funzione PositionVirtualMagic(), e così ho spostato questo ciclo oltre i limiti della funzione e ho dimostrato come utilizzare l’indicatore dell'accesso privato.
Quindi torniamo al PositionVirtualMagic() . Questa funzione, all'inizio ha un calcolo di precisione di una riga, a cui è necessario arrotondare il doppio valore del volume della posizione calcolata.
int DIGITS=(int)-log10(SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP)); if(DIGITS<0)DIGITS=0;
Questo è necessario per fare l'operazione di confronto a zero, altrimenti un certo equilibrio nelle 8 cifre dopo il punto decimale, ci impedirà di equiparare il valore a zero e causerà un errore di esecuzione.
Il volume della posizione viene arrotondato allo step minimo. E se lo step minimo è maggiore di 1, l'arrotondamento viene eseguito dalla parte integrale. Il prossimo è il ciclo while, ma viene usato in un modo nuovo (diverso da quello in mql4), poiché la verifica dell'espressione veritiera viene fatta alla fine del ciclo, piuttosto che all'inizio:
do { if(HistorySelect(0,TimeCurrent())) { History_Total_Deals=HistoryDealsTotal(); prHistory_Deals(DTicketbuf,History_Total_Deals); } HistorySelect(0,TimeCurrent()); } while(History_Total_Deals!=HistoryDealsTotal());
Tale approccio viene utilizzato perché l'espressione di veridicità viene calcolata all'interno del ciclo e, al suo lancio, non è ancora preparata per questa verifica.
Il ciclo contiene il Caricamento della cronologia, voglio attirare l'attenzione del lettore sul fatto che questa è una condizione richiesta al fine di garantire il lavoro delle funzioni integrate di lavorare con la cronologia.
HistorySelect(0,TimeCurrent())
Penso che dovrei spiegare il mio sistema di scelta dei nomi delle variabili.
Il lettore attento dovrebbe aver notato che i nomi delle classi sono definiti dalla lettera iniziale "C", questo non è richiesto dalla sintassi e può essere dato qualsiasi nome, ma in questo modo rende la lettura molto più facile. Se la lettera "C" appare prima del nome, sappiamo subito che questo è il nome della classe, e se la lettera è "S" - allora è una struttura. Se la variabile assume il valore di una funzione incorporata, allora cambio semplicemente i componenti del nome della funzione e ottengo il nome della variabile, ad esempio in questo modo:
CurrentTime = TimeCurrent();
Semplice e leggibile, possiamo vedere immediatamente cosa contiene la variabile. Soprattutto perché MetaEditor contiene la funzione per spostare un particolare parte di codice in una posizione specifica.
Rivedendo ulteriormente il codice, vediamo che dopo il caricamento della cronologia, segue la chiamata alla funzione:
History_Total_Deals=HistoryDealsTotal();
con la memorizzazione del numero di transazioni nella variabile. Con la stessa condizione, implementeremo la verifica per l'uscita dal ciclo. A cosa serve questa verifica? E perché non possiamo semplicemente caricare la cronologia e quindi recuperare le transazioni da essa?
Il problema risiede nel fatto che durante il lavoro degli Expert Advisor, la cronologia sarà richiesta separatamente da ciascun EA, e quindi se gli Expert Advisor sono in esecuzione in momenti diversi, la profondità della cronologia sarà diversa. E questo significa che quando un Expert Advisor entra nel ciclo e carica la cronologia per il suo periodo, quindi prima di raggiungere la fine del ciclo, potrebbe scoprire che questa cronologia è già stata caricata su richiesta di un altro Expert Advisor, e quindi è necessario fare una verifica per l'autenticità.
Per inciso, potrebbe non essere il miglior tipo di verifica, ma funziona. E quindi, continuiamo. Nel ciclo richiamiamo il metodo class, che inserisce i valori dei ticket delle transazioni in un buffer appositamente preparato. Dopo aver chiamato la funzione prHistory_Deals (), produciamo nuovamente il caricamento della cronologia.
Questo è il modo in cui viene organizzata la verifica se ci sono stati o meno cambiamenti nella storia delle negoziazioni, nel corso del lavoro della funzione prHistory_Deals (). Se non ci sono state modifiche, la variabile di History_Total_Deals sarà uguale a HistoryDealsTotal () e si verificherà un'uscita dal ciclo per un singolo passaggio. Se ci sono stati cambiamenti, il sistema avvierà un secondo ciclo e continuerà a ripetere fino a quando la cronologia dei ticket non verrà caricata senza errori (e non dimenticare di mettere ";" alla fine):
while(History_Total_Deals!=HistoryDealsTotal());
Più avanti nel ciclo per, avviene il calcolo delle posizioni virtuali.
Se la transazione ha superato con successo una serie di filtri (l'ora della transazione e il numero magico della transazione), il suo volume aumenta quella parte della posizione virtuale, il tipo a cui appartiene la transazione.
Voglio far notare che registro i calcoli delle posizioni virtuali solo dal lancio dell'Expert Advisor, sebbene siano possibili altre opzioni.
Qui va notato come viene calcolata esattamente la posizione. Dal libro dei record, che tutti noi abbiamo usato da tempo immemorabile e fino ad oggi, abbiamo spese e profitti, e il conteggio del saldo è mantenuto come differenza tra questi valori, lo stesso schema si applica al calcolo di una posizione: se apri lotti, 0,2 per vendere e 0,3 per comprare, ciò significa che mantieni la posizione di 0,1 per Buy. Il tempo di apertura e la differenza di livelli sono categorie di profitto, ma la posizione che avrai è di 0,1 lotti, il tipo Buy.
Questo è il motivo per cui riassumiamo semplicemente tutte le transazioni effettuate dall'Expert Advisor su Buy e separatamente su Sell, quindi le confrontiamo e otteniamo la posizione generale (in realtà, questo è ciò con cui è impegnato il resto della funzione esaminata).
Calcolo del volume delle posizioni:
volume=NormalizeDouble(volume_BUY-volume_SELL,DIGITS);
Riconoscimento del tipo di posizione con l'output del valore nella struttura:
if(volume<0)pvm.type=POSITION_TYPE_SELL; else { if(volume>0)pvm.type=POSITION_TYPE_BUY; }
Output di volume nella struttura:
pvm.volume=fabs(volume);
L'output del valore della funzione: se il volume della posizione è 0, allora è falso, altrimenti, se la posizione esiste, allora è vero:
if(pvm.volume==0)return(false); else return(true);
Ora, avendo la funzione della posizione virtuale, possiamo facilmente redigere il codice dell'Expert Advisor, che non sarà in conflitto con i suoi "vicini".
Per risparmiare spazio, fornirò alcune parti del codice, che non sono state esposte sopra, piuttosto che l'intero codice stesso.
//+------------------------------------------------------------------+ //| Code of the Expert Advisor | //+------------------------------------------------------------------+ //--- input parameters input ulong magic =1; // magic input int SL =300; // Stop Loss input int TP =1000; // Take Profit input int MA_Period =25; // MA period input double lot =0.1; // Volume of position input int MA_shift =0; // Shift of indicator input ENUM_MA_METHOD MA_smooth =MODE_SMA; // Smoothing type input ENUM_APPLIED_PRICE price =PRICE_OPEN; // Price type //--- we will store the indicator's handle int MA_handle,type_MA,rezult; double v[2]; datetime CurrentTime; // The variable stores the time of start of the Expert Advisor MqlTradeResult res; // Pointer to the structure of responding by OrderSend MqlTick tick; // Pointer to the structure of last market information CPositionVirtualMagic cpvm; CProvision prov; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { CurrentTime=TimeCurrent();// The variable stores the time of start of the Expert Advisor //--- Create the indicator's handle MA_handle=iMA(Symbol(),0,MA_Period,MA_shift,MA_smooth,price); return(0); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { if(CopyBuffer(MA_handle,0,0,2,v)<=0) {Print("#",magic,"Error of copying");return;} type_MA=prov.TYPE(v); // Determine type depending on MA indication if(cpvm.PositionVirtualMagic(magic,_Symbol,CurrentTime))// If there is ab open position { if((int)cpvm.cTYPE()!=type_MA)// Check if it is time to close { Print("#",magic,"Position by magic number has volume ",cpvm.cVOLUME(), " reverse position of type ",(int)cpvm.cTYPE()," by ",type_MA); //cpvm.cVOLUME() - volume of virtual position rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,cpvm.cVOLUME()+lot);// reverse the poistion if(rezult!=-1)Print("#",magic," Code of the operation result ",rezult," volume ",res.volume); else{Print("#",magic,"Error",GetLastError()); return;} } } else // If there is no open position then open { Print("#",magic,"Poistion by magic number has volume ",cpvm.cVOLUME()," open position of type ",type_MA); rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,lot);// Open position if(rezult!=-1)Print("#",magic," Code of the operation result ",rezult," volume ",res.volume); else{Print("#",magic,"Error",GetLastError()); return;} } }
Esegui l'Expert Advisor tre volte su un singolo strumento, ma con timeframe diversi, oltre a assegnare numeri magici diversi ogni volta:
Figura 3. Assegniamo numeri magici diversi a due Expert Advisor identici, (uno strumento, timeframe diversi) lancio del primo Expert Advisor
Figura 4. Assegniamo numeri magici diversi a due Expert Advisor identici (uno strumento, timeframe diversi) lancio del secondo Expert Advisor
Figura 5. Il risultato è un lavoro senza conflitti di Expert Advisor su un unico strumento con vari numeri magici
Il periodo di prova è stato superato con successo, gli Expert Advisor si sono gentilmente ceduti l'un l'altro e non sembrano essere presenti questioni contrastanti.
Il primo punto delle specifiche tecniche è stato implementato, ma non è tutto.
Codificare il numero magico
Per l'implementazione delle seguenti parti, dovremo sviluppare una classe di metodi, che codificano / decodificheranno le informazioni, oltre a recuperare i valori dalle funzioni integrate e trasformarli in un formato specifico.
Per fare ciò, ripetere i termini di codifica (per così dire, le specifiche tecniche per lo sviluppo):
- I metodi devono codificare il nome dell'Expert Advisor (chiamiamolo il nome digitale),
- Codice di riconoscimento tuo/straniero (chiamiamolo il codice di interazione)
- Codice simbolo, su cui è in esecuzione l'Expert Advisor (per poter determinare dalla transazione da cui sta lavorando l'EA).
Per cominciare, selezioniamo il nome per la nuova classe, - lascia che sia magico (un nome generico), assegna la nostra enumerazione per rendere il codice più visivamente comprensibile.
enum Emagic { ENUM_DIGITAL_NAME, // digital name if the Expert Advisor ENUM_CODE_INTERACTION,// code of interaction ENUM_EXPERT_SYMBOL // symbol, on which the EA is launched };
L'enumerazione funziona in maniera semplice: si descrivono i nomi, separati da virgole, e il compilatore assegna loro numeri in sequenza.
Prima di tutto, se assegni una variabile di un tipo diverso (non è applicabile ai numeri), quindi quando specifichi un parametro dall'enumerazione, riceverai un errore durante la compilazione e, in secondo luogo, ottieni chiarezza: non assegni semplicemente 0 , ma piuttosto dai il comando per assegnare ENUM_DIGITAL_NAME .
Come per la creazione di una struttura o di una classe, ho scelto un nome semplice per l'enumerazione. Ho semplicemente aggiunto E al nome generalmente selezionato, e ottenuto rispettivamente Emagic , la struttura corrispondente sarà Smagic e la classe Cmagic .
Anche in questo caso, bisogna prestare attenzione al fatto che questo argomento non è obbligatorio ed è possibile chiamare l’enumerazione Enumerator, la struttura Structurer e la classe Classifier. Ma questo non fornirà un’omogeneità nei nomi e leggere questo tipo di codice sarà scomodo.
Successivamente, creiamo una struttura per la memorizzazione dei nostri codici.
struct Smagic { ulong magicnumber; // magic in an assembled form - how it is written in the order int digital_name; // digital name int code_interaction; // code of interaction int expert_symbol; // symbol, on which the Expert Advisor is launched };
Dopodiché, dichiara la classe Cmagic, in cui registriamo tutti i metodi di codifica e decodifica di Magic, inclusi i metodi del precedente Expert Advisor (basta dichiararli nella classe corrente e riscrivere le intestazioni)
class Cmagic { protected: Smagic mag; SPositionVirtualMagic pvm; public: // the function returns the assembled magic, assembled from the incoming data ulong SetMagic_request(int digital_name=0,int code_interaction=0); // the function obtains the assembled magic and divides it according to the assembly logic ulong SetMagic_result(ulong magicnumber); // the function obtains the return identification and returns the requested part of the assembled magic ulong GetMagic_result(Emagic enum_); // the function obtains the return identification and returns the textual interpretation of the request part of the assembled magic string sGetMagic_result(Emagic enum_); // returns the voulme of the virtual position of the Expert Advisor double cVOLUME(){return(pvm.volume);} // returns the type of the virtual position of the Expert Advisor ENUM_POSITION_TYPE cTYPE(){return(pvm.type);} // method of calculating the virtual position, returns the presence of absence of the virtual position bool PositionVirtualMagic(Emagic enum_, string symbol, datetime CurrentTime); private: // function divides the magic into three parts of three charges, and returns the part to which the category points to int decodeMagic_result(int category); // interpretor of instrument symbols into the digital code int symbolexpert(); // interpretor of the digital code into the prescribed text (Expert Advisors) string expertcode(int code); // interpretor of the digital code into the prescribed text (interaction) string codeinterdescript(int code); // interpretor of the digital code into the instrument symbol string symbolexpert(int code); // cycle of recording tickets into the buffer void prHistory_Deals(ulong &buf[],int HTD); };
Ora svilupperemo i metodi.
Il primo metodo della classe:
//+---------------------------------------------------------------------+ //| Function returns the assembled magic, assembled from the input data | //+---------------------------------------------------------------------+ ulong Cmagic::SetMagic_request(int digital_name=0,int code_interaction=0) { if(digital_name>=1000)Print("Incorrectly specified digital name of the Expert Advisor (more than 1000)"); if(code_interaction>=1000)Print("Incorrectly specified the code of recognizing yours-foreign (more than 1000)"); mag.digital_name =digital_name; mag.code_interaction =code_interaction; mag.expert_symbol =symbolexpert(); mag.magicnumber =mag.digital_name*(int)pow(1000,2)+ mag.code_interaction*(int)pow(1000,1)+ mag.expert_symbol; return(mag.magicnumber); }
Questo metodo riceve due valori: un nome digitale dell'Expert Advisor e il codice di interazione.
ulong Cmagic::SetMagic_request(int digital_name=0,int code_interaction=0)
E ne verifica immediatamente la correttezza:
if(digital_name>=1000)Print("Incorrectly specified the digital name of the Expert Advisor(more than 1000)"); if(code_interaction>=1000)Print("Incorrectly specifies the code of recognizing yours-foreign (more than 1000)");
Ma non ci sono ritorsioni contro le azioni dell'utente, anche in caso di errore, continua docilmente a funzionare.
Segue l'assegnazione nella struttura dei dati di input, che l'utente specifica, tuttavia il simbolo dello strumento non è specificato e si ottiene dal metodo privato:
int Cmagic::symbolexpert()
Non fornirò il suo codice, perché è molto lungo ed è presente nel file allegato. Lasciatemi solo dire che questo metodo è fondamentalmente solo una tabella, che assegna a ogni simbolo dalla finestra "market view" un numero corrispondente: per EURUSD, ad esempio, è 1, ecc.
È certamente possibile ottenere questi dati in modo dinamico, scrivendo un codice per un sondaggio di quali valute sono presenti nella finestra "market view", ma la soluzione deve corrispondere alla complessità del problema, e non ha senso occuparsi di richiamare le finestre; quindi lo faremo nel modo semplice, comprendere un elenco di valute e assegnare un indice a ciascuna di esse.
E, infine, la linea più essenziale dell'intero metodo:
mag.magicnumber =mag.digital_name*(int)pow(1000,2)+ mag.code_interaction*(int)pow(1000,1)+ mag.expert_symbol;
assemblata dalle parti disparate di tutto il Magic. Questo è il Magic che verrà assegnato all'ordine del nostro Expert Advisor.
Il prossimo metodo pubblico della classe:
//+------------------------------------------------------------------+ //| Function obtains the assembled magic | //| and divides it according to the logic of the assembly | //+------------------------------------------------------------------+ ulong Cmagic::SetMagic_result(ulong magicnumber) { mag.magicnumber =magicnumber; mag.expert_symbol =decodeMagic_result(1); mag.code_interaction =decodeMagic_result(2); mag.digital_name =decodeMagic_result(3); return(mag.magicnumber); }
In realtà, questo metodo funge da shell, distribuendo attraverso la struttura i risultati di tre chiamate di un singolo metodo privato. La dichiarazione sotto tale indicatore è buona nel fatto che non vengono visualizzati nel messaggio di prompt pop-up, quando si richiama una variabile di classe, creando l'impressione che tutto il lavoro sia stato svolto da una funzione pubblica.
Ma torniamo alle nostre funzioni private:
//+------------------------------------------------------------------+ //| Function divides the magic into three parts of three charges | //| and returns the part, which the category points to | //+------------------------------------------------------------------+ int Cmagic::decodeMagic_result(int category) { string string_value=(string)mag.magicnumber; int rem=(int)MathMod(StringLen(string_value),3); if(rem!=0) { rem=3-rem; string srem="0"; if(rem==2)srem="00"; string_value=srem+string_value; } int start_pos=StringLen(string_value)-3*category; string value=StringSubstr(string_value,start_pos,3); return((int)StringToInteger(value)); }
Visivamente, questo metodo può essere rappresentato come una lettura di un numero di tre cifre dal campo specificato, ad esempio, se abbiamo un Magic 123456789 , possiamo rappresentarlo come | 123 | 456 | 789 | se il campo specificato è 1 , il risultato sarà 789 poiché i campi sono numerati da destra a sinistra.
Pertanto, dopo aver utilizzato tutti e tre i campi nel metodo chiamato, distribuiamo alla struttura tutti i dati acquisiti. Questo viene fatto attraverso una procedura in cui si riporta il Magic a un minuscolo tipo di stringa:
string string_value=(string)mag.magicnumber;
seguito dall'ordine dei singoli componenti di linea.
Successivamente segui due funzioni simili, che sono interruttori nella loro essenza interruttore, e differiscono solo nel tipo di valori di uscita:
//+------------------------------------------------------------------+ //| Function obtains the identifier of the return | //| and returns the requested part of the assembled magic | //+------------------------------------------------------------------+ ulong Cmagic::GetMagic_result(Emagic enum_) { switch(enum_) { case ENUM_DIGITAL_NAME : return(mag.digital_name); break; case ENUM_CODE_INTERACTION : return(mag.code_interaction); break; case ENUM_EXPERT_SYMBOL : return(mag.expert_symbol); break; default: return(mag.magicnumber); break; } } //+------------------------------------------------------------------------------+ //| Function obtains the identifier of the return and returns | //| a textual interpretation of the requested type of the assembled magic | //+------------------------------------------------------------------------------+ string Cmagic::sGetMagic_result(Emagic enum_) { switch(enum_) { case ENUM_DIGITAL_NAME : return(expertcode(mag.digital_name)); break; case ENUM_CODE_INTERACTION : return(codeinterdescript(mag.code_interaction)); break; case ENUM_EXPERT_SYMBOL : return(symbolexpert(mag.expert_symbol)); break; default: return((string)mag.magicnumber); break; } }
Le funzioni restituiscono la parte del Magic, che specifica il parametro di tipo Emagic, con il primo che dà il risultato sotto forma di ulong che viene utilizzato nei calcoli e il secondo che dà i risultati del tipo stringa che può essere utilizzato per la visualizzazione.
Nella funzione GetMagic_result () tutto è organizzato in maniera semplice, distribuisce i valori della struttura attraverso i rami switch, mentre sGetMagic_result () è un po’ più complicato. Ogni caso di ramo richiama una funzione di tabella, che trasferisce il valore della struttura in una forma visiva. Quindi, se il valore mag.expert_symbol = , 1, allora la prima funzione darà 1 , e la seconda EURUSD .
Ho già descritto i vantaggi delle funzioni di tabella nella codifica / decodifica delle informazioni, quindi menzionerò solo che ogni caso dovrebbe essere considerato separatamente in base alla complessità di implementazione di un metodo senza tabella e ai suoi vantaggi per il tempo necessario per scrivere le tabelle. Se è più facile scrivere una tabella di stati, allora non c'è bisogno di complicare le cose. Ma se la scrittura della tabella richiederà molto tempo, allora ovviamente, la preferenza dovrebbe essere data ai metodi procedurali. Non sto fornendo le tabelle per risparmiare spazio (possono essere trovate nei file allegati).
Fondamentalmente questo è tutto, la nostra classe è stata sviluppata, tuttavia, ci sono ancora le quattro funzioni rimanenti che abbiamo usato nello sviluppo del precedente Expert Advisor.
Li ho semplicemente dichiarati nuovamente in una nuova classe, soprattutto considerando che dovevano essere leggermente modificati.
Ora il metodo principale:
bool Cmagic::PositionVirtualMagic(Emagic enum_, string symbol, datetime CurrentTime)
non solo è dichiarato come un metodo di classe Cmagic, ma ha anche un diverso insieme di parametri.
Invece del Magic, ora ottiene l'identificazione dal campo del Magic, a cui è stata calcolata la posizione. Inoltre, anche se il simbolo era presente nell'ultima opzione, è stato utilizzato solo per ottenere informazioni sul passo del lotto dal simbolo. E ora è prescritto nel filtro e può partecipare, alla pari con gli altri, al filtrazione del conteggio delle posizioni.
Che cosa comporta questo? Ora possiamo filtrare contemporaneamente le transazioni, che erano aperte su uno strumento diverso, ma dallo stesso Expert Advisor. In tal modo non saranno confusi con altri Expert Advisor simili in esecuzione su uno strumento diverso. Ad essere onesti, è molto difficile descrivere tutti i diversi modi di utilizzare questo nuovo sistema di calcoli. E il lettore può decidere personalmente di cosa ha bisogno da un sistema così complicato. Ti consiglio vivamente di non complicare i casi, dove puoi scrivere semplicemente, e di non temere tali complicazioni quando ce n'è un evidente bisogno.
Poiché la classe è stata progettata, è tempo di testarla su un nuovo Expert Advisor:
//+------------------------------------------------------------------+ //| Code of the Expert Advisor | //+------------------------------------------------------------------+ //--- input parameters input ulong digital_name_ =4; // Digital name of Expert Advisor input ulong code_interaction_ =1; // Code of interaction input Emagic _enum =0; // Model of magic number input int SL =300; // Stop Loss input int TP =1000; // Take Profit input int MA_Period =25; // MA period input double lot =0.4; // Volume of position input int MA_shift =0; // Shift of indicator input ENUM_MA_METHOD MA_smooth =MODE_SMA; // Smoothing type input ENUM_APPLIED_PRICE price =PRICE_OPEN; // Price type //--- we will store the indicator's handle int MA_handle,type_MA,rezult; static ulong magic; double v[2]; datetime CurrentTime;// The variable stores the time of start of the Expert Advisor MqlTradeResult res; // Pointer to the structure of responding by OrderSend MqlTick tick; // Pointer to the structure of last market information CProvision prov; Cmagic mg; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { magic=mg.SetMagic_request(digital_name_,code_interaction_); // Stamp of Expert Advisor (the magic number identifier) the magic variable is declared at the global scope // used in int CProvision::SendOrder(ENUM_ORDER_TYPE type,double volume) CurrentTime=TimeCurrent();// The variable stores the time of start of the Expert Advisor //--- Create the indicator's handle MA_handle=iMA(Symbol(),0,MA_Period,MA_shift,MA_smooth,price); return(0); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { if(CopyBuffer(MA_handle,0,0,2,v)<=0) {Print("#",magic,"Error of copying");return;} type_MA=prov.TYPE(v); // Determine type depending on MA indication mg.SetMagic_result(magic);// put the information into the structure if(mg.PositionVirtualMagic(_enum,_Symbol,CurrentTime))// If three is an open position { if((int)mg.cTYPE()!=type_MA)// Check if it is time to close { mg.SetMagic_result(magic);// put the information into the structure Print("#",mg.GetMagic_result(_enum),"Position by magic number has volume ",mg.cVOLUME(), " reverse position of type ",(int)mg.cTYPE()," by ",type_MA); //cpvm.cVOLUME() - volume of virtual position rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,mg.cVOLUME()+lot);// reverse position if(rezult!=-1)Print("№",magic," Code of the operation result ",rezult," volume ",res.volume); else{Print("№",magic,"Error",GetLastError()); return;} } } else // If there is no open position then open { Print("#",magic,"Position by magic number has volume ",mg.cVOLUME()," open position of type",type_MA); rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,lot);// Open position if(rezult!=-1)Print("#",magic," Code of the operation result ",rezult," volume ",res.volume); else{Print("#",magic,"Error",GetLastError()); return;} } }
Come accennato in precedenza, questo Expert Advisor è estremamente semplice ed è stato creato solo per dimostrarne le diverse capacità, eseguirlo tre volte su un singolo strumento:
Figura 6. Installazione di tre Expert Advisor con magic differenti su grafici diversi
Figura 7. Il risultato è un trading senza conflitti di tre Expert Advisor con magic diversi
Come si può vedere dalle stampe dei messaggi degli Expert Advisor, tutti e tre i partecipanti sono stati lanciati con successo e non hanno dimostrato conflitti.
Conclusione
Fornendo l'opportunità di assegnare ordini magici alle operazioni di trading, i creatori di MQL5 hanno notevolmente facilitato la vita degli scrittori di Expert Advisor. Ma gli sviluppatori possono fornirti solo strumenti: devi essere tu a ottenere effettivamente i diamanti.
Buona fortuna e alla prossima.
Tradotto dal russo da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/ru/articles/112





- 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