English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
preview
Sviluppare un Expert Advisor per il trading da zero (Parte 16): Accesso ai dati sul web (II)

Sviluppare un Expert Advisor per il trading da zero (Parte 16): Accesso ai dati sul web (II)

MetaTrader 5Integrazione | 11 aprile 2023, 15:09
547 0
Daniel Jose
Daniel Jose

Introduzione

Nel precedente articolo "Sviluppare un Expert Advisor di trading da zero (Parte 15): Accesso ai dati sul web (I)", abbiamo presentato l'intera logica e le idee alla base dei metodi di utilizzo della piattaforma MetaTrader 5 per accedere ai dati contrassegnati da siti web specializzati.

In quell'articolo, abbiamo considerato come accedere a questi siti e come trovare e recuperare informazioni da essi in modo da utilizzarli nella piattaforma. Ma non finisce qui, poiché la semplice acquisizione dei dati non ha molto senso. La parte più interessante è imparare come prendere questi dati dalla piattaforma e usarli in un Expert Advisor. Il metodo per farlo non è così ovvio e quindi è difficile da implementare senza conoscere e comprendere tutte le funzioni disponibili in MetaTrader 5.


Progettazione e realizzazione

Se non hai letto e compreso l'articolo precedente, ti consiglio di farlo e di cercare di capire tutti i concetti lì presenti, perché qui continueremo quell'argomento - studieremo un numero enorme di cose, risolveremo una serie di problemi e alla fine arriveremo ad una meravigliosa soluzione, poiché utilizzeremo MetaTrader 5 in un modo che non è stato ancora esplorato. Dico questo perché è stato difficile trovare link per poter utilizzare alcune delle funzionalità presenti nella piattaforma, ma qui proverò a spiegare come utilizzare una di queste risorse.

Quindi, prepariamoci e mettiamoci al lavoro.


1. Accesso ai dati Internet tramite un Expert Advisor

Questa è la parte più interessante che può essere implementata in questo sistema. Sebbene sia una cosa semplice, è di gran lunga la più pericolosa se mal pianificata. Pericolosa perché può lasciare l'EA in attesa di una risposta dal server, anche se solo per un momento.

Questa logica è mostrata nella figura seguente:

Andiamo a vedere come l'EA interagisce direttamente con il server web che contiene le informazioni che vogliamo acquisire. Di seguito puoi vedere un esempio completo di codice che funziona esattamente in questo modo.

#property copyright "Daniel Jose"
#property version "1.00"
//+------------------------------------------------------------------+
int OnInit()
{
        EventSetTimer(1);
        
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
        EventKillTimer();
}
//+------------------------------------------------------------------+
void OnTick()
{
}
//+------------------------------------------------------------------+
void OnTimer()
{
        Print(GetDataURL("https://tradingeconomics.com/stocks", 100, "<!doctype html>", 2, "INDU:IND", 172783, 173474, 0x0D));
}
//+------------------------------------------------------------------+
string GetDataURL(const string url, const int timeout, const string szTest, int iTest, const string szFind, int iPos, int iInfo, char cLimit)
{
        string  headers, szInfo = "";
        char    post[], charResultPage[];
        int     counter;
   
        if (WebRequest("GET", url, NULL, NULL, timeout, post, 0, charResultPage, headers) == -1return "Bad";
        for (int c0 = 0, c1 = StringLen(szTest); c0 < c1; c0++) if (szTest[c0] != charResultPage[iTest + c0]) return "Failed";
        for (int c0 = 0, c1 = StringLen(szFind); c0 < c1; c0++) if (szFind[c0] != charResultPage[iPos + c0]) return "Error";
        for (counter = 0; charResultPage[counter + iInfo] == 0x20; counter++);
        for (;charResultPage[counter + iInfo] != cLimit; counter++) szInfo += CharToString(charResultPage[counter + iInfo]);
        
        return szInfo;
}
//+------------------------------------------------------------------+

Se guardi da vicino, possiamo vedere che il codice è esattamente lo stesso che è stato creato nell'articolo precedente, ma ora questo codice fa parte dell'EA ed è stato adattato per questo, cioè se qualcosa ha già funzionato lì, funzionerà qui. La differenza è che l'EA contiene una nuova condizione, il che implica che il codice verrà eseguito ogni secondo, ovvero, l'EA effettuerà una richiesta al server web desiderato ogni secondo e attenderà una risposta. Quindi fornirà i dati acquisiti e tornerà ad altre funzioni interne. Questo ciclo sarà ripetuto per tutta la durata dell'EA. Il risultato dell'esecuzione può essere visto sotto.


Sebbene ciò avvenga esattamente in questo modo, sconsiglio questa pratica perché l'EA si bloccherà in attesa della risposta del server anche se solo per pochi istanti - questo può mettere in pericolo il sistema di trading della piattaforma e l'EA stesso. D'altra parte, se sei interessato ad apprendere il metodo, puoi imparare molto da questo sistema.

Ma se disponi di un server locale che instraderà le informazioni tra Internet e la piattaforma, forse questo metodo sarà sufficiente. In questo caso, se il sistema effettua una richiesta, accadrà quanto segue: il server locale non avrà ancora alcuna informazione e risponderà rapidamente, il che ti risparmierà i passaggi successivi.

Consideriamo ora un altro modo per eseguire questo tipo di attività, che è un po' più sicuro. Poiché utilizzeremo il sistema MetaTrader 5 threading per ottenere almeno un po' di sicurezza e per prevenire che l'EA sia soggetto alle condizioni del server web remoto, possiamo rimanere in attesa per qualche istante in attesa che il server remoto risponda. Creeremo condizioni aggiuntive affinché l’EA sappia cosa sta succedendo, potendo raccogliere informazioni dal web.


2. Creazione di un canale di comunicazione

Un modo migliore ma più semplice per ottenere i dati raccolti online e utilizzarli in un EA è un canale. Sebbene funzioni, in alcuni casi non è molto adatto a noi, poiché ci sono limitazioni all'uso di questi canali. Ma almeno l'EA sarà in grado di accedere alle informazioni raccolte sul web senza dover attendere una risposta da un server remoto.

È già stato detto in precedenza che il modo più semplice per risolvere il problema è l'instradamento dei dati: creare un server locale che scarichi i dati e li fornisca alla piattaforma MetaTrader 5. Ma questo richiede una certa conoscenza e potenza di calcolo e complica quasi tutti i casi. Tuttavia, possiamo utilizzare le funzionalità di MetaTrader 5 per creare un canale molto simile, che sarebbe molto più semplice dell’instradamento attraverso un server locale.

La figura seguente mostra come promuoveremo questo canale.

Viene creato utilizzando un oggetto. Nota che l'EA cercherà all'interno dell'oggetto le informazioni che lo script ha inserito. Per capire come funziona effettivamente, diamo un'occhiata a tre codici, che sono mostrati per intero di seguito. Uno sarà un EA, l'altro sarà un'intestazione che contiene l'oggetto e il terzo sarà uno script.

#property copyright "Daniel Jose"
#property description "Testing Inner Channel"
#property version "1.00"
//+------------------------------------------------------------------+
#include <Inner Channel.mqh>
//+------------------------------------------------------------------+
int OnInit()
{
        
        EventSetTimer(1);
        
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
        EventKillTimer();
}
//+------------------------------------------------------------------+
void OnTick()
{
}
//+------------------------------------------------------------------+
void OnTimer()
{
        Print(GetInfoInnerChannel());
}
//+------------------------------------------------------------------+

Il codice seguente è l'intestazione che dobbiamo utilizzare. Si prega di notare che l'oggetto qui è dichiarato condiviso tra l'EA e lo script.

//+------------------------------------------------------------------+
//|                                          Canal Intra Process.mqh |
//|                                                      Daniel Jose |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#define def_NameObjectChannel   "Inner Channel Info WEB"
//+------------------------------------------------------------------+
void CreateInnerChannel(void)
{
        long id;
        
        ObjectCreate(id = ChartID(), def_NameObjectChannel, OBJ_LABEL, 0, 0, 0);
        ObjectSetInteger(id, def_NameObjectChannel, OBJPROP_COLOR, clrNONE);
}
//+------------------------------------------------------------------+
void RemoveInnerChannel(void)
{
        ObjectDelete(ChartID(), def_NameObjectChannel);
}
//+------------------------------------------------------------------+
inline void SetInfoInnerChannel(string szArg)
{
        ObjectSetString(ChartID(), def_NameObjectChannel, OBJPROP_TEXT, szArg);
}
//+------------------------------------------------------------------+
inline string GetInfoInnerChannel(void)
{
        return ObjectGetString(ChartID(), def_NameObjectChannel, OBJPROP_TEXT);
}
//+------------------------------------------------------------------+

E infine, ecco uno script. Sostituirà la creazione di un server locale e svolgerà effettivamente il lavoro del server.

#property copyright "Daniel Jose"
#property version   "1.00"
//+------------------------------------------------------------------+
#include <Inner Channel.mqh>
//+------------------------------------------------------------------+
void OnStart()
{
        CreateInnerChannel();
        while (!IsStopped())
        {
                SetInfoInnerChannel(GetDataURL("https://tradingeconomics.com/stocks", 100, "<!doctype html>", 2, "INDU:IND", 172783, 173474, 0x0D));
                Sleep(200);
        }
        RemoveInnerChannel();
}
//+------------------------------------------------------------------+
string GetDataURL(const string url, const int timeout, const string szTest, int iTest, const string szFind, int iPos, int iInfo, char cLimit)
{
        string  headers, szInfo = "";
        char    post[], charResultPage[];
        int     counter;
   
        if (WebRequest("GET", url, NULL, NULL, timeout, post, 0, charResultPage, headers) == -1) return "Bad";
        for (int c0 = 0, c1 = StringLen(szTest); (!_StopFlag) && (c0 < c1); c0++) if (szTest[c0] != charResultPage[iTest + c0]) return "Failed";
        for (int c0 = 0, c1 = StringLen(szFind); (!_StopFlag) && (c0 < c1); c0++) if (szFind[c0] != charResultPage[iPos + c0]) return "Error";
        for (counter = 0; (!_StopFlag) && (charResultPage[counter + iInfo] == 0x20); counter++);
        for (;(!_StopFlag) && (charResultPage[counter + iInfo] != cLimit); counter++) szInfo += CharToString(charResultPage[counter + iInfo]);
        
        return (_StopFlag ? "" : szInfo);
}
//+------------------------------------------------------------------+

Qui abbiamo un oggetto che l'EA può vedere e che viene creato dallo script. L'idea è che l'EA e lo script possano coesistere nello stesso grafico, quindi questo oggetto sarà il canale di comunicazione tra l'EA e lo script. L'EA sarà un client, lo script sarà un server e l'oggetto sarà il canale di comunicazione tra loro. Pertanto, lo script acquisirà i valori sul server web remoto e inserirà il valore desiderato nell'oggetto. L'EA vedrà di tanto in tanto quale valore c'è nell'oggetto (se l'oggetto esiste), perché se lo script non è in esecuzione, l'oggetto non dovrebbe essere disponibile. Ad ogni modo, il momento in cui l'EA guarda il valore nell'oggetto non viola in alcun modo lo script. Se lo script è bloccato perché è in attesa di una risposta da un server remoto, ciò non influirà sull'EA, poiché continuerà la sua attività a prescindere da ciò che fa lo script.

Sebbene questa sia un'ottima soluzione, non è perfetta: il problema è con lo script.

Per capire questo, guarda il seguente video, prestando attenzione ad ogni dettaglio.


Tutto funziona alla grande. Era previsto, poiché questo tipo di soluzione è ampiamente utilizzata nella programmazione durante lo sviluppo di un programma client-server, dove non vogliamo che uno blocchi l'altro. In altre parole, utilizziamo un canale per comunicare tra i processi. Spesso, quando si trovano nello stesso ambiente, il canale verrà creato utilizzando la memoria — per questo viene appositamente allocata un'area isolata, la quale è tuttavia condivisa e visibile sia al client che al server. Il server aggiunge i dati lì e il client visita la stessa area per acquisire i dati esistenti. Quindi, uno non dipende dall'altro, mentre entrambi sono collegati.

L'idea è quella di usare lo stesso principio. Ma il modo in cui funziona lo script genera un problema. Quando cambiamo il timeframe, lo script si chiude e anche quando utilizziamo un ciclo infinito, viene chiuso da MetaTrader 5. Poiché questo accade, dovremmo reinizializzare lo script e rimetterlo sul grafico. Ma se dobbiamo cambiare costantemente timeframe, questo sarà un problema, per non parlare della necessità di riavviare lo script sul grafico ogni volta.

Inoltre, potremmo dimenticare di controllare se lo script è sul grafico o meno e quindi finirai per utilizzare le informazioni sbagliate poiché a causa del modo in cui è codificato l'EA non possiamo sapere se lo script è sul grafico. Questo può essere risolto controllando se uno script è sul grafico oppure no. Questo compito non è difficile: basta scrivere un controllo dell'ora dell'ultima pubblicazione dello script nell'oggetto. Questo risolverebbe il problema.

Tuttavia, è possibile creare una soluzione decisamente migliore (almeno in alcuni casi), e ad essere onesti, questa è quasi la soluzione ideale, e useremo lo stesso concetto presentato sopra, solo che invece di uno script useremo un servizio.


3. Creazione di un servizio

Questa è una soluzione estrema, ma poiché lo script ha il problema di essere terminato ad ogni modifica del timeframe, dobbiamo utilizzare un altro metodo. Ma risolvendo un problema, ne creiamo un altro. In ogni caso, è bene sapere quali soluzioni sono possibili e come possono essere utilizzate. Ma la cosa principale è conoscere i limiti che ogni soluzione presenta e quindi provare a trovare qualcosa nel mezzo, che permetta di risolvere il problema nel miglior modo possibile.

La programmazione è una cosa tale che quando proviamo a risolvere un problema, ne creiamo uno nuovo.

Il nostro obiettivo è creare qualcosa di simile all'immagine qui sotto:

Sebbene possa sembrare una questione semplice, le risorse coinvolte sono generalmente molto poco esplorate. Quindi, cercherò di entrare nei dettagli per aiutare chiunque voglia saperne di più su come lavorare con queste risorse.


3.1. Accesso alle variabili globali

Questa parte è così poco studiata che all'inizio ho anche pensato di creare una dll solo per supportare questa funzione, ma dopo aver esaminato la documentazione di MQL5, l'ho trovata. Il problema è che dobbiamo accedere o creare un punto comune tra il servizio e l'EA. Quando usavamo uno script, questo punto era un oggetto, ma quando usiamo un servizio non possiamo fare lo stesso. La soluzione sarebbe utilizzare una variabile esterna, ma quando ho provato a farlo, le prestazioni non erano quelle previste. Per ulteriori dettagli è possibile consultare la documentazione relativa alle variabili esterne. Spiega cosa fare.

Quindi, questa idea non era buona, così ho deciso di utilizzare la dll. Tuttavia, volevo ancora imparare MetaTrader 5 e MQL5, così guardando nel terminale, ho trovato quello che puoi vedere nell'immagine qui sotto:

         

Questo è quello che cercavamo: abbiamo aggiunto una variabile per poter controllare come può essere configurata questa procedura. Tuttavia, possiamo usare solo valori double . Si potrebbe pensare che sia questo il problema (anche se questo è davvero un limite), ma questo è sufficiente quando vogliamo trasmettere messaggi brevi, come nel nostro caso. In realtà, il tipo double è una breve stringa di 8 caratteri, quindi possiamo trasmettere valori o brevi messaggi tra programmi.

Quindi, la prima parte del problema è risolta. MetaTrader 5 fornisce metodi per creare un canale senza dover creare una dll, ma ora abbiamo un altro problema: come accedere a queste variabili attraverso il programma? È possibile creare variabili globali all'interno del programma — all'interno di un Expert Advisor, uno script, un indicatore o un servizio? O dovremmo usare solo quelli dichiarati nel terminale?

Queste domande sono molto importanti se vogliamo davvero utilizzare questa soluzione. Se non fosse possibile utilizzarli tramite programmi, dovremmo utilizzare le dll. Ma è possibile. Per ulteriori informazioni, vedere Variabili globali del terminale.


3.2. Utilizzo di una variabile terminale per lo scambio di informazioni

Ora che abbiamo considerato le nozioni di base, creiamo qualcosa di semplice in modo da poter testare e capire come funzionerà nella pratica il processo di utilizzo delle variabili terminali.

A tale scopo, ho creato i seguenti codici. Il primo è il file di intestazione:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#define def_GlobalNameChannel   "InnerChannel"
//+------------------------------------------------------------------+

Qui definiamo semplicemente il nome della variabile globale del terminale che sarà la stessa per i due processi che verranno eseguiti sul terminale grafico.

Di seguito è riportato il codice che rappresenta il servizio da eseguire.

#property service
#property copyright "Daniel Jose"
#property version   "1.00"
//+------------------------------------------------------------------+
#include <Inner Channel.mqh>
//+------------------------------------------------------------------+
void OnStart()
{
        double count = 0;
        while (!IsStopped())
        {
                if (!GlobalVariableCheck(def_GlobalNameChannel)) GlobalVariableTemp(def_GlobalNameChannel);
                GlobalVariableSet(def_GlobalNameChannel, count);
                count += 1.0;
                Sleep(1000);
        }
}
//+------------------------------------------------------------------+

Il suo funzionamento è semplice: controlla se la variabile è già stata dichiarata e cosa sta facendo il GlobalVariableCheck . Se la variabile non esiste, verrà creata temporaneamente dalla funzione GlobalVariableTemp e riceverà quindi un valore dalla funzione GlobalVariableSet . In altre parole, stiamo testando, creando e scrivendo informazioni, il servizio agisce come un server, proprio come uno script, solo che non stiamo ancora accedendo al sito web. Innanzitutto, dovremmo capire come funziona il sistema.

Il passo successivo è creare un client che nel nostro caso è un Expert Advisor:

#property copyright "Daniel Jose"
#property description "Testing internal channel\nvia terminal global variable"
#property version "1.03"
//+------------------------------------------------------------------+
#include <Inner Channel.mqh>
//+------------------------------------------------------------------+
int OnInit()
{
        EventSetTimer(1);
        
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
        EventKillTimer();
}
//+------------------------------------------------------------------+
void OnTick()
{
}
//+------------------------------------------------------------------+
void OnTimer()
{
        double value;
        if (GlobalVariableCheck(def_GlobalNameChannel))
        {
                GlobalVariableGet(def_GlobalNameChannel, value);
                Print(value);           
        }
}
//+------------------------------------------------------------------+

Il codice è semplice: ogni secondo l'EA controllerà se la variabile esiste. Se esiste, l'EA leggerà il valore utilizzando GlobalVariableGet e produrrà questo valore nel terminale.

Vediamo come può essere implementato questo processo. Per prima cosa eseguiamo il servizio. È fatto come segue:

Ma è possibile un altro scenario, quando il servizio si è fermato e lo riavviamo. In questo caso procederemo come segue:

Successivamente, controlliamo le variabili del terminale e otteniamo il seguente risultato:

Si noti che il sistema sta ovviamente funzionando, ma ora dobbiamo posizionare l'EA sul grafico, ottenere i valori e quindi confermare la connessione sul canale. Quindi, dopo aver posizionato l'EA sul grafico, otteniamo il seguente risultato:

Questo è tutto, il sistema funziona come vogliamo. In realtà abbiamo un modello che è mostrato di seguito. È un tipico formato client-server, ed è esattamente quello che vogliamo fare. Stiamo cercando di implementare esattamente questo formato per i vantaggi che ho menzionato prima.

Ora dobbiamo solo aggiungere un sistema per leggere e ottenere valori dal web. Quindi avremo il modello finale da testare. Questa parte è abbastanza semplice: prendere il codice che abbiamo utilizzato sin dall'inizio e aggiungerlo al servizio. Per eseguire il test, dobbiamo solo modificare il file del server per leggere il valore dal sito Web e pubblicare tale valore affinché il client possa leggerlo. Il nuovo codice del servizio è il seguente.

#property service
#property copyright "Daniel Jose"
#property version   "1.00"
//+------------------------------------------------------------------+
#include <Inner Channel.mqh>
//+------------------------------------------------------------------+
void OnStart()
{
        string szRet;
        
        while (!IsStopped())
        {
                if (!GlobalVariableCheck(def_GlobalNameChannel)) GlobalVariableTemp(def_GlobalNameChannel);
                szRet = GetDataURL("https://tradingeconomics.com/stocks", 100, "<!doctype html>", 2, "INDU:IND", 172783, 173474, 0x0D);
                GlobalVariableSet(def_GlobalNameChannel, StringToDouble(szRet));
                Sleep(1000);
        }
}
//+------------------------------------------------------------------+
string GetDataURL(const string url, const int timeout, const string szTest, int iTest, const string szFind, int iPos, int iInfo, char cLimit)
{
        string  headers, szInfo = "";
        char    post[], charResultPage[];
        int     counter;
   
        if (WebRequest("GET", url, NULL, NULL, timeout, post, 0, charResultPage, headers) == -1) return "Bad";
        for (int c0 = 0, c1 = StringLen(szTest); c0 < c1; c0++) if (szTest[c0] != charResultPage[iTest + c0]) return "Failed";
        for (int c0 = 0, c1 = StringLen(szFind); c0 < c1; c0++) if (szFind[c0] != charResultPage[iPos + c0]) return "Error";
        for (counter = 0; charResultPage[counter + iInfo] == 0x20; counter++);
        for (;charResultPage[counter + iInfo] != cLimit; counter++) szInfo += CharToString(charResultPage[counter + iInfo]);
        
        return szInfo;
}
//+------------------------------------------------------------------+

Ora abbiamo un sistema che funziona come mostrato nell'immagine qui sotto:

Quindi, è pronto e otteniamo i seguenti risultati. Inoltre, cambiare il timeframe non sarà più un problema.



Conclusioni

Oggi abbiamo considerato diverse funzionalità di MetaTrader 5 che sono state poco esplorate. Uno di questi sono i canali di comunicazione. Tuttavia, non stiamo ancora sfruttando tutti i vantaggi di questa funzione. Ma possiamo andare ancora oltre — lo faremo nel prossimo articolo. Tutto ciò che abbiamo visto finora in questa serie ci mostra quanto possiamo fare nella piattaforma MetaTrader 5. Basta scegliere un percorso e andare avanti fino ad ottenere i risultati desiderati, anche se è utile conoscere i limiti, i benefici e i rischi associati a ciascuno dei possibili percorsi.


Tradotto dal portoghese da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/pt/articles/10442

File allegati |
Script_e_EA.zip (3.03 KB)
Serviso_e_EA.zip (2.39 KB)
Sviluppare un Expert Advisor per il trading da zero (Parte 17): Accesso ai dati sul web (III) Sviluppare un Expert Advisor per il trading da zero (Parte 17): Accesso ai dati sul web (III)
In questo articolo continuiamo a considerare come ottenere dati dal web e utilizzarli in un Expert Advisor. Questa volta procederemo allo sviluppo di un sistema alternativo.
Scienza dei dati e apprendimento automatico (Parte 06): Discesa del Gradiente Scienza dei dati e apprendimento automatico (Parte 06): Discesa del Gradiente
La discesa del gradiente gioca un ruolo significativo nell'addestramento delle reti neurali e di molti algoritmi di apprendimento automatico. È un algoritmo veloce e intelligente, nonostante il suo lavoro impressionante, è ancora frainteso da molti data scientist, vediamo di cosa si tratta.
Impara come progettare un sistema di trading tramite la Deviazione Standard Impara come progettare un sistema di trading tramite la Deviazione Standard
Ecco un nuovo articolo della nostra serie su come progettare un sistema di trading con gli indicatori tecnici più popolari nella piattaforma di trading MetaTrader 5. In questo nuovo articolo, impareremo come progettare un sistema di trading tramite l'indicatore Deviazione Standard.
Indicatore CCI. Aggiornamento e nuove funzionalità Indicatore CCI. Aggiornamento e nuove funzionalità
In questo articolo prenderò in considerazione la possibilità di aggiornare l'indicatore CCI. Inoltre, presenterò una modifica dell'indicatore.