Utilizzo di WinInet.dll per lo scambio di dati tra terminali tramite Internet
MetaTrader 5 apre opportunità uniche per gli utenti, utilizzando una serie di nuovi elementi dell'interfaccia utente nel suo arsenale. Per questo motivo, le funzioni che prima non erano disponibili ora possono essere utilizzate al massimo.
In questa lezione impareremo a:
- utilizzare le tecnologie Internet di base;
- scambiare dati tra terminali tramite il server;
- creare una classe di libreria generica per lavorare con Internet nell'ambiente MQL5.
Il CodeBase MQL5 contiene un esempio di script che funziona con la libreria wininet.dll e mostra un esempio della richiesta della pagina del server. Ma oggi andremo molto oltre e faremo in modo che il server non solo ci dia la pagina, ma invii e memorizzi questi dati per i successivi trasferimenti ad altri terminali richiedenti.
Nota: per chi non ha accesso ad un server configurato con PHP, suggeriamo di scaricare il Denwer kit e di utilizzarlo come piattaforma funzionante. Inoltre, ti consigliamo di utilizzare il server Apache e PHP sul tuo localhost per i test.
Per inviare qualsiasi richiesta al server, avremo bisogno delle 7 principali funzioni della libreria.
InternetAttemptConnect | Tenta di individuare una connessione Internet e stabilirla |
InternetOpen | Inizializza la struttura per il lavoro delle funzioni della libreria WinInet. Questa funzione deve essere attivata prima di attivare qualsiasi altra funzione della libreria. |
InternetConnect | Apre la risorsa specificata dall'indirizzo HTTP URL o FTP. Restituisce il descrittore a una connessione aperta |
HttpOpenRequest | Crea un descrittore per le richieste HTTP per l'impostazione di una connessione |
HttpSendRequest | Invia una query utilizzando il descrittore creato |
InternetReadFile | Legge i dati ricevuti dal server dopo che la query è stata elaborata |
InternetCloseHandle | Rilascia il descrittore trasferito |
Una descrizione dettagliata di tutte le funzioni e dei relativi parametri è disponibile nella Guida del sistema MSDN.
La dichiarazione delle funzioni è rimasta la stessa di MQL4, ad eccezione dell'uso di Unicode per chiamate e trasferimenti di linea tramite il collegamento.
#import "wininet.dll" int InternetAttemptConnect(int x); int InternetOpenW(string &sAgent,int lAccessType,string &sProxyName,string &sProxyBypass,int lFlags); int InternetConnectW(int hInternet,string &szServerName,int nServerPort,string &lpszUsername,string &lpszPassword,int dwService,int dwFlags,int dwContext); int HttpOpenRequestW(int hConnect,string &Verb,string &ObjectName,string &Version,string &Referer,string &AcceptTypes,uint dwFlags,int dwContext); int HttpSendRequestW(int hRequest,string &lpszHeaders,int dwHeadersLength,uchar &lpOptional[],int dwOptionalLength); int HttpQueryInfoW(int hRequest,int dwInfoLevel,int &lpvBuffer[],int &lpdwBufferLength,int &lpdwIndex); int InternetReadFile(int hFile,uchar &sBuffer[],int lNumBytesToRead,int &lNumberOfBytesRead); int InternetCloseHandle(int hInet); #import //To make it clear, we will use the constant names from wininet.h. #define OPEN_TYPE_PRECONFIG 0 // use the configuration by default #define FLAG_KEEP_CONNECTION 0x00400000 // do not terminate the connection #define FLAG_PRAGMA_NOCACHE 0x00000100 // no cashing of the page #define FLAG_RELOAD 0x80000000 // receive the page from the server when accessing it #define SERVICE_HTTP 3 // the required protocol
Una descrizione dettagliata dei flag si trova nella stessa sezione MSDN per ciascuna delle funzioni. Se desideri vedere la dichiarazione di altre costanti e funzioni, puoi scaricare il file wininet.h originale, che si trova negli allegati all'articolo.
1. Guide per la creazione e l'eliminazione di sessioni Internet
La prima cosa da fare è creare una sessione e aprire una connessione all'host. Una sessione deve preferibilmente essere creata solo una volta durante l'inizializzazione del programma (ad esempio, in una funzione OnInit). Oppure può essere fatto all'inizio dell'avvio dell'Expert Advisor, ma è importante assicurarsi che la sua creazione sia avvenuta con successo una sola volta prima della chiusura della sessione. E non deve essere invocato e ripetuto senza necessità ad ogni nuova iterazione dell'implementazione OnStart o OnTimer. È importante evitare chiamate frequenti e la creazione delle strutture necessarie per ogni chiamata.
Pertanto, utilizzeremo solo un'istanza di classe globale per descrivere i descrittori di sessione e connessione.
string Host; // host name int Port; // port int Session; // session descriptor int Connect; // connection descriptor bool MqlNet::Open(string aHost,int aPort) { if(aHost=="") { Print("-Host is not specified"); return(false); } // checking the DLL resolution in the terminal if(!TerminalInfoInteger(TERMINAL_DLLS_ALLOWED)) { Print("-DLL is not allowed"); return(false); } // if the session was identifies, then we close if(Session>0 || Connect>0) Close(); // record of attempting to open into the journal Print("+Open Inet..."); // if we were not able to check for the presence of an Internet connection, then we exit if(InternetAttemptConnect(0)!=0) { Print("-Err AttemptConnect"); return(false); } string UserAgent="Mozilla"; string nill=""; // open a session Session=InternetOpenW(UserAgent,OPEN_TYPE_PRECONFIG,nill,nill,0); // if we were not able to open a session, then exit if(Session<=0) { Print("-Err create Session"); Close(); return(false); } Connect=InternetConnectW(Session,aHost,aPort,nill,nill,SERVICE_HTTP,0,0); if(Connect<=0) { Print("-Err create Connect"); Close(); return(false); } Host=aHost; Port=aPort; // otherwise all attempts were successful return(true); }
Dopo l'inizializzazione, i descrittori Session e Connect possono essere utilizzati in tutte le seguenti funzioni. Una volta che tutto il lavoro è stato completato e i programmi MQL sono stati disinstallati, devono essere rimossi. Questa operazione viene eseguita utilizzando la funzione InternetCloseHandle.
void MqlNet::CloseInet() { Print("-Close Inet..."); if(Session>0) InternetCloseHandle(Session); Session=-1; if(Connect>0) InternetCloseHandle(Connect); Connect=-1; }
Attenzione! Quando si lavora con le funzioni Internet, è necessario liberare tutti i descrittori derivati da esse utilizzando InternetCloseHandle.
2. Invio di una richiesta al server e ricezione della pagina
Per inviare una richiesta e ricevere una pagina in risposta a questa richiesta, avremo bisogno delle restanti tre funzioni HttpOpenRequest, HttpSendRequest e InternetReadFile. L'essenza della ricezione della pagina in risposta a una richiesta è fondamentalmente il semplice processo di salvataggio del suo contenuto in un file locale.
Per la comodità di lavorare con richieste e contenuti creeremo due funzioni universali.
Invio di una richiesta:
bool MqlNet::Request(string Verb,string Object,string &Out,bool toFile=false,string addData="",bool fromFile=false) { if(toFile && Out=="") { Print("-File is not specified "); return(false); } uchar data[]; int hRequest,hSend,h; string Vers="HTTP/1.1"; string nill=""; if(fromFile) { if(FileToArray(addData,data)<0) { Print("-Err reading file "+addData); return(false); } } // read file in the array else StringToCharArray(addData,data); if(Session<=0 || Connect<=0) { Close(); if(!Open(Host,Port)) { Print("-Err Connect"); Close(); return(false); } } // create a request descriptor hRequest=HttpOpenRequestW(Connect,Verb,Object,Vers,nill,nill,FLAG_KEEP_CONNECTION|FLAG_RELOAD|FLAG_PRAGMA_NOCACHE,0); if(hRequest<=0) { Print("-Err OpenRequest"); InternetCloseHandle(Connect); return(false); } // send request // headline for request string head="Content-Type: application/x-www-form-urlencoded"; // sent file hSend=HttpSendRequestW(hRequest,head,StringLen(head),data,ArraySize(data)-1); if(hSend<=0) { Print("-Err SendRequest"); InternetCloseHandle(hRequest); Close(); } // read the page ReadPage(hRequest,Out,toFile); // close all handles InternetCloseHandle(hRequest); InternetCloseHandle(hSend); return(true); }
Parametri di funzione di MqlNet:: Richiesta:
- stringa Verb – tipo di richiesta “GET” o “POST”;
- stringa Object – nome della pagina con i suoi parametri passati;
- stringa &Out – riga verso cui si riceve la risposta;
- bool toFile – se toFile=true, allora Out indica il nome del file dove deve essere ricevuta la risposta;
- stringa addData - Dati aggiuntivi;
- bool fromFile - se fromFile = true, addData è il nome del file che deve essere inviato.
Lettura del contenuto del descrittore ricevuto
void MqlNet::ReadPage(int hRequest,string &Out,bool toFile) { // read the page uchar ch[100]; string toStr=""; int dwBytes,h; while(InternetReadFile(hRequest,ch,100,dwBytes)) { if(dwBytes<=0) break; toStr=toStr+CharArrayToString(ch,0,dwBytes); } if(toFile) { h=FileOpen(Out,FILE_BIN|FILE_WRITE); FileWriteString(h,toStr); FileClose(h); } else Out=toStr; }
Parametri di funzione di MqlNet:: ReadPage:
- Int hRequest - descrittore della richiesta, da cui vengono letti i dati;
- stringa &Out – riga verso cui si riceve la risposta;
- bool toFile - se toFile = true, Out è il nome del file in cui verrà ricevuta la risposta.
E riunendo tutto questo in uno, otterremo una classe di libreria MqlNet per lavorare con Internet.
class MqlNet { string Host; // host name int Port; // port int Session; // session descriptor int Connect; // connection descriptor public: MqlNet(); // class constructor ~MqlNet(); // destructor bool Open(string aHost,int aPort); // create a session and open a connection void Close(); // close session and connection bool Request(string Verb,string Request,string &Out,bool toFile=false,string addData="",bool fromFile=false); // send request bool OpenURL(string URL,string &Out,bool toFile); // somply read the page into the file or the variable void ReadPage(int hRequest,string &Out,bool toFile); // read the page int FileToArray(string FileName,uchar &data[]); // copy the file into the array for sending };
Queste sono fondamentalmente tutte le funzioni richieste che possono soddisfare le esigenze diversificate per lavorare con Internet. Considera gli esempi del loro uso.
Esempio 1. Download automatico dei programmi MQL nelle cartelle del terminale. Script MetaGrabber
Per iniziare il nostro test del lavoro della classe, iniziamo con le attività più semplici: leggere la pagina e salvarne il contenuto nella cartella specificata. Ma è improbabile che una semplice lettura delle pagine sia molto interessante, quindi, per ricavare qualcosa dal lavoro dello script, assegniamogli una funzione di grabber di programmi mql dai siti. Il compito dello script MetaGrabber sarà:
- Analisi dell'URL e sua separazione nell'host, nella richiesta e nel nome del file;
- inviare una richiesta all'host, ricevere e salvare il file nella cartella del terminale \\ Files;
- spostandolo dai File a una delle cartelle dati richieste:
\Experts, \Indicators, \Scripts, \Include, \Libraries, \Tester(set), \Templates.
Per risolvere il secondo problema utilizziamo la classe MqlNet. Per la terza attività utilizziamo la funzione MoveFileEx da Kernel32.dll
#import "Kernel32.dll" bool MoveFileExW(string &lpExistingFileName, string &lpNewFileName, int dwFlags); #import "Kernel32.dll"
Per il primo problema, creiamo una piccola funzione di servizio per analizzare la riga dell'URL.
Dobbiamo allocare tre righe dall'indirizzo: l'host, il percorso del sito e il nome del file.
Ad esempio, in linea http://www.mysite.com/folder/page.html
- Host = www.mysite.com
- Request = / folder / page.html
- File name = page.html
Nel caso di CodeBase sul sito MQL5, i percorsi hanno la stessa struttura. Ad esempio, il percorso della libreria ErrorDescription.mq5 nella pagina https://www.mql5.com/ru/code/79 è simile a http://p.mql5.com/data/18/79/ErrorDescription.mqh. Questo percorso è facilmente ottenibile facendo clic con il tasto destro sul link e selezionando "Copia link". Pertanto, l'URL è diviso in due parti, una per la richiesta e una per il nome del file per comodità di archiviazione dei file.
- Host = p.mql5.com
- Request = / data/18/79/5/ErrorDescription.mqh
- File name = ErrorDescription.mqh
Questo è il tipo di analisi della linea di cui si occuperà la seguente funzione ParseURL.
void ParseURL(string path,string &host,string &request,string &filename) { host=StringSubstr(URL,7); // removed int i=StringFind(host,"/"); request=StringSubstr(host,i); host=StringSubstr(host,0,i); string file=""; for(i=StringLen(URL)-1; i>=0; i--) if(StringSubstr(URL,i,1)=="/") { file=StringSubstr(URL,i+1); break; } if(file!="") filename=file; }
Nei parametri esterni dello script inseriremo solo due parametri, URL (percorso del file mql5) e il tipo di cartella di successivo posizionamento, ovvero in quale cartella di terminale si desidera inserirlo.
Di conseguenza, otteniamo uno script breve ma molto utile.
//+------------------------------------------------------------------+ //| MetaGrabber.mq5 | //| Copyright © 2010 www.fxmaster.de | //| Coding by Sergeev Alexey | //+------------------------------------------------------------------+ #property copyright "www.fxmaster.de © 2010" #property link "www.fxmaster.de" #property version "1.00" #property description "Download files from internet" #property script_show_inputs #include <InternetLib.mqh> #import "Kernel32.dll" bool MoveFileExW(string &lpExistingFileName,string &lpNewFileName,int dwFlags); #import #define MOVEFILE_REPLACE_EXISTING 0x1 enum _FolderType { Experts=0, Indicators=1, Scripts=2, Include=3, Libraries=4, Files=5, Templates=6, TesterSet=7 }; input string URL=""; input _FolderType FolderType=0; //------------------------------------------------------------------ OnStart int OnStart() { MqlNet INet; // variable for working in the Internet string Host,Request,FileName="Recieve_"+TimeToString(TimeCurrent())+".mq5"; // parse url ParseURL(URL,Host,Request,FileName); // open session if(!INet.Open(Host,80)) return(0); Print("+Copy "+FileName+" from http://"+Host+" to "+GetFolder(FolderType)); // obtained file if(!INet.Request("GET",Request,FileName,true)) { Print("-Err download "+URL); return(0); } Print("+Ok download "+FileName); // move to the target folder string to,from,dir; // if there is no need to move it elsewhere if(FolderType==Files) return(0); // from from=TerminalInfoString(TERMINAL_DATA_PATH)+"\\MQL5\\Files\\"+FileName; // to to=TerminalInfoString(TERMINAL_DATA_PATH)+"\\"; if(FolderType!=Templates && FolderType!=TesterSet) to+="MQL5\\"; to+=GetFolder(FolderType)+"\\"+FileName; // move file if(!MoveFileExW(from,to,MOVEFILE_REPLACE_EXISTING)) { Print("-Err move to "+to); return(0); } Print("+Ok move "+FileName+" to "+GetFolder(FolderType)); return(0); } //------------------------------------------------------------------ GetFolder string GetFolder(_FolderType foldertype) { if(foldertype==Experts) return("Experts"); if(foldertype==Indicators) return("Indicators"); if(foldertype==Scripts) return("Scripts"); if(foldertype==Include) return("Include"); if(foldertype==Libraries) return("Libraries"); if(foldertype==Files) return("Files"); if(foldertype==Templates) return("Profiles\\Templates"); if(foldertype==TesterSet) return("Tester"); return(""); } //------------------------------------------------------------------ ParseURL void ParseURL(string path,string &host,string &request,string &filename) { host=StringSubstr(URL,7); // removed int i=StringFind(host,"/"); request=StringSubstr(host,i); host=StringSubstr(host,0,i); string file=""; for(i=StringLen(URL)-1; i>=0; i--) if(StringSubstr(URL,i,1)=="/") { file=StringSubstr(URL,i+1); break; } if(file!="") filename=file; } //+------------------------------------------------------------------+
Conduciamo gli esperimenti sulla nostra sezione preferita https://www.mql5.com/it/code. I file scaricati appariranno immediatamente nel navigatore dell'editor e potranno essere compilati senza riavviare il terminale o l'editor. E non vagheranno attraverso i lunghi percorsi del file system nella ricerca della cartella desiderata in cui spostare i file.
Attenzione! Molti siti offrono una protezione contro il download di contenuti di massa e il tuo indirizzo IP, in caso di download di massa, potrebbe essere bloccato da questa risorsa. Quindi presta molta attenzione all'uso del download "machine" di file dalle risorse a cui accedi spesso e per cui non vuoi essere bandito dall'uso.
Chi volesse spingersi oltre, migliorando il servizio proposto, può utilizzare lo script Clipboard con l'intercettazione dei contenuti degli appunti e l'ulteriore download automatico.
Esempio 2. Monitoraggio delle quotazioni di più broker su un singolo grafico
Così abbiamo imparato a ottenere file da Internet. Consideriamo ora una domanda più interessante: come inviare e memorizzare questi dati sul server. Per questo abbiamo bisogno di un piccolo script PHP aggiuntivo, che si troverà sul server. Utilizzando la classe scritta MqlNet, creiamo un Expert Advisor per il monitoraggio, MetaArbitrage . Il compito dell'expert in combinazione con lo script PHP sarà:
- Invio di una richiesta di Expert Advisor al server;
- formazione della pagina di risposta (PHP) sul server;
- ricezione di questa pagina da parte dell'Expert Advisor;
- la sua analisi e la consegna dei risultati allo schermo.
Il diagramma schematico dell'interazione tra il modulo MQL e lo script PHP è il seguente:
Useremo la classe MqlNet per risolvere questi compiti.
Per evitare la duplicazione dei dati, nonché per eliminare quotazioni obsolete, invieremo 4 parametri principali: il nome del server del broker (la fonte dei prezzi correnti), la valuta, il prezzo e l'ora delle quotazioni in UTC. Ad esempio, una richiesta per accedere allo script dalle risorse della nostra azienda è la seguente:
www.fxmaster.de/metaarbitr.php?server=Metaquotes&pair=EURUSD&bid=1.4512&time=13286794
Questi parametri e la quotazione effettiva sono memorizzati sul server e verranno emessi nella pagina di risposta, insieme a tutte le altre quotazioni memorizzate di questa valuta.
La comodità "collaterale" di questo scambio è che le quotazioni possono essere inviate da MT5, così come da MT4!
La pagina, che è formata dal server, è un normale file CSV. In questo script appare così:
ServerName1; Bid1; Time1
ServerName 2; Bid2; Time2
ServerName 3; Bid3; Time3
…
ServerName N; BidN; TimeN
Ma puoi aggiungere i tuoi parametri aggiuntivi (ad esempio, tipo di server demo o reale). Archiviamo questo file CSV e lo analizziamo riga per riga, con l'output di una tabella di valori e prezzi di riga visualizzati sullo schermo.
L'elaborazione di questo file può essere eseguita in molti modi diversi, scegliendo quella richiesta in ogni caso particolare. Ad esempio, filtra le quotazioni ricevute dal server demo MetaTrader 4, ecc.
I vantaggi dell'utilizzo del server Internet sono evidenti: stai inviando le tue quotazioni, le quali possono essere ricevute e visualizzate da un altro trader. Allo stesso modo, riceverai preventivi che vengono inviati ad altri trader. Ovvero, l'interazione tra i terminali è bilaterale e lo scambio di dati si realizza come mostrato nel seguente schema:
Questo schema serve come base per il principio dello scambio di informazioni tra un numero qualsiasi di terminali. Un Expert Advisor MetaArbitrage completo e lo script PHP con commenti possono essere scaricati dal link negli allegati. Maggiori informazioni sulle funzioni utilizzate da PHP possono essere lette sul seguente sito php.su
Esempio 3. Scambio di messaggi (mini chat) all'interno del terminale. Expert Advisor MetaChat
Facciamo un passo lontano dal trading e dai numeri e creiamo un'applicazione che ci permetterà di chattare con più persone contemporaneamente, senza uscire dal terminale. Per fare ciò avremo bisogno di uno script PHP in più, che in generale è molto simile al precedente. Tranne per il fatto che in questo script, invece di analizzare le citazioni temporali, analizzeremo il numero di righe in un file. Il compito dell'Expert Advisor sarà:
- Invio di una riga di testo al server;
- aggiunta di questa riga nel file condiviso, controllo della dimensione del file, emissione del file di risposta (php);
- ricezione della chat corrente visualizzandola sullo schermo.
Il lavoro di MetaChat non differirà da quello del precedente Expert Advisor. Lo stesso principio e lo stesso semplice file CSV per l'output.
MetaChat e MetaArbitrage sono mantenuti sul sito dei suoi sviluppatori. Lì si trovano anche gli script PHP per il loro lavoro.
Pertanto, se vuoi testare un'opera o utilizzare questo servizio, puoi accedervi tramite il seguente link:
MetaСhat - www.fxmaster.de/metachat.php
MetaArbitrage - www.fxmaster.de/metaarbitr.php
Conclusione
E così abbiamo familiarizzato con le richieste HTTP. Abbiamo acquisito la capacità di inviare e ricevere dati tramite Internet e di organizzare il processo di lavoro in modo più confortevole. Ma qualsiasi capacità può sempre essere migliorata. Le seguenti possono essere considerate le nuove potenziali direzioni dei loro miglioramenti:
- leggere le notizie o ricevere altre informazioni direttamente nel terminale per l'analisi degli Expert Advisor;
- gestione remota degli Expert Advisor;
- Aggiornamenti automatici di Expert Advisor/indicatori;
- fotocopiatrici/traduttori di operazioni, invio di segnali;
- download di modelli insieme a light e file di set per Expert Advisor:
- E molto, molto altro ancora...
In questo articolo abbiamo utilizzato le richieste di tipo GET. Svolgono sufficientemente il compito quando è necessario ottenere un file o inviare una richiesta, con un numero limitato di parametri, per l'analisi di un server.
Nella nostra prossima lezione esamineremo attentamente le richieste POST, ovvero invio di file al server o condivisione di file tra terminali, e prenderemo in considerazione gli esempi del loro utilizzo.
Fonti utili
- Denver Set per l'installazione del server Apache + PHP http://www.denwer.ru/
- Un proxy per la visualizzazione dei titoli inviati http://www.charlesproxy.com/
- Tipi di richieste di titoli http://www.codenet.ru/webmast/php/HTTP-POST.php#part_3_2
- Tipo di contenuto http://www.w3.org/TR/REC-html40/interact/forms.html#form-content-type
- Tipi di richiesta ftp://ftp.isi.edu/in-notes/iana/assignments/media-types/media-types
- Descrizione di WinHTTP http://msdn.microsoft.com/en-us/library/aa385331%28VS.85%29. aspx
- Descrizione della sessione HTTP http://msdn.microsoft.com/en-us/library/aa384322%28VS.85% 29.aspx
- Struttura di utilizzo di HINTERNET http://msdn.microsoft.com/en-us/library/aa383766%28VS.85%29 .aspx
- Utilizzo dei file http://msdn.microsoft.com/en-us/library/aa364232%28VS.85%29. aspx
- Tipi di dati per i trasferimenti a MQL http://msdn.microsoft.com/en-us/library/aa383751%28VS. 85%29.aspx
Tradotto dal russo da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/ru/articles/73
- 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