
トレードレポートとSMS通知の作成と発行
はじめに
本稿ではトレード結果レポートをHTMLファイルとして作成し、FTPを介してWWWサーバーにアップロードする方法について述べます。また、トレードイベント通知をSMSとして携帯電話に送信することみついても考察します。
本稿で述べる素材を快適に利用するため、読者のみなさんにはHTML (HyperText Markup Language)について知っていただくことを提案します。
レポートのアップロードを実装するためにはWWWサーバーが必要です。(コンピュータは問いません。)それはFTPを介してデータを受け付けます。トレードイベント通知をSMSとして受け取る機能を実装するには、, EMAIL-SMSゲートウェイが必要です。(このサービスは携帯電話会社や第三者により提供されています。)
1. レポート作成とFTPによる送信
トレードレポートを作成し、FTPプロトコルを介して送信するMQL5プログラムを作成します。まずはそれを「スクリプト」として作成します。将来的にはそれを終了ブロックとして使用することが可能です。それはExpert Advisorsとインディケータに挿入することができます。たとえば、Expert Advisersでは、みなさんはこのブロックトレードまたはタイマーイベントハンドラとして使用し、このブロックをトレード依頼後実行、またはChartEventイベント用処理を設定することができます。インディケータでは、このブロックをタイマーまたは ChartEventイベントハンドラとしてインクルードすることが可能です。
プログラムにより作成されたレポートの例は図1~3に示されています。または、このレポートを本稿末尾にあるリンクからダウンロードできます。
図1 レポート例 - 取引きとポジション表
図2 レポート例 - 残高グラフ
図3 レポート例 - 現インスツルメントの価格グラフ
取引きとポジションの表(図1)では、都合のよいようにすべての取引がポジションに分けられています。表の左側にはマーケット開始時のボリューム、時間、価格(オープンポジションおよび追加)が示されています。表の右側には、マーケット終了時の同じパラメータ(部分的または全体的クローズポジション)が示されています。入/出には取引は2つの部分に分けられます。 - あるポジションのクローズと次のポジションのオープンです。
取引きとポジション表の下には、残高グラフ(横軸が時刻です)が表示されており、一番したには現インスツルメントについての価格グラフが表示されています。
プログラムは"report.html"、"picture1.gif"、"picture2.gif"ファイル (レポートのhtml ファイル、残高グラフと価格負ラフの画像)をMetaTarder5_istall_dir\MQL5\Filesフォルダに作成します。そして、端末設定で FTPパブリッシングが有効です。 - それはこれら3とおりのファイルを指定のサーバーに送信します。また、他にファイルが2つ必要です。 - オープンポジション方向を指す矢印画像 - 「買い」または「売り」("buy.gif" と "sell.gif")これら画像は取り込む(本稿末尾にダウンロード用リンクあり)か、グラフィックエディタで自分で描画することができます。これら2つのファイルは"report.html"ファイルと共にWWWサーバーの同じフォルダーに配置する必要があります。
入力パラメータとして、プログラムは期間の開始と終了を受け取ります。レポートはその期間について作成されます。われわれの例では、レポートの終了時期は現在時刻、ユーザーはレポート期間のバリアントを選択することが可能です。:全期間、前日、先週、先月、去年など。
レポート作成方法についてもう少しお話します。トレードサーバーは取引の利用可能な履歴すべてを依頼されます。取得された取引は順番に処理されます。deal_status[] 配列が取引処理がすんだかまだかについての情報を格納します。この関数のエレメントインデックスは取引のトレードサーバーリストから受け取る取引数です。そしてエレメント値は次のように解釈します。: 0 - 取引き未処理、 1 - 取引きは部分的に処理済み(入/出)、127 - 取引きは処理済み(その他の値は使われず将来的に使用するためとっておきます。)
symb_list[] 配列には、取引き対象のファイナンシャルインスツルメント名が含まれています。また lots_list[] 配列には、取引進行中の各インスツルメントに対するオープンポジションのボリュームが含まれます。ボリュームの正の値はロング、負の値はショートにあたります。ボリュームがゼロの場合は、このツールがオープンポジションを持たないことを意味します。取引き中リスト(symb_list[] 配列内)にないファイナンシャルインスツルメントに出くわすと、それはその場で追加され、ファイナンシャルインスツルメント数は1増加します。
各取引において、それぞれ続く次の取引きがポジションのクローズかin/outまで同じファイナンシャルインスツルメントによって分析されます。deal_status[] 配列の値が127より小さい取引きのみ分析されます。取引き後、対応する deal_status[] 配列のエレメントが値 127 で割り当てられ、その取引がポジションの in/out であれば、値は1となります。ポジションオープンの時刻がレポート期間(StartTime と EndTime 変数で定義された)と一致すれば、このポジションはレポートにログされます(全インプットおよびアウトプット)。
取引き表に加え、現ファイナンシャルインスツルメントの新規チャートが開かれます。この表には必要なプロパティがすべて提供され、ChartScreenShot() 関数を使用してスクリーンショットが作成されます - よって、現インスツルメントの価格表のイメージファイルを取得することとなります。次は、この表の上で価格表はマスキングされ、残高表が描かれます。そしえ、もう一つ別のスクリーンショットが作成されます。
レポートの表と HTML ファイルの2種類の画像ファイルが作成されるとき、FTP を介してファイルを送信する機能がチェックされます。それが可能であれば、MetaTrader 5内の設定に応じてSendFTP()関数を使用して、ファイル "report.html"、"picture1.gif" 、 "picture2.gif" が送信されます。
MetaQuotes Language Editor を起動し、スクリプトの作成にとりかかります。定数を定義します。表のリフレッシュのタイムアウト(秒単位)、価格表の幅と高さ、残高表の最大幅残高変動極性を表示する表の期間はレポート期間と表の最大幅に依存します。表の幅は残高表について必要とされるサイズに調整されます。
表の高さは幅の半分に自動的に計算されます。また、定数として縦軸の画場を特定します。それは画像の幅と比較して縦軸のためグラフ領域が減らされるピクセル数です。
#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)
入力パラメータがユーザーから依頼されるよう指定します。
// request input parameters #property script_show_inputs
Create enumeration of report periods.
// enumeration of report periods enum report_periods { All_periods, Last_day, Last_week, Last_month, Last_year };
レポート期間をユーザーに尋ねます(初期設定では全期間)。
// ask for report period input report_periods ReportPeriod=0;
OnStart() 関数本文を書きます。
void OnStart() {
レポート期間の開始と終了を決定します。
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)
プログラムで使用される変数を宣言します。コメントに変数の目的を記載します。
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
新しい表を開き、プロパティを設定します。これは価格表で、レポートの一番下に出力されます。
// 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
表のスクリーンショットは "picture2.gif" として保存されます。
// save chart as image file ChartScreenShot(hChart,"picture2.gif",Picture2_width,Picture2_height);
存在するアカウントの全期間の取引履歴をリクエストします。
// request deals history for entire period HistorySelect(0,TimeCurrent());
"report.html" ファイルを開きます。そこにレポート(ANSI エンコード)を伴なう HTML ページを書き込みます。
// open report file file_handle=FileOpen("report.html",FILE_WRITE|FILE_ANSI);
HTMLドキュメントの最初の部分を書きます。
- html ドキュメントの始まり (<html>)
- ブラウザウィンドウの一番上に表示されるタイトル (<head><title>Expert Trade Report</title></head>)
- 背景色を伴う htmlドキュメントの始まり(<body bgcolor='#EFEFEF'>)
- 中央寄せ(<center>)
- 取引きとポジション表のタイトル(<h2>Trade Report</h2>)
- 位置調整、境界線幅、背景色、境界線色、セルのスペースと追加を伴う取引とポジション表のはじまり (<table align='center' border='1' bgcolor='#FFFFFF' bordercolor='#7F7FFF' cellspacing='0' cellpadding='0'>)
- 表見出し
// 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>");
リスト内取引数の取得
// number of deals in history total_deals_number=HistoryDealsTotal();
symb_list[]、lots_list[] 、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);
deal_status[] 配列のエレメントを値 0 で初期化- 全取引未処理
// setting all elements of array with value 0 - deals are not processed ArrayInitialize(deal_status,0);
残高と残高の前回値を保存する変数の初期値設定
balance=0; // initial balance balance_prev=0; // previous balance
リスト上のファイナンシャルインスツルメント数を保存する変数の初期値設定
// number of instruments in the list symb_total=0;
リスト上の各取引を連続して処理するループを作成します。
// processing all deals in history for(i=0;i<total_deals_number;i++) {
現取引を選択し、そのチケットを取得します。
//select deal, get ticket ticket=HistoryDealGetTicket(i);
現取引の収益額による残高変更
// changing balance balance+=HistoryDealGetDouble(ticket,DEAL_PROFIT);
取引き時刻取得 - 後に頻繁に使用されます。
// reading the time of deal time_curr=HistoryDealGetInteger(ticket,DEAL_TIME);
それがリスト上の最初の取引であれば、レポート期間の境界を調整し、レポート期間と表がプロットされる領域幅に応じて残高表の期間を選択する必要があります。最大最小残高の初期値設定(これら変数は表の最大最小設定に使用されます。)
// 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); }
もし今回取引が最初のものでなければ、"line" オブジェクトを作成します。それにより残高表変動がプロットされます。ラインは、最低一つ端がレポート期間にあればプロットされます。両端がレポート期間にあれば線は『太く』なります。残高を表す線の色はグリーンです。残高が最小最大の範囲を越えたらこの範囲は調整されます。
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; }
変数に対応して時刻の前回値を割り当てます。
// changing the previous time value
time_prev=time_curr;
取引きがまだ処理されていなければ処理を行います。
// if the deal has not been processed yet if(deal_status[i]<127) {
この取引きがバランスチャージbalance chargeでレポート期間に含まれる場合 - 対応するストリングがレポートに書き込まれます。取引きが処理済みとマークされます。
// 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; }
この取引が「買い」または「売り」であれば、このインスツルメントがリスト(symb_list[] 配列)にあるか確認します。なければ、リストに入れます。symb_pointer 変数はsymb_list[] 配列のエレメントを指示します。これは現取引のインスツルメント名を含むものです。
// 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++; }
変数 position_StartTime と position_EndTime の値を設定します。それらはライフタイムの初期および最終ポジションを保存します。
// 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;
変数 in_table_volume、in_table_time、in_table_price、 out_table_volume、out_table_time、out_table_price、 out_table_swap、 out_table_profit はより大きい表のセル内の表を保存します。:大きい表とは、ボリューム、マーケット入りの時刻と価格;マーケット出のボリューム、時刻、価格、スワップ、プロフィットを表すものです。in_table_volume 変数はファイナンシャルインスツルメント名と画像へのリンクを保存します。それはオープンポジションの方向に対応します。これらすべての変数に初期値を割り当てます。
// 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'>";
現在から始め、ポジションがクローズするまですべての取引を処理ります。未処理のものはすべて処理します。
// 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) {
取引きを選択し、そのチケットを取得します。
// select deal, get ticket ticket=HistoryDealGetTicket(j);
取引きがオープンポジションと同じインスツルメントにあれば、それを処理します。取引き時刻を取得します。取引き時間がポジション時間の範囲を超えた場合は、範囲を拡げます。取引きボリュームを取得します。
// 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);
Buy and Sell deals are processed separately. Begin with the Buy deals.
// if this deal is buy if(HistoryDealGetInteger(ticket,DEAL_TYPE)==DEAL_TYPE_BUY) {
「売り」にポジションをオープンしてしまっていたら、これの「買い」はマーケットを出ます。取引きボリュームがオープン時のショートポジションのボリュームを上回っていたら、これは in/out です。要求される値でストリング変数に割り当てます。取引が全て処理済みであれば、deal_status[] 配列に値 127 を割り当てます。また、 in/out であれば、値1を割り当てます。なおこの取引は他のポジションに対して分析される必要があります。
// 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; } その他 { // 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>"); }
すでにロングポジションをオープンしてしまっていたら、この取引での買いはマーケットに入ります(最初または追加)。この取引に対応するdeal_status[] 配列エレメントが値1であれば、それは in/out が作成されたことを意味します。要求される値をストリング変数に割り当て、取引きを処理済みとマークします(対応するdeal_status[] 変数のエレメントに 127を割り当てます)。
その他 // 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; }
ポジションボリュームを現在取引ボリュームに変更します。ポジションがクローズ(ボリュームがゼロ)であれば、このポジション(j 変数でループを出ます)の処理を停止し、次の未処理の取引(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; }
売り取引も同様に処理され、ループを 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; } その他 { // 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>"); } その他 // 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; } } } }
ポジションオープンの時間がレポート期間内(少なくとも部分的に)であれば、対応づるエントリーは "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>");
balance_prev 変数に残高値を割り当てます。 i 変数でループを抜けます。
}
// changing balance
balance_prev=balance;
}
HTMLファイルの最後を書きます。(画像へのリンク、中央寄せの最後、主要部分の終わり HTMLドキュメントの終わり)"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);
タイムアウト定数に指定した時間を越えない表の更新待機
// 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);
表の固定最大最小設定
// 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);
残高表のプロパティ設定
// 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
残高表の再描画
// redraw the balance chart ChartRedraw(hChart); Sleep(8000);
表のスクリーンショット ( "picture1.gif" 画像を保存します)表の幅はレポート期間の幅に応じて調整します。(が、休日があるため、しばしば不正確となり、表は残高変動曲線よりも広くなります)高さは幅の半分に計算されます。
// screen shooting the balance chart ChartScreenShot(hChart,"picture1.gif",(int)(EndTime-StartTime)/PeriodSeconds(Picture1_period), (int)(EndTime-StartTime)/PeriodSeconds(Picture1_period)/2,ALIGN_RIGHT);
表からオブジェクトをすべて削除し、閉じます。
// delete all objects from the balance chart ObjectsDeleteAll(hChart); // close chart ChartClose(hChart);
ファイルを FTP で送信できれば、3種類のファイル:"report.html"、 picture1.gif "、" 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"); } }
これで、プログラム記述は完了です。FTP を介してファイルを送信するには、MetaTrader 5 設定を調整する必要があります。ツール メニュー --> オプション --> パブリシャ―タブと進みます。(図4)
図4 FTPを介してレポートを発行するオプション
オプションダイアログボックスで、オプション「有効」にチェックを入れ、アカウントナンバー、FTP アドレス、パス、ログイン、アクセスパスワードを指定する必要があります。間隔のリフレッシュは必要ありません。
これでスクリプトを実行することができます。実行後、残高表が画面に数秒間表示され、その後表示が消えます。「ジャーナル」内で、エラーがあるか見、ファイルが FTPによって送信されたか確認することができます。すべてうまくいっていれば、3種類の新規ファイルがサーバーの指定フォルダに表示されます。2つのファイルを矢印画像と共にそこに配置し、WWWサーバーが構成され動作していれば、ウェブブラウザを介してレポートを開くことができます。
2. 通知をSMSとして携帯電話へ送信
コンピュータやその他電気機器から離れている時間があり、手にしているのは携帯電話だけ、ということもあるでしょう。そういうときでも、アカウントの取引きを管理したり、ファイナンシャルインスツルメントのクオートを監視したいと思うものです。その場合、 SMS を介して通知を携帯電話に送信する設定が可能です。モバイル会社(および第三者)は EMAIL-SMS サービスを提供しており、それにより指定のeメールアドレスに送信されるメッセージをレターとして受け取ることができます。
おのためいは、 eメールボックスを所有する必要があります。(特にご自身の SMTP サーバーを知る必要があります。)MetaTrader 5 設定を調整します。ツール メニュー --> オプション --> eメールタブをひらく と進みます。(図5)
図5 eメールで通知送信を設定
「有効」オプションにチェックを入れ、SMTP サーバーアドレス、ログインとパスワード、送信者アドレス(あなたのeメールアドレス)、受信者アドレスを指定します。SMSとしてメッセージを送信するのに使用するeメールアドレスです。(ご自身のモバイルオペレータを確認ください)すべてが正しければ、「テスト」ボタンをクリックすると、確認メッセージが送信されます。(「ジャーナル」にて追加情報を確認ください)
価格が一定レベルに到達したことを知る一番簡単な方法は、アラートを作成することです。このためには、適切な「ツールボックス」タブを開き、右クリックして「作成」を選択します。(図6)
図6 アラート作成
このウィンドウ内で、「有効」オプションにチェックを入れ、「メール」処理を選び、ファイナンシャルインスツルメント、条件、条件のエンター値を選択し、メッセージ内容を書きます。メッセージを繰り返し受け取りたくない場合は、「最大反復」に1 を入れます。すべてのフィールどを記入したら OKをクリックします。
MQL5プログラムからメッセージを送ると、もっとできることが広がります。SendMail() 関数を使うのです。それには2つパラメータがあります。第一のパラメータはタイトル、第二のパラメータはメッセージ本文です。
リクエストのあと (OrderSend() 関数)、またはTradeイベントハンドラ内でSendMail() 関数を呼ぶことができます。よって、取引きイベントの通知を得ることができるのです。 - マーケット入り、発注、ポジションクローズなど。OnTimer() 関数内にSendMail()を配置することも可能です。 - 現在クオートに関して定期的に通知を受け取ります。特定のトレードシグナルが現れるとき通知を送信する設定も可能です。インディケータが交わるとき、価格があるラインやレベルに到達するとき、など。
例をいくつか挙げてみます。
Expert Advisor またはご自身が置き換える「スクリプト」内にであれば
OrderSend(request,result};
以下を伴い
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);
そうすると、取引リクエスト後、 OrderSend() 関数がSendMail() 関数を使ってメッセージを送信します。それには、取引アカウントNo.、ブローカー名、処理内容(買い、売り、発注、注文の変更や削除)が以下のように含まれます。
59181-MetaQuotes Software Corp. Buy 0.1 EURUSD at price 1.23809, SL=1.2345, TP=1.2415
Expert Advisor また OnInit() 本文のインディケータ内であれば、 EventSetTimer() 関数(パラメータは秒単位のタイマー期間1つだけです。)を使ってタイマーを開始します。
void OnInit() { EventSetTimer(3600); }
OnDeinit() 内でEventKillTimer()を使用するとき切ることを忘れないようにします。
void OnDeinit(const int reason) { EventKillTimer(); }
OnTimer()内で、SendMail()を使ってメッセージ送信するのも忘れないようにします。
void OnTimer() { SendMail(Symbol(),DoubleToString(SymbolInfoDouble(Symbol(),SYMBOL_BID),_Digits)); }
そして、指定の期間の現ファイナンシャルインスツルメントの価格に関するメッセージを受け取ります。
おわりに
本稿は HTML と画像ファイルの作成にあたってのMQL5の使用法、またそれらをFTPを介して WWWサーバーにアップロードする方法につて述べています。また、 SMSとして携帯電話に通知を送信する構成方法についても述べてきました。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/61





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索