English Русский 中文 Español Deutsch 日本語 Português Türkçe
Gli Swap (Parte I): Posizioni di Blocco e Sintetiche

Gli Swap (Parte I): Posizioni di Blocco e Sintetiche

MetaTrader 5Trading | 30 settembre 2024, 08:04
36 0
Evgeniy Ilin
Evgeniy Ilin

Sommario

    Introduzione

    Ho pensato a lungo all'argomento di questo articolo, ma non ho avuto il tempo di condurre una ricerca dettagliata. Il tema degli swap è piuttosto diffuso sul web, soprattutto tra i professionisti del trading che contano ogni pip, il che è effettivamente l'approccio migliore al trading. In questo articolo scoprirete come utilizzare gli swap per bene e vedrete che gli swap devono sempre essere presi in considerazione. Inoltre, l'articolo presenta un'idea molto complessa ma interessante su come modernizzare i metodi di swap trading. Tali metodi (se adeguatamente preparati) possono essere utilizzati o all'interno di un conto o come strumento di incremento dei profitti per il bloccaggio classico con due conti.


    Informazioni sugli Swap

    Non spiegherò l'idea degli swap e la loro teoria. Sono solo interessato all’applicazione pratica degli swap. La domanda più importante è se sia possibile generare profitti attraverso gli swap. Dal punto di vista del trader, uno swap rappresenta un profitto o una perdita. Inoltre, molti trader ignorano semplicemente perché si dedicano al trading intraday. Altri cercano di non farci caso, pensando che sia così insignificante da non poter influire sul trading. In effetti, quasi la metà dello spread può essere nascosta nello swap. Questo spread non viene preso al momento dell'acquisto o della vendita, ma quando il giorno cambia sul server.

    Lo swap viene addebitato in relazione al volume della posizione aperta. Ciò avviene nei seguenti momenti:

    1. Da Lunedì a Martedì
    2. Da Martedì a Mercoledì
    3. Da Mercoledì a Giovedì (quasi tutti i broker addebitano un triplo swap in questa notte)
    4. Da Giovedì a Venerdì

    Di solito, il valore dello swap è indicato nelle specifiche dello strumento di trading in punti o in percentuale. Ci possono essere altri metodi di calcolo, ma sono riuscito a capirne solo due, il che è abbastanza. Le informazioni strutturate sugli swap sono molto scarse. Tuttavia, se studiate la questione, potreste anche trovare alcune strategie efficienti basate sugli swap. Generano una percentuale di profitto minima, ma hanno un grande vantaggio: il profitto è assolutamente garantito. La difficoltà principale di questo approccio è che i broker più popolari hanno pochissimi strumenti con uno swap positivo, quindi è davvero difficile guadagnare con questa idea. Anche il possibile profitto potenziale è estremamente basso. Tuttavia, è sempre meglio che perdere completamente il deposito. E se utilizzate qualsiasi altro sistema di trading, molto probabilmente perderete. 

    Il punto fondamentale delle mie conclusioni a riguardo del trading sul Forex è che nulla può garantire un profitto, tranne gli swap positivi. Naturalmente, esistono alcuni sistemi in grado di generare un profitto. Tuttavia, quando li utilizziamo, accettiamo di pagare il nostro denaro al broker per qualsiasi operazione di trading, sperando che il prezzo vada nella giusta direzione. Uno swap positivo è il processo inverso. Vedo le seguenti affermazioni come indicatori di trading nella direzione dello swap positivo:

    • Uno swap positivo equivale a un movimento parziale del prezzo verso la nostra posizione aperta (profitto giornaliero).
    • Dopo un po' di tempo, uno swap può coprire le perdite sugli spread e sulle commissioni; dopo un po', lo swap aggiungerà denaro
    • Affinché lo swap funzioni, la posizione deve essere mantenuta il più a lungo possibile, in modo che il fattore di profitto di questa posizione sia massimo.
    • Se sviluppato a fondo, il profitto sarà assolutamente prevedibile e garantito.

    Naturalmente, lo svantaggio maggiore di questo approccio è la dipendenza dalla dimensione del deposito, ma nessun altro concetto è in grado di garantire con tanta sicurezza il profitto nel Forex. Questa dipendenza può essere ridotta riducendo il volume delle posizioni aperte o il rischio (che è la stessa cosa). Il rischio è il rapporto tra il volume della posizione e il deposito: un aumento del volume della posizione aumenta il nostro rischio che il prezzo possa andare nella direzione perdente e il deposito possa non essere sufficiente per aspettare che i profitti degli swap compensino le perdite degli spread e delle commissioni. È stato inventato un meccanismo di blocco per ridurre al minimo l'influenza di tutti i possibili effetti negativi.


    Bloccaggio Utilizzando Due Conti Trading

    Questo metodo di swap trading è il più popolare tra i trader. Per attuare questa strategia, avrete bisogno di due conti con swap diversi per le stesse coppie di valute o altri asset. Aprire due posizioni opposte all'interno dello stesso conto non ha senso — equivale a perdere semplicemente il deposito. Anche se un simbolo ha uno swap positivo, questo swap sarà negativo quando si fa trading nella direzione opposta. Il diagramma seguente riflette il concetto di questo metodo:

    Swap Trading Classico

    Come si può vedere dal diagramma, per uno strumento specificamente selezionato per il quale si desidera fare trading in swap esistono solo 10 scenari, 6 dei quali sono utilizzati attivamente. Le ultime quattro opzioni possono essere scelte come ultima risorsa, se non è possibile trovare una coppia di valute che corrisponda alle condizioni "1-6", poiché uno degli swap in questo caso è negativo. È possibile trarre profitto dallo swap positivo che è maggiore di quello negativo. È possibile trovare tutti i casi sopra menzionati se si analizzano i diversi broker e le loro tabelle di swap. Ma le opzioni migliori per questa strategia sono "2" e "5". Queste opzioni hanno swap positivi su entrambe le estremità. Quindi, il profitto viene guadagnato da entrambi i broker. Inoltre, non è necessario spostare spesso i fondi da un conto all'altro.

    Lo svantaggio principale di questa strategia è che dovete comunque spostare il denaro tra i conti, perché quando aprite posizioni opposte avrete una perdita con un broker e un profitto con un altro broker. Tuttavia, se si calcolano correttamente i volumi di trading in relazione al deposito esistente, non sarà necessario spostare i fondi troppo spesso. Ma c'è un vantaggio indiscutibile: in ogni caso ci sarà un profitto, di cui si può prevedere l'esatta entità. Penso che molti utenti preferirebbero evitare questa routine ed eseguire in qualche modo queste manipolazioni all'interno di un conto (cosa impossibile). Esiste però un metodo per aumentare i profitti del classico metodo di trading swap, anche se non consente di operare su un unico conto. Analizziamo le caratteristiche principali di questo metodo.


    Informazioni sui Tassi di Cambio

    Partiamo dalla base stessa su cui si fonda tutta la logica. Su questa base si possono costruire equazioni matematiche. Ad esempio, consideriamo EURUSD, USDJPY, EURJPY. Tutte queste 3 coppie sono correlate. Per comprendere la relazione, presentiamo questi simboli in una forma leggermente differente:

    • 1/P = EUR/USD
    • 1/P = USD/JPY
    • 1/P = EUR/JPY
    • P è il tasso della valuta selezionata

    Ogni strumento di trading ha una valuta (o un'asset equivalente) che acquistiamo e un'altra valuta che diamo in cambio. Ad esempio, se si prende il primo rapporto (coppia EURUSD), aprendo una posizione Buy di 1 lotto si acquisiscono 100.000 unità della valuta di base. Queste sono le regole del trading sul Forex: un lotto è sempre pari a 100.000 unità della valuta di base. La valuta di base di questa coppia è EUR e quindi acquistiamo EUR per USD. Il tasso di cambio "P" in questo caso indica quante unità di USD sono contenute in 1 EUR. Lo stesso vale per tutti gli altri simboli: la valuta di base è contenuta nel numeratore, mentre il denominatore è la "valuta principale" (se non siete d'accordo con questa denominazione, aggiungete un commento qui sotto). L'importo della valuta principale viene calcolato semplicemente moltiplicando il prezzo per il valore in EUR:

    • 1/P = EUR/USD ---> USD/P= EUR ---> USD = P*EUR
    • EUR = Lotti*100000

    Quando si apre una posizione Sell, le valute cambiano di posto. La valuta di base inizia a fungere da valuta principale e quella principale diventa una valuta di base. In altre parole, acquistiamo USD in cambio di EUR, ma la quantità di denaro di entrambe le valute è calcolata allo stesso modo — rispetto all'EUR. Questo è corretto, perché altrimenti si creerebbe molta confusione. I calcoli sono gli stessi per le altre valute. Quindi, nel calcolo successivo utilizziamo il segno "+" per la valuta di base e il segno "-" per la valuta principale. Di conseguenza, ogni trade ha una serie di due numeri corrispondenti che simboleggiano cosa e per cosa compriamo. Un'altra interpretazione è che c'è sempre una valuta che funge da prodotto e un'altra valuta che funge da moneta e che paghiamo per acquistare il prodotto. 

    Se apriamo diverse posizioni per diversi strumenti, allora ci saranno più valute principali e supplementari, e quindi avremo una sorta di posizione sintetica. Dal punto di vista dell'utilizzo degli swap, una posizione sintetica di questo tipo è assolutamente inutile. Ma possiamo creare una posizione sintetica che sarà molto utile. Lo mostrerò più tardi. Ho determinato il calcolo del volume espresso da due valute. In base a ciò, possiamo concludere che possiamo creare una posizione sintetica complessa che sarà equivalente ad alcune più semplici:

    • EUR/JPY = EUR/USD * USD/JPY — tasso di cambio composto da due derivati

    In realtà, esiste un numero infinito di rapporti di questo tipo, che sono composti da diverse valute, come ad esempio:

    • EUR - Unione Europea Euro
    • USD - Dollaro USA
    • JPY - Yen Giapponese
    • GBP - Sterlina Britannica
    • CHF - Franco Svizzero
    • CAD - Dollaro Canadese
    • NZD - Dollaro Neozelandese
    • AUD - Dollaro Australiano
    • CNY - Yuan Cinese
    • SGD - Dollaro di Singapore
    • NOK - Corona Norvegese
    • SEK - Corona Svedese

    Questo non è l'elenco completo delle valute. Ciò che dobbiamo sapere è che uno strumento di trading arbitrario può essere composto da qualsiasi valuta di questo elenco. Alcuni di questi strumenti di trading sono offerti dai broker, mentre altri possono essere ottenuti come combinazione di posizioni di altri strumenti. Un esempio tipico è la coppia EURJPY. Questo è solo l'esempio più semplice di composizione di tassi di cambio derivati, ma sulla base di queste idee possiamo concludere che qualsiasi posizione può essere presentata come un insieme di posizioni per altri strumenti. In base a quanto sopra, risulta che:

    • Valore1 è la valuta del simbolo base espressa da un valore assoluto
    • Valore2 è una valuta del simbolo aggiuntivo espressa da un valore assoluto
    • A è il volume del lotto della valuta di base della posizione
    • B è il volume del lotto della valuta principale della posizione
    • Il contratto è la quantità di valuta acquistata o venduta in valore assoluto (corrisponde a 1 lotto).
    • A = 1/P = Valore1/Valore2 - è l'equazione di qualsiasi strumento di trading (compresi quelli che non sono presentati nella finestra del market watch)
    • Valore1 = Contratto*A
    • Valore2 = Contratto*B

    Questi rapporti ci serviranno in seguito per calcolare i lotti. Per ora ricordatevi di loro. Questi rapporti descrivono il rapporto tra il numero di valute acquistate o vendute. Su questa base si può costruire una logica di codice più seria.


    Bloccaggio Tramite Posizioni Sintetiche

    In questo articolo, una posizione sintetica è una posizione che può essere composta da diverse altre posizioni, mentre queste altre posizioni devono necessariamente essere composte da altri strumenti. Questa posizione deve essere equivalente a una posizione aperta per qualsiasi strumento. Sembra complicato? In realtà, è tutto molto semplice. Tale posizione può essere necessaria per:

    1. Bloccare la posizione originale su uno strumento di trading simulato.
    2. Provare a creare l'equivalente di una posizione con tassi swap completamente differenti
    3. Altri scopi
    In origine, ho avuto questa idea in relazione al punto 2. I broker stabiliscono i valori degli swap per diversi scopi, il principale dei quali è il desiderio di generare profitti aggiuntivi. Credo che i broker tengano conto anche degli swap dei loro concorrenti per evitare che i trader facciano eccessivo trading sugli swap. I diagrammi seguenti spiegano questo concetto. Forse potete migliorare questo diagramma.

    Ecco lo schema generale di questo metodo:

    Diagramma del metodo

    Anche questo schema non copre tutti i dati su come aprire una posizione sintetica. Questo diagramma mostra solo come determinare la direzione di trading per una componente specifica di una posizione sintetica, che deve necessariamente essere rappresentata da uno degli strumenti disponibili del broker selezionato. 

    Ora dobbiamo stabilire come calcolare i volumi di queste posizioni. Logicamente, i volumi dovrebbero essere calcolati in base alla considerazione che la posizione dovrebbe essere equivalente a una posizione di 1 lotto per lo strumento risultante, a cui si riduce la variante selezionata dell'equazione. Per il calcolo del volume sono necessari i seguenti valori:

    • ContrattoB - la dimensione del contratto della coppia a cui viene ridotta l'equazione (nella maggior parte dei casi è pari a 100.000 unità della valuta di base)
    • Contratto[1] - la dimensione del contratto della coppia per la quale si vuole determinare il lotto
    • A[1] - l'importo della valuta di base espresso in lotti della precedente coppia bilanciata (o la prima della catena)
    • B[1] - la quantità della valuta principale espressa in lotti della precedente coppia bilanciata (o la prima della catena)
    • A[2] - la quantità di valuta di base espressa in lotti della coppia corrente da bilanciare
    • B[2] - la quantità di valuta principale espressa in lotti della coppia corrente che viene bilanciata
    • C[1] - dimensione del contratto della precedente coppia bilanciata (o la prima della catena)
    • C[2] - dimensione del contratto della coppia correntemente bilanciata

    Notare che non è sempre possibile determinare il "ContrattoB", poiché lo strumento risultante dalla combinazione potrebbe non essere fornito dal broker. In questo caso il contratto può essere impostato in modo arbitrario, ad esempio pari alla costante di base "100000".

    In primo luogo, viene determinata la prima coppia della catena, che contiene la valuta di base dello strumento risultante nella posizione desiderata. Quindi, vengono ricercate altre coppie che compensano le valute extra che non sono incluse nell'equivalente risultante. Il bilanciamento termina quando la valuta principale si trova nella giusta posizione nella coppia corrente. Ho creato un diagramma per mostrare come si procede:

    Normalizzazione

    Ora implementiamo queste tecniche nel codice e analizziamo i risultati. Il primo prototipo sarà molto semplice, poiché il suo unico scopo è quello di valutare la correttezza delle idee. Spero che i diagrammi sopra riportati vi aiutino a comprendere tutti i dettagli dell'idea.


    Scrivere un'Utilità per Esaminare i Poligoni di Swap

    Ordinamento del Market Watch e preparazione dei dati:

    Per utilizzare questa tecnica, è necessario selezionare solo quelle coppie i cui nomi sono lunghi esattamente 6 caratteri e sono composti solo da lettere maiuscole. Credo che tutti i broker si attengano a questa regola di denominazione. Alcuni broker aggiungono prefissi o suffissi, che devono essere presi in considerazione quando si scrivono algoritmi per lavorare con i dati delle stringhe. Per memorizzare le informazioni sui simboli in un formato conveniente, ho creato due strutture (la seconda sarà utilizzata in seguito):

    struct Pair// required symbol information
       {
       string Name;// currency pair
       double SwapBuy;// buy swap
       double SwapSell;// sell swap
       double TickValue;// profit from 1 movement tick of a 1-lot position
       double TickSize;// tick size in the price
       double PointX;// point size in the price
       double ContractSize;// contract size in the base deposit currency
       double Margin;// margin for opening 1 lot
       };
    
    struct PairAdvanced : Pair// extended container
       {
       string Side;// in numerator or denominator
       double LotK;// lot coefficient
       double Lot;// lot
       };
    

    Alcuni campi non verranno utilizzati nell'ordinamento delle coppie. Per non produrre contenitori inutili, l'ho ampliata un po' in modo che la struttura potesse essere utilizzata anche per altri scopi. Avevo un prototipo di algoritmo simile, ma con capacità molto limitate: poteva considerare solo le coppie che si trovavano nella finestra principale del terminale. Ora tutto è più semplice. Ma cosa più importante, tutte le operazioni sono automatizzate nell'algoritmo. La funzione seguente è necessaria per impostare la dimensione dell'array con gli strumenti:

    Pair Pairs[];// data of currency pairs
    void SetSizePairsArray()// set size of the array of pairs
       {
       ArrayResize(Pairs,MaxSymbols);
       ArrayResize(BasicPairsLeft,MaxPairs*2); // since each pair has 2 currencies, there can be a maximum of 2 times more base currencies
       ArrayResize(BasicPairsRight,MaxPairs*2);// since each pair has 2 currencies, there can be a maximum of 2 times more base currencies
       }
    

    La prima riga imposta il numero massimo di coppie dalla finestra Market Watch che possiamo utilizzare. Le altre due righe impostano la dimensione degli array che verranno utilizzati. I restanti 2 array svolgono un ruolo ausiliario — consentono di dividere una coppia di valute in due parti (2 valute composte). Le variabili evidenziate in giallo sono i parametri di input dell'EA.

    • MaxSymbols - dimensione massima di memorizzazione delle coppie (ho implementato la specifica manuale)
    • MaxPairs - il numero massimo di coppie in entrambe le parti della formula che generiamo (le formule più lunghe di questo numero non saranno ricercate dall'Expert Advisor).

    Per verificare se uno strumento di trading soddisfa i criteri (segni di due valute diverse che possono essere potenzialmente presenti in altri strumenti), ho creato la seguente funzione predicativa:

    bool IsValid(string s)// checking the instrument validity (its name must consist of upper-case letters)
       {
       string Mask="abcdefghijklmnopqrstuvwxyz1234567890";// mask of unsupported characters (lowercase letters and numbers)
       for ( int i=0; i<StringLen(s); i++ )// reset symbols
          {
          for ( int j=0; j<StringLen(Mask); j++ )
             {
             if ( s[i] == Mask[j] ) return false;
             }
          }   
       return true;
       }
    

    Questa funzione non è l'unica condizione per i futuri controlli degli strumenti. Ma questa condizione non può essere scritta all'interno di un'espressione logica, quindi è più facile implementarla come predicativa. Passiamo ora alla funzione principale, che riempie l'array con i dati necessari:

    void FillPairsArray()// fill the array with required information about the instruments
       {
       int iterator=0;
       double correction;
       int TempSwapMode;
       
       for ( int i=0; i<ArraySize(Pairs); i++ )// reset symbols
          {
          Pairs[iterator].Name="";
          }   
       
       for ( int i=0; i<SymbolsTotal(false); i++ )// check symbols from the MarketWatch window
          {
          TempSwapMode=int(SymbolInfoInteger(Pairs[iterator].Name,SYMBOL_SWAP_MODE));
          if ( StringLen(SymbolName(i,false)) == 6+PrefixE+PostfixE && IsValid(SymbolName(i,false)) && SymbolInfoInteger(SymbolName(i,false),SYMBOL_TRADE_MODE) == SYMBOL_TRADE_MODE_FULL  
          && ( ( TempSwapMode  == 1 )  ||  ( ( TempSwapMode == 5 || TempSwapMode == 6 ) && CorrectedValue(Pairs[iterator].Name,correction) )) )
             {
             if ( iterator >= ArraySize(Pairs) ) break;
             Pairs[iterator].Name=SymbolName(i,false);
             Pairs[iterator].TickSize=SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_TRADE_TICK_SIZE);
             Pairs[iterator].PointX=SymbolInfoDouble(Pairs[iterator].Name, SYMBOL_POINT);
             Pairs[iterator].ContractSize=SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_TRADE_CONTRACT_SIZE);
             switch(TempSwapMode)
               {
                case  1:// in points
                  Pairs[iterator].SwapBuy=SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_SWAP_LONG)*Pairs[iterator].TickValue*(Pairs[iterator].PointX/Pairs[iterator].TickSize);
                  Pairs[iterator].SwapSell=SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_SWAP_SHORT)*Pairs[iterator].TickValue*(Pairs[iterator].PointX/Pairs[iterator].TickSize);              
                  break;
                case  5:// in percent
                  Pairs[iterator].SwapBuy=correction*SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_SWAP_LONG)*SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_BID)*Pairs[iterator].ContractSize/(360.0*100.0);
                  Pairs[iterator].SwapSell=correction*SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_SWAP_SHORT)*SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_BID)*Pairs[iterator].ContractSize/(360.0*100.0);              
                  break;
                case  6:// in percent
                  Pairs[iterator].SwapBuy=correction*SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_SWAP_LONG)*SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_BID)*Pairs[iterator].ContractSize/(360.0*100.0);
                  Pairs[iterator].SwapSell=correction*SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_SWAP_SHORT)*SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_BID)*Pairs[iterator].ContractSize/(360.0*100.0);              
                  break;              
               }     
             Pairs[iterator].Margin=SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_MARGIN_INITIAL);
             Pairs[iterator].TickValue=SymbolInfoDouble(Pairs[iterator].Name,SYMBOL_TRADE_TICK_VALUE);         
             iterator++;
             }
          }
       }
    

    Questa funzione prevede una semplice iterazione di tutti i simboli e il filtraggio tramite una condizione complessa e composta che verifica sia il rispetto del requisito della lunghezza del nome della stringa sia la possibilità di negoziare questo simbolo, oltre ad altri parametri, che riguardano quei simboli per i quali il metodo di calcolo dello swap differisce da quello più comunemente utilizzato "in punti". Nel blocco "switch" viene selezionato uno dei metodi di calcolo dello swap. Attualmente sono stati implementati due metodi: in punti e in percentuale. Un ordinamento corretto è importante soprattutto per evitare calcoli inutili. Inoltre, prestate attenzione alla funzione evidenziata in rosso. Quando la valuta principale (non quella di base) è rappresentata da una valuta che non corrisponde alla valuta di deposito, è necessario aggiungere un certo fattore di correzione per convertire lo swap nella valuta di deposito. Questa funzione calcola i valori relativi. Ecco il suo codice:

    bool CorrectedValue(string Pair0,double &rez)// adjustment factor to convert to deposit currency for the percentage swap calculation method
       {
       string OurValue=AccountInfoString(ACCOUNT_CURRENCY);// deposit currency
       string Half2Source=StringSubstr(Pair0,PrefixE+3,3);// lower currency of the pair to be adjusted
       if ( Half2Source == OurValue )
          {
          rez=1.0;
          return true;
          }
       
       for ( int i=0; i<SymbolsTotal(false); i++ )// check symbols from the MarketWatch window
          {
          if ( StringLen(SymbolName(i,false)) == 6+PrefixE+PostfixE && IsValid(SymbolName(i,false)) )//find the currency rate to convert to the account currency
             {
             string Half1=StringSubstr(SymbolName(i,false),PrefixE,3);
             string Half2=StringSubstr(SymbolName(i,false),PrefixE+3,3);
         
             if ( Half2 == OurValue && Half1 == Half2Source )
                {
                rez=SymbolInfoDouble(SymbolName(i,false),SYMBOL_BID);
                return true;
                }
             if ( Half1 == OurValue && Half2 == Half2Source )
                {
                rez=1.0/SymbolInfoDouble(SymbolName(i,false),SYMBOL_BID);
                return true;
                }            
             }
          } 
       return false;
       }
    

    Questa funzione funge da predicato e restituisce il valore del fattore di correzione alla variabile passata dall'esterno per riferimento. Il fattore di correzione è calcolato in base al tasso della valuta desiderata, che include la nostra valuta di deposito.

    Formule Generate Casualmente

    Supponiamo che l'array sia stato riempito con i dati necessari. Ora, dobbiamo in qualche modo iterare su questi simboli e provare a creare al volo tutte le possibili combinazioni di formule che possono essere create da queste coppie. Innanzitutto, è necessario decidere in quale forma verrà memorizzata la formula. La struttura che memorizza tutti gli elementi di questa formula deve essere molto semplice e chiara per gli utenti nel caso in cui sia necessario visualizzare i registri (e sicuramente ci sarà questa necessità, altrimenti sarà impossibile identificare gli errori).

    La nostra formula è un insieme di fattori sia a sinistra che a destra del segno "=". Il fattore può essere il tasso di cambio alla potenza di 1 o di -1 (che equivale a una frazione invertita o a un'unità riferita al tasso corrente dello strumento). Ho deciso di utilizzare la seguente struttura:

    struct EquationBasic // structure containing the basic formula
       {
       string LeftSide;// currency pairs participating in the formula on the left side of the "=" sign
       string LeftSideStructure;// structure of the left side of the formula
       string RightSide;// currency pairs participating in the right side of the formula
       string RightSideStructure;// structure of the right side of the formula
       };
    

    Tutti i dati saranno memorizzati in formato stringa. Per studiare la formula, queste stringhe saranno analizzate per estrarre tutte le informazioni necessarie. Inoltre, possono essere stampate quando necessario. Le formule generate verranno stampate nella forma seguente:

    Equazioni Casuali

    Per me personalmente, un registro di questo tipo è assolutamente chiaro e leggibile. Il carattere "^" viene utilizzato come separatore tra le coppie. La struttura della formula non richiede separatori, poiché è composta da singoli caratteri "u" e "d", che indicano il grado del moltiplicatore:

    1. "u" è il tasso di cambio
    2. "d" è 1/tasso di cambio

    Come si può vedere, le formule risultanti hanno una lunghezza e una dimensione variabile da entrambi i lati dell'equazione, ma questa dimensione ha i suoi limiti. Questo approccio offre la massima variabilità delle formule generate. Questo, a sua volta, garantisce la massima qualità possibile delle varianti presenti nelle condizioni di trading del broker selezionato. I broker offrono condizioni completamente diverse. Per garantire il successo della generazione di queste formule, abbiamo bisogno di funzioni casuali aggiuntive in grado di generare numeri nell'intervallo richiesto. A tale scopo, creiamo la funzionalità relativa utilizzando le capacità della funzione integrata MathRand:

    int GenerateRandomQuantityLeftSide()// generate a random number of pairs on the left side of the formula
       {
       int RandomQuantityLeftSide=1+int(MathFloor((double(MathRand())/32767.0)*(MaxPairs-1)));
       if ( RandomQuantityLeftSide >= MaxPairs ) return MaxPairs-1;
       return RandomQuantityLeftSide;
       }
    
    int GenerateRandomQuantityRightSide(int LeftLenght)// generate a random number of pairs on the right side of the formula (taking into account the number of pairs on the left side)
       {
       int RandomQuantityRightSide=1+int(MathFloor((double(MathRand())/32767.0)*(MaxPairs-LeftLenght)));
       if ( RandomQuantityRightSide < 2 && LeftLenght == 1 ) return 2;// there must be at least 2 pairs in one of the sides, otherwise it will be equivalent to opening two opposite positions
       if ( RandomQuantityRightSide > (MaxPairs-LeftLenght) ) return (MaxPairs-LeftLenght);
       return RandomQuantityRightSide;
       }
       
    int GenerateRandomIndex()// generate a random index of a symbol from the MarketWatch window
       {
       int RandomIndex=0;
         
       while(true)
          {
          RandomIndex=int(MathFloor((double(MathRand())/32767.0) * double(MaxSymbols)) );
          if ( RandomIndex >= MaxSymbols ) RandomIndex=MaxSymbols-1;
          if ( StringLen(Pairs[RandomIndex].Name) > 0 ) return RandomIndex;
          }
    
       return RandomIndex;
       }
    

    Tutte e tre le funzioni saranno necessarie in una certa fase. Ora possiamo scrivere la funzione che genererà queste formule. Il codice diventerà sempre più complesso, ma non utilizzerò un approccio orientato agli oggetti, poiché il compito non è standard. Ho deciso di utilizzare un approccio procedurale. Le procedure risultanti sono piuttosto grandi e ingombranti, ma non ci sono funzionalità aggiuntive e ogni funzione implementa un compito specifico senza utilizzare funzioni intermedie, per evitare la duplicazione del codice. Altrimenti, il codice sarebbe ancora più difficile da comprendere a causa delle specificità del compito. La funzione sarà come segue:

    EquationBasic GenerateBasicEquation()// generate both parts of the random equation
       {
       int RandomQuantityLeft=GenerateRandomQuantityLeftSide();
       int RandomQuantityRight=GenerateRandomQuantityRightSide(RandomQuantityLeft);
       string TempLeft="";
       string TempRight="";
       string TempLeftStructure="";
       string TempRightStructure="";   
       
       for ( int i=0; i<RandomQuantityLeft; i++ )
          {
          int RandomIndex=GenerateRandomIndex();
          if ( i == 0 && RandomQuantityLeft > 1 ) TempLeft+=Pairs[RandomIndex].Name+"^";
          if ( i != 0 && (RandomQuantityLeft-i) > 1 ) TempLeft+=Pairs[RandomIndex].Name+"^";
          if ( i == RandomQuantityLeft-1 ) TempLeft+=Pairs[RandomIndex].Name;
          
          if ( double(MathRand())/32767.0 > 0.5 ) TempLeftStructure+="u";
          else TempLeftStructure+="d";
          }
          
       for ( int i=RandomQuantityLeft; i<RandomQuantityLeft+RandomQuantityRight; i++ )
          {
          int RandomIndex=GenerateRandomIndex();
          
          if ( i == RandomQuantityLeft && RandomQuantityRight > 1 ) TempRight+=Pairs[RandomIndex].Name+"^";
          if ( i != RandomQuantityLeft && (RandomQuantityLeft+RandomQuantityRight-i) > 1 ) TempRight+=Pairs[RandomIndex].Name+"^";
          if ( i == RandomQuantityLeft+RandomQuantityRight-1 ) TempRight+=Pairs[RandomIndex].Name;
          
          if ( double(MathRand())/32767.0 > 0.5 ) TempRightStructure+="u";
          else TempRightStructure+="d";
          }
          
       EquationBasic result;
       result.LeftSide=TempLeft;
       result.LeftSideStructure=TempLeftStructure;
       result.RightSide=TempRight;
       result.RightSideStructure=TempRightStructure;
       
       return result;
       }
    

    Come si può notare, tutte e tre le funzioni considerate in precedenza vengono qui utilizzate per generare una formula casuale. Queste funzioni non sono utilizzate in nessun altro punto del codice. Non appena la formula è pronta, possiamo procedere a un'analisi passo-passo di questa formula. Tutte le formule errate saranno scartate dal successivo complesso ed estremamente importante filtro. Prima di tutto, verifica l'uguaglianza. Se le parti non sono uguali, la formula non è corretta. Tutte le formule conformi passano alla fase di analisi successiva.

    Formula di Bilanciamento

    Questa fase copre diversi criteri di analisi contemporaneamente:

    1. Conta tutti i fattori aggiuntivi nel numeratore e nel denominatore e li rimuove.
    2. Verifica la disponibilità di 1 valuta al numeratore e di 1 valuta al denominatore
    3. Verifica la corrispondenza delle frazioni risultanti a destra e a sinistra
    4. Se il lato destro è il reciproco del lato sinistro, basta invertire la struttura destra della formula (che è simile all'elevazione a potenza "-1")
    5. Se tutte le fasi vengono completate con successo, il risultato viene scritto in una nuova variabile.

    Ecco come appaiono questi passaggi nel codice:

    BasicValue BasicPairsLeft[];// array of base pairs to the left
    BasicValue BasicPairsRight[];// array of base pairs to the right
    bool bBalanced(EquationBasic &CheckedPair,EquationCorrected &r)// if the current formula is balanced (if yes, return the corrected version to the "r" variable)
       {
       bool bEnd=false;
       string SubPair;// the full name of the currency pair
       string Half1;// the first currency of the pair
       string Half2;// the second currency of the pair
       string SubSide;// the currency pair in the numerator or denominator
       string Divider;// separator
       int ReadStartIterator=0;// reading start index
       int quantityiterator=0;// quantity
       bool bNew;
       BasicValue b0;
       
       for ( int i=0; i<ArraySize(BasicPairsLeft); i++ )//reset the array of base pairs 
          {
          BasicPairsLeft[i].Value = "";
          BasicPairsLeft[i].Quantity = 0;
          }
       for ( int i=0; i<ArraySize(BasicPairsRight); i++ )// resetting the array of base pairs
          {
          BasicPairsRight[i].Value = "";
          BasicPairsRight[i].Quantity = 0;
          }
       //// Calculate balance values for the left side
       quantityiterator=0;
       ReadStartIterator=0;
       for ( int i=ReadStartIterator; i<StringLen(CheckedPair.LeftSide); i++ )// extract base currencies from the left side of the equation
          {
          Divider=StringSubstr(CheckedPair.LeftSide,i,1);
          if ( Divider == "^" || i == StringLen(CheckedPair.LeftSide) - 1 )
             {
             SubPair=StringSubstr(CheckedPair.LeftSide,ReadStartIterator+PrefixE,6);
             SubSide=StringSubstr(CheckedPair.LeftSideStructure,quantityiterator,1);
             Half1=StringSubstr(CheckedPair.LeftSide,ReadStartIterator+PrefixE,3);
             Half2=StringSubstr(CheckedPair.LeftSide,ReadStartIterator+PrefixE+3,3);         
    
             bNew=true;
             for ( int j=0; j<ArraySize(BasicPairsLeft); j++ )// if the currency is not found in the list, add it
                {
                if ( BasicPairsLeft[j].Value == Half1 )
                   {
                   if ( SubSide == "u" ) BasicPairsLeft[j].Quantity++;
                   if ( SubSide == "d" ) BasicPairsLeft[j].Quantity--;
                   bNew = false;
                   break;
                   }
                }
             if ( bNew )
                {
                for ( int j=0; j<ArraySize(BasicPairsLeft); j++ )// if the currency is not found in the list, add it
                   {
                   if ( StringLen(BasicPairsLeft[j].Value) == 0 )
                      {
                      if ( SubSide == "u" ) BasicPairsLeft[j].Quantity++;
                      if ( SubSide == "d" ) BasicPairsLeft[j].Quantity--;                  
                      BasicPairsLeft[j].Value=Half1;
                      break;
                      }
                   }            
                }
                
             bNew=true;
             for ( int j=0; j<ArraySize(BasicPairsLeft); j++ )// if the currency is not found in the list, add it
                {
                if ( BasicPairsLeft[j].Value == Half2 )
                   {
                   if ( SubSide == "u" ) BasicPairsLeft[j].Quantity--;
                   if ( SubSide == "d" ) BasicPairsLeft[j].Quantity++;
                   bNew = false;
                   break;
                   }
                }
             if ( bNew )
                {
                for ( int j=0; j<ArraySize(BasicPairsLeft); j++ )// if the currency is not found in the list, add it
                   {
                   if (  StringLen(BasicPairsLeft[j].Value) == 0 )
                      {
                      if ( SubSide == "u" ) BasicPairsLeft[j].Quantity--;
                      if ( SubSide == "d" ) BasicPairsLeft[j].Quantity++;                  
                      BasicPairsLeft[j].Value=Half2;
                      break;
                      }
                   }            
                }            
                      
             ReadStartIterator=i+1;
             quantityiterator++;
             }
          }
       /// end of left-side balance calculation
          
       //// Calculate balance values for the right side
       quantityiterator=0;
       ReadStartIterator=0;
       for ( int i=ReadStartIterator; i<StringLen(CheckedPair.RightSide); i++ )// extract base currencies from the right side of the equation
          {
          Divider=StringSubstr(CheckedPair.RightSide,i,1);
    
          if ( Divider == "^"|| i == StringLen(CheckedPair.RightSide) - 1 )
             {
             SubPair=StringSubstr(CheckedPair.RightSide,ReadStartIterator+PrefixE,6);
             SubSide=StringSubstr(CheckedPair.RightSideStructure,quantityiterator,1);
             Half1=StringSubstr(CheckedPair.RightSide,ReadStartIterator+PrefixE,3);
             Half2=StringSubstr(CheckedPair.RightSide,ReadStartIterator+PrefixE+3,3);         
    
             bNew=true;
             for ( int j=0; j<ArraySize(BasicPairsRight); j++ )// if the currency is not found in the list, add it
                {
                if ( BasicPairsRight[j].Value == Half1 )
                   {
                   if ( SubSide == "u" ) BasicPairsRight[j].Quantity++;
                   if ( SubSide == "d" ) BasicPairsRight[j].Quantity--;
                   bNew = false;
                   break;
                   }
                }
             if ( bNew )
                {
                for ( int j=0; j<ArraySize(BasicPairsRight); j++ )// if the currency is not found in the list, add it
                   {
                   if (  StringLen(BasicPairsRight[j].Value) == 0 )
                      {
                      if ( SubSide == "u" ) BasicPairsRight[j].Quantity++;
                      if ( SubSide == "d" ) BasicPairsRight[j].Quantity--;                  
                      BasicPairsRight[j].Value=Half1;
                      break;
                      }
                   }            
                }
                
             bNew=true;
             for ( int j=0; j<ArraySize(BasicPairsRight); j++ )// if the currency is not found in the list, add it
                {
                if ( BasicPairsRight[j].Value == Half2 )
                   {
                   if ( SubSide == "u" ) BasicPairsRight[j].Quantity--;
                   if ( SubSide == "d" ) BasicPairsRight[j].Quantity++;
                   bNew = false;
                   break;
                   }
                }
             if ( bNew )
                {
                for ( int j=0; j<ArraySize(BasicPairsRight); j++ )// if the currency is not found in the list, add it
                   {
                   if (  StringLen(BasicPairsRight[j].Value) == 0 )
                      {
                      if ( SubSide == "u" ) BasicPairsRight[j].Quantity--;
                      if ( SubSide == "d" ) BasicPairsRight[j].Quantity++;                  
                      BasicPairsRight[j].Value=Half2;
                      break;
                      }
                   }            
                }            
                      
             ReadStartIterator=i+1;
             quantityiterator++;
             }
          }
       /// end of right-side balance calculation
     
       /// calculate the number of lower and upper currencies based on the received data from the previous block
       int LeftUpTotal=0;// the number of upper elements in the left part
       int LeftDownTotal=0;// the number of lower elements in the left part
       int RightUpTotal=0;// the number of upper elements in the right part
       int RightDownTotal=0;// the number of lower elements in the right part
       
       
       string LastUpLeft;
       string LastDownLeft;
       string LastUpRight;
       string LastDownRight;   
       for ( int i=0; i<ArraySize(BasicPairsLeft); i++ )
          {
          if ( BasicPairsLeft[i].Quantity > 0 && StringLen(BasicPairsLeft[i].Value) > 0 ) LeftUpTotal+=BasicPairsLeft[i].Quantity;
          if ( BasicPairsLeft[i].Quantity < 0 && StringLen(BasicPairsLeft[i].Value) > 0 ) LeftDownTotal-=BasicPairsLeft[i].Quantity;
          }
       for ( int i=0; i<ArraySize(BasicPairsRight); i++ )
          {
          if ( BasicPairsRight[i].Quantity > 0 && StringLen(BasicPairsRight[i].Value) > 0 ) RightUpTotal+=BasicPairsRight[i].Quantity;
          if ( BasicPairsRight[i].Quantity < 0 && StringLen(BasicPairsRight[i].Value) > 0 ) RightDownTotal-=BasicPairsRight[i].Quantity;
          }      
       ///
       /// check if both sides are equal
       if ( LeftUpTotal == 1 && LeftDownTotal == 1 && RightUpTotal == 1 && RightDownTotal == 1 )// there must be one pair in the upper and in the lower part of both sides of the equality, otherwise the formula is invalid
          {
          for ( int i=0; i<ArraySize(BasicPairsLeft); i++ )
             {
             if ( BasicPairsLeft[i].Quantity == 1 && StringLen(BasicPairsLeft[i].Value) > 0 ) LastUpLeft=BasicPairsLeft[i].Value;
             if ( BasicPairsLeft[i].Quantity == -1 && StringLen(BasicPairsLeft[i].Value) > 0 ) LastDownLeft=BasicPairsLeft[i].Value;
             }
          for ( int i=0; i<ArraySize(BasicPairsRight); i++ )
             {
             if ( BasicPairsRight[i].Quantity == 1 && StringLen(BasicPairsRight[i].Value) > 0 ) LastUpRight=BasicPairsRight[i].Value;
             if ( BasicPairsRight[i].Quantity == -1 && StringLen(BasicPairsRight[i].Value) > 0 ) LastDownRight=BasicPairsRight[i].Value;
             }      
          }
       else return false;
       if ( (LastUpLeft == LastUpRight && LastDownLeft == LastDownRight) || (LastUpLeft == LastDownRight && LastDownLeft == LastUpRight) )
          {
          if ( LastUpLeft == LastDownRight && LastDownLeft == LastUpRight )// If the formula is cross-equivalent, then invert the structure of the right part of the equation (it is the same as raising to the power of -1)
             {
             string NewStructure;// the new structure that will be built from the previous one
             for ( int i=0; i<StringLen(CheckedPair.RightSideStructure); i++ )
                {
                if ( CheckedPair.RightSideStructure[i] == 'u' ) NewStructure+="d";
                if ( CheckedPair.RightSideStructure[i] == 'd' ) NewStructure+="u";
                }
             CheckedPair.RightSideStructure=NewStructure;
             }      
          }
       else return false;// if the resulting fractions on both sides are not equivalent, then the formula is invalid
       if ( LastUpLeft == LastDownLeft ) return false;// if result in one, then the formula is invalid
    
      /// Now it is necessary to write all the above into a corrected and more convenient structure
       string TempResult=CorrectedResultInstrument(LastUpLeft+LastDownLeft,r.IsResultInstrument);
       if ( r.IsResultInstrument && LastUpLeft+LastDownLeft != TempResult ) 
          {
          string NewStructure="";// the new structure that will be built from the previous one
          for ( int i=0; i<StringLen(CheckedPair.RightSideStructure); i++ )
             {
             if ( CheckedPair.RightSideStructure[i] == 'u' ) NewStructure+="d";
             if ( CheckedPair.RightSideStructure[i] == 'd' ) NewStructure+="u";
             }
          CheckedPair.RightSideStructure=NewStructure;
          NewStructure="";// the new structure that will be built from the previous one
          for ( int i=0; i<StringLen(CheckedPair.LeftSideStructure); i++ )
             {
             if ( CheckedPair.LeftSideStructure[i] == 'u' ) NewStructure+="d";
             if ( CheckedPair.LeftSideStructure[i] == 'd' ) NewStructure+="u";
             }
          CheckedPair.LeftSideStructure=NewStructure;      
            
          r.ResultInstrument=LastDownLeft+LastUpLeft;
          r.UpPair=LastDownLeft;
          r.DownPair=LastUpLeft;      
          }
       else
          {
          r.ResultInstrument=LastUpLeft+LastDownLeft;
          r.UpPair=LastUpLeft;
          r.DownPair=LastDownLeft;
          }
    
       r.LeftSide=CheckedPair.LeftSide;
       r.RightSide=CheckedPair.RightSide;
       r.LeftSideStructure=CheckedPair.LeftSideStructure;
       r.RightSideStructure=CheckedPair.RightSideStructure;
       ///   
        
       /// if code has reached this point, it is considered that we have found the formula meeting the criteria, and the next step is normalization
          
       return true;
       }
    

    La funzione evidenziata in verde è necessaria per poter determinare se l'elenco dei simboli contiene quello a cui è stata ridotta la formula. Potrebbe risultare che la formula sia stata ridotta, ad esempio, non a "USDJPY", ma a "JPYUSD". Un tale simbolo ovviamente non esiste, anche se può essere creato. Ma il nostro compito è quello di modificare la formula in modo che produca uno strumento di trading corretto. In questo caso, entrambe le parti della formula devono essere elevate alla potenza di -1, il che equivale a invertire la struttura della formula (cambiare "d" con "u" e viceversa). Se nella finestra del Market Watch non è presente un simbolo di questo tipo, lasciatelo così com'è:

    string CorrectedResultInstrument(string instrument, bool &bResult)// if any equivalent symbol corresponds to the generated formula, return this symbol (or leave as is)
       {   
       string Half1="";
       string Half2="";   
       string Half1input=StringSubstr(instrument,0,3);//input upper currency
       string Half2input=StringSubstr(instrument,3,3);//input lower currency
       bResult=false;
       for ( int j=0; j<ArraySize(Pairs); j++ )
          {
          Half1=StringSubstr(Pairs[j].Name,PrefixE,3);
          Half2=StringSubstr(Pairs[j].Name,PrefixE+3,3);
          if ( (Half1==Half1input && Half2==Half2input) || (Half1==Half2input && Half2==Half1input) )// direct match or crossed match
             {
             bResult=true;
             return Pairs[j].Name;
             }
          }
       
       return instrument;
       }
    

    Ho preparato la seguente struttura per memorizzare le formule che vengono passate attraverso il filtro. La struttura presenta alcuni campi della struttura precedente e alcuni campi nuovi:

    struct EquationCorrected // corrected structure of the basic formula
       {
       string LeftSide;// currency pairs participating in the formula on the left side of the "=" sign
       string LeftSideStructure;// structure of the left side of the formula
       string RightSide;// currency pairs participating in the right side of the formula
       string RightSideStructure;// structure of the right side of the formula
       
       string ResultInstrument;// the resulting instrument to which both parts of the formula come after transformation
       bool IsResultInstrument;// has the suitable equivalent symbol been found
       string UpPair;// the upper currency of the resulting instrument
       string DownPair;// the lower currency of the resulting instrument
       };
    

    Formule di Normalizzazione

    Questa procedura è il passo successivo al filtraggio dei risultati. Consiste nelle seguenti operazioni sequenziali, che si susseguono una dopo l'altra:

    1. In base al simbolo risultante ottenuto da entrambi i lati dell'equazione, selezionare una coppia iniziale dall'elenco per entrambi i lati dell'uguaglianza.
    2. Entrambe le coppie, in base al loro potere nell'equazione, devono fornire la valuta di base nel numeratore della frazione
    3. Se viene trovata una coppia di questo tipo e la valuta più bassa della frazione non contiene la valuta principale dello strumento risultante, procedere oltre
    4. Proseguiamo in modo che la valuta superiore della coppia successiva compensi quella inferiore della precedente
    5. Ripetere questi passaggi fino a trovare la coppia risultante desiderata
    6. Una volta trovata la coppia risultante, tutti i componenti inutilizzati della formula vengono scartati (poiché il loro prodotto è uno)
    7. Parallelamente a questo processo, i "fattori del lotto" vengono calcolati in modo sequenziale da coppia a coppia (indicano quale lotto è necessario per aprire le posizioni per le coppie specifiche, per garantire il nostro strumento risultante).
    8. Il risultato viene scritto in una nuova variabile, che verrà utilizzata nella fase di analisi successiva.

    Il codice della funzione è il seguente:

    bool bNormalized(EquationCorrected &d,EquationNormalized &v)// formula normalization attempt (the normalized formula is returned in "v" )
       {
       double PreviousContract;// previous contract
       bool bWasPairs;// if any pairs have been found
       double BaseContract;// contract of the pair to which the equation is reduced
       double PreviousLotK=0.0;// previous LotK
       double LotK;// current LotK
       string PreviousSubSide;// in numerator or denominator (previous factor)
       string PreviousPair;// previous pair
       string PreviousHalf1;// upper currency of the previous pair
       string PreviousHalf2;// lower currency of the previous pair
       string SubPair;// the full name of the currency pair
       string Half1;// the first currency of the pair
       string Half2;// the second currency of the pair
       string SubSide;// the currency pair in the numerator or denominator
       string Divider;// separator
       int ReadStartIterator=0;// reading start index
       int quantityiterator=0;// quantity
       int tryiterator=0;// the number of balancing attempts
       int quantityleft=0;// the number of pairs on the left after normalization
       int quantityright=0;//the number of pairs on the right after normalization
       bool bNew;
       BasicValue b0;
       
       for ( int i=0; i<ArraySize(BasicPairsLeft); i++ )//reset the array of base pairs 
          {
          BasicPairsLeft[i].Value = "";
          BasicPairsLeft[i].Quantity = 0;
          }
       for ( int i=0; i<ArraySize(BasicPairsRight); i++ )// resetting the array of base pairs
          {
          BasicPairsRight[i].Value = "";
          BasicPairsRight[i].Quantity = 0;
          }
          
       if ( d.IsResultInstrument ) BaseContract=SymbolInfoDouble(d.ResultInstrument, SYMBOL_TRADE_CONTRACT_SIZE);// define the contract of the equivalent pair based on the instrument
       else BaseContract=100000.0;
          
       //// Calculate the number of pairs for the left side
       tryiterator=0;
       ReadStartIterator=0;
       for ( int i=ReadStartIterator; i<StringLen(d.LeftSide); i++ )// extract base currencies from the left side of the equation
          {
          Divider=StringSubstr(d.LeftSide,i,1);
          if ( Divider == "^" )
             {
             ReadStartIterator=i+1;
             tryiterator++;
             }
             
          if ( i == StringLen(d.LeftSide) - 1 )
             {
             ReadStartIterator=i+1;
             tryiterator++;
             }         
          }
       /// end of quantity calculation for the left part
          
       ArrayResize(v.PairLeft,tryiterator);
       /// calculate the lot coefficients for the left side
       
       bool bBalanced=false;// is the formula balanced
       bool bUsed[];
       ArrayResize(bUsed,tryiterator);
       ArrayFill(bUsed,0,tryiterator,false);
       int balancediterator=0;
       PreviousHalf1="";
       PreviousHalf2="";
       PreviousLotK=0.0;
       PreviousSubSide="";
       PreviousPair="";
       PreviousContract=0.0;
       bWasPairs=false;// have there been pairs
       for ( int k=0; k<tryiterator; k++ )// try to normalize the left side
          {
          if( !bBalanced )
             {
             quantityiterator=0;
             ReadStartIterator=0;
             for ( int i=ReadStartIterator; i<StringLen(d.LeftSide); i++ )// extract base currencies from the left side of the equation
                {
                Divider=StringSubstr(d.LeftSide,i,1);
                if ( Divider == "^" || i == StringLen(d.LeftSide) - 1 )
                   {
                   SubPair=StringSubstr(d.LeftSide,ReadStartIterator+PrefixE,6);
                   SubSide=StringSubstr(d.LeftSideStructure,quantityiterator,1);
                   Half1=StringSubstr(d.LeftSide,ReadStartIterator+PrefixE,3);
                   Half2=StringSubstr(d.LeftSide,ReadStartIterator+PrefixE+3,3);         
                
                   if ( ! bUsed[quantityiterator] && (( PreviousHalf1 == "" && ((Half1 == d.UpPair && SubSide == "u") || (Half2 == d.UpPair && SubSide == "d")) ) // if it is the first pair in the list
                   || ( (( PreviousHalf2 == Half1 && PreviousSubSide == "u" ) || ( PreviousHalf1 == Half1 && PreviousSubSide == "d" )) && SubSide == "u" ) // if the current pair is in the numerator
                   || ( (( PreviousHalf2 == Half2 && PreviousSubSide == "u" ) || ( PreviousHalf1 == Half2 && PreviousSubSide == "d" )) && SubSide == "d" )) )// if the current pair is in the denominator
                      {// find the entry point(pair) of the chain
                      if( PreviousHalf1 == "" )// define the lot coefficient of the first pair
                         {
                         if ( SubSide == "u" )
                            {
                            LotK=BaseContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);// (1 start)
                            v.PairLeft[balancediterator].LotK=LotK;
                            PreviousLotK=LotK;
                            bWasPairs=true;
                            PreviousContract=SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);
                            }
                         if ( SubSide == "d" )
                            {
                            double Pt=SymbolInfoDouble(SubPair,SYMBOL_BID);
                            if ( Pt == 0.0 ) return false; 
                            LotK=(BaseContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE))/Pt;// (2 start)
                            v.PairLeft[balancediterator].LotK=LotK;
                            PreviousLotK=LotK;
                            bWasPairs=true;
                            PreviousContract=SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);
                            }                     
                         }
                      else
                         {
                         if( PreviousSubSide == "u" )// define the lot coefficient of further pairs
                            {
                            if ( SubSide == "u" )
                               {
                               double Pp=SymbolInfoDouble(PreviousPair,SYMBOL_BID);
                               if ( Pp == 0.0 ) return false;
                               if ( PreviousContract <= 0.0 ) return false; 
                               LotK=PreviousLotK*Pp*(PreviousContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE));// ( 1 )
                               v.PairLeft[balancediterator].LotK=LotK;
                               PreviousLotK=LotK;
                               PreviousContract=SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);
                               }
                            if ( SubSide == "d" )
                               {
                               double Pt=SymbolInfoDouble(SubPair,SYMBOL_BID);
                               double Pp=SymbolInfoDouble(PreviousPair,SYMBOL_BID);
                               if ( Pt == 0.0 ) return false; 
                               if ( Pp == 0.0 ) return false;
                               if ( PreviousContract <= 0.0 ) return false;                           
                               LotK=PreviousLotK*(Pp/Pt)*(PreviousContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE));// ( 2 )
                               v.PairLeft[balancediterator].LotK=LotK;
                               PreviousLotK=LotK;
                               PreviousContract=SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);
                               }                     
                            }
                         if( PreviousSubSide == "d" )// define the lot coefficient of further pairs
                            {
                            if ( SubSide == "u" )
                               {
                               if ( PreviousContract <= 0.0 ) return false;
                               LotK=PreviousLotK*(PreviousContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE));// ( 3 )
                               v.PairLeft[balancediterator].LotK=LotK;
                               PreviousLotK=LotK;
                               PreviousContract=SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);
                               }
                            if ( SubSide == "d" )
                               {
                               double Pt=SymbolInfoDouble(SubPair,SYMBOL_BID);
                               if ( Pt == 0.0 ) return false;
                               if ( PreviousContract <= 0.0 ) return false;
                               LotK=(PreviousLotK/Pt)*(PreviousContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE));// ( 4 )
                               v.PairLeft[balancediterator].LotK=LotK;
                               PreviousLotK=LotK;
                               PreviousContract=SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);
                               }                     
                            }                  
                         }
                                       
                      bNew=true;
                      for ( int j=0; j<ArraySize(BasicPairsLeft); j++ )// if the currency is not found in the list, add it
                         {
                         if ( BasicPairsLeft[j].Value == Half1 )
                            {
                            if ( SubSide == "u" ) BasicPairsLeft[j].Quantity++;
                            if ( SubSide == "d" ) BasicPairsLeft[j].Quantity--;
                            bNew = false;
                            break;
                            }
                         }
                      if ( bNew )
                         {
                         for ( int j=0; j<ArraySize(BasicPairsLeft); j++ )// if the currency is not found in the list, add it
                            {
                            if ( StringLen(BasicPairsLeft[j].Value) == 0 )
                               {
                               if ( SubSide == "u" ) BasicPairsLeft[j].Quantity++;
                               if ( SubSide == "d" ) BasicPairsLeft[j].Quantity--;                  
                               BasicPairsLeft[j].Value=Half1;
                               break;
                               }
                            }            
                         }
                
                      bNew=true;
                      for ( int j=0; j<ArraySize(BasicPairsLeft); j++ )// if the currency is not found in the list, add it
                         {
                         if ( BasicPairsLeft[j].Value == Half2 )
                            {
                            if ( SubSide == "u" ) BasicPairsLeft[j].Quantity--;
                            if ( SubSide == "d" ) BasicPairsLeft[j].Quantity++;
                            bNew = false;
                            break;
                            }
                         }
                      if ( bNew )
                         {
                         for ( int j=0; j<ArraySize(BasicPairsLeft); j++ )// if the currency is not found in the list, add it
                            {
                            if (  StringLen(BasicPairsLeft[j].Value) == 0 )
                               {
                               if ( SubSide == "u" ) BasicPairsLeft[j].Quantity--;
                               if ( SubSide == "d" ) BasicPairsLeft[j].Quantity++;                  
                               BasicPairsLeft[j].Value=Half2;
                               break;
                               }
                            }            
                         }
                      
                      v.PairLeft[balancediterator].Name=SubPair;
                      v.PairLeft[balancediterator].Side=SubSide;
                      v.PairLeft[balancediterator].ContractSize=SymbolInfoDouble(v.PairLeft[balancediterator].Name, SYMBOL_TRADE_CONTRACT_SIZE);
                   
    
                      balancediterator++;                  
                      PreviousHalf1=Half1;
                      PreviousHalf2=Half2;
                      PreviousSubSide=SubSide;
                      PreviousPair=SubPair;
                      
                      quantityleft++;
                      if ( SubSide == "u" && Half2 == d.DownPair )// if the fraction is not inverted
                         {
                         bBalanced=true;// if the missing part is in the denominator, then we have balanced the formula
                         break;// since the formula is balanced, we don't need the rest
                         }
                      if ( SubSide == "d" && Half1 == d.DownPair )// if the fraction is inverted
                         {
                         bBalanced=true;// if the missing part is in the numerator, then we have balanced the formula
                         break;// since the formula is balanced, we don't need the rest
                         }
                      
                      int LeftUpTotal=0;// the number of upper elements in the left part
                      int LeftDownTotal=0;// the number of lower elements in the left part
                      string LastUpLeft;
                      string LastDownLeft;
                      for ( int z=0; z<ArraySize(BasicPairsLeft); z++ )
                         {
                         if ( BasicPairsLeft[z].Quantity > 0 && StringLen(BasicPairsLeft[z].Value) > 0 ) LeftUpTotal+=BasicPairsLeft[z].Quantity;
                         if ( BasicPairsLeft[z].Quantity < 0 && StringLen(BasicPairsLeft[z].Value) > 0 ) LeftDownTotal-=BasicPairsLeft[z].Quantity;
                         }
                      if ( bWasPairs && LeftUpTotal == 0 && LeftDownTotal == 0 ) return false;                                           
                      }
    
                   ReadStartIterator=i+1;
                   bUsed[quantityiterator]=true;
                   quantityiterator++;
                   }
                }
             }
             else break;
          }
       /// end of coefficient calculation for the left part
         
       if ( !bBalanced ) return false;// if the left side is not balanced, then there is no point in balancing the right side
         
       //// Calculate the number of pairs for the right side
       tryiterator=0;
       ReadStartIterator=0;
       for ( int i=ReadStartIterator; i<StringLen(d.RightSide); i++ )// extract base currencies from the right side of the equation
          {
          Divider=StringSubstr(d.RightSide,i,1);
          if ( Divider == "^" )
             {
             ReadStartIterator=i+1;
             tryiterator++;
             }
             
          if ( i == StringLen(d.RightSide) - 1 )
             {
             ReadStartIterator=i+1;
             tryiterator++;
             }         
          }   
       ArrayResize(v.PairRight,tryiterator);
       /// end of calculation of the number of pairs for the right side
         
       bBalanced=false;// is the formula balanced
       ArrayResize(bUsed,tryiterator);
       ArrayFill(bUsed,0,tryiterator,false);
       balancediterator=0;
       PreviousHalf1="";
       PreviousHalf2="";
       PreviousLotK=0.0;
       PreviousSubSide="";
       PreviousPair="";
       PreviousContract=0.0;
       bWasPairs=false;
       for ( int k=0; k<tryiterator; k++ )// try to normalize the right side
          {
          if ( !bBalanced )
             {
             quantityiterator=0;
             ReadStartIterator=0;
             for ( int i=ReadStartIterator; i<StringLen(d.RightSide); i++ )// extract base currencies from the right side of the equation
                {
                Divider=StringSubstr(d.RightSide,i,1);
                if ( Divider == "^" || i == StringLen(d.RightSide) - 1 )
                   {
                   SubPair=StringSubstr(d.RightSide,ReadStartIterator+PrefixE,6);
                   SubSide=StringSubstr(d.RightSideStructure,quantityiterator,1);
                   Half1=StringSubstr(d.RightSide,ReadStartIterator+PrefixE,3);
                   Half2=StringSubstr(d.RightSide,ReadStartIterator+PrefixE+3,3);         
                
                   if ( ! bUsed[quantityiterator] && (( PreviousHalf1 == "" && ((Half1 == d.UpPair && SubSide == "u") || (Half2 == d.UpPair && SubSide == "d")) ) // if it is the first pair in the list
                   || ( (( PreviousHalf2 == Half1 && PreviousSubSide == "u" ) || ( PreviousHalf1 == Half1 && PreviousSubSide == "d" )) && SubSide == "u" ) // if the current pair is in the numerator
                   || ( (( PreviousHalf2 == Half2 && PreviousSubSide == "u" ) || ( PreviousHalf1 == Half2 && PreviousSubSide == "d" )) && SubSide == "d" )) )// if the current pair is in the denominator
                      {// find the entry point(pair) of the chain
                      if( PreviousHalf1 == "" )// define the lot coefficient of the first pair
                         {
                         if ( SubSide == "u" )
                            {
                            LotK=BaseContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);// (1 start)
                            v.PairRight[balancediterator].LotK=LotK;
                            PreviousLotK=LotK;
                            bWasPairs=true;
                            PreviousContract=SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);
                            }
                         if ( SubSide == "d" )
                            {
                            double Pt=SymbolInfoDouble(SubPair,SYMBOL_BID);
                            if ( Pt == 0.0 ) return false; 
                            LotK=(BaseContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE))/Pt;// (2 start)
                            v.PairRight[balancediterator].LotK=LotK;
                            PreviousLotK=LotK;
                            bWasPairs=true;
                            PreviousContract=SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);
                            }                     
                         }
                      else
                         {
                         if( PreviousSubSide == "u" )// define the lot coefficient of further pairs
                            {
                            if ( SubSide == "u" )
                               {
                               double Pp=SymbolInfoDouble(PreviousPair,SYMBOL_BID);
                               if ( Pp == 0.0 ) return false;
                               if ( PreviousContract <= 0.0 ) return false;                            
                               LotK=PreviousLotK*Pp*(PreviousContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE));// (1)
                               v.PairRight[balancediterator].LotK=LotK;
                               PreviousLotK=LotK;
                               PreviousContract=SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);
                               }
                            if ( SubSide == "d" )
                               {
                               double Pt=SymbolInfoDouble(SubPair,SYMBOL_BID);
                               double Pp=SymbolInfoDouble(PreviousPair,SYMBOL_BID);
                               if ( Pt == 0.0 ) return false; 
                               if ( Pp == 0.0 ) return false;
                               if ( PreviousContract <= 0.0 ) return false;                            
                               LotK=PreviousLotK*(Pp/Pt)*(PreviousContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE));// (2)
                               v.PairRight[balancediterator].LotK=LotK;
                               PreviousLotK=LotK;
                               PreviousContract=SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);
                               }                     
                            }
                         if( PreviousSubSide == "d" )// define the lot coefficient of further pairs
                            {
                            if ( SubSide == "u" )
                               {
                               if ( PreviousContract <= 0.0 ) return false;
                               LotK=PreviousLotK*(PreviousContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE));// (3)
                               v.PairRight[balancediterator].LotK=LotK;
                               PreviousLotK=LotK;
                               PreviousContract=SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);
                               }
                            if ( SubSide == "d" )
                               {
                               double Pt=SymbolInfoDouble(SubPair,SYMBOL_BID);
                               if ( Pt == 0.0 ) return false;
                               if ( PreviousContract <= 0.0 ) return false; 
                               LotK=(PreviousLotK/Pt)*(PreviousContract/SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE));// (4)
                               v.PairRight[balancediterator].LotK=LotK;
                               PreviousLotK=LotK;
                               PreviousContract=SymbolInfoDouble(SubPair, SYMBOL_TRADE_CONTRACT_SIZE);
                               }                     
                            }                  
                         }                
                      bNew=true;
                      for ( int j=0; j<ArraySize(BasicPairsRight); j++ )// if the currency is not found in the list, add it
                         {
                         if ( BasicPairsRight[j].Value == Half1 )
                            {
                            if ( SubSide == "u" ) BasicPairsRight[j].Quantity++;
                            if ( SubSide == "d" ) BasicPairsRight[j].Quantity--;
                            bNew = false;
                            break;
                            }
                         }
                      if ( bNew )
                         {
                         for ( int j=0; j<ArraySize(BasicPairsLeft); j++ )// if the currency is not found in the list, add it
                            {
                            if ( StringLen(BasicPairsLeft[j].Value) == 0 )
                               {
                               if ( SubSide == "u" ) BasicPairsRight[j].Quantity++;
                               if ( SubSide == "d" ) BasicPairsRight[j].Quantity--;                  
                               BasicPairsRight[j].Value=Half1;
                               break;
                               }
                            }            
                         }
                
                      bNew=true;
                      for ( int j=0; j<ArraySize(BasicPairsRight); j++ )// if the currency is not found in the list, add it
                         {
                         if ( BasicPairsRight[j].Value == Half2 )
                            {
                            if ( SubSide == "u" ) BasicPairsRight[j].Quantity--;
                            if ( SubSide == "d" ) BasicPairsRight[j].Quantity++;
                            bNew = false;
                            break;
                            }
                         }
                      if ( bNew )
                         {
                         for ( int j=0; j<ArraySize(BasicPairsRight); j++ )// if the currency is not found in the list, add it
                            {
                            if (  StringLen(BasicPairsRight[j].Value) == 0 )
                               {
                               if ( SubSide == "u" ) BasicPairsRight[j].Quantity--;
                               if ( SubSide == "d" ) BasicPairsRight[j].Quantity++;                  
                               BasicPairsRight[j].Value=Half2;
                               break;
                               }
                            }            
                         }
                      
                      v.PairRight[balancediterator].Name=SubPair;
                      v.PairRight[balancediterator].Side=SubSide;
                      v.PairRight[balancediterator].ContractSize=SymbolInfoDouble(v.PairRight[balancediterator].Name, SYMBOL_TRADE_CONTRACT_SIZE);
    
    
                      balancediterator++;                  
                      PreviousHalf1=Half1;
                      PreviousHalf2=Half2;
                      PreviousSubSide=SubSide;
                      PreviousPair=SubPair;
                   
                      quantityright++;
                      if ( SubSide == "u" && Half2 == d.DownPair )// if the fraction is not inverted
                         {
                         bBalanced=true;// if the missing part is in the denominator, then we have balanced the formula
                         break;// since the formula is balanced, we don't need the rest
                         }
                      if ( SubSide == "d" && Half1 == d.DownPair )// if the fraction is inverted
                         {
                         bBalanced=true;// if the missing part is in the numerator, then we have balanced the formula
                         break;// since the formula is balanced, we don't need the rest
                         }
                         
                      int RightUpTotal=0;// the number of upper elements in the right part
                      int RightDownTotal=0;// the number of lower elements in the right part
                      string LastUpRight;
                      string LastDownRight; 
                        
                      for ( int z=0; z<ArraySize(BasicPairsRight); z++ )
                         {
                         if ( BasicPairsRight[z].Quantity > 0 && StringLen(BasicPairsRight[z].Value) > 0 ) RightUpTotal+=BasicPairsRight[z].Quantity;
                         if ( BasicPairsRight[z].Quantity < 0 && StringLen(BasicPairsRight[z].Value) > 0 ) RightDownTotal-=BasicPairsRight[z].Quantity;
                         }
                      if ( bWasPairs && RightUpTotal == 0 && RightDownTotal == 0 ) return false;                                       
                      }
    
                   ReadStartIterator=i+1;
                   bUsed[quantityiterator]=true;
                   quantityiterator++;
                   }
                }
             }
             else break;
          }     
       
       if ( quantityleft == 1 && quantityright == 1 ) return false;// if the equation has been normalized to only 2 pairs, it is not valid (at least 3 pairs are required)
          
       return bBalanced;
       }
    

    Si tratta di una procedura molto sana e complessa, ma mi sembra che in questi casi sia meglio non produrre stati intermedi, perché ciò porterebbe ad una significativa duplicazione del codice. Inoltre, tutte le fasi sono molto compatte e sono logicamente divise in blocchi. L'insieme di queste funzioni forniscono risultati assolutamente identici e paragonabili a quelli che si potrebbero ottenere eseguendo tutte le conversioni a mano. Tutte queste manipolazioni matematiche vengono eseguite con una serie di metodi complessi ma necessari.

    È necessario eseguire un'analisi speciale per capire quanto sia profittevole la formula trovata. Non dimenticate che per ogni coppia è possibile aprire una posizione sia al rialzo che al ribasso. Di conseguenza, per ogni formula possono esistere due varianti intermedie dei circuiti - diretta e inversa. Quello con la redditività maggiore verrà accettato come risultato.

    Per valutare la redditività, ho creato una metrica simile al fattore di profitto, che consiste nei profitti e nelle perdite derivanti dagli swap. Se lo swap positivo maturato del circuito esistente è maggiore del modulo negativo, allora tale circuito è considerato redditizio. In altri casi, tali circuiti non sono redditizi — in altre parole, il fattore di swap del nostro profilo sarà positivo solo quando è maggiore di uno.

    Il risultato restituito viene scritto in un contenitore completamente diverso, creato come pacchetto di comandi autosufficienti per il trading e per l'ulteriore sviluppo della logica di trading. Contiene tutto il necessario per aprire rapidamente e facilmente l'intero circuito:

    struct EquationNormalized // the final structure with the formula in normalized form
       {
       Pair PairLeft[];// currency pairs on the left side
       Pair PairRight[];// currency pairs on the right side
       double SwapPlusRelative;// relative equivalent of the positive swap
       double SwapMinusRelative;// relative equivalent of the negative swap
       double SwapFactor;// resulting swap factor
       };
    

    Ho aggiunto anche due metodi che consentono di visualizzare comodamente le informazioni sui contenuti, ma non sono rilevanti per questo articolo e quindi non li fornirò qui. È possibile visualizzarli nel codice sorgente allegato. Ora, le informazioni su ogni componente dell'equazione sono contenute separatamente come elementi di array. Ciò consente di lavorare più facilmente con i dati in un secondo momento, senza doverli analizzare costantemente dalle stringhe. Forse questa soluzione poteva essere utilizzata fin dall'inizio, ma avrebbe rovinato la leggibilità.

    Calcolo del fattore di swap e adeguamento finale della struttura dell'equazione

    Questa è l'ultima fase, in cui viene calcolata la variabile più importante di questo sistema — le varianti saranno confrontate in base a questo valore. Il valore più alto è il migliore.

    void CalculateBestVariation(EquationNormalized &ii)// calculation of the best swap factor of the formula and final structure adjustment if needed
       {
       double SwapMinus=0.0;// total negative swap
       double SwapPlus=0.0;// total positive swap
       double SwapMinusReverse=0.0;// total negative swap
       double SwapPlusReverse=0.0;// total positive swap
       
       double SwapFactor=0.0;// swap factor of the direct pass
       double SwapFactorReverse=0.0;// swap factor of the reverse pass
       
       for ( int i=0; i<ArraySize(ii.PairLeft); i++ )// define the missing parameters for calculating the left side
          {
          for ( int j=0; j<ArraySize(Pairs); j++ )
             {
             if ( Pairs[j].Name == ii.PairLeft[i].Name )
                {
                ii.PairLeft[i].Margin=Pairs[j].Margin;
                ii.PairLeft[i].TickValue=Pairs[j].TickValue;
                ii.PairLeft[i].SwapBuy=Pairs[j].SwapBuy;
                ii.PairLeft[i].SwapSell=Pairs[j].SwapSell;
                break;
                }
             }
          }
          
       for ( int i=0; i<ArraySize(ii.PairRight); i++ )// define the missing parameters for calculating the right side
          {
          for ( int j=0; j<ArraySize(Pairs); j++ )
             {
             if ( Pairs[j].Name == ii.PairRight[i].Name )
                {
                ii.PairRight[i].Margin=Pairs[j].Margin;
                ii.PairRight[i].TickValue=Pairs[j].TickValue;
                ii.PairRight[i].SwapBuy=Pairs[j].SwapBuy;
                ii.PairRight[i].SwapSell=Pairs[j].SwapSell;
                break;
                }
             }
          }      
       
       double TempSwap;
       // calculate all components taking into account a change in the structure
       for ( int i=0; i<ArraySize(ii.PairLeft); i++ )// for left parts
          {
          if ( ii.PairLeft[i].Side == "u" )
             {// for direct trading
             TempSwap=ii.PairLeft[i].SwapBuy*ii.LotKLeft[i];
             if ( TempSwap >= 0 ) SwapPlus+=TempSwap;
             else SwapMinus-=TempSwap;
             // for reverse trading
             TempSwap=ii.PairLeft[i].SwapSell*ii.LotKLeft[i];
             if ( TempSwap >= 0 ) SwapPlusReverse+=TempSwap;
             else SwapMinusReverse-=TempSwap;         
             }
          if ( ii.PairLeft[i].Side == "d" )
             {// for direct trading
             TempSwap=ii.PairLeft[i].SwapSell*ii.LotKLeft[i];
             if ( TempSwap >= 0 ) SwapPlus+=TempSwap;
             else SwapMinus-=TempSwap;
             // for reverse trading
             TempSwap=ii.PairLeft[i].SwapBuy*ii.LotKLeft[i];
             if ( TempSwap >= 0 ) SwapPlusReverse+=TempSwap;
             else SwapMinusReverse-=TempSwap;         
             }
          }
          
       for ( int i=0; i<ArraySize(ii.PairRight); i++ )// for right parts
          {
          if ( ii.PairRight[i].Side == "d" )
             {// for direct trading
             TempSwap=ii.PairRight[i].SwapBuy*ii.LotKRight[i];
             if ( TempSwap >= 0 ) SwapPlus+=TempSwap;
             else SwapMinus-=TempSwap;
             // for reverse trading
             TempSwap=ii.PairRight[i].SwapSell*ii.LotKRight[i];
             if ( TempSwap >= 0 ) SwapPlusReverse+=TempSwap;
             else SwapMinusReverse-=TempSwap;         
             }
          if ( ii.PairRight[i].Side == "u" )
             {// for direct trading
             TempSwap=ii.PairRight[i].SwapSell*ii.LotKRight[i];
             if ( TempSwap >= 0 ) SwapPlus+=TempSwap;
             else SwapMinus-=TempSwap;
             // for reverse trading
             TempSwap=ii.PairRight[i].SwapBuy*ii.LotKRight[i];
             if ( TempSwap >= 0 ) SwapPlusReverse+=TempSwap;
             else SwapMinusReverse-=TempSwap;         
             }
          }   
       // calculate the swap factor for the direct pass
       if ( SwapMinus > 0.0 && SwapPlus > 0.0 ) SwapFactor=SwapPlus/SwapMinus;
       if ( SwapMinus == 0.0 && SwapPlus == 0.0 ) SwapFactor=1.0;
       if ( SwapMinus == 0.0 && SwapPlus > 0.0 ) SwapFactor=1000001.0;
       if ( SwapMinus > 0.0 && SwapPlus == 0.0 ) SwapFactor=0.0;
       // calculate the swap factor for the reverse pass
       if ( SwapMinusReverse > 0.0 && SwapPlusReverse > 0.0 ) SwapFactorReverse=SwapPlusReverse/SwapMinusReverse;
       if ( SwapMinusReverse == 0.0 && SwapPlusReverse == 0.0 ) SwapFactorReverse=1.0;
       if ( SwapMinusReverse == 0.0 && SwapPlusReverse > 0.0 ) SwapFactorReverse=1000001.0;
       if ( SwapMinusReverse > 0.0 && SwapPlusReverse == 0.0 ) SwapFactorReverse=0.0;
       // select the best approach and calculate the missing values in the structure
       if ( SwapFactor > SwapFactorReverse )
          {
          ii.SwapPlusRelative=SwapPlus;
          ii.SwapMinusRelative=SwapMinus;
          ii.SwapFactor=SwapFactor;
          }
       else
          {
          ii.SwapPlusRelative=SwapPlusReverse;
          ii.SwapMinusRelative=SwapMinusReverse;
          ii.SwapFactor=SwapFactorReverse;
          bool bSigned;
          for ( int i=0; i<ArraySize(ii.PairRight); i++ )// if it is a reverse pass, then reverse the right structure of the formula
             {
             bSigned=false;
             if ( !bSigned && ii.PairRight[i].Side == "u" ) 
                {
                ii.PairRight[i].Side="d";
                bSigned=true;
                }
             if ( !bSigned && ii.PairRight[i].Side == "d" ) 
                {
                ii.PairRight[i].Side="u";
                bSigned=true;
                }
             }
          bSigned=false;    
          for ( int i=0; i<ArraySize(ii.PairLeft); i++ )// if it is a reverse pass, then reverse the left structure of the formula
             {
             bSigned=false;
             if ( !bSigned && ii.PairLeft[i].Side == "u" ) 
                {
                ii.PairLeft[i].Side="d";
                bSigned=true;
                }
             if ( !bSigned && ii.PairLeft[i].Side == "d" ) 
                {
                ii.PairLeft[i].Side="u";
                bSigned=true;
                }
             }              
          }
          
       bool bSigned;
       for ( int i=0; i<ArraySize(ii.PairRight); i++ )// reverse the right side anyway
          {
          bSigned=false;
          if ( !bSigned && ii.PairRight[i].Side == "u" ) 
             {
             ii.PairRight[i].Side="d";
             bSigned=true;
             }
          if ( !bSigned && ii.PairRight[i].Side == "d" ) 
             {
             ii.PairRight[i].Side="u";
             bSigned=true;
             }
          }
       }
    

    Per consentire l'output sequenziale del risultato, ho implementato il log che viene scritto solo se viene trovata la variante della formula filtrata con successo. Il registro è il seguente:

    Log

    Il colore rosso viene utilizzato per il simbolo risultante, a cui vengono ridotti entrambi i lati dell'equazione. La riga successiva mostra la variante normalizzata con i coefficienti del lotto. La terza riga mostra la variante con il fattore di swap calcolato. La quarta linea è la migliore delle varianti trovate durante la sessione di brute-force, anch'essa tracciata sul grafico dalla funzione Comment. Questo prototipo è allegato qui sotto, in modo da poterlo testare. In realtà, può servire come prototipo di assistenza al trading per lo swap trading. Per ora ha poche funzionalità, ma cercherò di ampliarle nel prossimo articolo. Il prototipo viene presentato in due versioni: per MetaTrader 4 e MetaTrader 5.


    Conclusioni dei Primi Test

    È piuttosto difficile trarre conclusioni su un argomento così complesso. Tuttavia, sono riuscito a capire qualcosa di utile, anche se finora non sono riuscito a trovare un fattore di swap maggiore di uno. Queste sono le prime conclusioni a cui sono giunto analizzando il lavoro di questo prototipo:

    • Per alcune coppie di valute, è possibile aumentare gli swap positivi o ridurre quelli negativi (a causa della presentazione della posizione come equivalente sintetico).
    • Anche se non si trova un circuito redditizio, una delle sue parti può sempre essere utilizzata come posizione alternativa, ad esempio per bloccare due conti di trading diversi.
    • Il blocco con tale posizione sintetica elimina la necessità di utilizzare conti Swap Free, poiché consente di avere uno swap positivo opposto a entrambe le estremità.
    • È necessario eseguire un'analisi più approfondita con i broker più popolari, per i quali è necessaria un'espansione delle funzionalità.
    • Spero di essere in grado di dimostrare che è possibile ottenere un fattore di swap redditizio (che per ora è solo un'ipotesi)
    • Gli swap possono fornire profitti piccoli ma costanti se usati con saggezza

    Conclusioni

    Spero che questo approccio sia interessante per voi e che possa fornire spunti di riflessione. Il metodo è molto difficile da capire, ma in realtà attua un semplice principio: aprire due posizioni opposte con lo stesso volume. La semplice apertura di queste due posizioni opposte genera sempre una perdita. Non esiste un broker che fornisca uno swap positivo unidirezionale di modulo maggiore rispetto a uno swap negativo unidirezionale. Naturalmente, non si troveranno mai swap positivi in entrambe le direzioni, perché è matematicamente impossibile.

    Non fornirò i dettagli della matematica sottostante, poiché si tratta di un argomento molto vasto. È meglio utilizzare le manifestazioni di questa matematica. Applicando il metodo descritto, è possibile ridurre la perdita causata dallo swap con il bloccaggio della posizione. Potete anche cercare di trovare un vuoto nelle tabelle di swap dei broker e godervi il blocco con un fattore di profitto positivo (uno swap totale redditizio di tutte le posizioni) - è un trading senza rischi che non dipende dalle fluttuazioni dei prezzi.

    Ritengo che i metodi di swap trading siano davvero sottovalutati, poiché uno swap positivo offre un potenziale profitto. Il metodo descritto è solo una delle possibili varianti dei metodi di swap trading, ma questo compito mi piace e cercherò di portarlo avanti nei prossimi articoli, sviluppando l'idea, modernizzando il codice e creando nuove funzionalità aggiuntive. Descriverò inoltre alcune idee relative alla previsione dei profitti e alle funzionalità di trading.

    Tradotto dal russo da MetaQuotes Ltd.
    Articolo originale: https://www.mql5.com/ru/articles/9198

    File allegati |
    Prototype.zip (20.52 KB)
    Arriva il Nuovo MetaTrader 5 e MQL5 Arriva il Nuovo MetaTrader 5 e MQL5
    Questa è solo una panoramica di MetaTrader 5. Non posso descrivere tutte le nuove funzionalità del sistema per un periodo di tempo così breve: i test sono iniziati il 09.09.2009. Questa è una data simbolica e sono sicuro che sarà un numero fortunato. Sono passati alcuni giorni da quando ho ricevuto la versione beta del terminale MetaTrader 5 e MQL5. Non sono riuscito a provare tutte le sue funzionalità, ma sono già sorpreso.
    Multibot in MetaTrader: Avvio di più robot da un singolo grafico Multibot in MetaTrader: Avvio di più robot da un singolo grafico
    In questo articolo, prenderò in considerazione un semplice modello per la creazione di un robot MetaTrader universale che può essere utilizzato su più grafici, pur essendo allegato a un solo grafico, senza la necessità di configurare ogni istanza del robot su ogni singolo grafico.
    Utilizza i canali MQL5.community e le chat di gruppo Utilizza i canali MQL5.community e le chat di gruppo
    Il sito web MQL5.com riunisce trader di tutto il mondo. Gli utenti pubblicano articoli, condividono codici gratuiti, vendono prodotti nel Market, offrono servizi da freelance e copiano segnali di trading. Puoi comunicare con loro sul Forum, nelle chat dei trader e nei canali MetaTrader.
    Sviluppare un Expert Advisor per il trading da zero (Parte 31): Verso il futuro (IV) Sviluppare un Expert Advisor per il trading da zero (Parte 31): Verso il futuro (IV)
    Continuiamo a rimuovere le parti separate dal nostro EA. Questo è l'ultimo articolo di questa serie. L'ultima cosa da rimuovere è il sistema audio. Questo può creare un po' di confusione se non si è seguita questa serie di articoli.