Forum sulla programmazione MQL5 Ora
Contenuto
- Introduzione
- Ora in MQL5
- Determinazione dell'ora corrente del server
- Determinazione dell'ora locale corrente
- Tempo di uscita
- Formattazione dell'ora
- Conversione del tempo in numeri. Aggiunta e sottrazione di tempo
- Componenti di data e ora
- Generazione di data dai suoi componenti
- Determinazione dell'ora della barra
- Determinazione dell'ora di inizio del giorno e della quantità di tempo trascorso dall'inizio del giorno
- Determinazione dell'ora di inizio della settimana e della quantità di tempo trascorso dall'inizio della settimana
- Determinazione del numero di settimane da una determinata data, inizio anno o inizio mese
- Creazione di un set di strumenti sperimentali
- Indicatore pivot - Opzione 1
- Determinazione della sessione temporale
- Determinazione di un punto di tempo durante il giorno
- Indicatore pivot - Opzione 2
- Determinazione dei giorni di trading della settimana
- Determinazione del tempo di trading della settimana
- Funzioni MQL5 aggiuntive
- Alcune funzioni più utili per affrontare il tempo
- Peculiarità del funzionamento della funzione temporale nel tester di strategia
- Conclusione
- File allegati
Introduzione
MQL5 offre una serie di semplici funzioni per lavorare con il tempo e non dovresti trovare difficile familiarizzare con loro. La gamma di attività che richiedono l'uso di data e ora è piuttosto piccola. I compiti principali sono:
Per eseguire determinate azioni in un determinato momento (Fig. 1). Queste possono essere azioni eseguite alla stessa ora ogni giorno o in una data ora del giorno e un dato giorno della settimana su base settimanale o semplicemente eseguite in una determinata data e ora.
Fig. 1. Punto nel tempo.Per abilitare o disabilitare determinate azioni entro un determinato intervallo di tempo (sessione temporale). Ciò può includere una sessione temporale all'interno del giorno (ogni giorno da un punto nel tempo ad un altro), abilitando / disabilitando 1.determinate azioni in determinati giorni della settimana, 2. sessioni temporali da un dato momento in un dato giorno della settimana a un dato momento in un altro giorno della settimana e 3. solo azioni che rientrano in una data e un intervallo di tempo specificati.
Fig. 2. Range Temporale.
In pratica, l'uso del tempo è piuttosto complesso. Le difficoltà sono associate alle peculiarità della misurazione del tempo e dell'ambiente in cui operano gli Expert Advisor e gli indicatori:
Assenza di barre sul grafico a causa della mancanza di variazioni di prezzo. Sono particolarmente evidenti in tempi più brevi: M1, M5 e persino M15. Le barre mancanti possono anche essere osservate su intervalli di tempo più lunghi.
Le citazioni di alcuni centri di negoziazione includono barre domenicali che dovrebbero in realtà appartenere al lunedì.
Fine settimana. Il giorno della settimana che precede il lunedì è venerdì, non domenica. Il venerdì è seguito dal lunedì, piuttosto che dal sabato.
Oltre alle barre della domenica, alcuni centri di negoziazione forniscono quotazioni continuamente, incluso l'intero fine settimana. L'attività dei prezzi, sebbene piuttosto bassa rispetto ai giorni feriali, è presente per tutto il fine settimana.
Differenza di fuso orario tra il server commerciale e un computer locale (computer del trader e terminale di trading). L'ora del server di diversi centri di negoziazione può variare.
Ora legale.
L'articolo inizierà con una teoria generale riguardante il tempo. Quindi procederemo a rivedere le funzioni MQL5 standard per lavorare con il tempo e contemporaneamente considereremo alcune tecniche di programmazione, tagliando alla gestione di problemi pratici.
L'articolo è uscito molto lungo, quindi i programmatori alle prime armi che hanno iniziato solo di recente a esplorare MQL5 difficilmente gestiranno tutto in una volta. Sarebbe meglio dedicarci almeno tre giorni.
Peculiarità della misurazione del tempo
Facciamo una digressione dall'argomento per un po 'e passiamo all'astronomia. È un fatto noto che la Terra ruota attorno al Sole, mentre allo stesso tempo ruota sul suo asse. L'asse terrestre è leggermente inclinato rispetto alla sua orbita attorno al Sole. Il tempo necessario per una rotazione completa della Terra sul suo asse in coordinate astronomiche (celesti, globali) è chiamato giorno astronomico o siderale.
La giornata siderale non è di alcun interesse per la gente comune sulla Terra (al contrario degli astronomi). Più importante è l'alternanza di giorno e notte. Il tempo necessario per un ciclo giorno-notte è chiamato giorno solare. Osservando il sistema solare da sopra il Polo Nord della Terra (Fig. 3), si può vedere che la Terra ruota sul suo asse e ruota attorno al Sole in senso antiorario. Pertanto, per fare una rotazione completa sul suo asse rispetto al Sole, la Terra deve ruotare di poco più di 360 gradi. Di conseguenza, un giorno solare è leggermente più lungo di un giorno siderale.
Fig. 3. Direzione della rotazione terrestre sul suo asse e attorno al Sole (vista da sopra il Polo Nord della Terra).
Per comodità e precisione, un giorno solare viene preso come base per le misurazioni del tempo. Un giorno solare è diviso in 24 parti che fanno un'ora che dura 60 minuti, ecc. Un giorno siderale è di 23 ore 56 minuti e 4 secondi. Una leggera inclinazione dell'asse terrestre rispetto al suo piano orbitale provoca cambiamenti delle stagioni visibili dagli esseri viventi sulla Terra.
Il numero di giorni solari in un anno non è completo. In effetti, è 365 giorni e 6 ore. Ecco perché il calendario viene regolato periodicamente, una volta ogni quattro anni (nel multiplo dell'anno di 4) aggiungendo un altro giorno, il 29 febbraio (anno bisestile). Tuttavia questa regolazione non è completamente accurata (viene aggiunto un po 'di tempo in più), quindi alcuni anni, sebbene siano multipli di 4, non sono anni bisestili. Nessun aggiustamento del calendario avviene negli anni che terminano con "00" (un multiplo di 100). Ma non è tutto.
Se un anno è un multiplo di 100 e 400 allo stesso tempo, tale anno è considerato un anno bisestile e il calendario deve essere regolato. L'anno 1900 è un multiplo di 4 e 100, ma non è un multiplo di 400, motivo per cui non è un anno bisestile. L'anno 2000 è un multiplo di 4, 100 e 400, rendendolo un anno bisestile. L'anno successivo, che è un multiplo di 4 e 100, è l'anno 2100. Ma poiché non è un multiplo di 400, non sarà un anno bisestile. Si scopre che i lettori di questo articolo partono nell'era in cui ogni anno che è un multiplo di 4 è un anno bisestile. L'anno successivo è un multiplo di 100 e allo stesso tempo un anno bisestile è l'anno 2400.
Fusi orari
La Terra ruota sul suo asse con conseguente alternanza di giorno e notte. Può essere giorno o notte, o meglio, in qualsiasi momento del giorno alla stessa ora in luoghi diversi della Terra. Poiché un giorno ha 24 ore, la circonferenza terrestre è divisa in 24 sezioni di 15 gradi ciascuna chiamata fuso orario. Per comodità, i confini dei fusi orari non seguono sempre le linee di longitudine, ma invece corrono in conformità con i confini della divisione territoriale amministrativa: confini di paesi, regioni, ecc.
Il punto di riferimento è il meridiano primo chiamato meridiano di Greenwich. Passa attraverso il quartiere londinese di Greenwich da cui ha preso il nome. Il tempo medio di Greenwich si estende di 7,5 gradi su entrambi i lati del meridiano di Greenwich. Ci sono 12 fusi orari misurati a est dal Greenwich Mean Time (da +1 a +12) e 12 fusi orari misurati a ovest dal Greenwich Mean Time (da -1 a -12). Infatti, i fusi orari -12 e +12 sono larghi solo 7,5 gradi invece di 15. I fusi orari -12 e +12 si trovano a destra e a sinistra del meridiano 180, la cosiddetta linea della data internazionale.
Supponiamo che, quando è mezzogiorno a Greenwich (12:00), siano le 00:00, cioè l'inizio della giornata nel fuso orario -12 e - 24:00, cioè 00:00 del giorno successivo al fuso orario +12. Lo stesso vale per qualsiasi altra ora : mentre l'ora sull'orologio è identica, le date nel calendario sono diverse. Il fuso orario -12 infatti non viene utilizzato e viene sostituito con +12. Lo stesso vale per il fuso orario -11 che viene sostituito dal fuso orario +13. Questo probabilmente ha a che fare con le peculiarità delle relazioni economiche: ad esempio lo Stato Indipendente di Samoa situato nel fuso orario +13 ha forti relazioni economiche con il Giappone, quindi il tempo che è più vicino al tempo del Giappone è visto come più conveniente.
Inoltre, ci sono fusi orari insoliti come -04:30, +05:45, ecc. I curiosi possono trovare l'elenco di tutti i fusi orari disponibili nelle impostazioni dell'ora di Windows.
Ora legale
Molti paesi del mondo spostano i loro orologi in avanti di un'ora come pratica dell'ora legale nel tentativo di uno sfruttamento più efficiente della luce diurna e del risparmio energetico. Circa 80 paesi del mondo osservano l'ora legale, mentre altri non lo fanno. Alcuni paesi che utilizzano l'ora legale su larga scala ne hanno parti che rinunciano alla pratica (compresi gli Stati Uniti). L'ora legale è osservata nei principali paesi e regioni economicamente sviluppati: quasi tutti i paesi europei (tra cui Germania, Regno Unito, Svizzera), Stati Uniti (New York, Chicago), Australia (Sydney) e Nuova Zelanda (Wellington). Il Giappone non osserva l'ora legale. Il 27 marzo 2011, la Russia ha spostato i suoi orologi di un'ora in avanti per l'ultima volta e non è mai tornata all'ora solare in ottobre. Da allora la Russia è stata ufficialmente fuori dalla pratica dell'ora legale.
La procedura di passaggio a un'ora legale varia da paese a paese. Negli Stati Uniti, lo spostamento avviene alle 02:00 ora locale della seconda domenica di marzo e l'orologio salta indietro alle 02:00 della prima domenica di novembre. In Europa, gli orologi passano all'ora legale alle 02:00 dell'ultima domenica di marzo e tornano all'ora solare alle 03:00 dell'ultima domenica di ottobre. Invece di essere impostato sull'ora locale, il cambiamento avviene tutto in una volta in tutti i paesi europei: quando sono le 02:00 a Londra, le 03:00 a Berlino, ecc., In base ai fusi orari. Gli orologi saltano indietro all'ora standard quando sono le 03:00 a Londra, le 04:00 a Berlino, ecc.
L'Australia e la Nuova Zelanda si trovano nell'emisfero australe dove l'estate inizia quando l'inverno arriva nell'emisfero settentrionale. Di conseguenza, l'Australia cambia l'ora legale la prima domenica di ottobre e si sposta indietro all'ora solare la prima domenica di aprile. È difficile essere più precisi quando si tratta di ora legale in Australia poiché le date di inizio e fine non sempre concordano in diverse parti del paese. In Nuova Zelanda, il passaggio all'ora legale avviene alle 02:00 dell'ultima domenica di settembre e salta indietro alle 03:00 della prima domenica di aprile.
Time Standard
Come accennato in precedenza, un giorno solare viene preso come base per le misurazioni del tempo e il Tempo medio di Greenwich viene preso come standard temporale in base al quale il tempo è determinato in tutti gli altri fusi orari. Greenwich Mean Time è spesso abbreviato in GMT.
Tuttavia, poiché è stato stabilito che la rotazione della Terra è leggermente non uniforme, è stato utilizzato un orologio atomico per misurare il tempo e UTC (Coordinated Universal Time) è diventato il nuovo standard temporale. Attualmente UTC funge da standard temporale primario per l'intero mondo alla base delle misurazioni dell'ora in tutti i fusi orari, con le necessarie regolazioni apportate per l'ora legale. L'UTC non è soggetto all'ora legale.
A causa del fatto che il GMT a base solare non è completamente in linea con l'UTC che si basa su orologi atomici, c'è una differenza tra UTC e GMT che si accumula fino a circa 1 secondo circa ogni 500 giorni. A questo proposito, un aggiustamento di un secondo viene eseguito di volta in volta il 30 giugno o il 31 dicembre.
Formati di data e ora
I formati di data variano da paese a paese. In Russia, ad esempio, è consuetudine scrivere prima il giorno seguito dal mese e dall'anno. I numeri nelle date sono separati da punti, ad esempio 01.12.2012 - 1 dicembre 2012. Negli Stati Uniti, il formato della data è mese/giorno/anno, dove i numeri nelle date sono separati da una barra "/". Oltre ai punti e alle barre "/", alcuni standard di formato possono utilizzare un trattino "-" per separare i numeri in una data. Quando si scrive il tempo, ore, minuti e secondi sono separati da due punti ":", ad esempio 12:15:30 - 12 ore, 15 minuti, 30 secondi.
Esiste un modo semplice per specificare il formato di data e ora. Ad esempio, "gg.mm.aaaa" significa che scriviamo prima il giorno (il giorno del mese composto da due cifre; se il giorno è un numero da 1 a 9, aggiungiamo 0 all'inizio), quindi il mese (deve essere composto da due cifre) e l'anno composto da quattro cifre. "d-m-yy" significa che il giorno (può essere un numero a una cifra) va per primo, seguito dal mese (è consentito un numero a una cifra) e dall'anno composto da due cifre, ad esempio 1/12/12 - 1 dicembre 2012. I valori di giorno, mese e anno sono separati da un trattino "-".
L'ora è separata dalla data utilizzando uno spazio. Il formato orario utilizza "h" per le ore, "m" per i minuti e "s" per i secondi, specificando allo stesso tempo il numero richiesto di cifre. Ad esempio, "hh:mi:ss" significa che prima scriviamo ore (0 dovrebbe essere aggiunto davanti ai valori da 1 a 9), poi minuti (2 cifre necessarie) e secondi (2 cifre), dove i valori di ore, minuti e secondi sono separati da due punti.
Il formato di data e ora che è considerato il più accurato dal punto di vista di un programmatore è "aaaa.mm.dd hh:mi:ss". Quando si ordinano stringhe con date scritte usando questa notazione, sono facilmente disposte in ordine cronologico. Supponiamo di archiviare ogni giorno le informazioni in file di testo e di conservarle nella stessa cartella. Se si assegnano nomi ai file utilizzando questo formato, i file nella cartella verranno opportunamente ordinati e disposti in ordine.
Ora che abbiamo finito con la parte teorica, passiamo all'implementazione.
Ora in MQL5
In MQL5, il tempo è misurato in secondi trascorsi dall'inizio della cosiddetta epoca Unix iniziata il 1 ° gennaio 1970. Per memorizzare l'ora, utilizziamo variabili del tipo datetime Il valore minimo di una variabile di tipo datetime è 0 (corrispondente alla data di inizio dell'epoca), mentre il valore massimo è 32 535 244 799 (corrispondente alle 23:59:59 del 31 dicembre 3000).
Determinazione dell'ora corrente del server
Per determinare l'ora corrente, utilizziamo la funzione TimeCurrent(). Restituisce l'ultima ora del server nota:
datetime tm=TimeCurrent(); //--- output result Alert(tm);
La stessa ora del server viene utilizzata per specificare l'ora delle barre nel grafico. L'ultima ora del server nota è l'ora dell'ultima modifica del prezzo di uno qualsiasi dei simboli aperti nella finestra Market Watch. Se nella finestra Market Watch è presente solo EURUSD, la funzione TimeCurrent() restituirà l'ultima variazione di prezzo di EURUSD. Poiché la finestra Market Watch, di regola, visualizza un numero considerevole di simboli, la funzione restituisce fondamentalmente l'ora corrente del server. Tuttavia, poiché i prezzi non cambiano nel fine settimana, il valore restituito dalla funzione sarà molto diverso dall'ora effettiva del server.
Se è necessario scoprire l'ora del server in un determinato simbolo (ora dell'ultima variazione di prezzo), è possibile utilizzare la funzione SymbolInfoInteger() con SYMBOL_TIME: identificatore:
datetime tm=(datetime)SymbolInfoInteger(_Symbol,SYMBOL_TIME); //--- output result Alert(tm);
Determinazione dell'ora locale corrente
L'ora locale (visualizzata dall'orologio del PC dell'utente) è determinata dalla funzione TimeLocal():
datetime tm=TimeLocal(); //--- output result Alert(tm);
In pratica, quando si programmano EA e indicatori, si arriva principalmente a utilizzare il tempo del server. L'ora locale è utile negli avvisi e nelle voci del diario: è più conveniente per un utente confrontare un messaggio o un timestamp di immissione con l'ora visualizzata dall'orologio del PC per vedere quanto tempo fa tale messaggio o voce è stato registrato. Tuttavia, il terminale MetaTrader 5 aggiunge automaticamente un timestamp ai messaggi e alle voci del diario che vengono emessi utilizzando le funzioni Alert() e Print(). Pertanto, la necessità di utilizzare la funzione TimeLocal() può sorgere solo molto raramente.
Tempo di uscita
Si noti che il valore della variabile tm nel codice precedente viene emesso utilizzando la funzione Alert(). Detto questo, il valore viene visualizzato in un formato facile da leggere, ad es. "2012.12.05 22:31:57". Ciò è dovuto al fatto che la funzione Alert() converte gli argomenti ad essa passati nel tipo string (lo stesso accade quando si utilizza la funzione Print() e Comment() e quando si esegue l'output in file di testo e csv). Nel generare un messaggio di testo che contiene il valore di una variabile di tipo datetime, è responsabilità dell'utente eseguire la conversione del tipo. Converti nel tipo di stringa se devi ottenere l'ora formattata o nei tipi long type long seguito dal tipo di stringa se quello che ti serve è un valore numerico:
datetime tm=TimeCurrent(); //--- output result Alert("Formatted: "+(string)tm+", in seconds: "+(string)(long)tm);
Poiché gli intervalli di valori delle variabili del tipo long e ulong coprono l'intervallo di valori della variabile datetime-type, essi possono anche essere utilizzati per memorizzare l'ora, ma in questo caso per produrre l'ora formattata, è necessario convertire il tipo long nel tipo datetime e quindi nel tipo stringa o nel solo tipo stringa se si desidera produrre un valore numerico:
long tm=TimeCurrent(); //--- output result Alert("Formatted: "+(string)(datetime)tm+", in seconds: "+(string)tm);
Formattazione dell'ora
La formattazione temporale è stata considerata nell'articolo intitolato "Nozioni di base sulla programmazione MQL5: Strings" nella sezione dedicata alla conversione di variabili in stringhe. Descriviamo brevemente i punti chiave. Oltre alla conversione dei tipi, MQL5 offre una funzione che consente di specificare il formato di data e ora durante la conversione in una stringa- la funzione TimeToString():
datetime tm=TimeCurrent(); string str1="Date and time with minutes: "+TimeToString(tm); string str2="Date only: "+TimeToString(tm,TIME_DATE); string str3="Time with minutes only: "+TimeToString(tm,TIME_MINUTES); string str4="Time with seconds only: "+TimeToString(tm,TIME_SECONDS); string str5="Date and time with seconds: "+TimeToString(tm,TIME_DATE|TIME_SECONDS); //--- output results Alert(str1); Alert(str2); Alert(str3); Alert(str4); Alert(str5);
La funzione TimeToString() può essere applicata a variabili del tipo datetime, nonché a variabili di tipo long e ulong e ad altri tipi di variabili integer, ma non devono essere utilizzate per memorizzare l'ora.
Quando si formatta un messaggio di testo utilizzando la funzione StringFormat(), è necessario occuparsi della conversione del tipo.
Quando si memorizza l'ora in una variabile di tipo datetime:
datetime tm=TimeCurrent(); //--- generating a string string str=StringFormat("Formatted: %s, in seconds: %I64i",(string)tm,tm); //--- output result Alert(str);
Quando si memorizza il tempo in una variabile di tipo lungo:
long tm=TimeCurrent(); //--- generating a string string str=StringFormat("Formatted: %s, in seconds: %I64i",(string)(datetime)tm,tm); //--- output result Alert(str);
Oppure utilizzando la funzione TimeToString():
datetime tm=TimeCurrent(); //--- generating a string string str=StringFormat("Date: %s",TimeToString(tm,TIME_DATE)); //--- output result Alert(str);
Conversione del tempo in numeri. Aggiunta e sottrazione di tempo
Per convertire una data formattata (stringa) in un numero (numero di secondi trascorsi dall'inizio dell'epoca), viene utilizzata la funzione StringToTime() :
datetime tm=StringToTime("2012.12.05 22:31:57"); //--- output result Alert((string)(long)tm);
Come risultato del codice di cui sopra, il tempo di uscita in secondi sarà "1354746717" corrispondente alla data come segue "2012.12.05 22:31:57".
Quando l'ora è rappresentata come un numero, varie operazioni con esso diventano facili e convenienti, ad esempio è possibile trovare la data e l'ora nel passato o nel futuro. Poiché il tempo è misurato in secondi, è necessario aggiungere un periodo di tempo espresso in secondi. Sapendo che un minuto è di 60 secondi e un'ora è di 60 minuti o 3600 secondi, non ci vorrà molto sforzo per calcolare la durata di qualsiasi periodo di tempo.
Sottrai o aggiungi un'ora (3600 secondi) da o all'ora corrente e otterrai l'ora che era un'ora fa o sarà in un'ora:
datetime tm=TimeCurrent(); datetime ltm=tm-3600; datetime ftm=tm+3600; //--- output result Alert("Current: "+(string)tm+", an hour ago: "+(string)ltm+", in an hour: "+(string)ftm);
Non è necessario che la data passata alla funzione StringToTime() sia completa. Inoltre, puoi passare la data senza l'ora o l'ora senza la data. Se si passa la data senza l'ora, la funzione restituirà il valore alle 00:00:00 della data specificata:
datetime tm=StringToTime("2012.12.05"); //--- output result Alert(tm);
Se si passa solo l'ora, la funzione restituirà il valore corrispondente all'ora specificata della data corrente:
datetime tm=StringToTime("22:31:57"); //--- output result Alert((string)tm);
Il tempo può anche essere passato senza secondi. Se si passa la data, l'unico componente temporale che può essere passato sono le ore. Questo non sarà quasi mai richiesto nella pratica. Tuttavia, se sei curioso, sentiti libero di sperimentarlo da solo.
Componenti di data e ora
Per determinare i valori dei singoli componenti di data e ora (anno, mese, data, ecc.), Utilizziamo la funzione TimeToStruct() e la MqlDateTime. La struttura viene passata alla funzione per riferimento. Dopo aver eseguito la funzione, la struttura verrà riempita con i valori dei componenti della data passata ad essa:
datetime tm=TimeCurrent(); MqlDateTime stm; TimeToStruct(tm,stm); //--- output date components Alert("Year: " +(string)stm.year); Alert("Month: " +(string)stm.mon); Alert("Day: " +(string)stm.day); Alert("Hour: " +(string)stm.hour); Alert("Minute: " +(string)stm.min); Alert("Second: " +(string)stm.sec); Alert("Day of the week: "+(string)stm.day_of_week); Alert("Day of the year: " +(string)stm.day_of_year);
Si noti che oltre ai componenti della data la struttura contiene anche un paio di campi aggiuntivi: giorno della settimana (il campo day_of_week) e giorno dell'anno (day_of_year). I giorni della settimana sono conteggiati da 0 (0 - domenica, 1 - lunedì, ecc.). Anche i giorni dell'anno vengono conteggiati da zero. Gli altri valori seguono l'ordine di conteggio generalmente accettato (i mesi vengono conteggiati da 1, così come i giorni).
Esiste un altro modo per chiamare la funzione TimeCurrent(). Una struttura del tipo MqlDateTime viene passata alla funzione per riferimento. Dopo l'esecuzione della funzione, la struttura verrà riempita con i componenti della data corrente:
MqlDateTime stm; datetime tm=TimeCurrent(stm); //--- output date components Alert("Year: " +(string)stm.year); Alert("Month: " +(string)stm.mon); Alert("Day: " +(string)stm.day); Alert("Hour: " +(string)stm.hour); Alert("Minute: " +(string)stm.min); Alert("Second: " +(string)stm.sec); Alert("Day of the week: "+(string)stm.day_of_week); Alert("Day of the year: " +(string)stm.day_of_year);
Anche la funzione TimeLocal() può essere chiamata in questo modo.
Generazione di data dai suoi componenti
È inoltre possibile invertire la trasformazione della struttura MqlDateTime nel tipo datetime. A tale scopo, utilizziamo la funzione StructToTime() .
Determiniamo il tempo che è stato esattamente un mese fa. Il numero di giorni in un mese varia. Ci sono mesi che hanno 30 o 31 giorni e febbraio può avere 28 o 29 giorni. Il metodo di sottrazione e addizione del tempo che abbiamo considerato in precedenza non è quindi del tutto appropriato. Quindi suddivideremo le date nei loro componenti, diminuiremo il valore del mese di 1 e se il valore del mese è 1, lo impostiamo su 12 e diminuiremo il valore dell'anno di 1:
datetime tm=TimeCurrent(); MqlDateTime stm; TimeToStruct(tm,stm); if(stm.mon==1) { stm.mon=12; stm.year--; } else { stm.mon--; } datetime ltm=StructToTime(stm); //--- output result Alert("Current: "+(string)tm+", a month ago: "+(string)ltm);
Determinazione dell'ora della barra
Quando si sviluppa un indicatore, MetaEditor crea automaticamente una delle due versioni della funzione OnCalculate().
Versione 1:
int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { return(rates_total); }
Versione 2:
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { return(rates_total); }
I parametri della prima versione della funzione includono la matrice time[] i cui elementi contengono l'ora di tutte le barre.
Quando si utilizza la seconda versione, così come in ogni momento durante la programmazione di EA e la disposizione dell'accesso dagli indicatori al tempo delle barre su altri intervalli di tempo, utilizziamo la funzione CopyTime(). Questa funzione esiste in tre versioni. In tutte le versioni, i primi due parametri di funzione definiscono il simbolo e l'intervallo di tempo del grafico su cui viene determinato il tempo della barra. L'ultimo parametro definisce una matrice che memorizza i valori restituiti e i due parametri intermedi dipendono dalla versione della funzione utilizzata.
CopyTime - Versione 1. È necessario specificare l'indice della barra e il numero di elementi da copiare:
//--- variables for function parameters int start = 0; // bar index int count = 1; // number of bars datetime tm[]; // array storing the returned bar time //--- copy time CopyTime(_Symbol,PERIOD_D1,start,count,tm); //--- output result Alert(tm[0]);
In questo esempio viene illustrata l'implementazione della copia dell'ora dell'ultima barra nell'intervallo di tempo D1. La versione della funzione che utilizziamo dipende dal tipo di parametri passati ad essa. L'esempio precedente presenta l'uso delle variabili di tipo int, il che significa che dobbiamo ottenere il tempo dal numero di barra per il numero specificato di barre.
Quando si utilizza la funzione CopyTime(), le barre vengono conteggiate da zero da destra a sinistra. Una matrice dinamica utilizzata per i valori ottenuti (l'ultimo parametro) viene ridimensionata alla dimensione richiesta dalla funzione CopyTime() stessa. È anche possibile utilizzare una matrice statica ma in questo caso la dimensione della matrice deve corrispondere strettamente al numero di elementi richiesti (il valore del 4° parametro).
È importante comprendere l'ordine degli elementi nella matrice restituita quando l'ora viene ottenuta da più barre contemporaneamente. Mentre le barre nel grafico vengono conteggiate da destra a sinistra a partire dalla barra specificata, gli elementi della matrice sono disposti da sinistra a destra:
//--- variables for function parameters int start = 0; // bar index int count = 2; // number of bars datetime tm[]; // array storing the returned bar time //--- copy time CopyTime(_Symbol,PERIOD_D1,start,count,tm); //--- output result Alert("Today: "+(string)tm[1]+", yesterday: "+(string)tm[0]);
Come risultato di questo codice, l'ora della barra di ieri verrà memorizzata nell'elemento 0 dell'array tm, mentre il 1 ° elemento conterrà l'ora della barra di oggi.
In alcuni casi, può sembrare più conveniente avere il tempo nell'array disposto nello stesso ordine dell'ordine delle barre di conteggio nel grafico. E qui la funzione ArraySetAsSeries() può essere utile:
//--- variables for function parameters int start = 0; // bar index int count = 2; // number of bars datetime tm[]; // array storing the returned bar time ArraySetAsSeries(tm,true); // specify that the array will be arranged in reverse order //--- copy time CopyTime(_Symbol,PERIOD_D1,start,count,tm); //--- output result Alert("Today: "+(string)tm[0]+", yesterday: "+(string)tm[1]);
Ora, l'ora della barra di oggi è nell'elemento con indice 0 e l'ora della barra di ieri è nell'elemento con indice 1.
CopyTime - Version 2. Qui, quando chiamiamo la funzione CopyTime(), dobbiamo specificare l'ora della barra da cui inizia la copia e il numero di barre da copiare. Questa versione sarà appropriata per determinare il tempo di un intervallo di tempo superiore che contiene una barra di un intervallo di tempo inferiore:
//--- get the time of the last bar on M5 int m5_start=0; int m5_count=1; datetime m5_tm[]; CopyTime(_Symbol,PERIOD_M5,m5_start,m5_count,m5_tm); //--- determine the bar time on H1 that contains the bar on M5 int h1_count=1; datetime h1_tm[]; CopyTime(_Symbol,PERIOD_H1,m5_tm[0],h1_count,h1_tm); //--- output result Alert("The bar on М5 with the time "+(string)m5_tm[0]+" is contained in the bar on H1 with the time "+(string)h1_tm[0]);
Diventa più complicato se è necessario determinare il tempo della barra su un intervallo di tempo inferiore, essendo l'inizio della barra su un intervallo di tempo più alto. Una barra con lo stesso tempo della barra su un intervallo di tempo più alto potrebbe mancare in un intervallo di tempo inferiore. In tal caso, otterremo il tempo dell'ultima barra su un intervallo di tempo inferiore contenuto nella barra precedente dell'intervallo di tempo più alto. Quindi dobbiamo determinare il numero della barra di un intervallo di tempo inferiore e ottenere il tempo della barra successiva.
Di seguito è riportata l'implementazione di quanto sopra, sotto forma di una funzione pronta per essere utilizzata:
bool LowerTFFirstBarTime(string aSymbol, ENUM_TIMEFRAMES aLowerTF, datetime aUpperTFBarTime, datetime& aLowerTFFirstBarTime) { datetime tm[]; //--- determine the bar time on a lower time frame corresponding to the bar time on a higher time frame if(CopyTime(aSymbol,aLowerTF,aUpperTFBarTime,1,tm)==-1) { return(false); } if(tm[0]<aUpperTFBarTime) { //--- we got the time of the preceding bar datetime tm2[]; //--- determine the time of the last bar on a lower time frame if(CopyTime(aSymbol,aLowerTF,0,1,tm2)==-1) { return(false); } if(tm[0]<tm2[0]) { //--- there is a bar following the bar of a lower time frame that precedes the occurrence of the bar on a higher time frame int start=Bars(aSymbol,aLowerTF,tm[0],tm2[0])-2; //--- the Bars() function returns the number of bars from the bar with time tm[0] to //--- the bar with time tm2[0]; since we need to determine the index of the bar following //--- the bar with time tm2[2], subtract 2 if(CopyTime(aSymbol,aLowerTF,start,1,tm)==-1) { return(false); } } else { //--- there is no bar of a lower time frame contained in the bar on a higher time frame aLowerTFFirstBarTime=0; return(true); } } //--- assign the obtained value to the variable aLowerTFFirstBarTime=tm[0]; return(true); }
Parametri funzionali:
- aSymbol - symbol;
- aLowerTF - intervallo di tempo inferiore;
- aUpperTFBarTime - tempo della barra su un intervallo di tempo più alto;
- aLowerTFFirstBarTime - valore restituito di un intervallo di tempo inferiore.
In tutto il codice, la funzione controlla se la chiamata della funzione CopyTime() ha esito positivo e, in caso di errore, la funzione restituisce false. L'ora della barra viene restituita per riferimento tramite il parametro aLowerTFFirstBarTime.
Di seguito è riportato un esempio di utilizzo della funzione:
//--- time of the bar on the higher time frame H1 datetime utftm=StringToTime("2012.12.10 15:00"); //--- variable for the returned value datetime val; //--- function call if(LowerTFFirstBarTime(_Symbol,PERIOD_M5,utftm,val)) { //--- output result in case of successful function operation Alert("val = "+(string)val); } else { //--- in case of an error, terminate the operation of the function from which the LowerTFFirstBarTime() function is called Alert("Error copying the time"); return; }
Se la funzione riceve il tempo di una barra inesistente di un intervallo di tempo superiore, può creare una situazione in cui non vi è alcuna barra su un intervallo di tempo inferiore contenuto in una barra di un intervallo di tempo superiore. In questo caso, la funzione restituisce true e il valore temporale 0 viene scritto nella variabile aLowerTFFirstBarTime. Se esiste una barra di un intervallo di tempo superiore, ci sarà sempre almeno una barra corrispondente in ciascuno degli intervalli di tempo inferiori.
Trovare l'ora dell'ultima barra su un intervallo di tempo inferiore contenuto in una barra di un intervallo di tempo più alto è leggermente più facile. Calcoliamo il tempo della barra successiva su un intervallo di tempo più alto e utilizziamo il valore ottenuto per determinare il tempo della barra corrispondente su un intervallo di tempo inferiore. Se il tempo risultante è uguale al tempo calcolato di un intervallo di tempo più elevato, è necessario determinare il tempo della barra precedente su un intervallo di tempo inferiore. Se il tempo risultante è inferiore, allora abbiamo il momento giusto.
Di seguito è riportata l'implementazione di quanto sopra, sotto forma di una funzione pronta per essere utilizzata:
bool LowerTFLastBarTime(string aSymbol, ENUM_TIMEFRAMES aUpperTF, ENUM_TIMEFRAMES aLowerTF, datetime aUpperTFBarTime, datetime& aLowerTFFirstBarTime) { //--- time of the next bar on a higher time frame datetime NextBarTime=aUpperTFBarTime+PeriodSeconds(aUpperTF); datetime tm[]; if(CopyTime(aSymbol,aLowerTF,NextBarTime,1,tm)==-1) { return(false); } if(tm[0]==NextBarTime) { //--- There is a bar on a lower time frame corresponding to the time of the next bar on a higher time frame. //--- Determine the time of the last bar on a lower time frame datetime tm2[]; if(CopyTime(aSymbol,aLowerTF,0,1,tm2)==-1) { return(false); } //--- determine the preceding bar index on a lower time frame int start=Bars(aSymbol,aLowerTF,tm[0],tm2[0]); //--- determine the time of this bar if(CopyTime(aSymbol,aLowerTF,start,1,tm)==-1) { return(false); } } //--- assign the obtain value to the variable aLowerTFFirstBarTime=tm[0]; return(true); }
Parametri funzionali:
- aSymbol - symbol;
- aUpperTF - intervallo di tempo più elevato;
- aLowerTF - intervallo di tempo inferiore;
- aUpperTFBarTime - tempo della barra su un intervallo di tempo più alto;
- aLowerTFFirstBarTime - valore restituito di un intervallo di tempo inferiore.
In tutto il codice, la funzione controlla se la chiamata della funzione CopyTime() ha esito positivo e, in caso di errore, la funzione restituisce false. L'ora della barra viene restituita per riferimento tramite il parametro aLowerTFFirstBarTime.
Di seguito è riportato un esempio di utilizzo della funzione:
//--- time of the bar on the higher time frame H1 datetime utftm=StringToTime("2012.12.10 15:00"); //--- variable for the returned value datetime val; //--- function call if(LowerTFLastBarTime(_Symbol,PERIOD_H1,PERIOD_M5,utftm,val)) { //--- output result in case of successful function operation Alert("val = "+(string)val); } else { //--- in case of an error, terminate the operation of the function from which the LowerTFFirstBarTime() function is called Alert("Error copying the time"); return; }
Attenzione! Si presume che il tempo passato alla funzione sia il tempo esatto di un intervallo di tempo più elevato. Se l'ora esatta della barra è sconosciuta e conosciamo solo il tempo di un certo tick su quella barra o di una barra su un intervallo di tempo inferiore contenuto nella barra su un intervallo di tempo più alto, sarà necessaria la normalizzazione del tempo. Gli intervalli di tempo utilizzati nel terminale MetaTrader 5 suddividono il giorno in un numero integrale di barre. Determiniamo il numero di barra dall'inizio dell'epoca e lo moltiplichiamo per la lunghezza della barra in secondi:
datetime BarTimeNormalize(datetime aTime,ENUM_TIMEFRAMES aTimeFrame) { int BarLength=PeriodSeconds(aTimeFrame); return(BarLength*(aTime/BarLength)); }
Parametri funzionali:
- aTime - tempo;
- aTimeFrame - intervallo di tempo.
Di seguito è riportato un esempio di utilizzo della funzione:
//--- the time to be normalized datetime tm=StringToTime("2012.12.10 15:25"); //--- function call datetime tm1=BarTimeNormalize(tm,PERIOD_H1); //--- output result Alert(tm1);
Tuttavia, questo non è solo il metodo di normalizzazione del tempo, esso serve anche per determinare il tempo di un intervallo di tempo più elevato entro il tempo di un intervallo di tempo inferiore.
CopyTime - Version 3. In questo caso, quando si chiama la funzione CopyTime(), specifichiamo l'intervallo di tempo rispetto alle barre da cui è necessario copiare il tempo. Questo metodo ci consente di ottenere facilmente tutte le barre di un intervallo di tempo inferiore contenute nella barra di un intervallo di tempo più alto:
//--- time of the bar start on H1 datetime TimeStart=StringToTime("2012.12.10 15:00"); //--- the estimated time of the last bar on //--- M5 datetime TimeStop=TimeStart+PeriodSeconds(PERIOD_H1)-PeriodSeconds(PERIOD_M5); //--- copy time datetime tm[]; CopyTime(_Symbol,PERIOD_M5,TimeStart,TimeStop,tm); //--- output result Alert("Bars copied: "+(string)ArraySize(tm)+", first bar: "+(string)tm[0]+", last bar: "+(string)tm[ArraySize(tm)-1]);
Determinazione dell'ora di inizio del giorno e della quantità di tempo trascorso dall'inizio del giorno
Il modo più ovvio per determinare l'ora di inizio del giorno entro un dato tempo è quello di suddividere il tempo nei suoi componenti, azzerare ore, minuti e secondi e sommarli di nuovo. Tuttavia, c'è un modo più semplice. Un giorno è di 86400 secondi. Dobbiamo ottenere un numero intero come risultato della divisione del tempo per il numero di secondi in un giorno e moltiplicarlo per il numero di secondi in un giorno:
datetime tm=TimeCurrent(); tm=(tm/86400)*86400; //--- output result Alert("Day start time: "+(string)tm);
Attenzione! Questo trucco è efficace solo con variabili intere. Se in uno qualsiasi dei calcoli vengono visualizzate variabili di tipo doppio e float, è necessario troncare la parte frazionaria utilizzando la funzione MathFloor():
MathFloor(tm/86400)*86400
Dopo la moltiplicazione, i valori devono essere normalizzati utilizzando la funzione NormalizeDouble(). Poiché il valore risultante deve essere un numero intero, è possibile utilizzare una funzione di arrotondamento, MathRound():
MathRound(MathFloor(tm/86400)*86400)
Quando si utilizzano variabili intere, il resto viene eliminato automaticamente. Nel trattare con il tempo, non ci sarà quasi bisogno di variabili di tipo doppio o float e il loro uso molto probabilmente indicherà un approccio fondamentalmente sbagliato.
Per determinare il numero di secondi trascorsi dall'inizio del giorno, dobbiamo solo prendere il resto della divisione del tempo per 86400:
datetime tm=TimeCurrent(); long seconds=tm%86400; //--- output result Alert("Time elapsed since the day start: "+(string)seconds+" sec.");
Metodi simili possono essere utilizzati per convertire il tempo ottenuto in secondi in ore, minuti e secondi. Implementazione come funzione:
int TimeFromDayStart(datetime aTime,int &aH,int &aM,int &aS) { //--- Number of seconds elapsed since the day start (aTime%86400), //--- divided by the number of seconds in an hour is the number of hours aH=(int)((aTime%86400)/3600); //--- Number of seconds elapsed since the last hour (aTime%3600), //--- divided by the number of seconds in a minute is the number of minutes aM=(int)((aTime%3600)/60); //--- Number of seconds elapsed since the last minute aS=(int)(aTime%60); //--- Number of seconds since the day start return(int(aTime%86400)); }
Il primo parametro passato è il tempo. Altri parametri vengono utilizzati per i valori restituiti: aH - ore, aM - minuti, aS - secondi. La funzione stessa restituisce il numero totale di secondi dall'inizio del giorno. Controlliamo la funzione:
datetime tm=TimeCurrent(); int t,h,m,s; t=TimeFromDayStart(tm,h,m,s); //--- output result Alert("Time elapsed since the day start ",t," s, which makes ",h," h, ",m," m, ",s," s ");
È inoltre possibile calcolare il numero del giorno da utilizzare negli indicatori per determinare la barra di inizio del giorno:
bool NewDay=(time[i]/86400)!=(time[i-1]/86400);
Si presume che le barre siano indicizzate da sinistra a destra, dove time[i] è il tempo della barra corrente e time[i-1] è il tempo della barra precedente.
Determinazione dell'ora di inizio della settimana e della quantità di tempo trascorso dall'inizio della settimana
Determinare l'ora di inizio della settimana è un po 'più complesso che determinare l'inizio del giorno. Sebbene il numero di giorni in una settimana sia costante e possiamo calcolare la durata di una settimana in secondi (604800 secondi), non è sufficiente calcolare semplicemente il numero integrale di settimane trascorse dall'inizio dell'epoca e moltiplicarlo per la durata della settimana.
Il fatto è che nella maggior parte dei paesi la settimana inizia il lunedì, mentre in alcuni altri paesi (USA, Canada, Israele e altri) inizia la domenica. Ma come ricordiamo, l'epoca della misurazione del tempo inizia giovedì. Se il giovedì fosse stato il primo giorno della settimana, quei semplici calcoli sarebbero bastati.
Per comodità, considereremo le peculiarità di determinare l'inizio della settimana attraverso l'esempio del giorno della prima epoca corrispondente al valore di 0. Abbiamo bisogno di trovare un valore tale che, se aggiunto al tempo, cambierebbe il primo giorno dell'epoca (1970.01.01 00:00), contato da zero, al quarto giorno, cioè dobbiamo aggiungere la durata di quattro giorni. Se la settimana inizia il lunedì, il giovedì dovrebbe essere il quarto giorno, quindi dobbiamo aggiungere la durata di tre giorni. Se invece la settimana inizia di domenica, il giovedì dovrebbe essere il quinto giorno, quindi dobbiamo aggiungere la durata di quattro giorni.
Scriviamo una funzione per il calcolo del numero di una settimana:
long WeekNum(datetime aTime,bool aStartsOnMonday=false) { //--- if the week starts on Sunday, add the duration of 4 days (Wednesday+Tuesday+Monday+Sunday), // if it starts on Monday, add 3 days (Wednesday, Tuesday, Monday) if(aStartsOnMonday) { aTime+=259200; // duration of three days (86400*3) } else { aTime+=345600; // duration of four days (86400*4) } return(aTime/604800); }
Questa funzione può essere utile negli indicatori per determinare la prima barra della nuova settimana:
bool NewWeek=WeekNum(time[i])!=WeekNum(time[i-1]);
Si presume che le barre siano indicizzate da sinistra a destra, dove time[i] è il tempo della barra corrente e time[i-1] è il tempo della barra precedente.
Ora possiamo calcolare l'ora di inizio della settimana. Poiché per calcolare il numero della settimana si presumeva che l'epoca iniziasse tre (o quattro) giorni prima, ora dobbiamo eseguire una correzione del tempo inversa:
long WeekStartTime(datetime aTime,bool aStartsOnMonday=false) { long tmp=aTime; long Corrector; if(aStartsOnMonday) { Corrector=259200; // duration of three days (86400*3) } else { Corrector=345600; // duration of four days (86400*4) } tmp+=Corrector; tmp=(tmp/604800)*604800; tmp-=Corrector; return(tmp); }
La funzione restituisce un valore del tipo long perché il valore per la prima settimana potrebbe essere negativo (da tre a quattro giorni prima dell'inizio dell'epoca). Il secondo parametro della funzione può determinare se la settimana inizia la domenica o il lunedì.
Ora che abbiamo l'ora di inizio della settimana, possiamo calcolare il numero di secondi trascorsi dall'inizio della settimana:
long SecondsFromWeekStart(datetime aTime,bool aStartsOnMonday=false) { return(aTime-WeekStartTime(aTime,aStartsOnMonday)); }
I secondi possono essere convertiti in giorni, ore, minuti e secondi. Mentre non è stato difficile calcolare ore, minuti e secondi dall'inizio del giorno, in questo caso sarà più facile utilizzare la funzione TimeToStruct():
long sfws=SecondsFromWeekStart(TimeCurrent()); MqlDateTime stm; TimeToStruct(sfws,stm); stm.day--; Alert("Time elapsed since the week start "+(string)stm.day+" d, "+(string)stm.hour+" h, "+(string)stm.min+" m, "+(string)stm.sec+" s");
Si prega di notare che il valore stm.day è diminuito di 1. I giorni del mese vengono conteggiati da 1 in quanto è necessario determinare il numero di giorni interi. Alcuni di voi potrebbero trovare le funzioni di questa sezione inutili da un punto di vista pratico, ma la vostra comprensione di queste funzioni sarà di grande valore come esperienza di lavoro con il tempo.
Determinazione del numero di settimane da una determinata data, inizio anno o inizio mese
Osservando i campi della struttura MqlDateTime, in particolare il campo day_of_year, si ha voglia di creare funzioni che determineranno il numero della settimana dall'inizio dell'anno e dall'inizio del mese. È meglio scrivere una funzione generale per determinare il numero della settimana da una determinata data. Il principio del funzionamento della funzione è simile a quello utilizzato per determinare il numero della settimana dall'inizio dell'epoca:
long WeekNumFromDate(datetime aTime,datetime aStartTime,bool aStartsOnMonday=false) { long Time,StartTime,Corrector; MqlDateTime stm; Time=aTime; StartTime=aStartTime; //--- determine the beginning of the reference epoch StartTime=(StartTime/86400)*86400; //--- determine the time that elapsed since the beginning of the reference epoch Time-=StartTime; //--- determine the day of the week of the beginning of the reference epoch TimeToStruct(StartTime,stm); //--- if the week starts on Monday, numbers of days of the week are decreased by 1, // and the day with number 0 becomes a day with number 6 if(aStartsOnMonday) { if(stm.day_of_week==0) { stm.day_of_week=6; } else { stm.day_of_week--; } } //--- calculate the value of the time corrector Corrector=86400*stm.day_of_week; //--- time correction Time+=Corrector; //--- calculate and return the number of the week return(Time/604800); }
Sulla base di questa funzione, scriviamo due funzioni per determinare il numero della settimana dall'inizio dell'anno e dall'inizio del mese. Per fare ciò, dobbiamo prima determinare il periodo di inizio dell'anno e l'ora di inizio del mese. Suddividere il tempo nei suoi componenti, regolare i valori di alcuni campi e convertire i componenti in notazione temporale.
La funzione per determinare il periodo di inizio dell'anno:
datetime YearStartTime(datetime aTime) { MqlDateTime stm; TimeToStruct(aTime,stm); stm.day=1; stm.mon=1; stm.hour=0; stm.min=0; stm.sec=0; return(StructToTime(stm)); }
La funzione per determinare l'ora di inizio del mese:
datetime MonthStartTime(datetime aTime) { MqlDateTime stm; TimeToStruct(aTime,stm); stm.day=1; stm.hour=0; stm.min=0; stm.sec=0; return(StructToTime(stm)); }
Ora, di seguito sono riportate le funzioni per determinare il numero della settimana dall'inizio dell'anno e dall'inizio del mese.
Dall'inizio dell'anno:
long WeekNumYear(datetime aTime,bool aStartsOnMonday=false) { return(WeekNumFromDate(aTime,YearStartTime(aTime),aStartsOnMonday)); }
Dall'inizio del mese:
long WeekNumMonth(datetime aTime,bool aStartsOnMonday=false) { return(WeekNumFromDate(aTime,MonthStartTime(aTime),aStartsOnMonday)); }
Infine, bisogna fare compiti puramente pratici.
Creazione di un set di strumenti sperimentali
Come già accennato, ci sono centri di negoziazione le cui quotazioni includono barre domenicali e che forniscono preventivi continuamente per tutto il fine settimana. Dovremmo assicurarci che le funzioni necessarie funzionino correttamente in tutti i casi. Naturalmente possiamo trovare i centri di negoziazione adatti in Internet e testare il funzionamento delle funzioni utilizzando le quotazioni sui conti demo. Tuttavia, oltre a cercare il guisto centro di negoziazione, dovremo cercare i luoghi appropriati nel grafico per eseguire i test richiesti.
Creiamo la nostra area di test per le funzioni di test. Venerdì, fine settimana e lunedì sono di nostro particolare interesse. Creeremo un array contenente l'ora delle barre del venerdì, del lunedì e del fine settimana, come richiesto. Ci saranno un totale di quattro opzioni:
- Nessuna barra nel fine settimana.
- Alcune barre alla fine della domenica, diciamo, 4.
- Quotazioni continue nel fine settimana.
- Barre il sabato ma nessun barra la domenica.
Per evitare che l'array cresca troppo grande, useremo l'intervallo di tempo H1. La dimensione massima dell'array sarà di 96 elementi (24 barre al giorno per 4 giorni) e l'array stesso si adatterà al grafico quando viene disegnato utilizzando oggetti grafici. Quindi otterremo qualcosa come un buffer di indicatori con il tempo e itereremo l'array in un ciclo nel modo simile alla prima esecuzione della funzione OnCalculate() all'avvio di un indicatore. In questo modo, saremo in grado di visualizzare l'operazione della funzione.
Questo strumento viene implementato sotto forma di script allegato a questo articolo (il file sTestArea.mq5). La preparazione viene eseguita nella funzione OnStart() dello script. La variabile Variant all'inizio del codice funzione consente di selezionare una delle quattro opzioni sopra elencate. Sotto la funzione OnStart(), puoi vedere la funzione LikeOnCalculate() simile alla funzione OnCalculate() degli indicatori. Questa funzione ha due parametri: rates_total - numero di barre e time[] - array con il tempo delle barre.
Inoltre, continuiamo a lavorare in questa funzione come se stessimo scrivendo un indicatore. È possibile impostare un marcatore dalla funzione chiamando la funzione SetMarker(). I parametri passati alla funzione SetMarker() sono: indice a barre, indice buffer (riga in cui viene visualizzato il marcatore) e colore del marcatore.
La Fig. 4 mostra i risultati delle prestazioni dello script, con la variabile Variant uguale a 2 e due righe di marcatori impostate sotto ogni barra (le barre sono contrassegnate con timestamp pertinenti). Il colore impostato su tutti gli elementi del grafico è invisibile.
Fig. 4. Prestazioni dello script sTestArea.mq5.
I timestamp delle barre assumono colore a seconda del giorno della settimana: Venerdì - rosso, sabato - magenta, domenica - verde, lunedì - blu. Ora possiamo procedere alla scrittura di varie funzioni che richiedono un approccio speciale alle barre del fine settimana, essendo in grado di monitorare visivamente il loro lavoro.
Indicatore pivot - Opzione 1
Proviamo prima a creare un semplice indicatore Pivot. Per calcolare la linea Pivot, dobbiamo conoscere il prezzo di chiusura di ieri, così come i prezzi massimi e minimi di ieri. Il valore dell'indicatore viene calcolato come la media di questi tre valori. Identificheremo nuovi massimi e minimi durante il giorno, calcoleremo il valore Pivot all'inizio del nuovo giorno e disegneremo e mostreremo ulteriormente il livello durante il giorno.
Forniremo due versioni del funzionamento dell'indicatore:
- Il pivot viene calcolato ogni nuovo giorno (si presume che non ci siano barre nel fine settimana). Se ci sono barre nel fine settimana, quelle del sabato e della domenica saranno trattate in modo diverso.
- Le barre del sabato apparterranno al venerdì, mentre le barre della domenica apparterranno al lunedì (questo vale per i casi in cui le quotazioni sono fornite continuamente durante il fine settimana e dove ci sono solo barre della domenica). Qui, dovresti tenere a mente che potrebbero non esserci barre durante il fine settimana.
Nella prima versione, è sufficiente determinare solo l'inizio del nuovo giorno. Passiamo l'ora corrente (aTimeCur) e l'ora precedente (aTimePre) alla funzione, calcoliamo i numeri dei giorni dall'inizio dell'epoca e se non corrispondono, deduciamo che il nuovo giorno è iniziato:
bool NewDay1(datetime aTimeCur,datetime aTimePre) { return((aTimeCur/86400)!=(aTimePre/86400)); }
La seconda versione. Se il sabato è iniziato, l'inizio della giornata dovrebbe essere ignorato. Se la domenica è iniziata, definiamo l'inizio del giorno (questo è naturalmente solo un altro giorno). Se il lunedì è iniziato dopo la domenica, salta l'inizio della giornata. Se il lunedì è stato preceduto da qualsiasi altro giorno della settimana, ad esempio sabato o venerdì, definisci l'inizio del giorno. Quello che otteniamo è una funzione come segue:
bool NewDay2(datetime aTimeCur,datetime aTimePre) { MqlDateTime stm; //--- new day if(NewDay1(aTimeCur,aTimePre)) { TimeToStruct(aTimeCur,stm); switch(stm.day_of_week) { case 6: // Saturday return(false); break; case 0: // Sunday return(true); break; case 1: // Monday TimeToStruct(aTimePre,stm); if(stm.day_of_week!=0) { // preceded by any day of the week other than Sunday return(true); } else { return(false); } break; default: // any other day of the week return(true); } } return(false); }
Ed ecco la funzione generale a seconda della versione:
bool NewDay(datetime aTimeCur,datetime aTimePre,int aVariant=1) { switch(aVariant) { case 1: return(NewDay1(aTimeCur,aTimePre)); break; case 2: return(NewDay2(aTimeCur,aTimePre)); break; } return(false); }
Testiamo il funzionamento della funzione utilizzando lo strumento "sTestArea" (il file sTestArea_Pivot1.mq5 allegato; l'inizio del giorno è contrassegnato in marrone). Sarà necessario eseguire otto test: 2 versioni della funzione per quattro opzioni di generazione di barre. Dopo aver fatto in modo che le funzioni funzionino correttamente, possiamo tranquillamente iniziare a sviluppare un indicatore. Tuttavia, poiché lo sviluppo degli indicatori non è al centro di questo articolo, abbiamo allegato qui un indicatore già pronto (il file Pivot1.mq5), con la parte più difficile del suo sviluppo considerata in dettaglio.
Determinazione della sessione temporale
Dobbiamo consentire all'Expert Advisor di negoziare entro l'intervallo di tempo specificato durante il giorno, ogni giorno allo stesso intervallo. Specifichiamo le ore e i minuti di inizio della sessione di trading, nonché le ore e i minuti della fine della sessione di trading. Ore e minuti specificati separatamente, piuttosto che variabili stringa con il tempo specificato come "14:00" ci permetteranno di eseguire ottimizzazioni nello Strategy Tester se la funzione viene utilizzata nell'Expert Advisor.
Per determinare la sessione temporale, eseguire le seguenti opzioni:
- Calcola il tempo in secondi dall'inizio del giorno per il punto di inizio del tempo e fai lo stesso per il punto finale del tempo.
- Calcola l'ora corrente in secondi dall'inizio del giorno.
- Confronta l'ora corrente con l'ora di inizio e di fine.
Non è impossibile che una sessione di trading inizi in un giorno e finisca in un altro giorno, cioè quando una sessione di trading va oltre la mezzanotte, nel qual caso l'ora di fine calcolata dall'inizio del giorno risulta essere inferiore all'ora di inizio. Pertanto dobbiamo fare due controlli. La funzione che otteniamo è la seguente:
bool TimeSession(int aStartHour,int aStartMinute,int aStopHour,int aStopMinute,datetime aTimeCur) { //--- session start time int StartTime=3600*aStartHour+60*aStartMinute; //--- session end time int StopTime=3600*aStopHour+60*aStopMinute; //--- current time in seconds since the day start aTimeCur=aTimeCur%86400; if(StopTime<StartTime) { //--- going past midnight if(aTimeCur>=StartTime || aTimeCur<StopTime) { return(true); } } else { //--- within one day if(aTimeCur>=StartTime && aTimeCur<StopTime) { return(true); } } return(false); }
Nel caso in cui la sessione vada oltre la mezzanotte, l'ora corrente dovrebbe essere maggiore o uguale all'ora di inizio della sessione O inferiore all'ora di fine della sessione. Se la sessione si svolge entro il giorno, l'ora corrente dovrebbe essere maggiore o uguale all'ora di inizio E inferiore all'ora di fine.
Troverete un indicatore creato per testare il funzionamento della funzione in allegato alla fine dell’articolo (il file Session.mq5). Come qualsiasi altro indicatore dell'applicazione, esso può essere utilizzato non solo per i test ma anche per altri scopi pratici.
Determinazione di un punto di tempo durante il giorno
Un semplice controllo per l'uguaglianza al tempo specificato non funzionerà correttamente poiché i tick non si verificano a intervalli regolari e potrebbero esserci ritardi da alcuni secondi a diversi minuti. È molto probabile che nel mercato non ci sarà alcun tick con il tempo specificato. Dobbiamo verificare l'intersezione di un determinato timestamp.
L'ora corrente deve essere uguale o maggiore dell'ora specificata, mentre l'ora precedente deve essere inferiore all'ora specificata. Poiché è necessario determinare un punto di tempo all'interno del giorno, è necessario convertire l'ora corrente (così come l'ora precedente) in secondi dall'inizio del giorno. Allo stesso modo, i parametri di tempo dati (ore e minuti) dovrebbero essere convertiti in secondi. Può darsi che l'ora precedente cada il giorno precedente, cioè sarà maggiore dell'ora corrente, quando viene convertita in secondi dall'inizio del giorno. In questo caso procediamo come quando determiniamo la sessione di tempo - facciamo due controlli.
La funzione che otteniamo è la seguente:
bool TimeCross(int aHour,int aMinute,datetime aTimeCur,datetime aTimePre) { //--- specified time since the day start datetime PointTime=aHour*3600+aMinute*60; //--- current time since the day start aTimeCur=aTimeCur%86400; //--- previous time since the day start aTimePre=aTimePre%86400; if(aTimeCur<aTimePre) { //--- going past midnight if(aTimeCur>=PointTime || aTimePre<PointTime) { return(true); } } else { if(aTimeCur>=PointTime && aTimePre<PointTime) { return(true); } } return(false); }
C'è un indicatore creato sulla base di questa funzione (il file TimePoint.mq5 allegato all'articolo).
Indicatore pivot - Opzione 2
Ora che abbiamo imparato a determinare un punto del tempo, rendiamo più sofisticato l'indicatore Pivot. Invece delle solite 00:00, la giornata inizierà ora in qualsiasi momento. Lo chiameremo un giorno definito dall'utente. Per determinare l'inizio di una giornata definita dall'utente, utilizzeremo la funzione TimeCross() descritta in precedenza. A causa delle diverse opzioni di generazione di bar nei fine settimana, alcuni giorni dovranno essere omessi. È difficile trovare tutte le regole di controllo in questo momento, quindi faremo un passo alla volta. Importante è avere qualcosa con cui iniziare e avere opzioni su come procedere. Abbiamo uno script di test, sTestArea.mq5, quindi la soluzione giusta può anche essere trovata sperimentalmente.
Il caso "No weekend bars" è il più semplice: un nuovo giorno inizia all'intersezione di un determinato timestamp con il tempo.
Nel caso in cui ci siano solo poche barre alla fine della domenica, la funzione TimeCross() definirà la prima barra della domenica come l'inizio del giorno, dati eventuali parametri di funzione. Si presume che non ci siano quotazioni nei fine settimana (le barre della domenica appartengono al lunedì), quindi la domenica dovrebbe essere ignorata. Se un dato orario cade da qualche parte nel mezzo di una serie di barre domenicali, dovrebbe anche essere ignorato poiché il nuovo inizio del giorno è già stato registrato il venerdì.
Quotazioni continue nel fine settimana: Se l'inizio di un giorno definito dall'utente cade nel mezzo di un giorno di calendario (Fig. 5),
Fig. 5. Un giorno definito dall'utente inizia nel mezzo di un giorno di calendario.
Venerdì - rosso, sabato - magenta, domenica - verde, lunedì - blu.
metà del sabato può essere trattato come venerdì e metà della domenica può essere trattata come lunedì. Ma ci sono alcune barre da metà sabato a metà domenica che non appartengono a nessun giorno. Naturalmente potremmo dividere l'intervallo da sabato a domenica in parti uguali e trattare una metà come venerdì e l'altra metà come lunedì. Ciò complicherebbe in modo significativo un indicatore molto semplice, anche se le quotazioni del fine settimana non sono così importanti.
La soluzione più ragionevole sarà quella di considerare tutte le barre del sabato e della domenica come un giorno definito dall'utente che dura dal venerdì al lunedì. Ciò significa che i giorni definiti dall'utente che iniziano il sabato e la domenica vengono saltati.
La funzione che otteniamo è la seguente:
bool NewCustomDay(int aHour,int aMinute,datetime aTimeCur,datetime aTimePre) { MqlDateTime stm; if(TimeCross(aHour,aMinute,aTimeCur,aTimePre)) { TimeToStruct(aTimeCur,stm); if(stm.day_of_week==0 || stm.day_of_week==6) { return(false); } else { return(true); } } return(false); }
C'è un indicatore creato sulla base di questa funzione (il file Pivot2.mq5 allegato all'articolo).
Determinazione dei giorni di trading della settimana
Consentire a un Expert Advisor di fare trading solo in determinati giorni è abbastanza facile. Scomponiamo il tempo nei suoi componenti utilizzando la funzione TimeToStruct() e dichiariamo variabili di tipo bool per ciascuno dei giorni della settimana nei parametri di Expert Advisor. A seconda del giorno della settimana, la funzione restituisce il valore della variabile corrispondente.
Questo processo può essere ottimizzato. Quando inizializzi un Expert Advisor o un indicatore, riempi un array con valori di variabili che consentono o non consentono di negoziare in determinati giorni. Quindi controlla il valore dell'elemento array corrispondente al giorno della settimana. Otteniamo due funzioni: una viene chiamata durante l'inizializzazione, mentre l'altra viene chiamata se serve.
Variabili:
input bool Sunday =true; // Sunday input bool Monday =true; // Monday input bool Tuesday =true; // Tuesday input bool Wednesday=true; // Wednesday input bool Thursday =true; // Thursday input bool Friday =true; // Friday input bool Saturday =true; // Saturday bool WeekDays[7];
Funzione di inizializzazione:
void WeekDays_Init() { WeekDays[0]=Sunday; WeekDays[1]=Monday; WeekDays[2]=Tuesday; WeekDays[3]=Wednesday; WeekDays[4]=Thursday; WeekDays[5]=Friday; WeekDays[6]=Saturday; }
Funzione principale:
bool WeekDays_Check(datetime aTime) { MqlDateTime stm; TimeToStruct(aTime,stm); return(WeekDays[stm.day_of_week]); }
Un indicatore creato sulla base di questa funzione può essere trovato allegato alla fine dell'articolo (il file TradeWeekDays.mq5).
Determinazione del tempo di trading della settimana
Dobbiamo determinare le sessioni di trading da un dato momento di un giorno della settimana a un dato momento di un altro giorno della settimana. Questa funzione è simile alla funzione TimeSession(), con l'unica differenza che i calcoli si basano sul tempo trascorso dall'inizio della settimana. La funzione che otteniamo è la seguente:
bool WeekSession(int aStartDay,int aStartHour,int aStartMinute,int aStopDay,int aStopHour,int aStopMinute,datetime aTimeCur) { //--- session start time since the week start int StartTime=aStartDay*86400+3600*aStartHour+60*aStartMinute; //--- session end time since the week start int StopTime=aStopDay*86400+3600*aStopHour+60*aStopMinute; //--- current time in seconds since the week start long TimeCur=SecondsFromWeekStart(aTimeCur,false); if(StopTime<StartTime) { //--- passing the turn of the week if(TimeCur>=StartTime || TimeCur<StopTime) { return(true); } } else { //--- within one week if(TimeCur>=StartTime && TimeCur<StopTime) { return(true); } } return(false); }
Un indicatore creato sulla base di questa funzione può essere trovato allegato alla fine dell'articolo (il file SessionWeek.mq5).
Abbiamo considerato praticamente tutte le attività più comuni legate al tempo e abbiamo esaminato le tecniche di programmazione pertinenti e le funzioni MQL5 standard necessarie per risolverle.
Funzioni MQL5 aggiuntive
Ci sono alcune altre funzioni MQL5 per lavorare con il tempo: TimeTradeServer(), TimeGMT(), TimeDaylightSavings() e TimeGMTOffset(). La principale particolarità di queste funzioni è che vengono utilizzate nelle impostazioni dell'orologio e dell'ora del PC di un utente.
La funzione TimeTradeServer(). È stato menzionato in precedenza nell'articolo che la funzione TimeCurrent() mostrerà l'ora sbagliata nei fine settimana (l'ora dell'ultima variazione di prezzo il venerdì). La funzione TimeTradeServer() calcola l'ora corretta del server:
datetime tm=TimeTradeServer(); //--- output result Alert(tm);
La funzione TimeGMT(). La funzione calcola l'ora GMT in base ai valori dell'orologio e alle impostazioni dell'ora del computer di un utente: fuso orario e ora legale:
datetime tm=TimeGMT(); //--- output result Alert(tm);
Per essere più precisi, la funzione restituisce l'ora UTC.
Funzione TimeDaylightSavings(). La funzione restituisce il valore di correzione per l'ora legale dalle impostazioni del computer dell'utente.
int val=TimeDaylightSavings(); //--- output result Alert(val);
Per ottenere l'ora senza il valore di correzione per l'ora legale, è necessario aggiungere il valore di correzione all'ora locale.
La funzione TimeGMTOffset(). La funzione consente di ottenere il fuso orario del computer di un utente. Il valore viene restituito in secondi per essere aggiunto all'ora locale per ottenere l'ora GMT.
int val=TimeGMTOffset(); //--- output result Alert(val);
L'ora sul computer di un utente sarà TimeGMT()-TimeGMTOffset()-TimeDaylightSavings():
datetime tm1=TimeLocal(); datetime tm2=TimeGMT()-TimeGMTOffset()-TimeDaylightSavings(); //--- output result Alert(tm1==tm2);
Alcune funzioni più utili per affrontare il tempo
Funzione per la determinazione dell'anno bisestile
bool LeapYear(datetime aTime) { MqlDateTime stm; TimeToStruct(aTime,stm); //--- a multiple of 4 if(stm.year%4==0) { //--- a multiple of 100 if(stm.year%100==0) { //--- a multiple of 400 if(stm.year%400==0) { return(true); } } //--- not a multiple of 100 else { return(true); } } return(false); }
Il principio di determinazione dell'anno bisestile è descritto sopra nella sezione Peculiarità della misurazione del tempo.
Funzione per determinare il numero di giorni in un mese
int DaysInMonth(datetime aTime) { MqlDateTime stm; TimeToStruct(aTime,stm); if(stm.mon==2) { //--- February if(LeapYear(aTime)) { //--- February in a leap year return(29); } else { //--- February in a non-leap year return(28); } } else { //--- other months return(31-((stm.mon-1)%7)%2); } }
La funzione controlla se l'anno è un anno bisestile per restituire il valore appropriato di 28 o 29 per febbraio e calcola il numero di giorni per gli altri mesi. Il numero di giorni nei primi 7 mesi si alterna come segue: 31, 30, 31, 30, ecc., nonché il numero di giorni dei restanti 5 mesi. Pertanto la funzione calcola il resto della divisione per 7. Quindi facciamo il controllo della parità dispari e la correzione ottenuta viene sottratta da 31.
Peculiarità del funzionamento della funzione temporale nel tester di strategia
Lo Strategy Tester genera il proprio flusso di virgolette e i valori della funzione TimeCurrent() corrispondono al flusso di virgolette nello Strategy Tester. I valori della funzione TimeTradeServer() corrispondono ai valori di TimeCurrent(). Analogamente, i valori della funzione TimeLocal() corrispondono ai valori di TimeCurrent(). La funzione TimeCurrent() in Strategy Tester non tiene conto dei fusi orari e della correzione dell'ora legale. Il funzionamento degli Expert Advisor si basa sulle variazioni di prezzo, quindi se il tuo Expert Advisor è tenuto a gestire il tempo, utilizza la funzione TimeCurrent(). Ciò ti consentirà di testare in sicurezza il tuo Expert Advisor nello Strategy Tester.
Le funzioni TimeGMT(), TimeDaylightSavings() e TimeGMTOffset() funzionano esclusivamente in base alle impostazioni correnti del computer di un utente (gli spostamenti all'ora legale e ritorno non sono simulati nello Strategy Tester). Se, quando si testa un Expert Advisor, è necessario simulare i cambiamenti di ora in ora legale e tornare all'ora solare (se questo è davvero necessario), è necessario che tu lo faccia per conto tuo. Ciò richiederà informazioni sulle date e l'ora esatte per spostare l'orologio, nonché un'analisi approfondita.
Una soluzione a questo problema va ben oltre lo scopo di un singolo articolo e non è considerata qui. Se un Expert Advisor lavora all'interno delle ore di sessione europee o americane, mentre il centro di negoziazione osserva l'ora legale, non ci sarà alcuna discrepanza tra l'ora del server e l'ora dell'evento, a differenza della sessione asiatica (il Giappone non osserva l'ora legale e l'Australia sposta i suoi orologi sull'ora legale a novembre).
Conclusione
L'articolo ha coperto tutte le funzioni standard MQL5 per lavorare con il tempo. Ha stabilito le tecniche di programmazione utilizzate nella gestione delle attività relative al tempo. L'articolo ha anche dimostrato come si creano diversi indicatori e alcune funzioni utili, con una descrizione dettagliata dei loro principi di funzionamento forniti.
Tutte le funzioni standard per lavorare con il tempo possono essere classificate in diverse categorie:
- TimeCurrent() e TimeLocal() sono le funzioni principali utilizzate per determinare l'ora corrente.
- TimeToString(), StringToTime(), TimeToStruct() e StructToTime() sono funzioni di elaborazione del tempo.
- CopyTime() è una funzione per lavorare con l'ora della barra.
- TimeTradeServer(), TimeGMT(), TimeDaylightSavings() e TimeGMTOffset() sono funzioni che dipendono dalle impostazioni del computer dell'utente.
File allegati
- sTestArea.mq5 - uno script per testare funzioni temporali complesse.
- sTestArea_Pivot1.mq5 - lo script sTestArea.mq5 utilizzato per testare le funzioni temporali dell'indicatore Pivot1.mq5.
- Pivot1.mq5 - un indicatore Pivot che utilizza giorni standard (la funzione NewDay).
- Session.mq5 - indicatore della sessione di trading del giorno (la funzione TimeSession).
- TimePoint.mq5 - un indicatore di un dato punto di tempo (la funzione TimeCross).
- Pivot2.mq5 - un indicatore pivot che utilizza giorni definiti dall'utente (la funzione NewCustomDay).
- TradeWeekDays.mq5 - un indicatore dei giorni di trading della settimana (la funzione WeekDays_Check).
- SessionWeek.mq5 - indicatore della sessione di trading settimanale (la funzione WeekSession).
- TimeFunctions.mqh - tutte le funzioni temporali fornite in questo articolo, in un unico file.
Tradotto dal russo da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/ru/articles/599
- 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