
Gli Swap (Parte I): Posizioni di Blocco e Sintetiche
Sommario
- Introduzione
- Informazioni sugli Swap
- Bloccaggio Utilizzando Due Conti Trading
- Informazioni sui Tassi di Cambio
- Bloccaggio Tramite Posizioni Sintetiche
- Scrivere un'Utilità per Utilizzare i Poligoni di Swap
- Conclusioni dei Primi Test
- Conclusioni
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:
- Da Lunedì a Martedì
- Da Martedì a Mercoledì
- Da Mercoledì a Giovedì (quasi tutti i broker addebitano un triplo swap in questa notte)
- 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:
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:
- Bloccare la posizione originale su uno strumento di trading simulato.
- Provare a creare l'equivalente di una posizione con tassi swap completamente differenti
- Altri scopi
Ecco lo schema generale di questo 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:
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:
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:
- "u" è il tasso di cambio
- "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:
- Conta tutti i fattori aggiuntivi nel numeratore e nel denominatore e li rimuove.
- Verifica la disponibilità di 1 valuta al numeratore e di 1 valuta al denominatore
- Verifica la corrispondenza delle frazioni risultanti a destra e a sinistra
- Se il lato destro è il reciproco del lato sinistro, basta invertire la struttura destra della formula (che è simile all'elevazione a potenza "-1")
- 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:
- In base al simbolo risultante ottenuto da entrambi i lati dell'equazione, selezionare una coppia iniziale dall'elenco per entrambi i lati dell'uguaglianza.
- Entrambe le coppie, in base al loro potere nell'equazione, devono fornire la valuta di base nel numeratore della frazione
- 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
- Proseguiamo in modo che la valuta superiore della coppia successiva compensi quella inferiore della precedente
- Ripetere questi passaggi fino a trovare la coppia risultante desiderata
- Una volta trovata la coppia risultante, tutti i componenti inutilizzati della formula vengono scartati (poiché il loro prodotto è uno)
- 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).
- 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:
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





- 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