
Comment préparer des cotations MetaTrader 5 pour d'autres applications
Contenu
Introduction1. Sujets couverts
2. Format des données
3. Paramètres externes du programme
4. Vérification des paramètres entrés par un utilisateur
5. Variables globales
6. Panneau d'information
7. Bloc principal de l'application
8. Création de dossiers et classement des données
Conclusion
Introduction
Avant de commencer à étudier MQL5, j'ai essayé de nombreuses autres applications pour le développement de systèmes de trading. Je ne peux pas dire que j'ai perdu mon temps. Certains d'entre eux contiennent quelques outils utiles permettant aux utilisateurs de gagner du temps, de traiter de nombreux problèmes, de détruire certains mythes et de sélectionner rapidement une autre direction de développement sans connaître les langages de programmation.
Ces applications ont besoin de données historiques. En raison de l'absence d'un certain format de données standard, elles ont souvent dû être modifiées avant d'être utilisées (par exemple, dans Excel) pour se conformer au format applicable au programme nécessaire. Même si vous êtes en mesure de régler tous les détails nécessaires, de nombreuses choses doivent encore être effectuées manuellement. Les utilisateurs peuvent trouver différentes versions de scripts conçus pour copier les cotations de MetaTrader 4 au format nécessaire. S'il y a une telle demande, nous pouvons également développer la version du script pour MQL5.
1. Sujets couverts
L'article traite des sujets suivants :
- Travailler avec la liste de symboles dans la fenêtre Market Watch et la liste de symboles commune sur le serveur.
- Vérification de la profondeur des données disponibles et téléchargement de la quantité manquante si nécessaire avec une gestion correcte des différentes situations.
- Affichage d'informations sur les données demandées sur le graphique de panneau personnalisé et le journal.
- Préparation des données pour le classement dans un format défini par l'utilisateur.
- Création de répertoires de fichiers.
- Archivage des données.
2. Format des données
Je vais donner un exemple de préparation des données destinées à être utilisées dans NeuroShell DayTrader Professional (NSDT). J'ai essayé les versions 5 et 6 du NSDT et j'ai découvert qu'elles avaient des exigences différentes en matière de format de données. Les données de date et d'heure de la version 5 de NSDT doivent être dans des colonnes différentes. La première ligne du fichier doit avoir l'aspect suivant :
« Date » « Heure » « Ouvrir » « Élevé » « Bas » « Fermer » « Volume »
La ligne d'en-tête dans NSDT version 6 doit avoir un aspect différent pour permettre à l'application d'accepter un fichier. Cela signifie que la date et l'heure doivent figurer dans la même colonne :
Date, Ouvrir, Haut, Bas, Fermer, Volume
MetaTrader 5 permet aux utilisateurs d'enregistrer les cotations dans des fichiers *.csv. Les données d'un fichier se présentent comme suit :
Fig. 1. Données enregistrées par le terminal MetaTrader 5
Cependant, nous ne pouvons pas simplement modifier la ligne d'en-tête car la date doit avoir un autre format. Pour NSDT v.5 :
jj.mm.aaaa,hh:mm,Ouvert,Haut,Bas,Ferme,VolumePour NSDT v.6 :
jj/mm/aaaa hh:mm,Ouvert,Haut,Bas,Ferme,Volume
Des listes déroulantes seront utilisées dans les paramètres externes du script où les utilisateurs pourront sélectionner le format nécessaire. Outre la sélection des formats d'en-tête et de date, nous donnerons aux utilisateurs la possibilité de sélectionner le nombre de symboles, données sur lesquelles ils veulent écrire dans les fichiers. Pour ce faire, nous allons préparer trois versions :
- Ecrire les données sur le symbole courant uniquement, sur le graphique duquel le script (ONLY CURRENT SYMBOL) a été lancé.
- Écrire les données sur les symboles situés dans la fenêtre Market Watch (MARKETWATCH SYMBOLS).
- Ecrire les données sur tous les symboles disponibles sur le serveur (ALL LIST SYMBOLS).
Entrons le code suivant avant les paramètres externes dans le code du script pour créer de telles listes :
//_________________________________ // HEADER_FORMATS_ENUMERATION enum FORMAT_HEADERS { NSDT_5 = 0, // "Date" "Time" "Open" "High" "Low" "Close" "Volume" NSDT_6 = 1 // Date,Open,High,Low,Close,Volume }; //--- //___________________________ // ENUMERATION_OF_DATA_FORMATS enum FORMAT_DATETIME { SEP_POINT1 = 0, // dd.mm.yyyy hh:mm SEP_POINT2 = 1, // dd.mm.yyyy, hh:mm SEP_SLASH1 = 2, // dd/mm/yyyy hh:mm SEP_SLASH2 = 3 // dd/mm/yyyy, hh:mm }; //--- //____________________________ // ENUMERATION_OF_FILING_MODES enum CURRENT_MARKETWATCH { CURRENT = 0, // ONLY CURRENT SYMBOLS MARKETWATCH = 1, // MARKETWATCH SYMBOLS ALL_LIST_SYMBOLS = 2 // ALL LIST SYMBOLS };
Plus d'informations sur les énumérations peuvent être trouvées dans la référence MQL5.
3. Paramètres externes du programme
Maintenant, nous pouvons créer la liste complète de tous les paramètres externes du script :
//____________________________________________________________________ //+------------------------------------------------------------------+ //| EXTERNAL_PARAMETERS | //+------------------------------------------------------------------+ input datetime start_date = D'01.01.2011'; // Start Date input datetime end_date = D'18.09.2012'; // End Date input FORMAT_HEADERS format_headers = NSDT_5; // Format Headers input FORMAT_DATETIME format_date = SEP_POINT2; // Format Datetime input CURRENT_MARKETWATCH curr_mwatch = CURRENT; // Mode Write Symbols input bool clear_mwatch = true; // Clear Market Watch input bool show_progress = true; // Show Progress (%)
Les paramètres externes sont utilisés aux fins suivantes :
- Les utilisateurs peuvent spécifier un intervalle de dates à l'aide des paramètres Date de début (start_date) et Date de fin (end_date).
- La liste déroulante Format Headers (format_headers) permet aux utilisateurs de choisir un format d'en-tête.
- La liste déroulante Format Datetime (format_date) permet aux utilisateurs de choisir les formats de date et d'heure.
- La liste déroulante Mode Write Symbols (curr_mwatch) permet aux utilisateurs de sélectionner le nombre de symboles à classer.
- Si le paramètre Clear Market Watch (clear_mwatch) est vrai, cela permet aux utilisateurs de supprimer tous les symboles de la fenêtre Market Watch après le dépôt. Cela ne concerne que les symboles avec les graphiques qui ne sont pas actifs pour le moment.
- Le paramètre Show Progress (%) (show_progress) affiche la progression du classement dans l'écran de contrôle. L'archivage sera effectué plus rapidement si ce paramètre est désactivé.
Voici comment se présenteront les paramètres externes lors du lancement :
Fig. 2. Paramètres externes de l'application
4. Vérification des paramètres entrés par un utilisateur
Créons la fonction de vérification des paramètres saisis par les utilisateurs avant le code de base. Par exemple, la date de début dans le paramètre Date de début doit être antérieure à celle de Date de fin. Le format des en-têtes doit correspondre aux formats de date et d'heure. Si un utilisateur a commis des erreurs lors du réglage des paramètres, le message d'avertissement suivant s'affiche et le programme est arrêté.
Exemple de message d'avertissement :
Fig. 3. Exemple de message d'erreur sur des paramètres incorrectement spécifiés
Fonction ValidationParameters() :
//____________________________________________________________________ //+------------------------------------------------------------------+ //| CHECKING_CORRECTNESS_OF_PARAMETERS | //+------------------------------------------------------------------+ bool ValidationParameters() { if(start_date>=end_date) { MessageBox("The start date should be earlier than the ending one!\n\n" "Application cannot continue. Please retry.", //--- "Parameter error!",MB_ICONERROR); //--- return(true); } //--- if(format_headers==NSDT_5 && (format_date==SEP_POINT1 || format_date==SEP_SLASH1)) { MessageBox("For the headers of the following format:\n\n" "\"Date\" ""\"Time\" ""\"Open\" ""\"High\" ""\"Low\" ""\"Close\" ""\"Volume\"\n\n" "Date/time format can be selected out of two versions:\n\n" "dd.mm.yyyy, hh:mm\n" "dd/mm/yyyy, hh:mm\n\n" "Application cannot continue. Please retry.", //--- "Header and date/time formats do not match!",MB_ICONERROR); //--- return(true); } //--- if(format_headers==NSDT_6 && (format_date==SEP_POINT2 || format_date==SEP_SLASH2)) { MessageBox("For the headers of the following format:\n\n" "Date,Open,High,Low,Close,Volume\n\n" "Date/time format can be selected out of two versions:\n\n" "dd.mm.yyyy hh:mm\n" "dd/mm/yyyy hh:mm\n\n" "Application cannot continue. Please retry.", //--- "Header and date/time formats do not match!",MB_ICONERROR); //--- return(true); } //--- return(false); }
5. Variables Globales
Ensuite, nous devons déterminer toutes les variables globales et les tableaux qui seront utilisés dans le script :
//____________________________________________________________________ //+------------------------------------------------------------------+ //| GLOBAL_VARIABLES_AND_ARRAYS | //+------------------------------------------------------------------+ MqlRates rates[]; // Array for copying data //--- string symbols[]; // Symbol array //--- // Array of graphic object names string arr_nmobj[22]= { "fon","hd01", "nm01","nm02","nm03","nm04","nm05","nm06","nm07","nm08","nm09","nm10", "nm11","nm12","nm13","nm14","nm15","nm16","nm17","nm18","nm19","nm20" }; //--- // Array of displayed text containing graphic objects string arr_txtobj[21]; //--- string path=""; // File path int cnt_symb=0; // Number of symbols int sz_arr_symb=0; // Symbol array size int bars=0; // Number of bars according to the specified TF int copied_bars=0; // Number of bars copied for writing double pgs_pcnt=0; // Writing progress int hFl=INVALID_HANDLE; // File handle //--- string // Variables for data formatting sdt="", // Date line dd="", // Day mm="", // Month yyyy="", // Year tm="", // Time sep=""; // Separator //--- int max_bars=0; // Maximum number of bars in the terminal settings //--- datetime first_date=0, // First available data in a specified period first_termnl_date=0, // First available data in the terminal's database first_server_date=0, // First available data in the server's database check_start_date=0; // Checked correct date value
6. Panneau d'information
Maintenant, nous devons traiter les éléments qui doivent être affichés dans le panneau d'information. Trois types d'objets graphiques peuvent être utilisés comme arrière-plan :
- Le plus simple et le plus évident - « Etiquette rectangle » (OBJ_RECTANGLE_LABEL).
- Ceux qui veulent que leur interface ait un aspect unique peuvent utiliser l'objet « Bitmap » (OBJ_BITMAP).
- L'objet « Editer » (OBJ_EDIT) peut également être utilisé comme arrière-plan. Définissez la propriété « lecture seule » pour supprimer la possibilité de saisir un texte. L'utilisation de l'objet « Modifier » présente également un autre avantage. Si vous avez créé un panneau d'information dans un Expert Advisor et que vous souhaitez qu'il ait le même aspect lors des tests en mode visualisation, le dernier type est la seule méthode pour y parvenir jusqu'à présent. Ni OBJ_RECTANGLE_LABEL, ni OBJ_BITMAP ne sont affichés lors des tests en mode visualisation.
Bien que nous ne développions qu'un script au lieu d'un Expert Advisor dans notre cas, l'arrière-plan avec l'objet OBJ_EDIT servira d'exemple. Le résultat est montré sur la figure ci-dessous :
Fig. 4. Panneau d'information
Décrivons toutes les données affichées sur le panneau :
- Symbole (actuel/total) – symbole dont les données sont actuellement téléchargées/copiées/écrites. Le nombre de gauche entre parenthèses indique le numéro de symbole actuel. Le chiffre de droite indique le nombre commun de symboles avec lesquels le script va travailler.
- Symbole du chemin - symbole du chemin ou de la catégorie à laquelle il appartient. Si vous affichez le menu contextuel en cliquant avec le bouton droit dans la fenêtre Market Watch et sélectionnez « Symboles… », la fenêtre avec la liste de tous les symboles apparaîtra. Vous trouverez plus d'informations à ce sujet dans le guide de l'utilisateur du terminal.
- Délai – période (délai). Le délai auquel le script sera lancé doit être utilisé.
- Date de début d'entrée – date de début des données spécifiée par un utilisateur dans les paramètres de script.
- Première date (H1) – la première date de données disponible (barre) de la période actuelle.
- Première date de terminal (M1) - la première date disponible de la période M1 dans les données de terminal déjà existantes.
- Date du premier serveur (M1) - la première date disponible de la période M1 sur le serveur.
- Max. Barres dans le terminal d'options – nombre maximal de barres à afficher sur le graphique spécifié dans les paramètres du terminal.
- Barres copiées – nombre de barres copiées pour l'écriture.
- Valeur de progression Symbole actuel – valeur en pourcentage des données écrites du symbole actuel.
Vous trouverez ci-dessous le code de ce panneau d'information :
//____________________________________________________________________ //+------------------------------------------------------------------+ //| INFORMATION_PANEL | //|------------------------------------------------------------------+ void InfoTable(int s) { int fnt_sz=8; // Font size string fnt="Calibri"; // Header font color clr=clrWhiteSmoke; // Color //--- int xH=300; int height_pnl=0; int yV1=1,yV2=12,xV1=165,xV2=335,xV3=1; //--- string sf="",stf="",ssf=""; bool flg_sf=false,flg_stf=false,flg_ssf=false; //--- if(show_progress) { height_pnl=138; } else { height_pnl=126; } //--- flg_sf=SeriesInfoInteger(symbols[s],_Period,SERIES_FIRSTDATE,first_date); flg_stf=SeriesInfoInteger(symbols[s],PERIOD_M1,SERIES_TERMINAL_FIRSTDATE,first_termnl_date); flg_ssf=SeriesInfoInteger(symbols[s],PERIOD_M1,SERIES_SERVER_FIRSTDATE,first_server_date); //--- if(flg_sf) { sf=TSdm(first_date); } else { sf="?"; } if(flg_stf) { stf=TSdm(first_termnl_date); } else { stf="?"; } if(flg_ssf) { ssf=TSdm(first_server_date); } else { ssf="?"; } //--- if(cnt_symb==0) { cnt_symb=1; } //--- int anchor1=ANCHOR_LEFT_UPPER,anchor2=ANCHOR_RIGHT_UPPER,corner=CORNER_LEFT_UPPER; //--- string path_symbol=SymbolInfoString(symbols[s],SYMBOL_PATH); path_symbol=StringSubstr(path_symbol,0,StringLen(path_symbol)-StringLen(symbols[s])); //--- arr_txtobj[0]="INFO TABLE"; arr_txtobj[1]="Symbol (current / total) : "; arr_txtobj[2]=""+symbols[s]+" ("+IS(s+1)+"/"+IS(cnt_symb)+")"; arr_txtobj[3]="Path Symbol : "; arr_txtobj[4]=path_symbol; arr_txtobj[5]="Timeframe : "; arr_txtobj[6]=gStrTF(_Period); arr_txtobj[7]="Input Start Date : "; arr_txtobj[8]=TSdm(start_date); arr_txtobj[9]="First Date (H1) : "; arr_txtobj[10]=sf; arr_txtobj[11]="First Terminal Date (M1) : "; arr_txtobj[12]=stf; arr_txtobj[13]="First Server Date (M1) : "; arr_txtobj[14]=ssf; arr_txtobj[15]="Max. Bars In Options Terminal : "; arr_txtobj[16]=IS(max_bars); arr_txtobj[17]="Copied Bars : "; arr_txtobj[18]=IS(copied_bars); arr_txtobj[19]="Progress Value Current Symbol : "; arr_txtobj[20]=DS(pgs_pcnt,2)+"%"; //--- Create_Edit(0,0,arr_nmobj[0],"",corner,fnt,fnt_sz,clrDimGray,clrDimGray,345,height_pnl,xV3,yV1,2,C'15,15,15'); //--- Create_Edit(0,0,arr_nmobj[1],arr_txtobj[0],corner,fnt,8,clrWhite,C'64,0,0',345,12,xV3,yV1,2,clrFireBrick); //--- Create_Label(0,arr_nmobj[2],arr_txtobj[1],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2,0); Create_Label(0,arr_nmobj[3],arr_txtobj[2],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2,0); //--- Create_Label(0,arr_nmobj[4],arr_txtobj[3],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*2,0); Create_Label(0,arr_nmobj[5],arr_txtobj[4],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*2,0); //--- Create_Label(0,arr_nmobj[6],arr_txtobj[5],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*3,0); Create_Label(0,arr_nmobj[7],arr_txtobj[6],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*3,0); //--- Create_Label(0,arr_nmobj[8],arr_txtobj[7],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*4,0); Create_Label(0,arr_nmobj[9],arr_txtobj[8],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*4,0); //--- Create_Label(0,arr_nmobj[10],arr_txtobj[9],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*5,0); Create_Label(0,arr_nmobj[11],arr_txtobj[10],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*5,0); //--- Create_Label(0,arr_nmobj[12],arr_txtobj[11],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*6,0); Create_Label(0,arr_nmobj[13],arr_txtobj[12],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*6,0); //--- Create_Label(0,arr_nmobj[14],arr_txtobj[13],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*7,0); Create_Label(0,arr_nmobj[15],arr_txtobj[14],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*7,0); //--- Create_Label(0,arr_nmobj[16],arr_txtobj[15],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*8,0); Create_Label(0,arr_nmobj[17],arr_txtobj[16],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*8,0); //--- Create_Label(0,arr_nmobj[18],arr_txtobj[17],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*9,0); Create_Label(0,arr_nmobj[19],arr_txtobj[18],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*9,0); //--- if(show_progress) { Create_Label(0,arr_nmobj[20],arr_txtobj[19],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*10,0); Create_Label(0,arr_nmobj[21],arr_txtobj[20],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*10,0); } } //____________________________________________________________________ //+------------------------------------------------------------------+ //| CREATING_LABEL_OBJECT | //+------------------------------------------------------------------+ void Create_Label(long chrt_id, // chart id string lable_nm, // object name string rename, // displayed name long anchor, // anchor point long corner, // attachment corner string font_bsc, // font int font_size, // font size color font_clr, // font color int x_dist, // X scale coordinate int y_dist, // Y scale coordinate long zorder) // priority { if(ObjectCreate(chrt_id,lable_nm,OBJ_LABEL,0,0,0)) // creating object { ObjectSetString(chrt_id,lable_nm,OBJPROP_TEXT,rename); // set name ObjectSetString(chrt_id,lable_nm,OBJPROP_FONT,font_bsc); // set font ObjectSetInteger(chrt_id,lable_nm,OBJPROP_COLOR,font_clr); // set font color ObjectSetInteger(chrt_id,lable_nm,OBJPROP_ANCHOR,anchor); // set anchor point ObjectSetInteger(chrt_id,lable_nm,OBJPROP_CORNER,corner); // set attachment corner ObjectSetInteger(chrt_id,lable_nm,OBJPROP_FONTSIZE,font_size); // set font size ObjectSetInteger(chrt_id,lable_nm,OBJPROP_XDISTANCE,x_dist); // set X coordinates ObjectSetInteger(chrt_id,lable_nm,OBJPROP_YDISTANCE,y_dist); // set Y coordinates ObjectSetInteger(chrt_id,lable_nm,OBJPROP_SELECTABLE,false); // unable to highlight the object, if FALSE ObjectSetInteger(chrt_id,lable_nm,OBJPROP_ZORDER,zorder); // Higher/lower priority ObjectSetString(chrt_id,lable_nm,OBJPROP_TOOLTIP,"\n"); // no tooltip, if "\n" } } //____________________________________________________________________ //+------------------------------------------------------------------+ //| CREATING_EDIT_OBJECT | //+------------------------------------------------------------------+ void Create_Edit(long chrt_id, // chart id int nmb_win, // window (subwindow) index string lable_nm, // object name string text, // displayed text long corner, // attachment corner string font_bsc, // font int font_size, // font size color font_clr, // font color color font_clr_brd, // font color int xsize, // width int ysize, // height int x_dist, // X scale coordinate int y_dist, // Y scale coordinate long zorder, // priority color clr) // background color { if(ObjectCreate(chrt_id,lable_nm,OBJ_EDIT,nmb_win,0,0)) // creating object { ObjectSetString(chrt_id,lable_nm,OBJPROP_TEXT,text); // set name ObjectSetInteger(chrt_id,lable_nm,OBJPROP_CORNER,corner); // set attachment corner ObjectSetString(chrt_id,lable_nm,OBJPROP_FONT,font_bsc); // set font ObjectSetInteger(chrt_id,lable_nm,OBJPROP_ALIGN,ALIGN_CENTER); // center alignment ObjectSetInteger(chrt_id,lable_nm,OBJPROP_FONTSIZE,font_size); // set font size ObjectSetInteger(chrt_id,lable_nm,OBJPROP_COLOR,font_clr); // font color ObjectSetInteger(chrt_id,lable_nm,OBJPROP_BORDER_COLOR,font_clr_brd); // background color ObjectSetInteger(chrt_id,lable_nm,OBJPROP_BGCOLOR,clr); // background color ObjectSetInteger(chrt_id,lable_nm,OBJPROP_XSIZE,xsize); // width ObjectSetInteger(chrt_id,lable_nm,OBJPROP_YSIZE,ysize); // height ObjectSetInteger(chrt_id,lable_nm,OBJPROP_XDISTANCE,x_dist); // set X coordinate ObjectSetInteger(chrt_id,lable_nm,OBJPROP_YDISTANCE,y_dist); // set Y coordinate ObjectSetInteger(chrt_id,lable_nm,OBJPROP_SELECTABLE,false); // unable to highlight the object, if FALSE ObjectSetInteger(chrt_id,lable_nm,OBJPROP_ZORDER,zorder); // Higher/lower priority ObjectSetInteger(chrt_id,lable_nm,OBJPROP_READONLY,true); // Read only ObjectSetString(chrt_id,lable_nm,OBJPROP_TOOLTIP,"\n"); // no tooltip if "\n" } }
Une fois que l'opération du script est terminée ou que le script est supprimé par un utilisateur à l'avance, tous les objets graphiques créés par le script doivent être supprimés. Les fonctions suivantes seront utilisées pour cela :
//____________________________________________________________________ //+------------------------------------------------------------------+ //| DELETE_ALL_GRAPHICAL_OBJECTS_CREATED_BY_THE_SCRIPT | //+------------------------------------------------------------------+ void DelAllScriptObjects() { // Receive the size of graphical object names array int sz_arr1=ArraySize(arr_nmobj); //--- // Delete all objects for(int i=0; i<sz_arr1; i++) { DelObjbyName(arr_nmobj[i]); } } //____________________________________________________________________ //+------------------------------------------------------------------+ //| DELETE_OBJECTS_BY_NAME | //+------------------------------------------------------------------+ int DelObjbyName(string Name) { int nm_obj=0; bool res=false; //--- nm_obj=ObjectFind(ChartID(),Name); //--- if(nm_obj>=0) { res=ObjectDelete(ChartID(),Name); //--- if(!res) { Print("Object deletion error: - "+ErrorDesc(Error())+""); return(false); } } //--- return(res); }
7. Bloc principal de l'application
La fonction principale des scripts est OnStart(). Il s'agit de la fonction utilisée pour appeler toutes les autres fonctions en vue de leur exécution. Les détails du fonctionnement du programme sont indiqués dans le code :
//____________________________________________________________________ //+------------------------------------------------------------------+ //| SCRIPT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> | //+------------------------------------------------------------------+ void OnStart() { // If user-defined parameters are incorrect, // error message is shown and the program is closed if(ValidationParameters()) { return; } //--- max_bars=TerminalInfoInteger(TERMINAL_MAXBARS); // Receive available number of bars in the window //--- GetSymbolsToArray(); // Filling symbol array with names sz_arr_symb=ArraySize(symbols); // Receive symbol array size //--- SetSeparateForFormatDate(); // Set a separator for date format //--- // Revise all symbols and write their data to file for(int s=0; s<=sz_arr_symb-1; s++) { copied_bars=0; // Reset copied bars variable to zero for writing pgs_pcnt=0.0; // Reset variable of the symbol data writing progress //--- InfoTable(s); ChartRedraw(); //--- // Receive current symbol data int res=GetDataCurrentSymbol(s); //--- if(res==0) { BC } // If zero, break the loop or start the next iteration //--- if(res==2) // Program operation interrupted by user { DelAllScriptObjects(); // Deleted objects created by the script from the chart //--- Print("------\nUser deleted the script!"); break; } //--- // Receive the path for creating the file and create directories for them // If the string is empty, break the loop or start the next iteration if((path=CheckCreateGetPath(s))=="") { BC } //--- WriteDataToFile(s); // Write data to file } //--- // Delete symbols from Market Watch window if necessary DelSymbolsFromMarketWatch(); //--- // Delete objects created by the script from the chart Sleep(1000); DelAllScriptObjects(); }
Examinons les fonctions où se déroulent les activités clés.
Le tableau de symboles (symbols[]) est rempli de noms de symboles dans la fonction GetSymbolsToArray(). La taille du tableau, ainsi que le nombre de symboles qu'il contient, dépendent de la variante choisie par l'utilisateur dans le paramètre Mode Write Symbols (curr_mwatch).
Si un utilisateur ne doit disposer que des données d'un seul symbole, la taille du tableau est égale à 1.
ArrayResize(symbols,1); // Set the array size to be equal to 1 symbols[0]=_Symbol; // Specify the current symbol's name
Si un utilisateur souhaite recevoir les données sur tous les symboles de la fenêtre Market Watch ou tous les symboles disponibles, la taille du tableau sera définie par la fonction suivante :
int SymbolsTotal( bool selected // true – only MarketWatch symbols );
Pour éviter la création de deux blocs pour deux variantes avec un code presque identique, nous allons faire fonction pointeur MWatchOrAllList(), qui retournera vrai ou faux. Cette valeur définit l'endroit d'où la liste des symboles doit être extraite - uniquement de la fenêtre Market Watch (true) ou de la liste commune des symboles disponibles (false).
//____________________________________________________________________ //+------------------------------------------------------------------+ //| POINTER_TO_MARKET_WATCH_WINDOW_OR_TO_COMMON_LIST | //+------------------------------------------------------------------+ bool MWatchOrAllList() { if(curr_mwatch==MARKETWATCH) { return(true); } if(curr_mwatch==ALL_LIST_SYMBOLS) { return(false); } //--- return(true); }
Après avoir obtenu le nombre de symboles dans la boucle, nous devons parcourir toute la liste et placer le nom du symbole dans le tableau à chaque itération en augmentant la taille du tableau d'une unité. Le nom du symbole, à son tour, est obtenu par le numéro d'index à l'aide de la fonction SymbolName().
int SymbolName( int pos, // list index number bool selected // true – only MarketWatch symbols );
La fonction de pointeur MWatchOrAllList() est également utilisée dans la fonction SymbolName() pour sélectionner la liste de symboles. Code complet de la fonction GetSymbolsToArray() :
//____________________________________________________________________ //+------------------------------------------------------------------+ //| FILLING_SYMBOL_ARRAY_WITH_NAMES | //+------------------------------------------------------------------+ void GetSymbolsToArray() { // If only the current symbol data is required if(curr_mwatch==CURRENT) { ArrayResize(symbols,1); symbols[0]=_Symbol; } //--- // If data on all symbols from Market Watch window or // or the entire symbol list is required if(curr_mwatch==MARKETWATCH || curr_mwatch==ALL_LIST_SYMBOLS) { // Receive the number of symbols in Market Watch window cnt_symb=SymbolsTotal(MWatchOrAllList()); //--- for(int i=0; i<=cnt_symb-1; i++) { string nm_symb=""; //--- ArrayResize(symbols,i+1); // Increase the array size by one once again //--- // Receive a name of a symbol from Market Watch window nm_symb=SymbolName(i,MWatchOrAllList()); symbols[i]=nm_symb; // Put the symbol name into the array } } }
La fonction SetSeparateForFormatDate() est très simple. Elle est utilisée pour définir le type de séparateur qui sera utilisé dans la date en fonction du choix de l'utilisateur dans la liste déroulante du paramètre Format Date (format_date).
//____________________________________________________________________ //+------------------------------------------------------------------+ //| DEFINING_SEPARATOR_FOR_DATE_FORMAT | //+------------------------------------------------------------------+ void SetSeparateForFormatDate() { switch(format_date) { case SEP_POINT1 : case SEP_POINT2 : sep="."; break; // Full point as a separator case SEP_SLASH1 : case SEP_SLASH2 : sep="/"; break; // Slash as a separator } }
La boucle de base avec divers contrôles vient ensuite. Si toutes les vérifications sont réussies, les données sont écrites dans le fichier. Sinon, la boucle est interrompue, tous les objets sont supprimés du graphique et le script est supprimé (dans le cas d'un symbole) ou l'itération suivante commence (dans le cas de plusieurs symboles). Chaque symbole du tableau symbols[] est systématiquement appelé dans la boucle. Le numéro d'index est envoyé à chaque fonction de la boucle. Ainsi, la séquence précise de toutes les fonctions est préservée.
Les données sur le symbole actuel dans la boucle sont reçues à chaque itération au tout début du corps de la boucle. La fonction GetDataCurrentSymbol() est utilisée pour cela. Voyons ce qui se passe dans cette fonction.
La disponibilité des données est vérifiée à l'aide de la fonction CheckLoadHistory() avant de copier les données de symboles dans le tableau rate[]. Cette fonction est fournie par les développeurs à titre d'exemple. Sa version initiale se trouve dans MQL5 Reference. Je n'ai apporté que de légères corrections pour l'utilisation dans ce script. La Référence contient une description détaillée (il serait bon de l'étudier également), je ne vais donc pas exposer ici ma version car elle est presque identique. En outre, on peut la trouver dans le code avec des commentaires détaillés.
La seule chose que l'on peut mentionner maintenant est que la fonction CheckLoadHistory() renvoie le code d'erreur ou d'exécution réussie, en fonction duquel le message approprié du bloc opérateur switch est enregistré dans le journal. Selon le code reçu, la fonction GetDataCurrentSymbol() poursuit son opération ou renvoie son code.
Si tout va bien, les données de l'historique sont copiées à l'aide de la fonction CopyRates(). La taille du tableau est enregistrée dans la variable globale. Ensuite, la sortie de la fonction accompagnée du retour du code 1 est effectuée. Si quelque chose ne va pas, la fonction arrête son fonctionnement dans l'opérateur de commutation et renvoie le code 0 ou 2.
//____________________________________________________________________ //+------------------------------------------------------------------+ //| RECEIVE_SYMBOL_DATA | //+------------------------------------------------------------------+ int GetDataCurrentSymbol(int s) { Print("------\n№"+IS(s+1)+" >>>"); // Save a symbol number in the journal //--- // Check and download the necessary amount of requested data int res=CheckLoadHistory(s,_Period); //--- InfoTable(s); ChartRedraw(); // Update the data in the data table //--- switch(res) { case -1 : Print("Unknown symbol "+symbols[s]+" (code: -1)!"); return(0); case -2 : Print("Number of requested bars exceeds the maximum number that can be displayed on a chart (code: -2)!...\n" "...The available amount of data will be used for writing."); break; //--- case -3 : Print("Execution interrupted by user (code: -3)!"); return(2); case -4 : Print("Download failed (code: -4)!"); return(0); case 0 : Print("All symbol data downloaded (code: 0)."); break; case 1 : Print("Time series data is sufficient (code: 1)."); break; case 2 : Print("Time series created based on existing terminal data (code: 2)."); break; //--- default : Print("Execution result is not defined!"); } //--- // Copy data to the array if(CopyRates(symbols[s],_Period,check_start_date,end_date,rates)<=0) { Print("Error when copying symbol data "+symbols[s]+" - ",ErrorDesc(Error())+""); return(0); } else { copied_bars=ArraySize(rates); // Receive array size //--- Print("Symbol: ",symbols[s],"; Timeframe: ",gStrTF(_Period),"; Copied bars: ",copied_bars); } //--- return(1); // Return 1, if all is well }
Après cela, le programme se trouve à nouveau dans le corps de la boucle principale de la fonction OnStart(). Le code est affecté par la variable locale res et le contrôle est effectué en fonction de sa valeur. La valeur zéro correspond à une erreur. Cela signifie que les données du symbole actuel dans la boucle ne peuvent pas être écrites. L'explication de l'erreur a été enregistrée dans le journal et la décision est prise si la boucle doit être interrompue (break) ou si l'itération suivante doit être lancée (continue).
if(res==0) { BC } // If zero, the loop is interrupted or the next iteration starts
La ligne de code ci-dessus montre que cette sélection est effectuée par de mystérieux caractères BC. Il s'agit d'une macro-expansion. Vous trouverez de plus amples informations à ce sujet dans MQL5 Reference. La seule chose à mentionner ici est que l'ensemble des expressions (tapées sur une ligne) peut être collé dans une entrée courte comme le montre l'exemple ci-dessus (BC). Dans certains cas, cette méthode peut être encore plus pratique et compacte qu'une fonction. Dans le cas présent, cela se présente comme suit :
// Macro expansion with further action selection #define BC if(curr_mwatch==CURRENT) { break; } if(curr_mwatch==MARKETWATCH || curr_mwatch==ALL_LIST_SYMBOLS) { continue; }
Vous trouverez ci-dessous d'autres exemples d'extensions de macros utilisées dans ce script :
#define nmf __FUNCTION__+": " // Macro expansion of the function name before sending the message to the journal //--- #define TRM_DP TerminalInfoString(TERMINAL_DATA_PATH) // Folder for storing the terminal data
Si GetDataCurrentSymbol() renvoie 2, le programme a été supprimé par un utilisateur. MQL5 a la fonction IsStopped() pour identifier cet événement. Cette fonction peut être très utile dans les boucles pour arrêter le fonctionnement du programme correctement et à temps. Si la fonction renvoie true, il y a environ trois secondes pour effectuer toutes les actions avant que le programme ne soit supprimé de force. Dans notre cas, tous les objets graphiques sont supprimés et le message est envoyé au journal :
if(res==2) // Program execution interrupted by user { DelAllScriptObjects(); // Delete all objects created by the script from the chart //--- Print("------\nUser deleted the script!"); break; }
8. Création de dossiers et classement des données
La fonction CheckCreateGetPath() vérifie la présence du dossier de données racine. Appelons-le DATA_OHLC et plaçons-le dans C:\Metatrader 5\MQL5\Files. Il contiendra des dossiers avec les noms des symboles. Les fichiers pour l'écriture des données seront créés à cet endroit.
Si le dossier racine ou le dossier du symbole actuel dans la boucle n'existe pas, la fonction le crée. Si tout se passe bien, la fonction renvoie une chaîne contenant le chemin de création d'un fichier. La fonction renvoie une chaîne vide en cas d'erreur ou de tentative de suppression du programme du graphique effectuée par un utilisateur.
Le code ci-dessous contient des commentaires détaillés facilitant la compréhension :
//____________________________________________________________________ //+------------------------------------------------------------------+ //| CHECK_DIRECTORY_AND_CREATE_NECESSARY_DATA_FOLDERS | //+------------------------------------------------------------------+ string CheckCreateGetPath(int s) { int i=1; long search=-1; string ffname="",lpath=""; string file="*.csv",folder="*"; string root="DATA_OHLC\\", // Root data folder fSmb=symbols[s]+"\\", // Symbol name fTF=gStrTF(_Period)+"\\"; // Symbol time frame //--- bool flgROOT=false,flgSYMBOL=false; //--- //+------------------------------------------------------------------+ //| SEARCHING_FOR_DATA_OHLC_ROOT_FOLDER | //+------------------------------------------------------------------+ lpath=folder; search=FileFindFirst(lpath,ffname); // Set search handle in Metatrader 5\MQL5\Files //--- Print("Directory: ",TRM_DP+"\\MQL5\\Files\\"); //--- // Set the flag if the first folder is a root one if(ffname==root) { flgROOT=true; Print("Root folder "+root+" present"); } //--- if(search!=INVALID_HANDLE) // If search handle received { if(!flgROOT) // If the first folder is not a root one { // Sort out all files searching for the root folder while(FileFindNext(search,ffname)) { if(IsStopped()) // Execution interrupted by user { // Delete objects created by the script from the chart DelAllScriptObjects(); //--- Print("------\nUser deleted the script!"); return(""); } //--- if(ffname==root) // Set the flag if found { flgROOT=true; Print("Root folder "+root+" present"); break; } } } //--- FileFindClose(search); search=-1; // Close root folder search handle } else { Print("Error when receiving the search handle or directory "+TRM_DP+" is empty: ",ErrorDesc(Error())); } //--- //+------------------------------------------------------------------+ //| SEARCHING_SYMBOL_FOLDER | //+------------------------------------------------------------------+ lpath=root+folder; //--- // Set search handle in the root folder ..\Files\DATA OHLC\ search=FileFindFirst(lpath,ffname); //--- // Set the flag if the first folder of the current symbol if(ffname==fSmb) { flgSYMBOL=true; Print("Symbol folder "+fSmb+" present"); } //--- if(search!=INVALID_HANDLE) // If search handle is received { if(!flgSYMBOL) // If the first folder is not of the current symbol { // Sort out all the files in the root folder searching the symbol folder while(FileFindNext(search,ffname)) { if(IsStopped()) // Execution interrupted by user { // Delete objects created by the script from the chart DelAllScriptObjects(); //--- Print("------\nUser deleted the script!"); return(""); } //--- if(ffname==fSmb) // Set the flag if found { flgSYMBOL=true; Print("Symbol folder"+fSmb+" present"); break; } } } //--- FileFindClose(search); search=-1; // Close symbol folder search handle } else { Print("Error when receiving search handle or the directory "+path+" is empty"); } //--- //+------------------------------------------------------------------+ //| CREATE_NECESSARY_DIRECTORIES_ACCORDING_TO_CHECK_RESULTS | //+------------------------------------------------------------------+ if(!flgROOT) // If there is no DATA_OHLC... root folder { if(FolderCreate("DATA_OHLC")) // ...we should create it { Print("..\DATA_OHLC\\ root folder created"); } else { Print("Error when creating DATA_OHLC: root folder",ErrorDesc(Error())); return(""); } } //--- if(!flgSYMBOL) // If there is no folder of the symbol, the values of which should be received... { if(FolderCreate(root+symbols[s])) // ...we should create it { Print("..\DATA_OHLC\\" symbol folder created+fSmb+""); //--- return(root+symbols[s]+"\\"); // Return the path for creating the file for writing } else { Print("Error when creating ..\DATA_OHLC\\ symbol folder"+fSmb+"\: ",ErrorDesc(Error())); return(""); } } //--- if(flgROOT && flgSYMBOL) { return(root+symbols[s]+"\\"); // Return the path for creating the file for writing } //--- return(""); }
Si la fonction CheckCreateGetPath() renvoie une ligne vide, la boucle est interrompue ou l'itération suivante commence en utilisant l'expansion de macro déjà connue (BC) :
// Receive the path for creating a file and create directories for them // If the line is empty, the loop is interrupted or the next iteration starts if((path=CheckCreateGetPath(s))=="") { BC }
Si vous avez atteint cette étape, cela signifie que les données ont été copiées avec succès et que la variable de chaîne de chemin contient le chemin de création du fichier d'écriture des données du symbole courant dans la boucle.
Créez la fonction WriteDataToFile() pour écrire des données dans le fichier. [Chemin]+[nom du fichier] est généré au début de la fonction. Un nom de fichier se compose d'un nom de symbole et de la période actuelle. Par exemple, EURUSD_H1.csv. Si le fichier portant ce nom est déjà présent, il est simplement ouvert à l'écriture. Les données précédemment écrites seront supprimées. De nouvelles données y seront écrites à la place. Si le fichier est créé/ouvert avec succès, la fonction FileOpen() renvoie le handle qui sera utilisé pour accéder au fichier.
Vérification du handle. S'il est présent, la ligne d'en-tête est écrite. La ligne appropriée sera écrite en fonction des en-têtes sélectionnés par un utilisateur. La boucle principale d'écriture des données historiques commence ensuite.
Avant d'écrire la ligne suivante, elle doit être convertie au format spécifié par l'utilisateur. Pour ce faire, nous devons recevoir l'heure d'ouverture de la barre et trier le jour, le mois, l'année et l'heure séparément par variables à l'aide de la fonction StringSubstr(). Ensuite, nous devons définir si la date et l'heure seront situées dans une seule ou plusieurs colonnes, selon le format spécifié par l'utilisateur. Ensuite, toutes les parties sont réunies en une seule ligne à l'aide de la fonction StringConcatenate(). Une fois toutes les lignes écrites, le fichier est fermé par la fonction FileClose().
L'intégralité du code de la fonction WriteDataToFile() est illustré ci-dessous :
//____________________________________________________________________ //+------------------------------------------------------------------+ //| WRITE_DATA_TO_FILE | //+------------------------------------------------------------------+ void WriteDataToFile(int s) { // Number of decimal places in the symbol price int dgt=(int)SymbolInfoInteger(symbols[s],SYMBOL_DIGITS); //--- string nm_fl=path+symbols[s]+"_"+gStrTF(_Period)+".csv"; // File name //--- // Receive file handle for writing hFl=FileOpen(nm_fl,FILE_WRITE|FILE_CSV|FILE_ANSI,','); //--- if(hFl>0) // If the handle is received { // Write the headers if(format_headers==NSDT_5) { FileWrite(hFl,"\"Date\" ""\"Time\" ""\"Open\" ""\"High\" ""\"Low\" ""\"Close\" ""\"Volume\""); } //--- if(format_headers==NSDT_6) { FileWrite(hFl,"Date","Open","High","Low","Close","Volume"); } //--- // Write the data for(int i=0; i<=copied_bars-1; i++) { if(IsStopped()) // If program execution interrupted by a user { DelAllScriptObjects(); // Delete objects created by the script from the chart //--- Print("------\nUser deleted the script!"); break; } //--- sdt=TSdm(rates[i].time); // Bar open time //--- // Divide the date by year, month and time yyyy=StringSubstr(sdt,0,4); mm=StringSubstr(sdt,5,2); dd=StringSubstr(sdt,8,2); tm=StringSubstr(sdt,11); //--- string sep_dt_tm=""; // Separator of Date and Time columns //--- // Join the data with the separator in the necessary order if(format_date==SEP_POINT1 || format_date==SEP_SLASH1) { sep_dt_tm=" "; } if(format_date==SEP_POINT2 || format_date==SEP_SLASH2) { sep_dt_tm=","; } //--- // Join everything in one line StringConcatenate(sdt,dd,sep,mm,sep,yyyy,sep_dt_tm,tm); //--- FileWrite(hFl, sdt,// Date-time DS_dgt(rates[i].open,dgt), // Open price DS_dgt(rates[i].high,dgt), // High price DS_dgt(rates[i].low,dgt), // Low price DS_dgt(rates[i].close,dgt), // Close price IS((int)rates[i].tick_volume)); // Tick volume price //--- // Update writing progress value for the current symbol pgs_pcnt=((double)(i+1)/copied_bars)*100; //--- // Update data in the table InfoTable(s); if(show_progress) { ChartRedraw(); } } //--- FileClose(hFl); // Close the file } else { Print("Error when creating/opening file!"); } }
C'était la dernière fonction de la boucle de base de la fonction OnStart(). Si ce n'était pas le dernier symbole, tout est répété pour le suivant. Sinon, la boucle est rompue. Si l'utilisateur a spécifié d'effacer la liste des symboles dans la fenêtre Market Watch dans les paramètres de script, les symboles avec des graphiques actuellement non actifs seront supprimés par la fonction DelSymbolsFromMarketWatch(). Après cela, tous les objets graphiques créés par le script sont supprimés et le programme s'arrête. Les données sont prêtes à être utilisées.
Les détails sur la façon de télécharger les données vers NeuroShell DayTrader Professional peuvent être trouvés dans mon blog . Vous trouverez ci-dessous la vidéo montrant le fonctionnement du script :
Conclusion
Quel que soit le programme que j'ai utilisé pour développer des stratégies de trading, j'ai toujours rencontré des limites qui m'empêchaient de poursuivre le développement de mes idées. Finalement, j'ai réalisé que la programmation est essentielle ici. MQL5 est la meilleure solution pour ceux qui veulent vraiment réussir. Cependant, d'autres programmes d'analyse de données et de développement de stratégies de trading peuvent également être utiles lors de la recherche de nouvelles idées. Il m'aurait fallu beaucoup plus de temps pour les trouver, si j'avais utilisé un seul outil.
Bonne chance !
Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/502





- Applications de trading gratuites
- Plus de 8 000 signaux à copier
- Actualités économiques pour explorer les marchés financiers
Vous acceptez la politique du site Web et les conditions d'utilisation