

Introduzione
Nel mondo del trading finanziario, la transizione da investitore discrezionale a trader sistematico rappresenta uno spartiacque fondamentale. Grazie a piattaforme come ProRealTime, oggi è possibile trasformare un’idea operativa in un algoritmo automatico capace di analizzare i mercati, generare segnali e testare strategie su anni di dati storici.
Se hai preso in mano questo libro, significa che hai già superato la fase iniziale. Ora, però, ti trovi di fronte a quel salto di qualità che separa chi si limita a usare la piattaforma da chi, invece, impara a padroneggiarla. Questo testo nasce esattamente per colmare questo spazio vuoto, posizionandosi un passo oltre i manuali introduttivi di base.
Nelle pagine che seguono esploreremo le strutture logiche intermedie del linguaggio di programmazione ProBuilder: dalla gestione avanzata della sintassi fino alla costruzione di indicatori personalizzati e sistemi di trading automatici.
Il focus del testo è fornire un metodo pratico per costruire trading system robusti, leggibili e facilmente modificabili. Affronteremo nel dettaglio la gestione delle condizioni operative, l’utilizzo efficiente delle variabili, l’ottimizzazione dei parametri, il risk management e l’analisi rigorosa delle prestazioni attraverso i backtest.
La programmazione finanziaria è un percorso che unisce logica, disciplina e creatività. Ogni codice nasce da un’intuizione, ma solo attraverso test rigorosi e una corretta implementazione può trasformarsi in uno strumento operativo efficace. Questo libro vuole accompagnarti proprio in questo percorso: dalla semplice regola di trading alla costruzione di strategie strutturate e professionali.
La programmazione è un’abilità che si acquisisce per tentativi ed errori. Abbandona l’idea del trading system miracoloso venduto su internet: la vera libertà operativa passa solo dalla tua capacità di scrivere, testare e validare le tue idee in totale autonomia.
Buona lettura e buon lavoro sui mercati!
Parte Prima: Le basi di ProRealTime
ProRealTime è un software professionale per l’analisi tecnica ed il trading online, utilizzato sia da trader principianti che esperti. ProRealTime è una piattaforma completa che consente di analizzare i mercati finanziari attraverso grafici ed indicatori tecnici avanzati.
Una delle sue caratteristiche distintive è la possibilità di programmare strategie di trading automatico: gli utenti possono creare, testare e ottimizzare sistemi basati su regole precise, che vengono poi eseguiti in modo automatico sui mercati.
Nel trading discrezionale il trader prende direttamente le decisioni operative, analizzando grafici, indicatori e contesto di mercato. Nel trading automatico, invece, le regole di ingresso e uscita vengono tradotte in un sistema che esegue le operazioni secondo criteri predefiniti.
Tra i principali punti di forza del trading sistematico ne elenchiamo tre:
– il primo è il rispetto della disciplina operativa, possibile attraverso l’eliminazione delle emozioni che sono una delle cause principali di errore nel trading
– il secondo punto di forza è la verifica della validità di un idea operativa, che può sembrare buona a livello intuitivo, ma che dopo dei test non si dimostra più tale
– il terzo punto di forza è la possibilità di operare 24 ore al giorno, ossia di sfruttare tutte le tendenze che un mercato può offrire, cosa non fattibile discrezionalmente.
Con questa guida intendiamo fornire le basi per conoscere le principali funzioni di programmazione di ProRealTime, affinché ciascuno possa iniziare a scrivere codici basati sulle proprie idee di trading e testarli per verificarne l’efficacia.
Capitolo 1: Le funzioni base sul tempo
Per orientarsi in un grafico finanziario servono delle coordinate. Si utilizzano due scale: quella orizzontale, l’asse delle ascisse, che rappresenta il tempo, e quella verticale, l’asse delle ordinate, che rappresenta il valore, cioè il prezzo dello strumento finanziario.
§ 1.1 – La funzione: barIndex
Partiamo dalla scala temporale, l’asse delle ascisse. Il contatore delle barre (o candele) di un grafico si chiama BARINDEX. BarIndex conta le barre da sinistra a destra, partendo dalla prima dello storico visualizzato. La prima barra caricata a sinistra rappresenta la barra numero 1. Se barIndex ha valore 1000 vuol dire che sono passate 1000 barre dall’inizio della strategia.
BarIndex è un numero progressivo e si utilizza con le parentesi quadre, per cui se in questo momento barIndex ha valore [2000] (ultima barra a destra del grafico), barIndex[1] ha valore 1999 e così via (barIndex[1] si può anche scrivere: barIndex-1).
Nell’esempio sopra per avere come riferimento l’ultima candela in assoluto in questo momento basta scrivere: close[BarIndex] ed è come scrivere: close[2000], ed alla candela successiva close[2001].
Vediamo un semplice esempio in cui utilizziamo barIndex per ignorare le prime cinque barre e il close viene restituito dalla sesta barra in poi:
if barIndex < 5 then
risultato = 0
else
risultato = close
endif
return risultato AS "Close dopo le prime 5 barre"In questo esempio più complesso utilizziamo barIndex per determinare la durata della validità di un ordine:
numeroMassimoBarre = 10
media1 = average[20](close)
media2 = average[50](close)
if media1 crosses over media2 then
prezzoIncrocio = close
contatoreBarre = barIndex
endif
if barIndex > contatoreBarre + numeroMassimoBarre then
prezzoIncrocio = 0
endif
// Entro al prezzo dell'incrocio durante le 10 barre
if prezzoIncrocio > 0 and not longOnmarket then
buy 1 contract at prezzoIncrocio LIMIT
endif
Se l’ordine limite non è eseguito, si può entrare con un ordine a mercato:
// Se non sono entrato entro le 10 barre, entro a mercato
if prezzoIncrocio > 0 and barIndex > contatoreBarre + numeroMassimoBarre and not longOnMarket then
buy 1 contract at market
prezzoIncrocio = 0
endifIn quest’altro esempio usiamo barIndex (che, ripetiamo, è un contatore intero che possiamo utilizzare per fare confronti, filtri o calcoli sulla posizione della barra nel tempo) per eseguire azioni periodiche ogni numero N di barre (utilizziamo in questo esempio l’operatore MOD che ci restituisce il resto della divisione).
Apriamo in questo esempio posizioni ogni 10 barre se è presente il segnale crossover:
nBarStep = 10
if (barIndex MOD nBarStep) = 0 then // resto 0 ogni 10 barre (1, 11 ...)
if close crosses over average(close) then
buy 1 contract at market
endif
endif§ 1.2 – La funzione: intradayBarIndex
Per contare le barre intraday si usa la funzione INTRADAYBARINDEX.
Il contatore mostra il numero di barre a partire dall’inizio della giornata e rimette il valore a zero ad ogni nuovo inizio di giornata.
Questa funzione è molto utile nella programmazione in quanto permette di resettare dei valori all’inizio del giorno per poi verificare come questi si modificano nell’arco della giornata.
Con intradayBarIndex possiamo identificare in modo preciso la posizione di una barra (candela) all’interno della giornata di contrattazione, assegnando un numero progressivo ad ogni barra a partire dall’apertura del mercato.
Vediamo alcuni esempi didattici.
In questo esempio fissiamo il massimo ed il minimo della prima candela della giornata, ed entriamo al superamento di uno dei due:
once highFirstBar = 0
once lowFirstBar = 0
if intradayBarIndex = 0 then
highFirstBar = high
lowFirstBar = low
endif
if close > highFirstBar then
buy 1 contract at market
endif
if close < lowFirstBar then
sellshort 1 contract at market
endifIn quest’altro esempio entriamo a mercato dopo che sono passate dieci candele:
if intradayBarIndex > 10 then
buy 1 contract at market
endifQui operiamo soltanto entro le prime sei barre della giornata se abbiamo una chiusura maggiore a quella precedente:
barreOperative = 6
if intradayBarIndex < barreOperative then
if close > close[1] then
buy 1 contract at market
endif
endif§ 1.3 – La funzione: tradeIndex
Abbiamo visto che barIndex ed intradayBarIndex sono funzioni che determinano in automatico rispettivamente il numero di barre generali ed intraday.
La funzione TRADEINDEX (che è ugualmente un contatore di barre sull’asse temporale delle ascisse) indica invece un punto temporale statico: la barra di apertura di un trade operativo.
TradeIndex si scrive con le parentesi rotonde. La barra dell’ordine aperto precedentemente si scrive: tradeIndex (1) (il primo ordine si trova spesso scritto anche senza numerazione come: tradeIndex).
TradeIndex in pratica è il numero della barra (sempre seguendo la progressione di barIndex), di quando un trade è iniziato. Quindi all’apertura di un trade barIndex e tradeIndex hanno lo stesso numero, ad esempio barra numero 1000, poi tradeIndex rimane invariato mentre barIndex viene costantemente incrementato.
Con tradeIndex (che troviamo usato spesso insieme a barIndex) passiamo quindi alla parte operativa del trading. Tramite l’espressione: (barIndex – tradeIndex), possiamo infatti sapere quante candele sono passate dall’inizio del trade.
Vediamo alcuni esempi.
In questo primo esempio chudiamo l’operazione se dopo 10 barre dall’entrata non si è formato un nuovo massimo.
if mieCondizioni then
buy 1 contract at market
entryHigh = high
endif
if onMarket and (barIndex - tradeIndex) > 10 and close < entryHigh then
sell at market
endifAltro esempio in cui, dopo essere entrati a mercato, chiudiamo la posizione se è stata aperta da almeno 3 barre (in pratica dopo 3 barre dall’apertura):
if longOnMarket and (barIndex - tradeIndex) >= 3 then
sell at market
endifNei codici troviamo spesso questa formula usata per limitare la strataegia a una sola operazione al giorno. Si definisce la variabile “firstBar” che calcola l’indice della prima barra della giornata. TradeIndex(1) invece come abbiamo visto restituisce l’indice della barra dell’ultimo trade chiuso/aperto.
Se l’ultimo trade è avvenuto prima dell’inizio della sessione attuale, allora notTraded = 1, se è avvenuto dopo l’inizio della sessione invece: notTraded = 0.
firstBar = barIndex - intradayBarIndex
if tradeIndex(1) < firstBar then
notTraded = 1
else
notTraded = 0
endifPossiamo usare tradeIndex anche per incrementare una posizione ogni 5 giorni. Verifichiamo che tradeIndex sia divisibile per 5 (quindi quando la divisione per 5 darà come resto ZERO in modo che non dobbiamo fare calcoli delle barre):
// MOD restituisce il resto della divisione tra due numeri.
if (tradeIndex(1) MOD 5) = 0 then
...
endif
In questo esempio, verifichiamo se tradeIndex(1) è divisibile per 5 senza resto.
Se tradeIndex(1) = 30 allora: 30 MOD 5 = 0 il resto è 0, quindi la condizione è vera.
Se invece tradeIndex(1) = 32 abbiamo: 32 MOD 5 = 2 il resto non è 0, quindi la condizione è falsa.Abbiamo visto che tradIndex(1) segna sull’asse temporale l’ultimo trade e si usa spesso come indice di apertura di una posizione.
Un costante specifica per la chiusura di un trade non c’è, ma la possiamo creare in questo modo e la chiamiamo, per esempio, closeIndex:
if strategyProfit <> strategyProfit[1] then
closeIndex = barIndex[1]
endifQuesta espressione è molto utile nella pratica in quanto ci permette di sapere da quante barre l’ultimo trade è stato chiuso.
In questo esempio di un trading system rientriamo in posizione dopo una precedente chiusura se permane lo stesso segnale.
if close crosses over average[20] then
buy 1 contract at market
set stop %loss 0.7
set target %profit 2
endif
if strategyProfit <> strategyProfit[1] then
closeIndex = barIndex[1]
endif
if not onMarket and (barIndex - closeIndex) > 10 and close > average[20] then
buy 1 contract at market
set stop %loss 0.7
set target %profit 1.6
endifCapitolo 2: Le funzioni sul prezzo
Dopo aver visto le principali funzioni per orientarsi nell’asse temporale di un grafico, passiamo al prezzo.
Quando parliamo genericamente di prezzo, ci riferiamo alla chiusura. In ProRealTime, infatti, i segnali vengono generati alla chiusura della barra e, normalmente, l’ordine viene eseguito all’apertura della barra successiva, secondo le regole di esecuzione della strategia.
In una barra distinguiamo: Open, High, Low e Close. ProRealTime fornisce anche delle utili costanti derivate dai prezzi che sono:
range: high - low
typicalPrice: media tra high - low - close
weightedClose: media ponderata di high (peso 1) - low (peso 1) - close (peso 2)
medianPrice: media tra high - low
totalPrice: media tra open - high - low - close
§ 2.1 – La funzione: tradePrice
Il comando TRADEPRICE(n) si utilizza con le parentesi rotonde e permette di trovare il prezzo della transazione precedentemente eseguita. Se non è specificato tradePrice equivale all’ultimo ordine eseguito, ossia tradePrice(1). TradePrice(2) è invece il trade precedente e così via.
La funzione tradePrice in ProRealTime serve per recuperare il prezzo di esecuzione di un trade. È molto utile per costruire logiche su stop, target o gestione della posizione basate sul prezzo reale di ingresso.
Occorre fare attenzione ad una cosa: tradePrice(1) non è il prezzo di entrata, ma semplicemente l’ultimo prezzo tradato. Dobbiamo distinguere due casi per fare chiarezza. Se siamo dentro una posizione, tradePrice(1) corrisponde al prezzo di apertura del trade. Se invece non siamo a mercato, tradePrice(1) è il prezzo di uscita dal trade: è cioè il prezzo dell’ultima posizione chiusa. In questo caso se vogliamo sapere il prezzo di entrata dobbiamo scrivere tradePrice(2).
Ecco un esempio di utiilizzo di tradePrice(1): usciamo da una posizione quando si guadagna il 5% rispetto al trade precedente:
if longOnMarket and close > tradePrice(1) * 1.05 then
sell at market
endifQui usciamo quando abbiamo raggiunto come target 10 punti sopra il prezzo di entrata:
if close > tradePrice(1) + 10 then
sell at market
endifIn questo esempio utilizziamo tradePrice(1) per creare uno stop loss in una posizione long:
stopDistance = 20 // 20 punti di stop
if longOnMarket then
myStop = tradePrice(1) - stopDistance
if close < myStop then
sell at market
endif
endifPer riassumere: tradeIndex ci dice quando abbiamo eseguito il trade (numero di barra), mentre tradePrice ci dice a che prezzo lo abbiamo eseguito.
§ 2.2 – La funzione: positionPrice
Il comando POSITIONPRICE permette di conoscere il prezzo medio di acquisto della posizione aperta in corso. A differenza di tradePrice(1) che restituisce il prezzo dell’ultimo singolo trade, positionPrice riflette la media ponderata di tutti i contratti aperti.
PositionPrice viene calcolato come la somma di tutti i prezzi di entrata ponderati dalla quantità di ogni ordine. Se si aggiunge una posizione cambierà quindi il valore di positionPrice.
L’istruzione positionPrice si utilizza con le parentesi quadre, come strategyProfit. PositionPrice[1] restituisce il valore della posizione alla chiusura della barra precedente.
Un esempio: se si acquista un’azione a 5$, poi un’altra a 10$ ed infine un’altra a 15$, il valore di positionPrice sarà di 10$: (5+10+15)/3.
Se si aggiungono posizioni la media verrà chiaramente ricalcolata, mentre se si vende una parte della posizione (ad esempio una delle tre azioni acquistate nell”esempio), positionPrice non cambierà e sarà sempre 10$.
In questo esempio apriamo una seconda posizione se il prezzo scende più di 20 punti dall’ultimo trade, sono passate almeno 5 barre e abbiamo meno di due posizioni aperte. Usciamo quando il prezzo risale sopra il prezzo medio e si hanno almeno due contratti aperti:
myMACD = MACD[12,26,9](close)
long = myMACD crosses over 0
// Primo ingresso
if not longOnMarket and long then
buy 1 contract at market
endif
// Secondo ingresso
if longOnMarket and countOfPosition < 2 then
if barIndex-tradeIndex(1) >= 5 and tradePrice(1)-close >= 20 then
buy 1 contract at market
endif
endif
// Uscita sopra prezzo medio
if countOfPosition >= 2 and close > positionPrice then
sell at market
endifChiaramente con una sola posizione aperta positionPrice (prezzo medio della posizione) è identico a tradePrice (prezzo dell’ultimo trade) e si possono usare indistintamente.
§ 2.3 – La funzione: positionPerf
Il comando POSITIONPERF(n), vuole le parentesi rotonde e restituisce la performance di una posizione aperta in percentuale sul prezzo (commissioni non incluse).
PositionPerf, ad esempio, può restituire 0.01 se la performance è positiva oppure -0.07 se la performance è negativa.
In pratica fornisce la performance attuale se n = 0, oppure la performance dell’ennesima ultima posizione chiusa se n > 0 .
Positionperf è quindi il moltiplicatore diviso 100 (100% = 1): 0.3% è uguale a 0.003, positivo se in guadagno e negativo se in perdita.
In generale positionPerf può servire quindi sia a conoscere:
– il risultato di un’operazione in corso,
– l’esito di operazioni chiuse.
Ecco un esempio di utilizzo di positionPerf (con operazione aperta) in cui si chiude un trade se stiamo guadagnando più del 2%.
E’ importante ricordare che positionPerf varia ad ogni candela, in quanto si aggiorna costantemente con la formazione della candela.
In questo esempio usciamo da un trade quando stiamo guadagnando più del 2%.
if positionPerf(0) >= 0.02 then
exitShort at market
endif
//nei codici possiamo moltiplicare positionPer*100 per maggiore chiarezza:
if positionPerf(0)*100 >= 2 then
exitShort at market
endifEcco un esempio di utilizzo di positionPerf (questa volta con operazioni già chiuse) in cui apriamo un nuovo trade se il trade precedente ha guadagnato almeno il 10%:
if not onMarket and positionPerf(1) > 0.1 then
buy 1 contract at market
endifPositionPerf può quindi servire a conoscere in generale se il trade precedente è stato chiuso in profitto: positionPerf(1) > 0, oppure in perdita: positionPerf(1) < 0.
In questo esempio modifichiamo lo stop loss se ci sono stati due trade in perdita consecutivi:
if positionPerf(1) < 0 and positionPerf(2) < 0 then
set stop %loss 1
else
set stop %loss 0.5
endifUtilizziamo in questo ultimo esempio positionPerf e barIndex per ritardare nuovi ingressi di un certo numero di barre dopo un operazione in perdita.
if not onMarket and onMarket[1] and positionPerf(1) < 0 then
myBar = barIndex[1] // barra di uscita (barIndex invece è la barra successiva a quella di uscita)
endif
if (barIndex - myBar) >= 5 THEN
tradeOn = 1
else
tradeOn = 0
endif§ 2.4 – La funzione: strategyProfit
L’istruzione STRATEGYPROFIT restituisce i guadagni o le perdite assolute, nella valuta dello strumento utilizzato (commissioni escluse) realizzate dall’inizio del sistema di trading.
StrategyProfit restituisce il risultato complessivo della strategia, aggiornato all’ultima posizione chiusa.
Si può usare con l’istruzione “quit” (che vedremo meglio più avanti) per interrompere un sistema di trading quando questo eccede determinate perdite.
if strategyProfit < -500 then
quit
endifOccorre ricordare che i sistemi di trading sono valutati alla chiusura della barra. Nell’esempio sopra riportato quindi le perdite possono anche essere superiori al valore stabilito nel caso di una grossa perdita in una singola candela o nel caso di un gap. Quindi prima di usare questo codice è necessario impostare degli stop loss per una maggiore protezione.
Questa istruzione si utilizza con le parentesi quadre: strategyprofit[1] dà il profitto/perdita alla chiusura del trade precedente.
StrategyProfit quindi è il risultato, aggiornato all’ultima operazione chiusa, del TOTALE dei profitti/perdite di un sistema e si usa principalmente per sapere quanto una strategia ha guadagnato complessivamente fino a quel momento.
Tuttavia si può anche utilizzare per verificare se l’ultima operazione chiusa è stata profittevole: strategyProfit > strategyProfit[1] oppure perdente: strategyProfit < strategyProfit[1].
Ecco un esempio in cui interrompiamo l’operatività se abbiamo più di due perdite:
once tradeOn = 1
once nLoss = 0
if strategyProfit < strategyProfit[1] then
nLoss = nLoss + 1
if nLoss = 2 then
tradeOn = 0
endif
endifRicordiamo che per sapere se l’ultima operazione era stata positiva si poteva anche usare positionPerf(1) > 0. PositionPerf(0) infatti è la posizione in tempo reale, mentre strategyProfit non si usa per il tempo reale.
Spesso, come vedremo, si usano insieme queste due istruzioni per determinati scopi.
Da ricordare: strategyProfit viene aggiornato solo alla CHIUSURA di un operazione.
Se un’operazione ha prodotto risultato 0, allora strategyProfit è uguale a strategyProfit[1].
A questo punto si possono verificare varie condizioni, che sono utili nella costruzione di codici:
- not onMarket and onMarket[1] // eravamo a mercato e ora non lo siamo più
- not onMarket and not onMarket[1] and strategyprofit <> strategyProfit[1] // non eravamo a mercato ma c'è stato un profit/lossUna formula del moderatore italiano del forum di ProRealCode, Roberto Gozzi, che riportiamo in quanto si usa spesso in vari codici operativi per verificare che ci sia stata una operazione anche aperta e chiusa sulla stessa barra è la seguente:
newTrade = (onMarket and not onMarket[1]) or ((not onMarket and not onMarket[1]) and (strategyProfit <> strategyProfit[1])) or (longOnMarket and ShortOnMarket[1]) or (longOnMarket[1] and shortOnMarket) or ((tradeIndex(1) = tradeIndex(2)) and (barIndex = tradeIndex(1)) and (barIndex > 0) and (strategyProfit = strategyProfit[1])) In pratica verifica che STRATEGYPROFIT sia identica, che non fosse a mercato prima, né dopo (quindi una sola barra), che l’operazione si sia davvero chiusa la barra precedente e che si sia anche aperta nella stessa barra, tramite TRADEINDEX.
Un utilizzo molto importante di strategyProfit è quello che consente di salvare il profitto/perdita di un trading system. Qui salviamo il profitto all’inizio di ogni mese per limitare la massima perdita mensile di un sistema.
once strategyON = 1
once stopStrategy = 0
if openMonth <> openMonth[1] then
strategyON = 1
stopStrategy = strategyProfit
endif
floatingProfit = (strategyProfit - stopStrategy) / pointValue
if floatingProfit <= -(700*positionSize) then
strategyON = 0
endifPossiamo anche immagazzinare strategyprofit in una variabile per calcolare il profitto giornaliero della strategia per sapere quanto si è guadagnato/perso dall’inizio della giornata:
once myStrategyProfit = 0
once tempStrategyProfit = 0
if intradayBarIndex = 0 then
myStrategyProfit = strategyProfit
endif
tempStrategyProfit = strategyProfit - myStrategyProfit // profitto daily della strategiaPer riassumere le differenze ed il principale utilizzo tra queste due fondamentali funzioni:
– strategyProfit restituisce il totale progressivo delle operazioni chiuse e permette di conoscere lo storico della strategia
– positionPerf restituisce la percentuale di guadagno o perdita (se è un valore negativo) dell’operazione aperta.
Alla chiusura di un’operazione strategyProfit viene aggiornato e positionPerf viene azzerato. Quando si apre una nuova posizione strategyProfit rimane invariato come era prima mentre positionPerf viene aggiornato candela per candela.
§ 2.5 – Come controllare la posizione in tempo reale
StrategyProfit rappresenta il risultato, aggiornato all’ultima operazione chiusa, del totale dei profitti/perdite di ogni operazione della strategia.
Per conoscere il profitto attuale (ad ogni barra) dobbiamo aggiungere positionPerf.
Nello specifico, per conoscere i PUNTI per singola posizione o totali usiamo questa formula:
Punti per posizione: positionPerf * positionPrice / pointSize
Punti totali: positionPerf * positionPrice / pointSize * abs (countOfPosition)Per conoscere il GUADAGNO aggiornato alla candela corrente, per singola posizione o totale, usiamo:
Importo per posizione: positionPerf * positionPrice / pointSize * pointvalue / abs (countOfPosition)
Importo totale: positionPerf * positionPrice / pointSize * pointValueVediamo questo esempio in cui si lega una condizione monetaria ad un indicatore per uscire da una posizione:
puntiTarget = 100
livelloRsi = 50
actualGain = positionPerf * positionPrice / pointSize // guadagno in punti
if actualGain > puntiTarget and rsi[14] < livelloRsi then
sell at market
endifDa notare che strategyProfit (profitto all’ultima operazione chiusa) più positionPerf*positionPrice / pointSize *pointValue (profitto temporaneo globale) rappresenta la curva dei guadagni e delle perdite della strategia.
equityLine = strategyProfit + (positionPerf * positionProce / pointSize * pointValue)Possiamo quindi inserire questa curva in una variabile e applicarci degli indicatori, come una media, per analizzare l’andamento della strategia.
Un altro modo per sapere quanto sta guadagnando in punti un TS dopo un certo numero di barre è il seguente (calcola il profit/loss corrente in punti della posizione aperta):
gain = 0
if longOnMarket then
gain = (close - tradePrice(1)) / pointSize
elsif shortOnMarket then
gain = (tradePrice(1) - close) / pointSize
endif§ 2.6 – La funzione: countOfPosition
In ProRealTime abbiamo 3 variabili che permettono di verificare lo stato della nostra posizione:
– onMarket: vale 1 se abbiamo delle posizioni aperte altrimenti 0
– longOnMarket: vale 1 se abbiamo delle posizioni lunghe aperte altrimenti 0
– shortOnMarket: vale 1 se abbiamo delle posizioni corte aperte altrimenti 0
Per contare queste posizioni durante il trading operativo utilizziamol’istruzione COUNTOFPOSITION, che è un contatore di posizioni.
Se siamo long countOfPosition sarà = 1, se siamo short di un contratto countOfPosition sarà: -1. Se abbiamo due ingressi long countOfPosition sarà uguale a 2.
defParam cumulateOrders = true
if rsi[14] < 30 and not onMarket then
buy 1 contract at market
endif
// Se c'è una posizione aperta long e la chiusura è maggiore della chiusura precedente, allora ogni volta compriamo una quantità addizionale fino ad un massimo di tre posizioni.
if longOnMarket and countOfPosition <= 3 and close > close[1] then
buy 1 contract at market
endif
if close crosses under average[14] then
sell at market
endifSe non utilizziamo strategie con posizioni cumulate (in cui vogliamo controllare il numero dei contratti) per sapere se, ad esempio, non siamo a mercato (ossia non abbiamo posizioni aperte) possiamo scrivere sia: not onMarket, che countOfPosition = 0. Se invece siamo a mercato, possiamo scrivere sia: onMarket che countOfPosition = 1.
§ 2.7 – La funzione: countOfLong/ShortShares
CountOfPosition è un contatore generale. Con COUNTOFLONGSHARES e COUNT OF SHORTSHARES possiamo invece riferirci specificatamente a posizioni aperte lunghe o corte.
Questo contatore si usa spesso con posizioni cumulate.
Ecco un esempio in cui si entra al cross del macd e poi si incrementa la posizione se il prezzo continua a salire limitando l’accumulo di nuove posizioni ad un massimo prefissato.
myMacd = macd[12,26,9](close)
cLong = myMacd crosses over 0
if not longOnMarket and cLong then
buy 1 contract at market
endif
maxShares = countOfLongShares <3
if longOnMarket and maxShares and tradePrice(1)>10 then
buy 1 contracts at market
endifIn questo esempio invece si utilizza la funzione countOfShortShares per evitare lo stop&reverse di un trading system trend following in cui long e short hanno segnali indipendenti per cui le posizioni si potrebbero invertire senza attendere la chiusura naturale della posizione (spesso basta usare: “not onMarket” per evitare lo stop and reverse).
maFast = average[20](close)
maSlow = average[50](close)
buySignal = maFast crosses over maSlow
sellSignal = maFast crosses under maSlow
if buySignal and countOfShortShares = 0 and not longOnMarket then
buy 1 contract at market
endif
if sellSignal and countOfLongShares = 0 and not shortOnMarket then
sellshort 1 contract at market
endifIl codice sopra riportato permette nuove entrate solo se siamo flat, ecco perché basterebbe usare: not onMarket.
Se invece vogliamo chiudere prima una posizione e poi aprirne una di segno opposto in cui non passiamo da short a long nella stessa barra, ma prima chiudiamo lo short e poi entriamo long se il segnale buy è ancora valido, allora usiamo questa scrittura:
if buySignal and shortOnMarket then
exitShort at market
elsif buySignal and not onMarket then
buy 1 contract at market
endif
if sellSignal and longOnMarket then
sell at market
elsif sellSignal and not onMarket then
sellshort 1 contract at market
endifCapitolo 3: Funzioni avanzate
Dopo aver visto le istruzioni base di ProRealTime, il passo successivo è usare strumenti più “potenti” con cui poter analizzare il comportamento di un insieme di barre.
Le funzioni iterative permettono questo: ripetono un calcolo su più barre, sia implicitamente con SUMMATION (ProRealTime gestisce il ciclo internamente e noi vediamo solo il risultato), sia esplicitamente con FOR (decidiamo noi il numero di iteraziioni e cosa fare).
§ 3.1 – La funzione: summation
SUMMATION (somma totale) è una funzione abbastanza utilizzata che si usa per sommare un insieme di valori in un certo periodo temporale, ossia un determinato numero di barre. Nelle parentesi quadre si inserisce il numero di candele da sommare e nelle parentesi rotonde cosa si vuole sommare.
Un semplice esempio chiarisce meglio le cose: ecco come possiamo sommare i volumi delle ultime 10 candele:
summation[10](volume)In questo esempio controlliamo quante delle ultime quattro barre hanno avuto una chiusura positiva:
numeroBarre = 4 // definizione del numero di barre da analizzare
candelaRialzista = close > close[1]
controlloCandeleRialziste = summation[numeroBarre](candelaRialzista)Un utilizzo interessante è usare summation per vedere se si è verificata una determinata condizione negli ultimi periodi. Possiamo ad esempio verificare se almeno una volta ( > 0), nelle ultime 10 candele, c’è stato il cross del prezzo su una determinata media:
condizioneCross = summation[10](close crosses over average[20]) > 0Oppure possiamo verificare se ci sono state cinque candele consecutive di prezzo sopra una determinata media:
condizione = summation[5](close > average[20]) = 5In quest’altro esempio usiamo summation per evitare che ci sia stato un cross di un indicatore in un numero precedente di barre (con [1] partiamo dalla barra precedente al cross):
if mieCondizioni e summation[10](average[20] crosses over average[50][1]) = 0Oppure con summation possiamo creare delle condizioni da inserire in un TS.
In questo esempio utilizziamo summation per definire due scenari di mercato che diventano delle condizioni da inserire in un sistema.
x = summation[period](high > average[20])
y = summation[period](high < average[20])
if x > y then
flag = 1
endif
if y > x then
flag = -1
endifQui cerchiamo una zona del grafico in cui abbiamo le ultime dieci barre sopra una media, mentre le precedenti dieci barre erano sotto questa media:
avg200 = average[200,0](close)
c1 = summation[10](close > avg200) = 10
c2 = summation[10](close < avg200) = 10
cLong = c1 and c2[10]Quindi il segnale long scatta solo se:
– da 20 a 11 barre fa abbiamo 10 barre consecutive sotto la media
– da 10 barre fa ad oggi abbiamo 10 barre consecutive sopra la media
Se vogliamo delle condizioni meno restrittive in cui oggi siamo sopra la media, mentre prima del cross eravamo sotto la media per 10 candele:
cLong = close crosses over avg200 and summation[10](close < avg200)[1] = 10Questo codice in ProRealTime usa summation per cercare un pattern specifico: 3 candele verdi consecutive seguite da una candela rossa (la candela attuale è ribassista):
myRed = close < open
my3Green = summation[3](close > open) = 3
cExit = my3Green[1] and myRedVediamo infine un esempio di come si costruisce con summation l’uscita di un trading system con l’oscillatore bPercent: usciamo quando in due delle ultime quattro barre l’indicatore è sotto un certo livello.
bollPeriod = 20
mm = 2
bollMA = average[bollPeriod](close)
stDev = std[bollPeriod]
bollUp = bollMA + mm * stDev
bollDown = bollMA - mm * stDev
bollPercent = 100 * (close - bollDown) / (bollUp - bollDown)
period = 4
levelPercent = 40
candleNum = 2 //due candele su quattro
cExitBoll = summation[period](bollPercent < levelPercent) = candleNum§ 3.2 – I cicli FOR
Il ciclo FOR è una delle funzioni avanzate del linguaggio ProBuilder di ProRealTime. Permette di ripetere un blocco di istruzioni un numero definito di volte, scorrendo un intervallo di valori.
L’istruzione FOR è utilizzata quando si desidera richiamare uno per uno una serie finita di elementi. Questa è la sintassi di base:
FOR variabile = valore_inizio TO valore_fine [STEP passo] DO
// istruzioni
NEXTIl ciclo più semplice: somma i valori di chiusura delle ultime n barre e li accumula in una variabile.
n = 10
somma = 0
FOR i = 0 TO N - 1 DO
Somma = somma + Close[i]
NEXT
risultato = sommaQui contiamo quante delle ultime n barre sono rialziste (chiusura > apertura) e quante ribassiste. Ottimo punto di partenza per capire come usare IF dentro un FOR.
N = 20
contaRialzo = 0
contaRibasso = 0
contaNeutra = 0
FOR i = 0 TO N - 1 DO
if close[i] > open[i] then
contaRialzo = contaRialzo + 1
elsif close[i] < open[i] then
contaRibasso = contaRibasso + 1
else
contaNeutra = contaNeutra + 1
endif
NEXT
// Percentuale di barre rialziste
percRialzo = (contaRialzo / N) * 100
RETURN percRialzoPossiamo identificare il numero di barre tra un massimo ed un minimo all’interno di un range:
massimoX = highest[90](high)
minimoX = lowest[90](low)
highBar = 0
lowBar = 0
FOR i = 0 TO 89 DO
if high[i] = massimoX then
highBar = barIndex[i]
endif
if low[i] = minimoX then
lowBar = barIndex[i]
endif
if highBar > 0 and lowBar > 0 then
BREAK
endif
NEXT
diff = abs(highBar - lowBar)Possiamo ad esempio cercare la candela con il range più grande di un determinato periodo per filtrare il mercato. Qui entriamo a mercato quando la candela corrente ha un range maggiore di tutti i 20 range precedenti:
biggestRange = 0
FOR i = 1 TO 20 DO
biggestRange = max(biggestRange, range[i])
NEXT
if range > biggestRange and not onMarket and close > open then
buy 1 contract at market
endifMolte volte possiamo anche evitare il loop e possiamo scrivere:
biggestRange = highest[20](range[1])
if range > biggestRange and not onMarket and close > open then
buy 1 contract at market
endifPossiamo contare, per crearci ad esempio una condizione da inserire in un sistema, il numero di barre rialziste di un periodo. Questo codice conta quante candele rialziste ci sono nelle ultime 100 barre
periodo = 100
valore = 0
FOR i = 0 TO periodo-1 DO
if close[i] > open[i] then
valore = valore + 1
endif
NEXT
RETURN valoreQuando cerchiamo di uscire da una istruzione FOR senza arrivare all’ultimo valore utilizziamo BREAK. Ecco un esempio che somma il numero dei rialzi consecutivi delle ultime 20 barre fermandosi in caso di massimi inferiori:
indicator = 0
for i = 0 to 19 do
if high[1] > (high[i] + 1) then
indicator = indicator + 1
else
break
endif
next
return indicatorBisogna ricordare infine che ogni barra riesegue tutto il loop. Per questo è consigliato di utlizzare l’istruzione FOR con moderazione per non eseguire un numero troppo elevato di calcoli.
§ 3.3 – Le funzioni: MIN – MAX
Le funzioni MIN e MAX sono fondamentali per analizzare il comportamento del prezzo: servono a individuare rispettivamente il valore minimo e massimo all’interno di determinati valori.
La differenza principale con highest[n] e lowest[n], è che queste funzioni guardano tutte le ultime n barre di una serie storica, mentre max(a, b) e min(a, b) confrontano soltanto i valori specifici che gli passiamo.
MAX(open, close) // prendiamo il massimo tra apertura e chiusuraVediamo la differenza con highest che invece ricerca un valore in un range:
highestHigh = highest[20](high) // cerca il massimo tra gli ultimi 20 massimi
if close > highestHigh then
buy 1 contract at market
endifUn utilizzo molto frequente di min/max è quello di confrontare più valori contemporaneamente.
In questo esempio usiamo questa funzione per scegliere lo stop loss dinamico, posto inizialmente sul minimo e poi aggiornato a minimi crescenti (snippet 317):
if not onMarket then
mySL = 0
endif
if condizioniLong then
buy 1 contract at market
mySL = Low[1]
endif
if longOnMarket then
mySL = MAX(mySL, Low[1])
sell at mySL STOP
endifUn altro utilizzo interessante è quello che ci consente di scegliere uno stop loss tra il più distante di due valori:
// Stop Loss = MAX tra il minimo delle ultime 2 barre e la media mobile a 100 periodi
minUltime2Barre = lowest[2](low)
media100 = average[100](close)
stopLoss = MAX(minUltime2Barre, media100)Un ulteriore utilizzo che segnaliamo è quello per costruire dei pattern.
Qui definiamo la figura dell’Hammer nell’analisi candlestick:
// Corpo piccolo in alto, lunga ombra inferiore, ombra superiore quasi assente
corpo = ABS(CLOSE - OPEN)
ombraInf = MIN(CLOSE, OPEN) - LOW
ombraSup = HIGH - MAX(CLOSE, OPEN)
range = HIGH - LOW
hammer = corpo <= range * 0.3 AND ombraInf >= corpo * 2 AND ombraSup <= corpo * 0.5
RETURN hammerCapitolo 4: Istruzioni di controllo
Vediamo ora altre funzioni importanti che si usano nella programmazione di trading system, ossia le istruzioni di controllo: ONCE e QUIT. Queste controllano come e quando il codice viene eseguito, senza fare calcoli.
§ 4.1 – La funzione ONCE
L’istruzione ONCE è un’istruzione di inizializzazione, è una direttiva di quando eseguire il codice: serve a dichiarare una variabile “una sola volta”.
Il linguaggio proBuilder legge il codice per un numero di volte pari al numero di barre presenti sul grafico. Con “ONCE” invece leggiamo il codice soltanto la prima volta, in quanto, in caso di rilettura del codice da parte del linguaggio, saranno conservati i valori calcolati durante la lettura precedente.
Vediamo un esempio con un contatore progressivo:
ONCE count = 0
count = count + 1
RETURN countEsaminiamo cosa succede in questo codice:
– prima barra → count = 0
– poi diventa 1
– seconda barra → diventa 2
– terza barra → diventa 3 …
Senza ONCE, il contatore si resetterebbe sempre.
Quasi sempre once si usa per inizializzare una variabile (con valore iniziale = 0) e poi assegnargli il valore in seguito. In questo esempio si memorizza il prezzo di chiusura della prima barra che rompe il massimo delle 20 barre precedenti (solo il primo breakout finché non si resetta entryPrice):
ONCE entryPrice = 0
if close > highest[20](high[1]) and entryPrice = 0 then
entryPrice = close
endif
RETURN entryPriceUn esempio molto didattico è inizializzare il capitale iniziale di una strategia e poi calcolare l’equity corrente (senza once capital verrebbe sempre riassegnato a 10000 ad ogni barra). Invece con once capital = 10000 viene memorizzato solo all’inizio e poi cambia solo strategyProfit.
ONCE capital = 10000
equity = capital + strategyProfit
RETURN equity§ 4.2 – La funzione QUIT
QUIT è un controllo di flusso, interrompe l’esecuzione del codice sulla barra corrente.
Con la funzione QUIT possiamo bloccare in qualsiasi momento una strategia per differenti ragioni. Una motivazione può essere temporale:
if date > 20260101 then // interrompi la strategia il 1 gennaio 2026
QUIT
endifOppure per motivazioni monetarie:
capitale = 10000
equity = capitale + strategyProfit
if equity <= 9000 then
QUIT
endifCapitolo 5: Il concetto di flag
In ProRealCode (il linguaggio di programmazione di ProRealTime), un flag è semplicemente una variabile che funziona come un interruttore: vale 0 (spento/falso) oppure 1 (acceso/vero).
Un flag permette di ricordare se una certa condizione si è verificata e di condizionare il comportamento del codice nelle barre successive.
Di solito viene utilizzato così:flag = 0 → la condizione non è attiva / l’evento non è ancora avvenutoflag = 1 → la condizione è attiva / l’evento è avvenuto
In pratica, il flag permette alla strategia di “ricordare” che qualcosa è successo e di utilizzare questa informazione per decidere cosa fare nelle barre successive.
ONCE flag = 0
// Se il prezzo supera il massimo delle 20 barre precedenti attiviamo il flag.
if close > highest[20](high[1]) then
flag = 1
endif
// Se il flag è attivo, possiamo eseguire un'azione.
if flag = 1 and not onMarket then
buy 1 contract at market
flag = 0
endifIn questo esempio didattico di flag, vengono mostrate chiaramente le tre fasi: inizializzazione → attivazione → utilizzo → reset.
// Inizializzazione: alla prima barra la flag viene impostata a 0
if barIndex = 0 then
flagAcquisto = 0
endif
// Attivazione della flag se il prezzo supera la media mobile a 20 periodi
if close > average[20](close) and flagAcquisto = 0 then
flagAcquisto = 1
endif
// Utilizzo della flag se la flag è attiva
if flagAcquisto = 1 and not longOnMarket then
buy 1 contract at market
endif
// Reset della flag: quando il prezzo scende sotto la media la flag torna a 0
if close < average[20](close) then
flagAcquisto = 0
endif§ 5.1 – Flag binario
La forma più comune dei flag è quella binaria, con cui attiviamo o meno determinati comportamenti in base a determinate condizioni.
In questo esempio attiviamo il flagLong se l’indicatore RSI supera al rialzo la soglia dei 50.
flagLong = 0
if crossOver(RSI(close,14), 50) then
flagLong = 1
endifI flag sono il fondamento di qualsiasi strategia complessa in ProRealCode: gestione degli ordini, filtri anti-ripetizione, trailing stop condizionali ed altro. Padroneggiarli significa poter costruire sistemi di trading robusti e controllabili barra per barra.
Vediamo alcuni modi equivalenti di scrivere un codice.
Nel primo esempio memorizziamo il prezzo di un breakout (myBuy) mentre nel secondo esempo per ricordare il breakout utilizziamo un flag (cExit = 1).
hh = highest[10](high)
nBar = 10
// Individuiamo il breakout del massimo delle 10 barre precedenti
if close crosses over hh[1] then
myBuy = close
crossOverIndex = barIndex
endif
// Il segnale rimane valido per 10 barre
if barIndex >= (crossOverIndex + nBar) then
myBuy = 0
endifOppure possiamo scrivere il codice con un flag utilizzando valori booleani:
hh = highest[10](high)
nBar = 10
// Attivazione del flag quando avviene il breakout
if close crosses over hh[1] then
cExit = 1
crossOverIndex = barIndex
endif
// Dopo 10 barre il segnale non è più valido
if barIndex >= (crossOverIndex + nBar) then
cExit = 0
endifIn questo esempio tratto da un trading system usiamo dei flag binari per verificare se la distanza tra il massimo raggiunto nelle ore notturne e la chiusura di ieri è maggiore di un determinato livello.
// Salva la chiusura della barra delle 17:30
if time = 173000 then
chiusuraIeri = close
endif
// Quando non siamo a mercato, resettiamo le variabili
if not onMarket then
maxPrice = 0
minPrice = close
highReached = 0
lowReached = 0
endif
// Delta di 50 punti come distanza di riferimetno
delta = 50 * pipsize
// Fascia oraria di controlo: dalle 01:00 alle 08:00
if time > 010000 AND time < 080000 then
// Memorizza il massimo raggiunto durante la fascia oraria
maxPrice = MAX(maxPrice, high)
// Verifica se il massimo ha superato la chiusura delle 17:30
if maxPrice - chiusuraIeri > delta then
pointsReached = 1
else
pointsReached = 0
endif
endif§ 5.2 – Flag contatore
Un flag contatore è una variabile numerica che non si limita a dire “sì/no” come un flag binario, ma conta quante barre sono trascorse da quando si è verificato un evento: è lo strumento fondamentale ogni volta che il tempo, misurato come numero di barre, diventa parte della logica di trading.
Il flag binario risponde alla domanda: “è successo qualcosa?”.
Il flag contatore risponde a domande più complesse come:
– “Quante barre fa è avvenuto il segnale?”
– “Siamo ancora dentro la finestra temporale valida?”
– “Dopo quante barre devo uscire dalla posizione?”
– “Il pattern deve completarsi entro n candele?”
Senza un contatore, ProRealCode non ha modo di misurare il tempo trascorso tra due eventi: ogni barra ricomincia da zero e non ricorda nulla di ciò che è venuto prima.
Vediamo ora tre utilizzi pratici del flag contatore:
– Uscita a tempo: entriamo a mercato e vogliamo uscire automaticamente dopo un numero fisso di candele, indipendentemente dal prezzo.
Il flag memorizza uno stato e barreLong misura quante barre sono trascorse.
once flagLong = 0
once barreLong = 0
// Entrata: close incrocia al rialzo la media a 20 periodi
if close crosses over average[20](close) and flagLong = 0 and not longOnMarket then
buy 1 contract at market
flagLong = 1
barreLong = 0
endif
// Conta le barre trascorse dall'ingresso
if flagLong = 1 then
barreLong = barreLong + 1
endif
// Uscita dopo 10 barre
if flagLong = 1 and barreLong >= 10 then
sell at market
flagLong = 0
barreLong = 0
endif– Finestra di conferma entro n barre: dato un segnale, aspettiamo che un secondo segnale arrivi entro una finestra temporale. Se non arriva in tempo, il primo segnale decade. In questo esempio un flag mantiene in memoria un primo segnale in attesa di una conferma, con una finestra temporale di 5 barre.
once flagPrimoSegnale = 0
once contatoreFinestra = 0
// Primo segnale: RSI esce dall'ipervenduto
if RSI[14](close) crosse over 30 and flagPrimoSegnale = 0 then
flagPrimoSegnale = 1
contatoreFinestra = 0
endif
// Conteggio barre della finestra
if flagPrimoSegnale = 1 then
contatoreFinestra = contatoreFinestra + 1
endif
// Conferma: prezzo sopra media 50
if flagPrimoSegnale = 1 and close > average[50](close) and not longOnMarket then
but 1 contact at market
flagPrimoSegnale = 0
contatoreFinestra = 0
endif
// Timeout dopo 5 barre
if contatoreFinestra >= 5 then
flagPrimoSegnale = 0
contatoreFinestra = 0
endif– Blocco dopo un segnale: dopo un segnale, impediamo che il sistema generi nuovi ordini per n barre. Essenziale per evitare di rientrare immediato dopo uno stop.
once flag = 0
once tally = 0
// Contatore barre dopo ingresso, quando tally arriva a 8 il flag viene disattivato
if flag = 1 then
tally = tally + 1
if tally >= 8 then
flag = 0
tally = 0
endif
endif
// Condizione di ingresso
if flag = 0 and not longOnMarket and RSI[14](close) < 25 then
buy 1 contract at market
flag = 1
tally = 0
endifUn errore da evitare quando si usa il flag è dimenticare di azzerare il contatore al reset della flag.
if flagLong = and barreLong >= 10 then
sell at market
flagLong = 0 // azzerare il contatore!
barreLong = 0 // azzerare il contatore!
endifCome abbiamo visto i flag sono fondamentali quando si inizia a scrivere del codice minimamente più avanzati del livello base.
Un consiglio per rendere più chiaro il codice è di dare al flag un nome che ricordi la funzione per il quale si utilizza.
Capitolo 6: Date e tempo in PRT
ProRealTime mette a disposizione un ricco insieme di funzioni per lavorare con date e orari direttamente nel codice ProBuilder. Queste funzioni sono fondamentali per costruire strategie che rispettino le sessioni di mercato, filtrino per giorno della settimana, o reagiscano a momenti specifici del calendario.
Le categorie principali sono:
Date — Le funzioni Year, Month e Day restituiscono il componente della data della barra corrente. Ad esempio Month = 12 è vero solo per le barre di dicembre.
Giorno della settimana/anno — DayOfWeek restituisce un numero da 1 (lunedì) a 7 (domenica), permettendo di escludere facilmente certi giorni dalla strategia. DayOfYear conta i giorni dall’inizio dell’anno.
Time — Hour, Minute e Second restituiscono l’ora della barra. Sono indispensabili per delimitare le sessioni operative: apertura europea, americana …
§ 6.1 – La funzione: Date
In ProRealTime la funzione DATE permette di identificare la data della barra corrente durante l’esecuzione di un indicatore o di una strategia automatica.
La funzione DATE è molto utile quando vogliamo far eseguire una strategia solo in determinati giorni o periodi temporali. Può essere usata per diversi scopi come:
– attivare/disattivare strategie in date specifiche
– escludere determinati giorni
– effettuare backtest su intervalli precisi
– creare logiche legate a eventi temporali
La funzione DATE restituisce la data della barra corrente nel seguent formato: YYYYMMJJ (anno, mese, giorno). In ProRealTime DATE indica la data di chiusura della candela, cioè il momento in cui la candela si “chiude” formalmente.
// Esempio: filtrare solo le barre di gennaio 2024
if DATE >= 20240101 and DATE <= 20240131 then
endifPossiamo usare DATE per filtrare un sistema da un certo perido:
if Date > 20240101 then
buy 1 contract at market
endifOPENDATE restituisce la data di apertura della barra, anch’essa nel formato YYYYMMJJ.
La distinzione diventa rilevante su timeframe multiday o settimanali/mensili: una barra settimanale, ad esempio, apre di lunedì e chiude di venerdì. OPENDATE restituisce il lunedì, DATE restituisce il venerdì.
Sul daily e intraday sono intercambiabili, su weekly/monthly fanno la differenza.
Su weekly, OPENDATE restituisce il lunedì di apertura e DATE il venerdì di chiusura. Su monthly, OPENDATE restituisce il primo giorno lavorativo del mese, mentre DATE è tecnicamente la data di chiusura della barra
§ 6.2 – La funzione: DayOfWeek
DAYOFWEEK restituisce il giorno della settimana relativo alla data di chiusura della barra.
Si parte da domenica = 0, lunedi = 1, martedi = 2, mercoledi = 3 ….
Vediamo alcuni esempi. Qui operiamo il lunedi:
if dayOfWeek = 1 then
buy 1 contract at market
endifIn ProRealTime DAYOFWEEK restituisce il numero del giorno della settimana relativo alla data corrente della barra, mentre OPENDAYOFWEEK invece restituisce il giorno della settimana relativo alla data di apertura della barra (stessa logica della coppia DATE/OPENDATE, applicata al giorno della settimana).
In questo esempio si opera dal lunedi al venerdi.
if dayOfWeek >= 1 and dayOfWeek <= 5 then
buy 1 contract at market
endfiInteressante anche l’utilizzo di dayOfWeek per evitare di operare in determinati giorni della settima. Se, ad esempio, un backtest di un trading system ci dice di non operare il venerdi allora inseriamo questo codice:
if dayOfWeek = 5 and time >= 220000 and longOnMarket then
sell at market
endifSu compressione settimanale DAYOFWEEK restituisce sempre 5 (venerdì) e OPENDAYOFWEEK restituisce sempre 1 (lunedì) — quindi su quel timeframe sono di fatto costanti e poco utili singolarmente.
// Eseguire logica solo il lunedì
if opendayOfWeek = 1 then
// strategia del lunedì mattina
endif
// Chiudere posizioni il venerdì
if dayOfWeek = 5 then
sell at market
endifSu timeframe intraday brevi, ad esempio 1 minuto, OPENDAYOFWEEK e DAYOFWEEK coincidono nella maggior parte dei casi, perché apertura e chiusura della candela cadono nello stesso giorno.
Se vuoi agire in corrispondenza dell’apertura di una sessione, ad esempio entrare solo all’apertura del mercato del martedì, usa OPENDAYOFWEEK insieme a un controllo sull’orario, ad esempio TIME.
Se invece vuoi associare una regola al giorno di chiusura della candela, usa DAYOFWEEK; questa distinzione è rilevante soprattutto per candele che attraversano la mezzanotte. Per una condizione basata sul giorno corrente in tempo reale, indipendente dalla candela, usa CURRENTDAYOFWEEK.
§ 6.3 – La funzione: Time
In ProRealTime, TIME e OPENTIME sono costanti temporali per strategie e indicatori, ma si riferiscono a momenti diversi della barra corrente.
TIME restituisce l’orario di chiusura della barra nel formato HHMMSS.
OPENTIME restituisce l’orario di apertura della barra corrente, anche esso nel formato HHMMSS (es. 093000 per 09:30:00). Non indica il momento in cui il sistema è entrato a mercato, ma l’inizio temporale della candela.
Su un grafico a 1 ora, la candela che apre alle 16:00:00 ha OPENTIME = 160000 e chiude alle 17:00:00, quindi ha TIME = 170000. Di conseguenza, OPENTIME = 170000 precede temporalmente TIME = 170000 di un’ora.
Se scrivi OPENTIME = 170000, identifichi l’inizio della candela delle 17:00. Se scrivi TIME = 170000, identifichi la candela che chiude alle 17:00, quindi quella aperta alle 16:00. In pratica, su grafico orario, TIME = 170000 è associato alla chiusura della candela 16:00–17:00.
Ecco un esempio dove chiudiamo tutte le posizioni dopo le 17:30:
if time >= 173000 then
sell at market
exitshort at market
endifSpesso usiamo time per definire delle fascie di tempo in cui operare, ecco un esempio:
cTime = (time>=000300 and time <153000) or (time >= 160000 and time <= 213000)Possiamo anche fissare il massimo ed il minimo in una certa ora:
if time = 080000 then
max = high
low = low
endifIn questo esempio compriamo all’inizio di una candela orario ed usciamo all’inizio di un altra candela oraria.
if openTime = 080000 and not onMarket then
buy 1 contract at Market
endif
if openTime = 170000 and longOnMarket then
sell at market
endif§ 6.4 – La funzione DCLOSE
In ProRealTime le funzioni giornaliere come DClose, DHigh, DLow e DOpen permettono di richiamare i dati del timeframe daily anche se stiamo lavorando su grafici intraday (es. 5 minuti, 15 minuti, 1 ora).
Queste funzioni sono molto utili in quanto consentono di integrare informazioni della giornata corrente o precedente all’interno di strategie intraday.
– DClose(n) è la chiusura giornaliera
– DHigh(n) è il massimo giornaliero
– DLow(n) è il minimo giornaliero
– DOpen(n) è l’ apertura giornaliera
Queste quattro funzioni restituiscono i dati della sessione giornaliera (daily), indipendentemente dal timeframe del grafico su cui si sta lavorando.
In particolare sono importanti quando si opera su grafici intraday ma si ha bisogno di fare riferimento ai valori del giorno.
– DHIGH e DLOW si aggiornano in tempo reale man mano che il prezzo si muove.- DCLOSE durante la seduta corrente coincide con il prezzo corrente (close dell’ultima barra).- DOPEN rimane fisso per tutta la seduta.
Nel seguente esempio compriamo quando oggi abbiamo una chiusura più alta del massimo di ieri.
prevHigh = DHigh(1)
prevLow = DLow(1)
if not longOnMarket and close > prevHigh then
buy at market
endifIn ProRealTime DHigh(0) rappresenta il massimo del giorno corrente aggiornato in tempo reale. Quindi se durante la giornata viene fatto un nuovo massimo, DHigh(0) cambia immediatamente, DHigh(1) invece è fisso.
Ricordiamo che con i CFD la chiusura non è alle 17:30, ma spesso è all’una di notte.
In ogni caso possiamo scegliere quando deve terminare la giornata con Dclose e per farlo possiamo utilizzare questo utile codice del moderatore dl forum di ProRealCode Ivan Gonzales che si trova nel forum inglese di ProRealTime.
once todayOpen=open
once todayHigh=high
once todayLow=low
once todayClose=close
once Dailyclose=todayClose
once DailyOpen=todayopen
once DailyHigh=todayHigh
once DailyLow=todayLow
checkTime = 010000
if opentime>=checktime and opentime[1]<checktime then
Dailyclose=todayClose
DailyOpen=todayopen
DailyHigh=todayHigh
DailyLow=todayLow
todayOpen=open
todayHigh=high
todayLow=low
todayClose=close
else
todayHigh=max(high,todayHigh)
todayLow=min(low,todayLow)
todayClose=close
endif
return dailyClose as "Dclose", dailyHigh as "DHigh", dailyLow as "Dlow", dailyOpen as "Dopen"§ 6.5 – Chiusure giornaliere
Abbiamo visto che è molto importante nella costruzione di sistemi riferirsi alla chiusura di giorni precedenti. Questo lo possiamo fare come visto con la funzione DClose, ma anche in altri modi.
Per riferirci al close di ieri potremmo anche scrivere: close = 173000, ma questo solo se oggi non sono ancora le 17:30, altrimenti, se sono ad esempio le 18:00 ci riferiremmo alla chiusura di oggi.
Quindi per avere sempre il close di ieri usiamo questo codice:
once prevClose = 0 // formula per determinare un orario del giorno prima
once chiusura = 0
if time = 173000 then
prevClose = chiusura
chiusura = close
endif
if close > prevClose then
sell at market
exitShort at market
endifEntrata al superamento del massimo di tre giorni fa:
once close1 = 0
once close2 = 0
once close3 = 0
if time = 173000 then
close3 = close2
close2 = close1
close1 = close
endif
if close3 <> 0 and close > close3 then
buy at market
endifCon la stessa logica ci possiamo riferire a massimi e minimi di una determinata fascia temporale di ieri:
once HH = high //today's HIGH
once LL = low //today's LOW
once yHH = high //yesterday's HIGH
once yLL = low //yesterday's LOW
if openTime >= 153000 and openTime <= 220000 then
if openTime = 153000 then
yHH = HH
yLL = LL
HH = high
LL = low
endif
HH = max(HH,high)
LL = min(LL,low)
endif§ 6.6 – Chiusura settimanale e mensile
Oltre alle chiusure dei precedenti giorni, è utile anche potersi riferire a quelle settimanali e mensili.
In questo modo ci riferiamo invece alle chiusure settimanali:
once lastWeekBarIndex = 0
once weeklyHigh = 0
once weeklyLow = 0
dailyHigh = DHigh(1)
dailyLow = DLow(1)
// nuovo inizio settimana
if dayOfWeek < dayOfWeek[1] then
barsInWeek = BarIndex - lastWeekBarIndex
weeklyHigh = highest[barsInWeek](dailyHigh)
weeklyLow = lowest[barsInWeek](dailyLow)
lastWeekBarIndex = barIndex
endif
graphOnPrice weeklyHigh coloured(189,252,201)
graphOnPrice weeklyLow coloured(255,215,0)Entrata con massimo superiore a quello di due settimane fa:
once lastWeekBarIndex = 0
once weekHigh = 0
once prev2WeekHigh = 0
dailyHigh = DHigh(1)
// rilevo cambio settimana
if dayOfWeek < dayOfWeek[1] then
// salvo il massimo di 2 settimane fa
prev2WeekHigh = weekHigh
// reset settimana corrente
weekHigh = 0
lastWeekBarIndex = BarIndex
endif
// costruzione massimo settimana corrente
weekHigh = max(weekHigh, dailyHigh)
// segnale di ingresso
if not LongOnMarket and close > prev2WeekHigh and prev2WeekHigh <> 0 then
buy at market
endifEntrata con massimo superiore a quello di 4 settimane fa:
once weekHigh = 0
once w1 = 0
once w2 = 0
once w3 = 0
once w4 = 0
dailyHigh = DHigh(1)
// cambio settimana
if dayOfWeek < dayOfWeek[1] then
// shift storico settimane
w4 = w3
w3 = w2
w2 = w1
w1 = weekHigh
// reset settimana corrente
weekHigh = 0
endif
// costruzione settimana corrente
weekHigh = max(weekHigh, dailyHigh)
// segnale: breakout della settimana di 4 settimane fa
if not longOnMarket and close > w4 and w4 <> 0 then
buy at market
endifIn questo modo ci riferiamo invece alle chiusure mensili:
once lastMonthBarIndex = 0
once monthlyHigh = 0
once monthlyLow = 0
dailyHigh = DHigh(1)
dailyLow = DLow(1)
// cambio mese
if month <> month[1] then
barsInMonth = BarIndex - lastMonthBarIndex
monthlyHigh = Highest[barsInMonth](dailyHigh)
monthlyLow = Lowest[barsInMonth](dailyLow)
lastMonthBarIndex = BarIndex
endif
graphOnPrice monthlyHigh coloured(189,252,201)
graphOnPrice monthlyLow coloured(255,215,0)Cosi entriamo se abbiamo un massimo superiore a quello del mese precedente:
once lastMonthBarIndex = 0
once monthHigh = 0
once prevMonthHigh = 0
dailyHigh = DHigh(1)
// rilevo cambio mese
if month <> month[1] then
barsInMonth = BarIndex - lastMonthBarIndex
prevMonthHigh = monthHigh
monthHigh = 0
lastMonthBarIndex = BarIndex
endif
// costruisco massimo mese corrente
monthHigh = max(monthHigh, dailyHigh)
// segnale di ingresso
if not longOnMarket and close > prevMonthHigh and prevMonthHigh <> then
buy at market
endifCapitolo 7: MultiTimeframe
Un modo ulteriore e più semplice di riferirsi ai dati passati è usare la funzione che ci mette a dispozione proRealTime che si chiama multiTimeFrame (MTF).
Con questa funzione possiamo:
– utilizzare contemporaneamente dati che provengono da diversi orizzonti temporali
– riferirci ai dati passati
La multitimeframe analysis in ProRealTime serve a usare più timeframe nello stesso sistema o indicatore. Un esempio: trend principale su timeframe ad 1 ora, ingresso sul timeframe a 5 minuti.
Il Multi-Timeframe trading (MFT) infatti è una metodologia che prevede l’analisi dello stesso strumento su più orizzonti temporali contemporaneamente, con l’obiettivo di aumentare la precisione delle operazioni filtrando i segnali in base al contesto più ampio.
Il principio di base è l’approccio top-down: dal timeframe superiore a quello inferiore.
L’idea fondamentale è semplice: il trend di un timeframe superiore ha la priorità:
Timeframe superiore (es. Daily): si stabilisce il contesto di mercato, siamo in tendenza rialzista, ribassista o in laterale? Si identificano i livelli chiave (supporti, resistenze, massimi/minimi significativi).
Timeframe intermedio (es. 4H -1H): si osserva la struttura di prezzo. Il movimento attuale è un ritracciamento del trend principale o un’inversione? Si individuano le zone di interesse per un possibile ingresso.
Timeframe inferiore (operativo, es. 15min – 5min): si cerca il segnale concreto di ingresso in accordo con i due livelli superiori, definendo stop loss e target in modo preciso.
Il MTF riduce significativamente i falsi segnali e migliora il rapporto rischio/rendimento perché si opera solo quando più timeframe convergono.
Un punto che potrebbe risultare problematico è che più livelli si consultano, più si rischia di trovare sempre una ragione per non entrare. Per ovviare a questo occorre definire regole chiare e gerarchiche prima di operare.
Attraverso la funzione multitimeframe (MTF) in ProRealTime possiamo combinare indicatori su timeframe diversi nello stesso grafico o strategia, migliorando la precisione dei segnali.
Nel timeframe superiore spesso si usa la funzione “updateonclose”
che permette di attendere la chiusura della barra superiore, evitando segnali instabili che si possono generare durante la formazione della candela.
Nel timeframe inferiore invece si indica il il tempo che si utilizza a livello operativo, oppure si scrive: “default”.
§ 7.1 – Trading MultiTimeFrame
Un utilizzo frequente del multitimeframe è quello di utilizzare i dati della giornata come riferimento generale ed i dati di un timeframe intraday per l’operatività (di solito si utilizzano due o tre orizzonti temporali), oppure due timeframe giornalieri abbastanza differenti.
Abbiamo detto che con il MTF possiamo riferirci con facilità ai dati passati.
Vediamo un esempio in cui compriamo se abbiamo un massimo superiore a quello della settimana scorsa:
TIMEFRAME(weekly, updateonclose)
weekHigh = high
weekLow = low
TIMEFRAME(default)
if close > weekHigh then // uso dei valori weekly sul timeframe operativo
buy at market
endifOccorre fare attenzione che scriviamo massimo e minimo della settimana scorsa senza [1] solo perchè usiamo updateonclose, altrimenti sarebbe sbagliato.
Con updateonclose il timeframe superiore viene aggiornato solo quando la barra di quel timeframe si chiude. Per questo, la barra corrente [0] rappresenta l’ultima barra chiusa, mentre [1] rappresenta quella precedente.
Senza UPDATEONCLOSE, la barra [0] è la settimana corrente, cioè quella ancora in formazione.
Se vogliamo conoscere la chiusura dell’altro ieri con updateOnClose:
TIMEFRAME(daily, updateonclose)
closedayBeforeYesterday = close[1]
//ricordimao che possiamo scrivere lo stesso con DClose:
closeYesterday = DClose(2)In sintesi:
– con updateOnClose: [0] è l’ultima settimana chiusa e [1] è la settimana precedente già chiusa,
– senza updateOnClose [0] è la settimana corrente, ancora in formazione e [1] è la settimana precedente, già chiusa.
Prendiamo un esempio intraday e supponiamo di avere un grafico a 15 minuti di default e di voler utilizzare i dati del timeframe orario.
1. Senza UPDATEONCLOSE [0] rappresenta la barra oraria corrente, che è ancora in formazione. Supponiamo che siano le 10:30. La situazione sarà:
barra oraria 10:00–11:00 → [0], ancora in formazione,
barra oraria 09:00–10:00 → [1], già chiusa,
barra oraria 08:00–09:00 → [2], già chiusa.
Quindi high[0] è il massimo della barra 10:00–11:00, ancora in formazione, mentre:
high[1] è il massimo della barra 09:00–10:00, già chiusa.
2. Con UPDATEONCLOSE il timeframe orario viene aggiornato solo quando la barra oraria si chiude.
Supponiamo sempre che siano le 10:30. La barra 10:00–11:00 è ancora in formazione, quindi non viene ancora considerata come nuova barra disponibile dal timeframe orario. Di conseguenza:
ultima barra oraria chiusa → 09:00–10:00 → [0]
barra oraria precedente → 08:00–09:00 → [1]
barra precedente ancora → 07:00–08:00 → [2]
Quindi high[0] è il massimo dell’ultima barra oraria chiusa, cioè 09:00–10:00 mentre high[1] è il massimo della barra oraria precedente, cioè 08:00–09:00.
In pratica [0] indica sempre la barra corrente del timeframe che stiamo utilizzando.
La differenza è quale barra viene considerata corrente:
senza UPDATEONCLOSE → [0] è la barra ancora in formazione;
con UPDATEONCLOSE → [0] è l’ultima barra chiusa disponibile.
Se vogliamo ad esempio comprare un minimo daily di ieri utilizzando un timeframe operativo a 15 minuti scriviamo:
timeframe(1 day, updateOnClose)
dailyHigh = high
dailyLow = low
timeframe(15 minutes)
cLong = close < dailyLow
if cLong then
buy 1 contract at market
set target pProfit 100
set stop pLoss 100
endif
graphOnPrice dailyLow coloured("red")
graphOnPrice dailyHigh coloured("green")Vediamo ora per chiarezza un altro confronto in cui trovare i massimi e minimi di un ora di ieri sia con la scrittura classica che con il multiTimeFrame:
if time = 090000 then // scrittura classica
prevHH = HH
prevLL = LL
HH = high
LL = low
endif
if time > 090000 and time <= 100000 then
HH = max(HH, high)
LL = min(LL, low)
endif
TIMEFRAME(1 hour, updateonclose) // scrittura MTF: alle 10:00 salviamo il range 09-10
if time = 100000 then
prevHH = high
prevLL = low
endif
TIMEFRAME(default)§ 7.2 – MTF esempi completi tipici
Vediamo ora come si applica il MTF con dei sistemi completi: uno di breakout e l’altro di falso breakout.
Una tecnica nota è quella di entrare al breakout di massimi importanti. In questo esempio compriamo il massimo di tre giorni fa.
defParam cumulateOrders = false
defParam preLoadBars = 10000
positionSize = 1
timeframe (daily, updateOnClose)
dayHigh = high
dayLow = low
myDailyHigh3 = highest[3](dayHigh)
timeframe (default)
cLong = close crosses over (myDailyHigh3)
if not onMarket and cLong then
buy positionSize contract at market
set stop pLoss 200
set target pProfit 270
endif
if longOnMarket and close crosses under dayLow then
sell positionSize contract at market
endif
graphonprice myDailyHigh3 coloured(255,20,147)Altro esempio classico. Spesso il mercato per trovare liquidita effettua dei falsi breakout di punti importanti, in particolare dei massimi e minimi importanti di giornata, oppure il massimo ed il minimo del giorno prima.
In questo sistema se il prezzo rompe il massimo di ieri e poi rientra allora si può pensare che sia un falso breakout e proviamo ad entrare short.
DEFPARAM CumulateOrders = False
timeframe(daily,updateOnClose)
massimoIeri = high
timeframe(3 minute)
if not onMarket and close[1] > massimoIeri and close < massimoIeri then
sellshort 1 contract at market
SET TARGET pPROFIT 100
SET STOP pLOSS 50
endif§ 7.3 – Numero operazioni timeframe superiore
In alcune strategie vogliamo utilizzare il MTF per consentire una sola operazione all’interno di ogni barra del timeframe superiore di riferimento.
Per ottenere questo comportamento possiamo utilizzare un flag che abilita l’ingresso all’inizio di ogni nuova barra del timeframe superiore e lo disattiva dopo l’esecuzione dell’operazione. La struttura di base è la seguente:
defParam cumulateOrders = False
timeframe(1hour, updateOnClose)
barCount = BarIndex
//----------------------------------------------------
timeframe(1 minute)
ONCE tradeOn = 1
// reset giornaliero
if intradayBarIndex = 0 then
tradeOn = 1
endif
// nuova barra oraria
if barCount <> barCount[1] then
tradeOn = 1
endif
// ingresso
if not onMarket and tradeOn = 1 and condizioneIngresso then
buy 1 contract at market
tradeOn = 0
endifQui non vogliamo più consentire una sola operazione per ogni ora, ma massimo 3 operazioni nella stessa ora.
defParam CumulateOrders = False
timeframe(1hour, updateOnClose)
barCount = BarIndex
//----------------------------------------------------
timeframe(1 minute)
ONCE tradeCount = 0
ONCE maxTrades = 3
tradeBar = barCount
// reset nuova ora
if tradeBar <> tradeBar[1] then
tradeCount = 0
endif
// reset inizio giornata
if intradayBarIndex = 0 then
tradeCount = 0
endif
// ingresso
if not onMarket and tradeCount < maxTrades and condizioneIngresso then
buy 1 contract at market
tradeCount = tradeCount + 1
endifCon il trading multitimeframe non ci limitiamo quindi a utilizzare informazioni provenienti da un timeframe superiore: possiamo anche stabilire quante operazioni consentire all’interno di ciascuna barra di quel timeframe.
Attraverso un flag possiamo limitare l’operatività a una sola operazione, mentre utilizzando un contatore possiamo impostare un numero massimo di operazioni.
Il timeframe superiore stabilisce la finestra temporale di riferimento, mentre flag e contatore determinano quanta operatività concedere all’interno di quella finestra.
In questo modo il MTF diventa non solo uno strumento per utilizzare informazioni provenienti da timeframe diversi, ma anche un efficace sistema per controllare e limitare la frequenza delle operazioni.
