English Русский 中文 Español Deutsch Português 한국어 Français Italiano Türkçe
トレードレポートとSMS通知の作成と発行

トレードレポートとSMS通知の作成と発行

MetaTrader 5 | 5 10月 2015, 14:54
895 0
Denis Zyatkevich
Denis Zyatkevich

はじめに

本稿ではトレード結果レポートを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 レポート例 - 取引きとポジション表

図1 レポート例 - 取引きとポジション表

図2 レポート例 - 残高グラフ

図2 レポート例 - 残高グラフ

図3 レポート例 - 現インスツルメントの価格グラフ

図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を介してレポートを発行するオプション

図4 FTPを介してレポートを発行するオプション

オプションダイアログボックスで、オプション「有効」にチェックを入れ、アカウントナンバー、FTP アドレス、パス、ログイン、アクセスパスワードを指定する必要があります。間隔のリフレッシュは必要ありません。

これでスクリプトを実行することができます。実行後、残高表が画面に数秒間表示され、その後表示が消えます。「ジャーナル」内で、エラーがあるか見、ファイルが FTPによって送信されたか確認することができます。すべてうまくいっていれば、3種類の新規ファイルがサーバーの指定フォルダに表示されます。2つのファイルを矢印画像と共にそこに配置し、WWWサーバーが構成され動作していれば、ウェブブラウザを介してレポートを開くことができます。


2. 通知をSMSとして携帯電話へ送信

コンピュータやその他電気機器から離れている時間があり、手にしているのは携帯電話だけ、ということもあるでしょう。そういうときでも、アカウントの取引きを管理したり、ファイナンシャルインスツルメントのクオートを監視したいと思うものです。その場合、 SMS を介して通知を携帯電話に送信する設定が可能です。モバイル会社(および第三者)は EMAIL-SMS サービスを提供しており、それにより指定のeメールアドレスに送信されるメッセージをレターとして受け取ることができます。

おのためいは、 eメールボックスを所有する必要があります。(特にご自身の SMTP サーバーを知る必要があります。)MetaTrader 5 設定を調整します。ツール メニュー --> オプション --> eメールタブをひらく と進みます。(図5)

図5 eメールで通知送信を設定

図5 eメールで通知送信を設定

「有効」オプションにチェックを入れ、SMTP サーバーアドレス、ログインとパスワード、送信者アドレス(あなたのeメールアドレス)、受信者アドレスを指定します。SMSとしてメッセージを送信するのに使用するeメールアドレスです。(ご自身のモバイルオペレータを確認ください)すべてが正しければ、「テスト」ボタンをクリックすると、確認メッセージが送信されます。(「ジャーナル」にて追加情報を確認ください)

価格が一定レベルに到達したことを知る一番簡単な方法は、アラートを作成することです。このためには、適切な「ツールボックス」タブを開き、右クリックして「作成」を選択します。(図6)

図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

添付されたファイル |
report.zip (33.08 KB)
sendreport_en.mq5 (32.06 KB)
MQL5におけるトレーディング用コントロールパネルの作成 MQL5におけるトレーディング用コントロールパネルの作成
この記事は、MQL5のコントロールパネルの開発における問題を取り扱っています。インターフェイスは、イベントハンドリングによって管理されています。加えて、管理の柔軟なセットアップ方法が複数あります。コントロールパネルは、ポジションを扱い、また、設定、修正、削除や、未決注文も管理します。
異なる国での異なるタイムゾーンに基づくトレーディング戦略例 異なる国での異なるタイムゾーンに基づくトレーディング戦略例
インターネット検索をしていると、多くの戦略を見つけるのはたやすいことです。そこから多様な提案を得ることができます。インサイダーの方法を採り入れ、異なる大陸の異なるタイムゾーンに基づく戦略作成の手順を見ていきます。
MQL5でICQを用いたExpert Advisorの連携 MQL5でICQを用いたExpert Advisorの連携
本稿は、Expert Advisor と ICQ ユーザー間の情報交換について述べていきます。いくつかの例を提供します。ICQ クライアントを使用し、携帯電話やPDAでクライアント端末から遠隔でトレーディング情報を受け取りたい方には興味を引かれる資料を提供することとなるでしょう。
遺伝的アルゴリズム - とても簡単です! 遺伝的アルゴリズム - とても簡単です!
この記事では、執筆者は遺伝的アルゴリズムを使用した進化計算について紹介しています。例を用いながらアルゴリズムの機能について紹介し、実用的な推奨される用例を提示しています。