iTime MQL5: La Funzione Più Sottovalutata per i Tuoi Indicatori | Manuel Trotta

iTime MQL5:
La Funzione Più Sottovalutata per i Tuoi Indicatori

In quasi ogni progetto MQL5 complesso che ho sviluppato — e Oracle v78 con i suoi 8.293 righe di codice è tra i più complessi — la gestione corretta del tempo tra timeframe diversi è stata una delle fonti principali di bug sottili.

Perché iTime Merita un Articolo Dedicato

In quasi ogni progetto MQL5 complesso che ho sviluppato — e Oracle v78 con i suoi 8.293 righe di codice è tra i più complessi — la gestione corretta del tempo tra timeframe diversi è stata una delle fonti principali di bug sottili.

Il problema non è che iTime sia difficile da capire. La sintassi è semplice. Il problema è che la gestione del tempo in MetaTrader 5 ha alcune peculiarità che non sono ovvie fino a quando non ti sbattono in faccia risultati inconsistenti durante il testing.

Questo articolo copre tutto quello che devi sapere: sintassi, casi d'uso, il bug del "recalculate" che causa valori stantii, la sincronizzazione multi-timeframe, e il pattern esatto usato in UlcerZenith v78 per gestire la matrice dei timeframe H1/H4/D1/W1 senza mai restituire dati obsoleti.

Sintassi di Base: iTime()

PROTOTIPO FUNZIONE
datetime iTime(string symbol, ENUM_TIMEFRAMES timeframe, int shift)
symbol — simbolo (es. "XAUUSD", _Symbol per il corrente)
timeframe — PERIOD_M1, PERIOD_H4, PERIOD_D1, ecc. | PERIOD_CURRENT per TF corrente
shift — indice della candela (0 = corrente, 1 = precedente, ecc.)
Ritorna — datetime di apertura della candela | 0 in caso di errore

Esempi di base:

// Apertura della candela H4 corrente sul simbolo corrente datetime h4Open = iTime(_Symbol, PERIOD_H4, 0); // Apertura della candela D1 di ieri datetime d1Yesterday = iTime(_Symbol, PERIOD_D1, 1); // Su un simbolo diverso datetime spxH4 = iTime("SP500m", PERIOD_H4, 0); // Differenza tra timeframe: quante ore fa è aperta la H4 corrente? double oraAperturaH4 = (datetime)TimeCurrent() - iTime(_Symbol, PERIOD_H4, 0); double oreTrascorse = oraAperturaH4 / 3600.0;

Fin qui, semplice. Ma vediamo i casi dove la funzione tradisce le aspettative.

Bug #1: iTime Restituisce 0

Il caso più comune: chiami iTime con un timeframe che non è quello corrente, e ottieni 0. Cosa succede?

MetaTrader 5 carica i dati storici per un timeframe solo quando qualcosa lo richiede. Se apri un indicatore su H1 ma non hai mai aperto un grafico H4 per quel simbolo, i dati H4 potrebbero non essere ancora in memoria quando il tuo codice li richiede la prima volta.

⚠️ CAUSA PIÙ COMUNE DI iTime = 0 I dati del timeframe richiesto non sono ancora stati caricati da MetaTrader. Questo accade tipicamente al primo avvio dell'indicatore, dopo aver cambiato simbolo, o su account con storico limitato. iTime non aspetta — se i dati non ci sono, restituisce 0 immediatamente.

La soluzione corretta non è controllare solo se iTime restituisce 0 — è usare CopyTime() che ti dice quanti elementi ha copiato e forzare il caricamento dei dati:

// PATTERN CORRETTO — attesa caricamento dati timeframe esterno bool EnsureTimeframeLoaded(string sym, ENUM_TIMEFRAMES tf, int barsRequired = 500) { datetime buf[]; int copied = CopyTime(sym, tf, 0, barsRequired, buf); return (copied >= barsRequired); } // In OnCalculate o OnInit: if (!EnsureTimeframeLoaded(_Symbol, PERIOD_H4, 200)) { // Dati non ancora pronti — usciamo e aspettiamo il prossimo tick return(0); // Forza re-calculate al prossimo aggiornamento } // Ora iTime è sicuro datetime h4T = iTime(_Symbol, PERIOD_H4, 0);

Bug #2: Il Valore "Stantio" in OnCalculate

Questo è il bug più insidioso — non causa un errore visibile, solo calcoli sbagliati che sembrano giusti.

Scenario: hai un indicatore su M5. Nel tuo OnCalculate usi iTime(_Symbol, PERIOD_H4, 0) per trovare quale H4 stai analizzando e leggere un buffer H4. Il problema: il buffer H4 del tuo indicatore ausiliario potrebbe non essere ancora stato ricalcolato per questa H4.

⚠️ SEQUENZA DI RICALCOLO Quando si apre una nuova candela H4 in MetaTrader 5, l'ordine di ricalcolo degli indicatori non è deterministico. Il tuo indicatore M5 potrebbe ricevere il tick prima che l'indicatore H4 ausiliario sia stato ricalcolato — quindi leggeresti il valore H4 della candela precedente.

Il fix è la "doppia verifica temporale" — confrontare l'orario della candela H4 corrente con l'orario memorizzato nell'ultimo ciclo:

// ============================================================ // Pattern "doppia verifica temporale" per indicatori multi-TF // Usato in UlcerZenith v78 per matrice H1/H4/D1/W1 // ============================================================ // Variabili globali dell'indicatore datetime g_lastH4Bar = 0; datetime g_lastD1Bar = 0; bool g_h4New = false; bool g_d1New = false; int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], /* ... altri parametri ... */) { // Verifica disponibilità dati if (rates_total < 2) return(0); // --- STEP 1: Ottieni orario candele sui timeframe superiori --- datetime curH4Time = iTime(_Symbol, PERIOD_H4, 0); datetime curD1Time = iTime(_Symbol, PERIOD_D1, 0); // Verifica che i dati siano stati caricati if (curH4Time == 0 || curD1Time == 0) return(0); // --- STEP 2: Rileva apertura nuova candela su ogni TF --- g_h4New = (curH4Time != g_lastH4Bar); g_d1New = (curD1Time != g_lastD1Bar); if (g_h4New) { g_lastH4Bar = curH4Time; // Ricalcola tutto ciò che dipende dall'H4 RecalcH4Components(rates_total); } if (g_d1New) { g_lastD1Bar = curD1Time; // Ricalcola filtri daily RecalcD1Regime(rates_total); } // Calcoli per ogni bar M5 solo dopo che i dati HTF sono aggiornati int startBar = (int)MathMax(prev_calculated - 1, 0); for (int i = startBar; i < rates_total; i++) { CalcCurrentBar(i, rates_total); } return(rates_total); }

iTime vs. iBarShift: La Coppia Inseparabile

iTime restituisce il datetime dato un indice. iBarShift fa l'opposto: dato un datetime, restituisce l'indice della candela corrispondente. Insieme, sono gli strumenti per mappare qualsiasi candela su qualsiasi timeframe.

PROTOTIPO iBarShift
int iBarShift(string symbol, ENUM_TIMEFRAMES tf, datetime time, bool exact=false)
exact=false → restituisce l'indice della candela più vicina (arrotonda in giù nel tempo)
exact=true → restituisce -1 se non esiste una candela con esattamente quel datetime
Ritorna -1 in caso di errore

Caso d'uso fondamentale: hai una candela M5 all'indice i. Vuoi sapere qual è la corrispondente candela H4 (e quindi leggere il valore dell'indicatore H4 in quella posizione):

// Mappare M5[i] → H4[j] // Usato in Oracle per applicare il gate H4 ai segnali M5/M30 void CalcCurrentBar(int i, int rates_total) { // Orario di apertura della candela M5 corrente datetime m5Time = iTime(_Symbol, PERIOD_M5, rates_total - 1 - i); // NOTA: se il tuo array time[] è in modalità as-series, // usa time[i] direttamente invece di iTime // Trova l'indice H4 che contiene questa candela M5 int h4Idx = iBarShift(_Symbol, PERIOD_H4, m5Time, false); if (h4Idx < 0) return; // Dato non disponibile // Leggi valore dell'indicatore H4 per questa posizione temporale double h4UlcerValue = GetH4UlcerAtShift(h4Idx); // Applica il gate: segnale M5 valido solo se H4 è in regime corretto bool h4GateOpen = (h4UlcerValue >= ULCER_THRESHOLD_LOW && h4UlcerValue <= ULCER_THRESHOLD_HIGH); if (h4GateOpen) { ProcessM5Signal(i); } }

Il Pattern Oracle: Matrice Multi-Timeframe con iTime

UlcerZenith v78 gestisce quattro timeframe contemporaneamente (H1, H4, D1, W1) per costruire la matrice di scoring. Ogni timeframe contribuisce con un punteggio parziale, e il segnale finale richiede che tutti i gate siano aperti simultaneamente.

La sfida implementativa: in un indicatore che gira su H4, i dati D1 e W1 cambiano raramente (una o due volte al giorno), mentre H1 cambia ogni ora. Ricalcolare tutto ad ogni tick sarebbe inefficiente. La soluzione usa iTime per rilevare le aperture di nuove candele solo sui timeframe dove è necessario:

// ============================================================ // MATRIX_GATE.mq5 — Matrice Multi-TF con iTime // Estratto semplificato da UlcerZenith v78 // ============================================================ struct TFState { ENUM_TIMEFRAMES tf; datetime lastBarTime; double cachedScore; bool needsUpdate; }; TFState g_matrix[4]; // H1, H4, D1, W1 void InitMatrix() { ENUM_TIMEFRAMES tfs[] = {PERIOD_H1, PERIOD_H4, PERIOD_D1, PERIOD_W1}; for (int k = 0; k < 4; k++) { g_matrix[k].tf = tfs[k]; g_matrix[k].lastBarTime = 0; g_matrix[k].cachedScore = 0.0; g_matrix[k].needsUpdate = true; } } void UpdateMatrix() { for (int k = 0; k < 4; k++) { datetime curTime = iTime(_Symbol, g_matrix[k].tf, 0); // Salta se dati non disponibili if (curTime == 0) { g_matrix[k].needsUpdate = false; continue; } // Aggiorna solo se è aperta una nuova candela su questo TF if (curTime != g_matrix[k].lastBarTime) { g_matrix[k].lastBarTime = curTime; g_matrix[k].needsUpdate = true; // Ricalcola score per questo TF g_matrix[k].cachedScore = CalcUlcerScore(g_matrix[k].tf); } else { g_matrix[k].needsUpdate = false; // Usa cache } } } double GetMatrixScore() { UpdateMatrix(); double score = 0.0; double weights[] = {0.15, 0.45, 0.25, 0.15}; // H1/H4/D1/W1 for (int k = 0; k < 4; k++) { score += g_matrix[k].cachedScore * weights[k]; } return(score); }

CopyTime() vs iTime(): Quando Usare Quale

FUNZIONE USO IDEALE VANTAGGIO LIMITE
iTime(sym, tf, shift) Singola candela specifica Sintassi concisa, accesso diretto Restituisce 0 senza errore dettagliato
CopyTime(sym, tf, start, cnt, buf[]) Buffer di N candele Restituisce n. elementi copiati, gestione errori migliore Overhead per singolo elemento
Time[shift] Candele del TF corrente Accesso diretto all'array, massima velocità Solo per il TF dell'indicatore/EA
iBarShift(sym, tf, time) Mappatura datetime → indice Fondamentale per sincronizzazione TF Operazione di ricerca, leggermente più lenta

Regola pratica usata in UlcerZenith v78: usa iTime per "rilevatori di nuova candela" (chiamati ad ogni tick per verificare se il TF superiore ha una nuova candela), usa CopyTime per il caricamento iniziale dei buffer, usa Time[] per tutti gli accessi interni al TF corrente.

Il Bug del T-Student e iTime: Il Fix di Oracle v78

Durante lo sviluppo di UlcerZenith v78, ho scoperto un bug sottile che coinvolgeva iTime indirettamente. L'indicatore usa una distribuzione T-Student per normalizzare i punteggi di rischio — ma la finestra di lookback era calcolata in modo errato su simboli con sessioni di trading discontinue (come XAUUSD che chiude nel weekend).

Il problema: quando calcolavi iTime(_Symbol, PERIOD_H4, lookbackBars) per trovare il datetime di inizio finestra, in presenza di gap settimanali il numero di "barre temporali" non corrispondeva al numero di "barre di prezzo" disponibili. Le H4 del fine settimana non esistono, ma il calcolo della finestra le contava come se esistessero.

// BUG: lookback conta barre di calendario (include weekend) datetime windowStart_WRONG = iTime(_Symbol, PERIOD_H4, lookbackBars); // FIX: usa iBars per verificare il conteggio reale disponibile int availBars = iBars(_Symbol, PERIOD_H4); int safeShift = MathMin(lookbackBars, availBars - 1); datetime windowStart_OK = iTime(_Symbol, PERIOD_H4, safeShift); // Verifica aggiuntiva: l'orario ottenuto è ragionevole? datetime currentH4 = iTime(_Symbol, PERIOD_H4, 0); double hoursDiff = (currentH4 - windowStart_OK) / 3600.0; double expectedHours = safeShift * 4.0; // H4 = 4 ore teoriche // Su XAUUSD H4: hoursDiff > expectedHours perché include gap weekend // Questo è il comportamento corretto — il fix è nell'indexing, non nel datetime Print("Gap settimanali inclusi: ", hoursDiff - expectedHours, " ore extra");

iTime in un Expert Advisor: Gestione delle Sessioni

In un EA, iTime è spesso usato per limitare il trading a specifiche sessioni di mercato. Pattern classico per evitare di operare nell'ultima ora di ogni candela H4 (zona di manipolazione più alta):

// ============================================================ // Session filter con iTime // Blocca segnali nell'ultima ora di una candela H4 // ============================================================ bool IsEntryAllowedBySession() { datetime h4OpenTime = iTime(_Symbol, PERIOD_H4, 0); datetime serverNow = TimeCurrent(); if (h4OpenTime == 0) return(false); // Dati non pronti // Secondi trascorsi dall'apertura della H4 corrente long secsElapsed = (long)(serverNow - h4OpenTime); long h4Duration = 4 * 3600; // 4 ore in secondi // Blocca se siamo nell'ultima ora della candela H4 bool inLastHour = (secsElapsed > (h4Duration - 3600)); // Blocca anche nella prima mezz'ora (spread alto all'apertura sessione) bool inFirstHalf = (secsElapsed < 1800); return(!inLastHour && !inFirstHalf); } // Utilizzo in OnTick() void OnTick() { if (!IsEntryAllowedBySession()) return; // ... elaborazione segnale ... }

Performance: iTime in Loop Intensivi

iTime chiama internamente la funzione di accesso ai dati storici di MT5. Per singole chiamate occasionali è velocissima. Per loop su migliaia di barre, può diventare un collo di bottiglia.

Regola pratica: se devi chiamare iTime per migliaia di barre consecutive nello stesso ciclo, usa CopyTime() una sola volta per caricare tutto l'array, e poi accedi all'array locale:

// LENTO: iTime chiamata n volte in loop for (int i = 0; i < 5000; i++) { datetime t = iTime(_Symbol, PERIOD_H4, i); // 5000 chiamate separate // ... elaborazione ... } // VELOCE: CopyTime una volta sola, poi accesso diretto all'array datetime timeBuf[]; ArraySetAsSeries(timeBuf, true); int copied = CopyTime(_Symbol, PERIOD_H4, 0, 5000, timeBuf); if (copied < 5000) return; // Dati insufficienti for (int i = 0; i < copied; i++) { datetime t = timeBuf[i]; // Accesso diretto — molto più veloce // ... elaborazione ... }

Il guadagno di velocità per loop su 5.000 barre è nell'ordine di 10-50x, a seconda della configurazione hardware e del numero di simboli aperti in MT5.

Checklist: Uso Corretto di iTime

✓ SEMPRE fare prima del primo uso di iTime su un TF esterno 1. Chiamare CopyTime() e verificare che restituisca il numero richiesto di elementi
2. Verificare che iTime non restituisca 0 — se restituisce 0, uscire e aspettare il prossimo tick
3. Usare ArraySetAsSeries() correttamente prima di CopyTime se vuoi indici in stile as-series
4. Non assumere che i dati siano pronti su simboli non aperti nel Market Watch
⊕ PATTERN CONSIGLIATO per rilevatori nuova candela Memorizza sempre l'ultimo orario noto di ogni TF in variabili statiche o globali. Confronta con iTime(0) ad ogni tick. Se cambiato = nuova candela = ricalcola. Questo è l'approccio usato in UlcerZenith v78 per tutti e quattro i timeframe della matrice.
⚠️ EVITARE Non usare iTime come sostituto di Time[] per accessi al TF corrente — Time[] è sempre più veloce.
Non fare iBarShift senza controllare il risultato per -1.
Non assumere che shift 0 = "ultima candela chiusa" — shift 0 è la candela corrente (ancora aperta).

Riepilogo: iTime nell'Ecosistema MQL5

iTime() è una funzione semplice nel design ma fondamentale nelle implicazioni. Ogni indicatore multi-timeframe corretto la usa — o dovrebbe usarla — per sincronizzare i dati tra timeframe in modo affidabile.

I punti chiave di questo articolo:

1. iTime restituisce 0 quando i dati non sono caricati — controlla sempre il risultato prima di procedere.

2. Per loop intensivi, CopyTime + accesso array è 10-50x più veloce di iTime ripetuto.

3. Il pattern "confronta iTime(0) con il valore memorizzato" è il modo corretto per rilevare aperture di nuove candele su TF superiori.

4. iBarShift + iTime è la coppia inseparabile per sincronizzare qualsiasi TF con qualsiasi altro.

5. Su strumenti con gap (XAUUSD nel weekend), la conversione datetime ↔ indice va verificata contro iBars() per evitare shift fuori range.

Se vuoi vedere questi pattern applicati a un sistema completo — con matrice multi-timeframe H1/H4/D1/W1, gestione buffer, e 65.587 trade verificati come proof-of-concept — Oracle v78 è disponibile su oracle.mtinvestments.it.

Oracle v78: Codice Reale, Risultati Reali

8.293 righe di MQL5 con pattern multi-timeframe, gestione iTime corretta, e 19 anni di validazione su XAUUSD H4. Se vuoi capire come funziona, parla con Rossella.

SCOPRI ORACLE v78

FAQ — iTime MQL5

A cosa serve iTime in MQL5?
iTime(symbol, timeframe, shift) restituisce il datetime di apertura della candela all'indice shift per il simbolo e timeframe specificati. È fondamentale per sincronizzare candele tra timeframe diversi, rilevare aperture di nuove candele su TF superiori, e mappare posizioni temporali tra H1, H4, D1 ecc.
Qual è la differenza tra iTime e Time[] in MQL5?
Time[] accede all'array temporale del timeframe corrente dell'indicatore/EA — è l'accesso più veloce. iTime(symbol, tf, shift) accede all'array temporale di qualsiasi simbolo e timeframe. In un indicatore H1 che legge dati H4, Time[0] = orario H1 corrente, iTime(_Symbol, PERIOD_H4, 0) = orario H4 corrente.
Perché iTime restituisce 0 in MQL5?
Le cause più comuni: (1) dati del TF non ancora caricati — usare CopyTime() e verificare il ritorno; (2) shift fuori range — verificare che shift < iBars(symbol, tf); (3) simbolo non nel Market Watch — aggiungere con SymbolSelect() prima.
Come trovare l'indice della candela H4 corrispondente a una candela M5?
Usa iBarShift(_Symbol, PERIOD_H4, iTime(_Symbol, PERIOD_M5, shift_m5), false). Restituisce l'indice della candela H4 il cui orario è il più vicino (senza superarlo) alla candela M5 specificata. Verifica sempre che il risultato non sia -1.
È meglio usare iTime o CopyTime in un loop su 1000 barre?
Sempre CopyTime. Chiama CopyTime una volta sola, carica l'intero array, poi accedi all'array direttamente nel loop. Il guadagno di performance rispetto a iTime ripetuta 1000 volte è nell'ordine di 10-50x.
Manuel Trotta — Trader quantitativo, fondatore MTInvestments, autore Hoepli
◆ Scritto da

Manuel Trotta

Trader quantitativo italiano dal novembre 2007 — 19 anni online. Fondatore MTInvestments, creatore UlcerZenith Oracle v78 con 65.587 trade reali verificati H4 XAUUSD. Autore di 18 libri sul trading e finanza personale: "Fare Trading con Excel" (Hoepli 2019), "Diventa Investitore Profittevole in 15 Minuti al Giorno", "Il Latte con la Mucca" (memoir 2026), più 2 volumi tecnici. Speaker IT Forum Rimini. Online dal 2007 — stesso nome, stessa P.IVA.

P.IVA 14349331000 Dal Novembre 2007 18 Libri su Amazon ↗ 150K+ Follower