
Création et publication des rapports de trade et de notifications par SMS
Introduction
Cet article décrit comment générer un rapport des résultats du trade (à l'aide d'Expert Advisor, d'un indicateur ou d'un script) sous forme de fichier HTML et le télécharger via FTP sur le serveur WWW. Nous envisagerons également d'envoyer des notifications d'événements de trade sous forme de SMS sur un téléphone mobile.
Pour être plus à l'aise avec le matériel décrit dans cet article, il est recommandé au lecteur de se familiariser avec le langage HTML (HyperText Markup Language).
Pour implémenter les rapports de téléchargement, nous avons besoin d'un serveur WWW (cela peut être n'importe quel ordinateur), qui peut accepter les données via FTP. Pour mettre en œuvre la possibilité de recevoir des notifications sur les événements de trade sous forme de SMS, nous avons besoin d'une passerelle EMAIL-SMS (ce service est fourni par la plupart des opérateurs mobiles et des organisations tierces).
1. Créer un Rapport et l' Envoyer via FTP
Créons un programme MQL5, qui génère un rapport de trade et l'envoie via le protocole FTP. D'abord, nous le rédigeons comme un script. À l'avenir, nous pouvons l'utiliser comme un bloc fini, qui peut être introduit dans les Expert Advisors et les Indicateurs. Par exemple, dans Expert Advisers, vous pouvez utiliser ce bloc comme gestionnaire d'événement du Trade ou Timer, pour exécuter ce bloc après la demande de trade, ou pour définir certaines actions pour l'événement ChartEvent. Dans Indicateurs, vous pouvez inclure ce bloc dans les gestionnaires d'événements Timer ou ChartEvent.
L'exemple de rapport, créé par programme, est illustré dans les figures 1, 2 et 3. Ou vous pouvez télécharger ce rapport via un lien qui se trouve à la fin de l’article.
.
Figure 1. Exemple de rapport - Tableau des Deals et des Positions.
Figure 2. Exemple de rapport - Tableau de solde.
Figure 3. Exemple de rapport - Graphique des prix sur l'instrument actuel.
Dans le tableau des deals et des positions (Figure 1), tous les deals par commodité sont divisées en positions. Le côté gauche du tableau indique le volume, le temps et le prix pour entrer sur le marché (des positions d'ouverture et des additions). La partie droite du tableau indique les mêmes paramètres pour quitter le marché (clôture partielle ou totale de la position). Lors de l'entrée/sortie, le deal est divisé en deux parties - la clôture d'une position et l'ouverture de la suivante.
Sous le tableau des deals et des positions apparaît le graphique du solde (axe horizontal - temps), et en bas - le graphique des prix de l'instrument actuel.
Le programme crée les fichiers "report.html", "picture1.gif" et "picture2.gif" (fichier html du rapport, fichiers image du tableau de solde et du tableau des prix) dans le dossier MetaTarder5_istall_dir\MQL5\Files. Et la publication FTP est activée dans les paramètres du terminal - elle envoie ces trois fichiers au serveur indiqué. De plus, nous aurons besoin de deux autres fichiers - des images avec des flèches désignant la position ouverte - Acheter ou Vendre ("buy.gif" et "sell.gif"). Vous pouvez prendre ces images (lien de téléchargement en fin d'article) ou les dessiner vous-même dans n'importe quel éditeur graphique. Ces deux fichiers doivent être placés dans le même dossier du serveur WWW avec le fichier "report.html".
Comme paramètres d'entrée, le programme accepte l'heure de début et de fin de la période pour laquelle le rapport est généré. Dans notre exemple, la fin de la période du rapport est l'heure actuelle et l'utilisateur sélectionne la variante de la période du rapport : période entière, dernier jour, semaine dernière, mois dernier ou année dernière.
Quelques mots sur la façon dont le rapport est créé. Le serveur du trade est demandé pour tout l'historique disponible des deals. Les deals obtenus sont traités les uns après les autres. Le tableau deal_status[] stocke des informations indiquant si le deal est traité ou pas. Les index d'éléments de ce tableau sont les numéros de deals, reçus de la liste des deals depuis le serveur du trade. Et les valeurs des éléments sont interprétées comme suit : 0 - le deal n'a pas encore été traité, 1 - le deal a déjà été partiellement traité (entrée/sortie), 127 - le deal a déjà été traité (les autres valeurs ne sont pas utilisées et réservées pour une utilisation future).
Le tableau symb_list[] comporte la liste des noms d'instruments financiers, par lesquels le trade a été effectué, et le tableau lots_list[] - volumes des positions ouvertes pour chaque instrument au moment du traitement du deal. Les valeurs positives du volume correspondent aux positions longues, négatives - aux positions courtes. Si le volume est égal à zéro, cela indique que cet outil n'a pas de positions ouvertes. Si, lors du traitement des deals, un instrument financier qui n'est pas dans la liste (dans le tableau symb_list[]) est rencontré - il y est ajouté et le nombre d'instruments financiers (la variable symb_total) est incrémenté par 1.
Sur chaque traitement de deal, chaque deal suivant est analysé par le même instrument financier, jusqu'à la clôture de la position ou jusqu'à l'entrée/sortie. Seuls les deals sont analysés, pour lesquels la valeur du tableau deal_status[] est inférieure à 127. Après le traitement du deal, l'élément correspondant du tableau deal_status[] est affecté avec la valeur 127, et si le deal est l'entrée/sortie de position - avec la valeur 1. Si l'heure, lorsque la position a été ouverte, correspond à la période du rapport (définie par les variables StartTime et EndTime) - cette position est enregistrée dans le rapport (toutes les entrées et sorties).
En plus du tableau des deals, un nouveau graphique pour l'instrument financier actuel est ouvert. Pour ce graphique, toutes les propriétés nécessaires sont fournies et, à l'aide de la fonction ChartScreenShot(), une capture d'écran est réalisée - nous obtenons donc un fichier image avec le graphique des prix pour l'instrument actuel. Ensuite, sur ce graphique, le graphique des prix est masqué et le graphique des variations du solde est dessiné, puis une autre capture d'écran est créée.
Lorsque deux fichiers image avec des graphiques et un fichier HTML avec rapport sont créés, la possibilité d'envoyer des fichiers via FTP est vérifiée. Si cela est autorisé - les fichiers "report.html", "picture1.gif" et "picture2.gif" sont envoyés à l'aide de la fonction SendFTP(), conformément aux paramètres indiqués dans MetaTrader 5.
Lancez l'éditeur de langue MetaQuotes et commencez la création du script. Définissez des constantes : le délai d'expiration de l'actualisation du graphique (en secondes), la largeur et la hauteur du graphique des prix et la largeur maximale du graphique de solde. La période du graphique, qui affichera la courbe d'évolution du solde, est choisie en fonction de la durée de la période du rapport et de la largeur maximale du graphique. La largeur du graphique est ajustée à la taille, nécessaire pour le graphique du solde.
La hauteur du graphique est automatiquement calculée comme la moitié de la largeur. De plus, nous indiquerons la largeur de l'axe vertical comme constante - c'est le nombre de pixels par lequel la zone graphique est réduite par rapport à la largeur de l'image en raison de l'axe vertical.
#define timeout 10 // chart refresh timeout #define Picture1_width 800 // max width of chart in report #define Picture2_width 800 // width of price chart in report #define Picture2_height 600 // height of price chart in report #define Axis_Width 59 // width of vertical axis (in pixels)
Précisez que les paramètres d'entrée seront demandés à l'utilisateur.
// request input parameters #property script_show_inputs
Créez une énumération des périodes de rapport.
// enumeration of report periods enum report_periods { All_periods, Last_day, Last_week, Last_month, Last_year };
Demandez à l'utilisateur la période du rapport (par défaut, il s'agit de la période entière).
// ask for report period input report_periods ReportPeriod=0;
Écrivez le corps de la fonction OnStart().
void OnStart() {
Déterminez le début et la fin de la période de rapport.
datetime StartTime=0; // beginning of report period datetime EndTime=TimeCurrent(); // end of report period // calculating the beginning of report period switch(ReportPeriod) { case 1: StartTime=EndTime-86400; // day break; case 2: StartTime=EndTime-604800; // week break; case 3: StartTime=EndTime-2592000; // month break; case 4: StartTime=EndTime-31536000; // year break; } // if none of the options is executed, then StartTime=0 (entire period)
Déclarez les variables qui seront utilisées dans le programme. Le but des variables est décrit dans les commentaires.
int total_deals_number; // number of deals for history data int file_handle; // file handle int i,j; // loop counters int symb_total; // number of instruments, that were traded int symb_pointer; // pointer to current instrument char deal_status[]; // state of deal (processed/not processed) ulong ticket; // ticket of deal long hChart; // chart id double balance; // current balance value double balance_prev; // previous balance value double lot_current; // volume of current deal double lots_list[]; // list of open volumes by instruments double current_swap; // swap of current deal double current_profit; // profit of current deal double max_val,min_val; // maximal and minimal value string symb_list[]; // list of instruments, that were traded string in_table_volume; // volume of entering position string in_table_time; // time of entering position string in_table_price; // price of entering position string out_table_volume; // volume of exiting position string out_table_time; // time of exiting position string out_table_price; // price of exiting position string out_table_swap; // swap of exiting position string out_table_profit; // profit of exiting position bool symb_flag; // flag that instrument is in the list datetime time_prev; // previous value of time datetime time_curr; // current value of time datetime position_StartTime; // time of first enter to position datetime position_EndTime; // time of last exit from position ENUM_TIMEFRAMES Picture1_period; // period of balance chart
Ouvrez un nouveau graphique et définissez ses propriétés - il s'agit du graphique des prix, qui sera affiché au bas du rapport.
// open a new chart and set its properties hChart=ChartOpen(Symbol(),0); ChartSetInteger(hChart,CHART_MODE,CHART_BARS); // bars chart ChartSetInteger(hChart,CHART_AUTOSCROLL,true); // autoscroll enabled ChartSetInteger(hChart,CHART_COLOR_BACKGROUND,White); // white background ChartSetInteger(hChart,CHART_COLOR_FOREGROUND,Black); // axes and labels are black ChartSetInteger(hChart,CHART_SHOW_OHLC,false); // OHLC are not shown ChartSetInteger(hChart,CHART_SHOW_BID_LINE,true); // show BID line ChartSetInteger(hChart,CHART_SHOW_ASK_LINE,false); // hide ASK line ChartSetInteger(hChart,CHART_SHOW_LAST_LINE,false); // hide LAST line ChartSetInteger(hChart,CHART_SHOW_GRID,true); // show grid ChartSetInteger(hChart,CHART_SHOW_PERIOD_SEP,true); // show period separators ChartSetInteger(hChart,CHART_COLOR_GRID,LightGray); // grid is light-gray ChartSetInteger(hChart,CHART_COLOR_CHART_LINE,Black); // chart lines are black ChartSetInteger(hChart,CHART_COLOR_CHART_UP,Black); // up bars are black ChartSetInteger(hChart,CHART_COLOR_CHART_DOWN,Black); // down bars are black ChartSetInteger(hChart,CHART_COLOR_BID,Gray); // BID line is gray ChartSetInteger(hChart,CHART_COLOR_VOLUME,Green); // volumes and orders levels are green ChartSetInteger(hChart,CHART_COLOR_STOP_LEVEL,Red); // SL and TP levels are red ChartSetString(hChart,CHART_COMMENT,ChartSymbol(hChart)); // comment contains instrument <end segm
Faites une capture d’écran et enregistrez-la sous le nom "picture2.gif".
// save chart as image file ChartScreenShot(hChart,"picture2.gif",Picture2_width,Picture2_height);
Demandez l'historique des deals pour toute la durée d'existence du compte.
// request deals history for entire period HistorySelect(0,TimeCurrent());
Ouvrez le fichier "report.html", dans lequel nous écrirons la page HTML avec le rapport (encodage ANSI).
// open report file file_handle=FileOpen("report.html",FILE_WRITE|FILE_ANSI);
Écrivez le début du document HTML :
- début du document html (<html>)
- titre qui sera affiché en haut de la fenêtre de votre navigateur (<head><title>Expert Trade Report</title></head>)
- début de la partie principale du document html avec la couleur d'arrière-plan (<body bgcolor='#EFEFEF'>)
- alignement du centre (<center>)
- titre du tableau des deals et des positions (<h2>Trade Report</h2>)
- début du tableau des deals et des positions avec alignement, largeur de la bordure, couleur d'arrière-plan, couleur de la bordure, espacement des cellules et remplissage des cellules (<table align='center' border='1' bgcolor='#FFFFFF' bordercolor='#7F7FFF' cellspacing ='0' cellpadding='0'>)
- en-tête de tableau
// write the beginning of HTML FileWrite(file_handle,"<html>"+ "<head>"+ "<title>Expert Trade Report</title>"+ "</head>"+ "<body bgcolor='#EFEFEF'>"+ "<center>"+ "<h2>Trade Report</h2>"+ "<table align='center' border='1' bgcolor='#FFFFFF' bordercolor='#7F7FFF' cellspacing='0' cellpadding='0'>"+ "<tr>"+ "<th rowspan=2>SYMBOL</th>"+ "<th rowspan=2>Direction</th>"+ "<th colspan=3>Open</th>"+ "<th colspan=3>Close</th>"+ "<th rowspan=2>Swap</th>"+ "<th rowspan=2>Profit</th>"+ "</tr>"+ "<tr>"+ "<th>Volume</th>"+ "<th>Time</th>"+ "<th>Price</th>"+ "<th>Volume</th>"+ "<th>Time</th>"+ "<th>Price</th>"+ "</tr>");
Obtenir le nombre des deals dans la liste.
// number of deals in history total_deals_number=HistoryDealsTotal();
Définition des dimensions des tableaux symb_list[], lots_list[] et deal_status[].
// setting dimensions for the instruments list, the volumes list and the deals state arrays ArrayResize(symb_list,total_deals_number); ArrayResize(lots_list,total_deals_number); ArrayResize(deal_status,total_deals_number);
Initialisation de tous les éléments du tableau deal_status[] avec la valeur 0 - tous les deals ne sont pas traités.
// setting all elements of array with value 0 - deals are not processed ArrayInitialize(deal_status,0);
Réglage des valeurs initiales du solde et de la variable, utilisé pour stocker la valeur précédente du solde.
balance=0; // initial balance balance_prev=0; // previous balance
Réglage de la valeur initiale de la variable, utilisé pour stocker le nombre d'instruments financiers dans la liste.
// number of instruments in the list symb_total=0;
Créez une boucle qui traite séquentiellement chaque deal de la liste.
// processing all deals in history for(i=0;i<total_deals_number;i++) {
Sélectionnez le deal en cours et obtenez son billet.
//select deal, get ticket ticket=HistoryDealGetTicket(i);
Modification du solde par le montant du profit dans le deal en cours.
// changing balance balance+=HistoryDealGetDouble(ticket,DEAL_PROFIT);
Obtenir l'heure du deal -elle sera utilisée fréquemment plus loin.
// reading the time of deal time_curr=HistoryDealGetInteger(ticket,DEAL_TIME);
S'il s'agit du premier deal de la liste, nous devons ajuster les limites de la période du rapport et sélectionner la période pour le graphique du solde, en fonction de la durée de la période du rapport et de la largeur de la région dans laquelle le graphique sera tracé. Définition des valeurs initiales, des soldes maximal et minimal (ces variables seront utilisées pour définir le maximum et le minimum du graphique.)
// if this is the first deal if(i==0) { // if the report period starts before the first deal, // then the report period will start from the first deal if(StartTime<time_curr) StartTime=time_curr; // if report period ends before the current time, // then the end of report period corresponds to the current time if(EndTime>TimeCurrent()) EndTime=TimeCurrent(); // initial values of maximal and minimal balances // are equal to the current balance max_val=balance; min_val=balance; // calculating the period of balance chart depending on the duration of // report period Picture1_period=PERIOD_M1; if(EndTime-StartTime>(Picture1_width-Axis_Width)) Picture1_period=PERIOD_M2; if(EndTime-StartTime>(Picture1_width-Axis_Width)*120) Picture1_period=PERIOD_M3; if(EndTime-StartTime>(Picture1_width-Axis_Width)*180) Picture1_period=PERIOD_M4; if(EndTime-StartTime>(Picture1_width-Axis_Width)*240) Picture1_period=PERIOD_M5; if(EndTime-StartTime>(Picture1_width-Axis_Width)*300) Picture1_period=PERIOD_M6; if(EndTime-StartTime>(Picture1_width-Axis_Width)*360) Picture1_period=PERIOD_M10; if(EndTime-StartTime>(Picture1_width-Axis_Width)*600) Picture1_period=PERIOD_M12; if(EndTime-StartTime>(Picture1_width-Axis_Width)*720) Picture1_period=PERIOD_M15; if(EndTime-StartTime>(Picture1_width-Axis_Width)*900) Picture1_period=PERIOD_M20; if(EndTime-StartTime>(Picture1_width-Axis_Width)*1200) Picture1_period=PERIOD_M30; if(EndTime-StartTime>(Picture1_width-Axis_Width)*1800) Picture1_period=PERIOD_H1; if(EndTime-StartTime>(Picture1_width-Axis_Width)*3600) Picture1_period=PERIOD_H2; if(EndTime-StartTime>(Picture1_width-Axis_Width)*7200) Picture1_period=PERIOD_H3; if(EndTime-StartTime>(Picture1_width-Axis_Width)*10800) Picture1_period=PERIOD_H4; if(EndTime-StartTime>(Picture1_width-Axis_Width)*14400) Picture1_period=PERIOD_H6; if(EndTime-StartTime>(Picture1_width-Axis_Width)*21600) Picture1_period=PERIOD_H8; if(EndTime-StartTime>(Picture1_width-Axis_Width)*28800) Picture1_period=PERIOD_H12; if(EndTime-StartTime>(Picture1_width-Axis_Width)*43200) Picture1_period=PERIOD_D1; if(EndTime-StartTime>(Picture1_width-Axis_Width)*86400) Picture1_period=PERIOD_W1; if(EndTime-StartTime>(Picture1_width-Axis_Width)*604800) Picture1_period=PERIOD_MN1; // changing the period of opened chart ChartSetSymbolPeriod(hChart,Symbol(),Picture1_period); }
Si ce deal n'est pas le premier, créez l'objet "ligne", à l'aide duquel le graphique de l'évolution du solde est tracé. La ligne n'est tracée que si au moins une de ses extrémités se situe dans la période du rapport. Si les deux extrémités se trouvent dans la période du rapport, la ligne sera « épaisse ». La couleur de la ligne de l’équilibre est verte. Si l'équilibre est au-delà de la plage d'équilibre minimal et maximal - cette plage est ajustée.
else // if this is not the first deal { // plotting the balance line, if the deal is in the report period, // and setting properties of the balance line if(time_curr>=StartTime && time_prev<=EndTime) { ObjectCreate(hChart,IntegerToString(i),OBJ_TREND,0,time_prev,balance_prev,time_curr,balance); ObjectSetInteger(hChart,IntegerToString(i),OBJPROP_COLOR,Green); // if both ends of line are in the report period, // it will be "thick" if(time_prev>=StartTime && time_curr<=EndTime) ObjectSetInteger(hChart,IntegerToString(i),OBJPROP_WIDTH,2); } // if new value of balance exceeds the range // of minimal and maximal values, it must be adjusted if(balance<min_val) min_val=balance; if(balance>max_val) max_val=balance; }
Attribuez la valeur précédente du temps à la variable correspondante.
// changing the previous time value
time_prev=time_curr;
Si le deal n'a pas encore été traité, traitez-le.
// if the deal has not been processed yet if(deal_status[i]<127) {
Si ce deal est une charge de solde et qu'il se situe dans la période du rapport, la chaîne correspondante est écrite pour le rapport. La deal est marqué comme traité.
// If this deal is balance charge if(HistoryDealGetInteger(ticket,DEAL_TYPE)==DEAL_TYPE_BALANCE) { // if it's in the report period - write the corresponding string to report. if(time_curr>=StartTime && time_curr<=EndTime) FileWrite(file_handle,"<tr><td colspan='9'>Balance:</td><td align='right'>",HistoryDealGetDouble(ticket,DEAL_PROFIT), "</td></tr>"); // mark deal as processed deal_status[i]=127; }
Si ce deal est Acheter ou Vendre, vérifiez si cet instrument est dans la liste (tableau symb_list[]). Sinon, mettez-le là-bas La variable symb_pointer pointe vers l'élément du tableau symb_list[], qui contient le nom de l'instrument du deal en cours.
// if this deal is buy or sell if(HistoryDealGetInteger(ticket,DEAL_TYPE)==DEAL_TYPE_BUY || HistoryDealGetInteger(ticket,DEAL_TYPE)==DEAL_TYPE_SELL) { // check if there is instrument of this deal in the list symb_flag=false; for(j=0;j<symb_total;j++) { if(symb_list[j]==HistoryDealGetString(ticket,DEAL_SYMBOL)) { symb_flag=true; symb_pointer=j; } } // if there is no instrument of this deal in the list if(symb_flag==false) { symb_list[symb_total]=HistoryDealGetString(ticket,DEAL_SYMBOL); lots_list[symb_total]=0; symb_pointer=symb_total; symb_total++; }
Définissez les valeurs initiales des variables position_StartTime et position_EndTime, qui stockent la durée de vie de la position initiale et finale.
// set the initial value for the beginning time of deal position_StartTime=time_curr; // set the initial value for the end time of deal position_EndTime=time_curr;
Les variables in_table_volume, in_table_time, in_table_price, out_table_volume, out_table_time, out_table_price, out_table_swap et out_table_profit stockeront des tableaux, qui seront à l'intérieur des cellules d'un tableau plus grand : volume, heure et prix d'entrée sur le marché ; volume, temps, prix, swap et profit de la sortie du marché. La variable in_table_volume stockera également le nom de l'instrument financier et un lien vers une image, qui correspond à la direction de la position ouverte. Attribuez à toutes ces variables des valeurs initiales.
// creating the string in report - instrument, position direction, beginning of table for volumes to enter the market if(HistoryDealGetInteger(ticket,DEAL_TYPE)==DEAL_TYPE_BUY) StringConcatenate(in_table_volume,"<tr><td align='left'>",symb_list[symb_pointer], "</td><td align='center'><img src='buy.gif'></td><td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>"); if(HistoryDealGetInteger(ticket,DEAL_TYPE)==DEAL_TYPE_SELL) StringConcatenate(in_table_volume,"<tr><td align='left'>",symb_list[symb_pointer], "</td><td align='center'><img src='sell.gif'></td><td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>"); // creating the beginning of time table to enter the market in_table_time="<td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>"; // creating the beginning of price table to enter the market in_table_price="<td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>"; // creating the beginning of volume table to exit the market out_table_volume="<td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>"; // creating the beginning of time table to exit the market out_table_time="<td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>"; // creating the beginning of price table to exit the market out_table_price="<td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>"; // creating the beginning of swap table to exit the market out_table_swap="<td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>"; // creating the beginning of profit table to exit the market out_table_profit="<td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>";
Traitez tous les deals en commençant par le deal actuel jusqu'à ce que la position soit clôturée. Traitez-les tous, s'ils n'ont pas été traités plus tôt.
// process all deals for this position starting with the current(until position is closed) for(j=i;j<total_deals_number;j++) { // if the deal has not been processed yet - process it if(deal_status[j]<127) {
Sélectionnez le deal et obtenez son billet.
// select deal, get ticket ticket=HistoryDealGetTicket(j);
Si le deal porte sur le même instrument que la position ouverte, traitez-le. Obtenez le temps du deal. Si le temps de deal dépasse la plage de temps de position, étendez la plage. Obtenez le volume du deal.
// if the instrument of deal matches the instrument of position, that is processed if(symb_list[symb_pointer]==HistoryDealGetString(ticket,DEAL_SYMBOL)) { // get the deal time time_curr=HistoryDealGetInteger(ticket,DEAL_TIME); // If the deal time goes beyond the range of position time // - extend position time if(time_curr<position_StartTime) position_StartTime=time_curr; if(time_curr>position_EndTime) position_EndTime=time_curr; // get the volume of deal lot_current=HistoryDealGetDouble(ticket,DEAL_VOLUME);
Les deals d'achat et de vente sont traités séparément. Commencez par les deals d'achat.
// if this deal is buy if(HistoryDealGetInteger(ticket,DEAL_TYPE)==DEAL_TYPE_BUY) {
Si vous avez déjà ouvert une position pour la vente, ce deal pour l'achat quittera le marché. Et si le volume du deal est supérieur au volume de la position courte ouverte, ce sera l'entrée/sortie. Attribuez des variables de chaîne avec les valeurs requises. Affectez au tableau deal_status[] la valeur 127, si le deal est entièrement traité, ou la valeur 1 s'il s'agit de l'entrée/sortie, et ce deal doit être analysé pour une autre position.
// if position is opened for sell - this will be exit from market if(NormalizeDouble(lots_list[symb_pointer],2)<0) { // if buy volume is greater than volume of opened short position - then this is in/out if(NormalizeDouble(lot_current+lots_list[symb_pointer],2)>0) { // creating table of volumes to exit the market - indicate only volume of opened short position StringConcatenate(out_table_volume,out_table_volume,"<tr><td align='right'>",DoubleToString(-lots_list[symb_pointer],2),"</td></tr>"); // mark position as partially processed deal_status[j]=1; } else { // if buy volume is equal or less than volume of opened short position - then this is partial or full close // creating the volume table to exit the market StringConcatenate(out_table_volume,out_table_volume,"<tr><td align='right'>",DoubleToString(lot_current,2),"</td></tr>"); // mark deal as processed deal_status[j]=127; } // creating the time table to exit the market StringConcatenate(out_table_time,out_table_time,"<tr><td align='center'>",TimeToString(time_curr,TIME_DATE|TIME_SECONDS),"</td></tr>"); // creating the price table to exit the market StringConcatenate(out_table_price,out_table_price,"<tr><td align='center'>",DoubleToString(HistoryDealGetDouble(ticket,DEAL_PRICE), (int)SymbolInfoInteger(symb_list[symb_pointer],SYMBOL_DIGITS)),"</td></tr>"); // get the swap of current deal current_swap=HistoryDealGetDouble(ticket,DEAL_SWAP); // if swap is equal to zero - create empty string of the swap table to exit the market if(NormalizeDouble(current_swap,2)==0) StringConcatenate(out_table_swap,out_table_swap,"<tr></tr>"); // else create the swap string in the swap table to exit the market else StringConcatenate(out_table_swap,out_table_swap,"<tr><td align='right'>",DoubleToString(current_swap,2),"</td></tr>"); // get the profit of current deal current_profit=HistoryDealGetDouble(ticket,DEAL_PROFIT); // if profit is negative (loss) - it is displayed as red in the profit table to exit the market if(NormalizeDouble(current_profit,2)<0) StringConcatenate(out_table_profit,out_table_profit,"<tr><td align=right><SPAN style='COLOR: #EF0000'>", DoubleToString(current_profit,2),"</SPAN></td></tr>"); // else - it is displayed as green else StringConcatenate(out_table_profit,out_table_profit,"<tr><td align='right'><SPAN style='COLOR: #00EF00'>", DoubleToString(current_profit,2),"</SPAN></td></tr>"); }
Si vous avez déjà ouvert une position longue, l'achat de ce deal sera l'entrée sur le marché (le premier ou l'ajout). Si l'élément de tableau deal_status[], qui correspond à ce deal, a la valeur 1 - cela indique que l'entrée/sortie a été effectuée. Affectez des variables de chaîne avec les valeurs requises et marquez le deal comme traité (affectez l'élément correspondant du tableau deal_status[] avec la valeur 127).
else // if position is opened for buy - this will be the enter to the market { // if this deal has been already partially processed (in/out) if(deal_status[j]==1) { // create the volume table of entering the market (volume, formed after in/out, is put here) StringConcatenate(in_table_volume,in_table_volume,"<tr><td align='right'>",DoubleToString(lots_list[symb_pointer],2),"</td></tr>"); // indemnity of volume change, which will be produced (the volume of this deal is already taken into account) lots_list[symb_pointer]-=lot_current; } // if this deal has not been processed yet, create the volume table to enter the market else StringConcatenate(in_table_volume,in_table_volume,"<tr><td align='right'>",DoubleToString(lot_current,2),"</td></tr>"); // creating the time table of entering the market StringConcatenate(in_table_time,in_table_time,"<tr><td align center>",TimeToString(time_curr,TIME_DATE|TIME_SECONDS),"</td></tr>"); // creating the price table of entering the market StringConcatenate(in_table_price,in_table_price,"<tr><td align='center'>",DoubleToString(HistoryDealGetDouble(ticket,DEAL_PRICE), (int)SymbolInfoInteger(symb_list[symb_pointer],SYMBOL_DIGITS)),"</td></tr>"); // mark deal as processed deal_status[j]=127; }
Remplacez le volume de la position par le volume du deal actuelle. Si la position est fermée (le volume est égal à zéro) - arrêtez le traitement de cette position (sortez de la boucle avec la variable j) et recherchez le prochain deal non traité (dans la boucle avec la variable i).
// change of position volume by the current instrument, taking into account the volume of current deal lots_list[symb_pointer]+=lot_current; // if the volume of opened position by the current instrument became equal to zero - position is closed if(NormalizeDouble(lots_list[symb_pointer],2)==0 || deal_status[j]==1) break; }
Les deals de vente sont traitées de la même manière, puis nous sortons de la boucle avec la variable j.
// if this deal is sell if(HistoryDealGetInteger(ticket,DEAL_TYPE)==DEAL_TYPE_SELL) { // if position has been already opened for buy - this will be the exit from market if(NormalizeDouble(lots_list[symb_pointer],2)>0) { // if sell volume is greater than volume of opened long position - then this is in/out if(NormalizeDouble(lot_current-lots_list[symb_pointer],2)>0) { // creating table of volumes to exit the market - indicate only volume of opened long position StringConcatenate(out_table_volume,out_table_volume,"<tr><td align='right'>",DoubleToString(lots_list[symb_pointer],2),"</td></tr>"); // mark position as partially processed deal_status[j]=1; } else { // if sell volume is equal or greater than volume of opened short position - then this is partial or full close // creating the volume table to exit the market StringConcatenate(out_table_volume,out_table_volume,"<tr><td align='right'>",DoubleToString(lot_current,2),"</td></tr>"); // mark deal as processed deal_status[j]=127; } // creating the time table to exit the market StringConcatenate(out_table_time,out_table_time,"<tr><td align='center'>",TimeToString(time_curr,TIME_DATE|TIME_SECONDS),"</td></tr>"); // creating the price table to exit the market StringConcatenate(out_table_price,out_table_price,"<tr><td align='center'>",DoubleToString(HistoryDealGetDouble(ticket,DEAL_PRICE), (int)SymbolInfoInteger(symb_list[symb_pointer],SYMBOL_DIGITS)),"</td></tr>"); // get the swap of current deal current_swap=HistoryDealGetDouble(ticket,DEAL_SWAP); // if swap is equal to zero - create empty string of the swap table to exit the market if(NormalizeDouble(current_swap,2)==0) StringConcatenate(out_table_swap,out_table_swap,"<tr></tr>"); // else create the swap string in the swap table to exit the market else StringConcatenate(out_table_swap,out_table_swap,"<tr><td align='right'>",DoubleToString(current_swap,2),"</td></tr>"); // get the profit of current deal current_profit=HistoryDealGetDouble(ticket,DEAL_PROFIT); // if profit is negative (loss) - it is displayed as red in the profit table to exit the market if(NormalizeDouble(current_profit,2)<0) StringConcatenate(out_table_profit,out_table_profit,"<tr><td align='right'> <SPAN style='COLOR: #EF0000'>",DoubleToString(current_profit,2),"</SPAN></td></tr>"); // else - it is displayed as green else StringConcatenate(out_table_profit,out_table_profit,"<tr><td align='right'><SPAN style='COLOR: #00EF00'>", DoubleToString(current_profit,2),"</SPAN></td></tr>"); } else // if position is opened for sell - this will be the enter to the market { // if this deal has been already partially processed (in/out) if(deal_status[j]==1) { // create the volume table of entering the market (volume, formed after in/out, is put here) StringConcatenate(in_table_volume,in_table_volume,"<tr><td align='right'>",DoubleToString(-lots_list[symb_pointer],2),"</td></tr>"); // indemnity of volume change, which will be produced (the volume of this deal is already taken into account) lots_list[symb_pointer]+=lot_current; } // if this deal has not been processed yet, create the volume table to enter the market else StringConcatenate(in_table_volume,in_table_volume,"<tr><td align='right'>",DoubleToString(lot_current,2),"</td></tr>"); // creating the time table of entering the market StringConcatenate(in_table_time,in_table_time,"<tr><td align='center'>",TimeToString(time_curr,TIME_DATE|TIME_SECONDS),"</td></tr>"); // creating the price table of entering the market StringConcatenate(in_table_price,in_table_price,"<tr><td align='center'>",DoubleToString(HistoryDealGetDouble(ticket,DEAL_PRICE), (int)SymbolInfoInteger(symb_list[symb_pointer],SYMBOL_DIGITS)),"</td></tr>"); // mark deal as processed deal_status[j]=127; } // change of position volume by the current instrument, taking into account the volume of current deal lots_list[symb_pointer]-=lot_current; // if the volume of opened position by the current instrument became equal to zero - position is closed if(NormalizeDouble(lots_list[symb_pointer],2)==0 || deal_status[j]==1) break; } } } }
Si l'heure, lorsque la position a été ouverte, est dans la période du rapport (au moins partiellement) - l'entrée correspondante est sortie dans le fichier "report.html".
// if the position period is in the the report period - the position is printed to report if(position_EndTime>=StartTime && position_StartTime<=EndTime) FileWrite(file_handle, in_table_volume,"</table></td>", in_table_time,"</table></td>", in_table_price,"</table></td>", out_table_volume,"</table></td>", out_table_time,"</table></td>", out_table_price,"</table></td>", out_table_swap,"</table></td>", out_table_profit,"</table></td></tr>");
Attribuez à la variable balance_prev la valeur du solde. Quittez la boucle avec la variable i.
}
// changing balance
balance_prev=balance;
}
Écrivez la fin du fichier HTML (liens vers les images, la fin de l'alignement du centre, la fin de la partie principale, la fin du document HTML). Clôturez le fichier "report.html".
// create the end of html-file FileWrite(file_handle, "</table><br><br>"+ "<h2>Balance Chart</h2><img src='picture1.gif'><br><br><br>"+ "<h2>Price Chart</h2><img src='picture2.gif'>"+ "</center>"+ "</body>"+ "</html>"); // close file FileClose(file_handle);
Attente de la mise à jour du graphique ne dépassant pas le temps indiqué dans la constante de délai d'attente.
// get current time time_curr=TimeCurrent(); // waiting for chart update while(SeriesInfoInteger(Symbol(),Picture1_period,SERIES_BARS_COUNT)==0 && TimeCurrent()-time_curr<timeout) Sleep(1000);
Réglage du maximum et du minimum fixes du graphique.
// setting maximal and minimal values for the balance chart (10% indent from upper and lower boundaries) ChartSetDouble(hChart,CHART_FIXED_MAX,max_val+(max_val-min_val)/10); ChartSetDouble(hChart,CHART_FIXED_MIN,min_val-(max_val-min_val)/10);
Définition des propriétés du tableau de solde.
// setting properties of the balance chart ChartSetInteger(hChart,CHART_MODE,CHART_LINE); // chart as line ChartSetInteger(hChart,CHART_FOREGROUND,false); // chart on foreground ChartSetInteger(hChart,CHART_SHOW_BID_LINE,false); // hide BID line ChartSetInteger(hChart,CHART_COLOR_VOLUME,White); // volumes and orders levels are white ChartSetInteger(hChart,CHART_COLOR_STOP_LEVEL,White); // SL and TP levels are white ChartSetInteger(hChart,CHART_SHOW_GRID,true); // show grid ChartSetInteger(hChart,CHART_COLOR_GRID,LightGray); // grid is light-gray ChartSetInteger(hChart,CHART_SHOW_PERIOD_SEP,false); // hide period separators ChartSetInteger(hChart,CHART_SHOW_VOLUMES,CHART_VOLUME_HIDE); // hide volumes ChartSetInteger(hChart,CHART_COLOR_CHART_LINE,White); // chart is white ChartSetInteger(hChart,CHART_SCALE,0); // minimal scale ChartSetInteger(hChart,CHART_SCALEFIX,true); // fixed scale on vertical axis ChartSetInteger(hChart,CHART_SHIFT,false); // no chart shift ChartSetInteger(hChart,CHART_AUTOSCROLL,true); // autoscroll enabled ChartSetString(hChart,CHART_COMMENT,"BALANCE"); // comment on chart
Redessiner le tableau de solde
// redraw the balance chart ChartRedraw(hChart); Sleep(8000);
Capture d'écran de la carte (enregistrez l'image "picture1.gif"). La largeur du graphique s'ajuste à la largeur de la période du rapport (mais à cause des jours fériés, des inexactitudes se produisent souvent et le graphique devient plus large que la courbe de variation du solde), la hauteur est calculée comme la moitié de la largeur.
// screen shooting the balance chart ChartScreenShot(hChart,"picture1.gif",(int)(EndTime-StartTime)/PeriodSeconds(Picture1_period), (int)(EndTime-StartTime)/PeriodSeconds(Picture1_period)/2,ALIGN_RIGHT);
Supprimez tous les objets du graphique et clôturez-le.
// delete all objects from the balance chart ObjectsDeleteAll(hChart); // close chart ChartClose(hChart);
Si l'envoi de fichiers via FTP est autorisé, envoyez trois fichiers : "report.html", picture1.gif "et" picture2.gif ".
// if report publication is enabled - send via FTP // HTML-file and two images - price chart and balance chart if(TerminalInfoInteger(TERMINAL_FTP_ENABLED)) { SendFTP("report.html"); SendFTP("picture1.gif"); SendFTP("picture2.gif"); } }
Pour l'instant, la description du programme est achevée Pour envoyer des fichiers via FTP, vous devez ajuster les paramètres de MetaTrader 5 - allez dans le menu Outils, puis Options et ouvrez l'onglet Éditeur (Figure 4).
Figure 4. Options de publication de rapport via FTP.
Dans la boîte de dialogue Options, vous devez cocher l'option "Activer", indiquer le numéro de compte, l'adresse FTP, le chemin, l’identifiant et le mot de passe pour l'accès. La périodicité d'actualisation n'a pas d'importance.
Vous pouvez maintenant exécuter le script. Après avoir exécuté le tableau de solde ce dernier apparaît à l'écran pendant quelques secondes puis disparaît. Dans le Journal, vous pouvez trouver une éventuelle erreur et voir si les fichiers ont été envoyés via FTP. Si tout fonctionne correctement, trois nouveaux fichiers apparaîtront sur le serveur, dans le dossier indiqué. Si vous y placez deux fichiers avec des images de flèches et si le serveur WWW est configuré et opérationnel, vous pouvez ouvrir un rapport via un navigateur Web.
2. Envoi de notifications sous forme de SMS vers un téléphone mobile
Il y a des moments où vous êtes loin de votre ordinateur et d'autres appareils électroniques, et vous n'avez qu'un téléphone portable à portée de main. Mais vous souhaitez contrôler le trade sur votre compte ou surveiller les cotations d'instruments financiers. Dans ce cas, vous pouvez définir l'envoi de notifications par SMS vers le téléphone mobile. De nombreux opérateurs mobiles (et tiers) fournissent un service EMAIL-SMS, qui vous permet de recevoir des messages sous forme de lettres, envoyées à une adresse e-mail spécifique.
Pour cela, vous devez disposer d'une boîte e-mail (vous devez notamment connaître votre serveur SMTP). Ajustez vos paramètres MetaTrader 5 - allez dans le menu Outils, puis Options et ouvrez l'onglet E-mail (Figure 5).
Figure 5. Configuration l'envoi de notifications par email
Cochez l'option "Activer", indiquez l'adresse du serveur SMTP, l’identifiant et le mot de passe, l'adresse de l'expéditeur (votre e-mail) et l'adresse du destinataire - l'adresse e-mail utilisée pour envoyer des messages sous forme de SMS (vérifiez auprès de votre opérateur mobile). Si tout est bon, lorsque vous cliquez sur le bouton "Test", un message de vérification sera envoyé (voir les informations supplémentaires dans le Journal).
Le moyen le plus simple de recevoir les notifications lorsque le prix atteint un certain niveau est de créer une alerte. Pour se faire, ouvrez l'onglet "Boîte à outils" approprié, faites un clic droit et sélectionnez "Créer" (Figure 6).
Figure 6. Création d'alerte.
Dans cette fenêtre, cochez l'option "Activer", sélectionnez l'action "Courrier", sélectionnez l'instrument financier, la condition, entrez la valeur de la condition et écrivez le texte du message. Dans les « itérations maximales », entrez 1 si vous ne souhaitez pas que le message revienne à plusieurs reprises. Lorsque tous les champs sont remplis, cliquez sur OK.
Si nous envoyons un message depuis un programme MQL5, nous aurons plus de possibilités. Nous utiliserons la fonction SendMail(). Il dispose de deux paramètres. Premièrement - le titre, deuxièmement - le corps du message.
Vous pouvez appeler la fonction SendMail() après la demande d'échange (fonction OrderSend()) ou dans le gestionnaire d'événements Trade. Nous aurons donc des notifications d'événements de trade - entrée sur le marché, passation de commandes, clôture de positions. Ou vous pouvez placer SendMail() dans la fonction OnTimer() - nous recevrons des notifications périodiques sur les cotations en cours. Vous pouvez organiser l'envoi de notifications lorsque certains signaux de trade apparaissent - lorsque les lignes indicatrices se croisent, lorsque le prix atteint certaines lignes et niveaux, etc.
Examinons ces quelques exemples.
Si en Expert Advisor ou en Script vous remplacez
OrderSend(request,result};
avec ce qui suit
string msg_subj,msg_text; if(OrderSend(request,result)) { switch(request.action) { case TRADE_ACTION_DEAL: switch(request.type) { case ORDER_TYPE_BUY: StringConcatenate(msg_text,"Buy ",result.volume," ",request.symbol," at price ",result.price,", SL=",request.sl,", TP=",request.tp); break; case ORDER_TYPE_SELL: StringConcatenate(msg_text,"Sell ",result.volume," ",request.symbol," at price ",result.price,", SL=",request.sl,", TP=",request.tp); break; } break; case TRADE_ACTION_PENDING: switch(request.type) { case ORDER_TYPE_BUY_LIMIT: StringConcatenate(msg_text,"Set BuyLimit ",result.volume," ",request.symbol," at price ",request.price,", SL=",request.sl,", TP=",request.tp); break; case ORDER_TYPE_SELL_LIMIT: StringConcatenate(msg_text,"Set SellLimit ",result.volume," ",request.symbol," at price ",request.price,", SL=",request.sl,", TP=",request.tp); break; case ORDER_TYPE_BUY_STOP: StringConcatenate(msg_text,"Set BuyStop ",result.volume," ",request.symbol," at price ",request.price,", SL=",request.sl,", TP=",request.tp); break; case ORDER_TYPE_SELL_STOP: StringConcatenate(msg_text,"Set SellStop ",result.volume," ",request.symbol," at price ",request.price,", SL=",request.sl,", TP=",request.tp); break; case ORDER_TYPE_BUY_STOP_LIMIT: StringConcatenate(msg_text,"Set BuyStopLimit ",result.volume," ",request.symbol," at price ",request.price,", stoplimit=",request.stoplimit, ", SL=",request.sl,", TP=",request.tp); break; case ORDER_TYPE_SELL_STOP_LIMIT: StringConcatenate(msg_text,"Set SellStop ",result.volume," ",request.symbol," at price ",request.price,", stoplimit=",request.stoplimit, ", SL=",request.sl,", TP=",request.tp); break; } break; case TRADE_ACTION_SLTP: StringConcatenate(msg_text,"Modify SL&TP. SL=",request.sl,", TP=",request.tp); break; case TRADE_ACTION_MODIFY: StringConcatenate(msg_text,"Modify Order",result.price,", SL=",request.sl,", TP=",request.tp); break; case TRADE_ACTION_REMOVE: msg_text="Delete Order"; break; } } else msg_text="Error!"; StringConcatenate(msg_subj,AccountInfoInteger(ACCOUNT_LOGIN),"-",AccountInfoString(ACCOUNT_COMPANY)); SendMail(msg_subj,msg_text);
puis après la demande du trade, la fonction OrderSend() enverra un message à l'aide de la fonction SendMail(). Il comprendra des informations sur le numéro de compte de trade, le nom d'un courtier et les actions effectuées (achat, vente, commande en attente, modification ou suppression d'ordre), comme les suivantes :
59181-MetaQuotes Software Corp. Buy 0.1 EURUSD at price 1.23809, SL=1.2345, TP=1.2415
Et si dans n'importe quel Expert Advisor ou Indicateur à l'intérieur du corps de OnInit(), vous démarrez la minuterie en utilisant la fonction EventSetTimer() (elle n'a qu'un seul paramètre - la période de minuterie en secondes):
void OnInit() { EventSetTimer(3600); }
dans le OnDeinit(), n'oubliez pas de le désactiver à l'aide de EventKillTimer() :
void OnDeinit(const int reason) { EventKillTimer(); }
et dans OnTimer() pour envoyer des messages à l'aide de SendMail() :
void OnTimer() { SendMail(Symbol(),DoubleToString(SymbolInfoDouble(Symbol(),SYMBOL_BID),_Digits)); }
alors vous recevrez des messages sur le prix de l'instrument financier actuel avec une période indiquée.
Conclusion
Cet article décrit comment utiliser le programme MQL5 pour créer des fichiers HTML et image et comment les télécharger sur un serveur WWW via FTP. il décrit également comment configurer l'envoi de notifications sur votre téléphone mobile sous forme de SMS.
Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/61





- 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