
チャート上で取引を視覚化する(第2回):データのグラフ表示
はじめに
この記事では、「チャート上で取引を視覚化する(第1回):分析期間の選択」稿で実装を開始したチャート上での取引の視覚化スクリプトを完成させます。ユーザーが選択した単一の取引のデータを選択するコードと、チャート上に必要なデータオブジェクトを描画するコードを記述し、対応するチャートの印刷画面としてファイルに保存します。このスクリプトを使用すると、取引グラフの形成に関連する技術的な作業や、遡及分析のために印刷画面に保存する作業にかかる時間を大幅に節約できます。 プロジェクトの組み立てに時間を費やしたくない場合は、マーケットでスクリプトの既製バージョンをダウンロードできます。
1つの取引のデータを選択する
特定の期間の取引データを選択する場合とは異なり、単一の取引データを選択する場合、履歴注文選択の実装は大幅に簡素化されます。主な違いは、定義済みの端末関数HistorySelect()の代わりにHistorySelectByPosition()メソッドを使用して履歴データを要求することです。メソッドパラメータは、MetaTrader 5端末([表示]>[ツールボックス]>[履歴]>[チケット列])で確認できるPOSITION_IDENTIFIERを受け取る必要があります。このPOSITION_IDENTIFIERの値は、スクリプト内で定義された入力用のグローバル変数inp_d_ticketを通じて渡されます。
その他のロジックに関しては、Select_one_dealケースの実装は、特定期間のデータ選択ロジックをほぼそのまま踏襲しています。ユーザーに表示される情報も同様であり、その完全なコード例が以下に示されています。
//--- if one deal is needed case Select_one_deal: res = MessageBox("You have selected analysis of one deal. Continue?","",MB_OKCANCEL); // informed in the message if(res == IDCANCEL) // if interrupted by user { printf("%s - %d -> Scrypt was stoped by user.",__FUNCTION__,__LINE__); // informed in the journal return; // interrupted } MessageBox("Please press 'Ok' and wait for the next message until script will be done."); // informed in the message //--- select by one position if(HistorySelectByPosition(inp_d_ticket)) // select position by id { int total = HistoryDealsTotal(); // total deals if(total <= 0) // if nothing found { printf("%s - %d -> Deal was not found.",__FUNCTION__,__LINE__); // notify MessageBox("Deal was not found with this tiket: "+IntegerToString(inp_d_ticket)+". Script is done."); // informed in the message return; } for(int i=0; i<total; i++) // iterate through the number of deals { //--- try to get deals ticket if((ticket=HistoryDealGetTicket(i))>0) // took the deal number { //--- get deals properties position_id = HistoryDealGetInteger(ticket,DEAL_POSITION_ID); // took the main id entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket,DEAL_ENTRY);// entry or exit? if(entry == DEAL_ENTRY_IN) // if this is an entry { open = HistoryDealGetDouble(ticket,DEAL_PRICE); // take open price time_open =(datetime)HistoryDealGetInteger(ticket,DEAL_TIME); // take open time symbol=HistoryDealGetString(ticket,DEAL_SYMBOL); // take symbol stop_loss = HistoryDealGetDouble(ticket,DEAL_SL); // take Stop Loss take_profit = HistoryDealGetDouble(ticket,DEAL_TP); // take Take Profit //--- magic = (int)HistoryDealGetInteger(ticket,DEAL_MAGIC); // take Magic comment=HistoryDealGetString(ticket,DEAL_COMMENT); // take comment externalID=HistoryDealGetString(ticket,DEAL_EXTERNAL_ID); // take external id volume = HistoryDealGetDouble(ticket,DEAL_VOLUME); // take volume commission = HistoryDealGetDouble(ticket,DEAL_COMMISSION); // take commission value } if(entry == DEAL_ENTRY_OUT) // if this is an exit { close = HistoryDealGetDouble(ticket,DEAL_PRICE); // take close price time_close =(datetime)HistoryDealGetInteger(ticket,DEAL_TIME);// take close time //--- reason = (ENUM_DEAL_REASON)HistoryDealGetInteger(ticket,DEAL_REASON); // take reason swap = HistoryDealGetDouble(ticket,DEAL_SWAP); // take swap profit = HistoryDealGetDouble(ticket,DEAL_PROFIT); // take profit fee = HistoryDealGetDouble(ticket,DEAL_FEE); // take fee } //--- enter data into the main storage //--- check if there is such id if(Find(PositionID,position_id)==-1) // if there is no such deal, { //--- change the dimensions of the arrays ArrayResize(arr_time_open,ArraySize(arr_time_open)+1); // open time ArrayResize(arr_time_close,ArraySize(arr_time_close)+1); // close time ArrayResize(arr_symbol,ArraySize(arr_symbol)+1); // symbols ArrayResize(arr_stop_loss,ArraySize(arr_stop_loss)+1); // stop levels ArrayResize(arr_take_profit,ArraySize(arr_take_profit)+1);// profits ArrayResize(arr_open,ArraySize(arr_open)+1); // entries ArrayResize(arr_close,ArraySize(arr_close)+1); // exits ArrayResize(PositionID,ArraySize(PositionID)+1); // position id //--- ArrayResize(arr_magic,ArraySize(arr_magic)+1); // Magic ArrayResize(arr_extermalID,ArraySize(arr_extermalID)+1); // external id ArrayResize(arr_comment,ArraySize(arr_comment)+1); // comment ArrayResize(arr_volume,ArraySize(arr_volume)+1); // volume ArrayResize(arr_commission,ArraySize(arr_commission)+1); // commission ArrayResize(arr_reason,ArraySize(arr_reason)+1); // reason ArrayResize(arr_swap,ArraySize(arr_swap)+1); // swap ArrayResize(arr_profit,ArraySize(arr_profit)+1); // profit ArrayResize(arr_fee,ArraySize(arr_fee)+1); // fee PositionID[ArraySize(arr_time_open)-1]=position_id; // id if(entry == DEAL_ENTRY_IN) // if this is an entry { arr_time_open[ ArraySize(arr_time_open)-1] = time_open; // deal time arr_symbol[ ArraySize(arr_symbol)-1] = symbol; // instrument symbol arr_stop_loss[ ArraySize(arr_stop_loss)-1] = stop_loss; // deal stop loss arr_take_profit[ ArraySize(arr_take_profit)-1] = take_profit; // deal take profit arr_open[ ArraySize(arr_open)-1] = open; // open price //--- arr_magic[ ArraySize(arr_magic)-1] = magic; // Magic arr_comment[ ArraySize(arr_comment)-1] = comment; // comment arr_extermalID[ ArraySize(arr_extermalID)-1] = externalID; // external id arr_volume[ ArraySize(arr_volume)-1] = volume; // volume arr_commission[ ArraySize(arr_commission)-1] = commission; // commission } if(entry == DEAL_ENTRY_OUT) // if this is an exit { arr_time_close[ ArraySize(arr_time_close)-1] = time_close; // close time arr_close[ ArraySize(arr_close)-1] = close; // close prices //--- arr_reason[ ArraySize(arr_reason)-1] = reason; // reason arr_swap[ ArraySize(arr_swap)-1] = swap; // swap arr_profit[ ArraySize(arr_profit)-1] = profit; // profit arr_fee[ ArraySize(arr_fee)-1] = fee; // fee } } else { int index = Find(PositionID,position_id); // if there was a record already, if(entry == DEAL_ENTRY_IN) // if this was an entry { arr_time_open[index] = time_open; // deal time arr_symbol[index] = symbol; // symbol arr_stop_loss[index] = stop_loss; // deal stop loss arr_take_profit[index] = take_profit; // deal take profit arr_open[index] = open; // open price //--- arr_magic[index] = magic; // Magic arr_comment[index] = comment; // comment arr_extermalID[index] = externalID; // external id arr_volume[index] = volume; // volume arr_commission[index] = commission; // commission } if(entry == DEAL_ENTRY_OUT) // if this is an exit { arr_time_close[index] = time_close; // deal close time arr_close[index] = close; // deal close price //--- arr_reason[index] = reason; // reason arr_swap[index] = swap; // swap arr_profit[index] = profit; // profit arr_fee[index] = fee; // fee } } } } } else { printf("%s - %d -> Error of selecting history deals: %d",__FUNCTION__,__LINE__,GetLastError()); // informed in the journal printf("%s - %d -> Deal was not found.",__FUNCTION__,__LINE__); // informed in the journal MessageBox("Deal was not found with this tiket: "+IntegerToString(inp_d_ticket)+". Script is done."); // informed in the message return; } break;
両方のオプションが説明され、プログラム実行中に必要なデータがすべてのストレージに入力されたので、このデータを端末チャートに表示できるようになります。
必要なチャートを表示する
チャートに取引を保存するには、まずプログラムレベルで必要な銘柄を含む新しいウィンドウを開き、取引全体が明確に表示されるように右側のインデントの個別のシフトを含む必要なデザイン設定をおこない、必要なフォルダに印刷画面を保存する定義済み関数を呼び出す必要があります。
まず、目的のチャートのウィンドウを開くために必要なローカル変数を宣言しましょう。bars変数には右側のチャートのオフセット値が保存され、chart_widthおよびchart_height変数には保存用の対応するサイズが保存され、新しいチャートが開かれると、そのハンドルがhandle変数に保存され、将来チャートにアクセスできるようになります。
//--- data collected, moving on to printing int bars = -1; // number of bars in a shift int chart_width = -1; // chart width int chart_height =-1; // chart height long handle =-1; // chart handle
新しい銘柄ウィンドウを開く要求を開始する前に、履歴からこれらの銘柄の有効性を要求する必要があります。このチェックは、口座に「存在しない銘柄」を開くというエラーを回避するために絶対に必要です。ここで、「存在しない銘柄」が取引履歴に保存されている場合、つまりかつて存在していた場合、その銘柄がどこから来るのかを説明する必要があると思います。
まず第一に、これはブローカーの口座タイプに関連している可能性があります。現在、多くのブローカーは、トレーダーの取引戦略に応じて収益性と利便性を最大限に高めるため、複数の口座オプションを提供しています。一部の口座では取引に手数料が発生しますが、スプレッドが非常に低く設定されています。一方で、他の口座タイプではスプレッドが高い代わりに取引ごとの手数料がかからない仕組みになっています。このような条件の違いにより、中期的な取引をおこなうトレーダーは手数料が発生しない口座を選ぶことが一般的です。中期取引ではスプレッドの大きさがそれほど重要ではないためです。一方で、日中の短期取引を頻繁におこなうトレーダーは、スプレッドの急な変動による損失を避けるために、取引手数料が発生する口座を選ぶ傾向があります。通常、ブローカーはこれらの条件を「スタンダード」「ゴールド」「プラチナ」「ECN」などの口座タイプとして分類し、各口座タイプ専用の銘柄名を割り当てます。例えば、スタンダード口座でのEURUSDペアの場合、別の口座タイプではEURUSDb、EURUSDz、あるいはEURUSD_iなどと表記されることがあります(ブローカーによって異なります)。
また、Forex取引においては、通貨ペア以外の特定の商品が有効期限に応じて銘柄名を変更する場合もあります。ただし、本記事は通貨ペアに特化しているため、この点についてはここでは詳しく扱いません。
銘柄の有効性を確認する必要があるもう1つの重要な条件は、端末の気配値表示ウィンドウにおいて、必要な銘柄がサブスクリプションされていない場合です。たとえ銘柄名が口座で承認されていても、端末のコンテキストメニュー([表示]>[気配値表示])で選択されていない場合、呼び出し関数でエラーが発生し、チャートを開くことができません。
チェックを実装するには、以下のようにストレージ内の各ツールを反復処理するループを配置することから始めます。
for(int i=0; i<ArraySize(arr_symbol); i++) // iterate through all deal symbols
コンテナに保存された銘柄の有効性を確認するには、定義済み端末関数SymbolSelect()を使用します。これに渡す最初のパラメータは、文字列形式の銘柄名です。これは、有効性を確認したい銘柄です。論理値「true」が2番目に来ます。2番目のパラメータとして「true」を渡すと、指定された銘柄が有効であるが気配値表示で選択されていない場合は、自動的に選択されることになります。完全なチェックロジックは次のようになります。
//--- check for symbol availability for(int i=0; i<ArraySize(arr_symbol); i++) // iterate through all deal symbols { if(!SymbolSelect(arr_symbol[i],true)) // check if the symbol is in the book and add if not { printf("%s - %d -> Failed to add a symbol %s to the marketbook. Error: %d", __FUNCTION__,__LINE__,arr_symbol[i],GetLastError()); // informed in the journal MessageBox("Failed to add a symbol to the marketbook: "+arr_symbol[i]+ ". Please select 'show all' in the your market book and try again. Script is done."); // informed in the message return; // if failed, abort } }
したがって、銘柄の有効性チェックに合格しない場合は、ユーザーに適切な通知を送信してプログラムの実行を終了します。すべての有効性チェックに合格したら、必要な銘柄チャートを端末で直接開くことができます。
まず、MqlDateTimeデータ型のdeal_close_date補助変数を指定します。これにより、保存されたすべてのチャートを対応する期間フォルダに簡単に並べ替えることができます。ストレージ内のdatetimeデータ型をMqlDateTimeデータ型に明示的に縮小するには、以下に示すように、定義済みの端末関数TimeToStruct()を使用します。
MqlDateTime deal_close_date; // deal closure date in the structure TimeToStruct(arr_time_close[i],deal_close_date); // pass date to the structure
チャートは、main_graph、addition_graph、addition_graph_2、addition_graph_3変数内のユーザー定義データに従って描画されます。変数にPERIOD_CURRENT列挙値が含まれている場合、チャートは描画されません。変数に特定の値(たとえばPERIOD_D1)を入力すると、このチャートが描画されます。次の形式で入力されたすべての変数に対してこのチェックを実行します(メイン変数は例として以下に示されています)。
//--- check the main one if(main_graph != PERIOD_CURRENT) // if the main one selected
各チャートの描画は、必要な銘柄の新しいチャートを開くことから始まります。以下に示すように、必要な銘柄と時間枠をストレージから渡しながら、ChartOpen()定義済み端末関数を使用して銘柄チャートが開かれます。
//--- open the required chart handle = ChartOpen(arr_symbol[i],main_graph); // open the necessary symbol chart
チャートが開いたら、上で説明したすべての標準ユーザー設定を適用します。これをおこなうには、ChartApplyTemplate()定義済み端末関数を使用します。これは非常に役立ち、自分でコードを記述する必要がなくなります。ChartApplyTemplate()関数のパラメータは、ChartOpen()関数の呼び出しから取得されたチャートハンドルと、取引時間枠に対してユーザーがdailyHistorytemp形式で指定したテンプレートの名前を取得します。テンプレートアプリケーション関数を呼び出すコードを以下に示します。
ChartApplyTemplate(handle,main_template); // apply template
ここで、これまでMetaTrader 5端末でテンプレートを使用したことがない人のために、少し余談をしておきましょう。「見苦しい」テンプレートを使用すると、取引の保存された印刷画面が「不快」なもの、あるいは「役に立たないもの」になる可能性があります。独自のdailyHistorytempテンプレートを作成するには、次の手順に従います。
- [ファイル]-[新規チャート]から任意の銘柄のチャートを開きます。
- チャートが開いたら、F8キーを押してプロパティウィンドウ(例:「PropertiesGBPAUD,Daily」)を開きます。
- プロパティウィンドウには[共通]、[表示]、[色]タブがあります。それぞれで、たとえば日次チャートなど、より使い慣れた設定を行い、[OK]をクリックします。詳細はチャート設定(公式端末ヘルプ)をご覧ください。
- [OK]をクリックすると、プロパティウィンドウが閉じられ、チャートは必要な形式になります。
- 次に、コンテキストメニューで、[チャート]>[テンプレート]>[テンプレートの保存]を選択します。テンプレート保存ウィンドウが表示され、テンプレート保存ウィンドウが表示されます。[ファイル名]にdailyHistorytemp.tplと入力し、[保存]をクリックします。
- その後、..MQL5\Profiles\Templates端末フォルダにdailyHistorytemp.tplファイルが作成され、スクリプトで使用できるようになります。注目すべき主な点は、テンプレート名が「.tpl」拡張子なしでスクリプトに入力されることです。
さて、コードに戻りましょう。必要なテンプレートが適用されたら、チャートが目的の品質でロードされる時間を確保するために、コードの実行を少し遅らせる必要があります。そうしないと、必要な過去の価格データを端末に読み込むのに時間がかかるため、チャートが正しく表示されない可能性があります。たとえば、チャートをしばらく開いていない場合は、端末でチャートを正しく表示するには時間がかかります。以下に示すように、定義済み端末関数Sleep()を通じて時間遅延を通知します。
Sleep(2000); // wait for the chart to load
遅延としては、純粋に実践から得た2000ミリ秒または2秒の値を使用します。これにより、チャートがロードする時間が保証され、大量の取引でスクリプトの実行に長時間かかることがなくなります。この値を自分でカスタマイズするには、スクリプト設定にこの値を個別に入力して、機器やインターネット接続のパフォーマンスに応じてプロセスを高速化または低速化することができます。実践が示すように、ほとんどの場合、2秒で十分です。
ここでは、履歴を分析しており、チャートを常に右にシフトするための新しいティックは必要ないため、チャートを最新のバー値にスクロールするのを無効にする必要があります。これは、以下に示すように、ChartSetInteger()定義済み関数を使用して、必要なチャートのCHART_AUTOSCROLLプロパティをfalseに設定することで実行できます。
ChartSetInteger(handle,CHART_AUTOSCROLL,false); // disable auto scroll
自動スクロールが無効になったので、まず、該当する取引を終了した期間の履歴に向かってチャートをシフトするために、左側の対応する時間枠のチャート上のバーの数を数える必要があります。最初から最後までの取引全体を印刷画面に表示したいので、銘柄、チャートの時間枠、取引終了時間をパラメータとして渡し、iBarShift()定義済み端末関数を介して値を取得できます。exactパラメータでは、履歴が非常に深い場合に備えてfalseを渡します。ただし、この場合の実装ではそれほど重要ではありません。パラメータを含む完全なメソッド呼び出しを以下に示します。
bars = iBarShift(arr_symbol[i],main_graph,arr_time_close[i],false); // get the shift for the deal time
必要なチャートシフトがわかれば、履歴の中で必要な取引を捉える期間を正確に表示できます。以下に示すように、ChartNavigate()定義済み端末変数に次のパラメータを渡すことで、チャートを必要な距離だけ目的の方向にシフトできます。
ChartNavigate(handle,CHART_CURRENT_POS,-bars+bars_from_right_main); // shifted the chart with a custom margin
チャートをシフトするために、チャートハンドル、ENUM_CHART_POSITION列挙のCHART_CURRENT_POSの現在のポジション値、および、ポジションを終了した後の価格変動の可能性を評価するためにユーザーが入力したオフセットを含むbars変数で以前に取得した取引へのシフトを渡しました。
説明したチャート変換の後、念のためChartRedraw()メソッドを呼び出し、チャートに追加のデータを描画して履歴取引を分析します。
ポジションの開始と終了、およびストップロスとテイクプロフィットのレベルを示すカスタム情報パネル要素と線を描画するには、対応するpaintDeal()およびpaintPanel()カスタム関数を使用します。端末チャートを操作するための標準的な動作パターンに基づいて、paintDeal()が取引の開始価格と終了価格、およびテイクプロフィットとストップロスの線を描画するのを定義します。一方、paintPanel()メソッドには、画面の隅に完全な取引情報を含むテーブルが含まれます。
メソッドの詳細な定義については、次のセクションで説明します。ここでは、メソッドがこのコードセグメントで呼び出されることを単に示します。これは、これらの2つの要素グループを描画するために必ずしもこの記事で示した実装を使用する必要はないという観点からも行われます。希望する署名を維持しながら、自分で再定義することができます。本記事で紹介したメソッドの実装は、コードに書き込んだ時点でのグラフィックスの美しさと情報量の最適な比率の例です。ここでの主な目的は、メインコード内でのメソッド呼び出しの位置を維持することです。
//--- draw the deal paintDeal(handle,PositionID[i],arr_stop_loss[i],arr_take_profit[i],arr_open[i],arr_close[i],arr_time_open[i],arr_time_close[i]); //--- draw the information panel paintPanel(handle,PositionID[i],arr_stop_loss[i],arr_take_profit[i],arr_open[i], arr_close[i],arr_time_open[i],arr_time_close[i],arr_magic[i],arr_comment[i], arr_extermalID[i],arr_volume[i],arr_commission[i],arr_reason[i],arr_swap[i], arr_profit[i],arr_fee[i],arr_symbol[i],(int)SymbolInfoInteger(arr_symbol[i],SYMBOL_DIGITS));
メソッドによってチャート上に取引ラインと情報パネルが描画されたら、現在のチャートで発生したすべての内容の印刷画面を保存する実装に進むことができます。これをおこなうには、以下に示すように、再定義されたChartSetInteger()端末関数を使用して、開いているチャートからこのデータを要求することによって、印刷画面の幅と高さの将来の寸法を最初に決定します。
//--- get data by screen size chart_width = (int) ChartGetInteger(handle,CHART_WIDTH_IN_PIXELS); // look at the chart width chart_height = (int) ChartGetInteger(handle,CHART_HEIGHT_IN_PIXELS); // look at the chart height
チャートを表示するための対応するパラメータとして、幅CHART_WIDTH_IN_PIXELSと高さCHART_HEIGHT_IN_PIXELSにそれぞれhttps://www.mql5.com/ja/docs/constants/chartconstants/enum_chart_property#enum_chart_property_integer列挙値を渡しました。
サイズデータを受け取ったら、標準の端末フォルダに取引の印刷画面チャートを保存するためのパスを作成する必要があります。EAがすべてのファイルを1つのフォルダに配置するのを防ぎ、代わりにユーザーの利便性のためにファイルを並べ替えるために、次の文字列のファイル名を通じてこのプロセスを自動化します。
string name_main_screen = brok_name+"/"+ IntegerToString(account_num)+"/"+ IntegerToString(deal_close_date.year)+"-"+IntegerToString(deal_close_date.mon)+ "-"+IntegerToString(deal_close_date.day)+"/"+ IntegerToString(PositionID[i])+"/"+ EnumToString(main_graph)+IntegerToString(PositionID[i])+".png"; // assign the name
グラフィカルに言えば、標準ディレクトリ内のフォルダにファイルを分類する構造は、図1のようになります。
図1:取引ごとに保存された印刷画面のフォルダアドレスの構造
ご覧のとおり、チャートファイルはブローカー名、口座番号、実行年、月、日で並べ替えられるため、ユーザーは1つの一般的なリストでファイル名を探すことなく、目的の取引を簡単に見つけることができます。異なる時間枠は、端末の対応するポジション番号のフォルダに配置されます。
ChartScreenShot()定義済み端末関数を呼び出して情報を直接保存し、必要なチャートのハンドル、チャートのサイズに対応する先ほど取得した印刷画面のサイズ、およびフォルダアドレスの構造全体を含むファイルの名前を、図1および以下のコードに示すようにパラメータとして渡します。
ChartScreenShot(handle,name_main_screen,chart_width,chart_height,ALIGN_LEFT); // make a screenshot
階層で指定されたフォルダが標準の端末フォルダに存在しない場合は、ユーザーの介入なしに端末によって自動的に作成されます。
ファイルを保存した後、特にダウンロードに口座の過去の取引が多数含まれている場合は、端末のビューが乱雑にならないようにチャートを閉じることができます。不要なものを閉じないように、必要なチャートのハンドルを渡すChartClose()定義済み端末関数を使用してチャートを閉じます。関数呼び出しを以下に示します。
ChartClose(handle); // closed the chart
ユーザーが入力で指定したすべての時間枠に対して同様にこの操作を繰り返します。ここで、スクリプトを完成させるために、メインプログラムコードの外部でpaintDeal()メソッドとpaintPanel()メソッドの動作を定義する必要があります。
チャート上にデータオブジェクトを描画する
印刷画面のチャートに情報を便利に配置するには、ユーザーが必要とするデータがどのように描画されるかを決定する2つのメソッドを再定義するだけで済みます。
まず、paintDeal()メソッドの説明から始めましょう。その目的は、始値と終値、ストップロス、および利益確定ポジションの位置に関連付けられたポジションのグラフィックを描画することです。これをおこなうには、メインコード本体の外側で次のシグネチャを使用してメソッドの説明を宣言します。
void paintDeal(long handlE, ulong tickeT, double stop_losS, double take_profiT, double opeN, double closE, datetime timE, datetime time_closE)
メソッドパラメータでは、次handlelE(描画するチャートのハンドル)、ticketT(取引チケット)、stop_losS(存在する場合はストップロスの価格)、take_profiT(存在する場合はテイクプロフィットの価格)、opeN(始値)、closE(終値)、timeE(取引開始時刻)、time_closE(取引終了時刻)の値が指定されます。
まず、重複しない一意の名前に対応するオブジェクトの名前から描画を始めましょう。したがって、名前では、このオブジェクトが「name_sl_」の形式でストップに対応する機能を実装します。名前を一意にするために、以下に示すように、取引のチケット番号も追加します。
string name_sl = "name_sl_"+IntegerToString(tickeT); // assign the name
ここで、チャート上の履歴ポジションによってストップロスレベルを描画するObjectCreate()定義済み端末関数を使用して、グラフィックオブジェクト自体を作成できます。渡されるパラメータは、チャートのハンドルとname_sl変数からの一意の名前です。OBJ_ARROW_LEFT_PRICE値をオブジェクト型として指定します。これは、以下に示すように、ENUM_OBJECT列挙からの左の価格ラベル、実際の価格値、およびラベルがチャートに配置された時刻を意味します。
ObjectCreate(handlE,name_sl,OBJ_ARROW_LEFT_PRICE,0,timE,stop_losS); // create the left label object
オブジェクトが作成されたので、OBJPROP_COLORフィールドとOBJPROP_TIMEFRAMESフィールドの値を設定しましょう。ストップロスは通常赤色で表示されるため、OBJPROP_COLORをclrRedに設定し、OBJPROP_TIMEFRAMESをOBJ_ALL_PERIODSに設定してすべての時間枠で表示します。ただし、この実装では2番目の条件は重要ではありません。一般的に、ストップロス描画ブロックは次のようになります。
//--- draw stop loss string name_sl = "name_sl_"+IntegerToString(tickeT); // assign the name ObjectCreate(handlE,name_sl,OBJ_ARROW_LEFT_PRICE,0,timE,stop_losS); // create the left label object ObjectSetInteger(handlE,name_sl,OBJPROP_COLOR,clrRed); // add color ObjectSetInteger(handlE,name_sl,OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS); // set visibility ChartRedraw(handlE); // redraw
各ブロックを描画した後、ChartRedraw()メソッドを呼び出します。
テイクプロフィットブロックの描画は、以下の例外を除いてストップロスの描画と似ています。まず、一意のオブジェクト名に「name_tp_」と取引チケットを追加し、受け取った利益の従来の指定に対応する緑色のパレットから、clrLawnGreen色を介して色を設定します。それ以外の点では、ロジックはストップロスブロックに似ており、ここで完全に紹介されています。
//--- draw take profit string name_tp = "name_tp_"+IntegerToString(tickeT); // assign the name ObjectCreate(handlE,name_tp,OBJ_ARROW_LEFT_PRICE,0,timE,take_profiT); // create the left label object ObjectSetInteger(handlE,name_tp,OBJPROP_COLOR,clrLawnGreen); // add color ObjectSetInteger(handlE,name_tp,OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS); // set visibility ChartRedraw(handlE); // redraw
左の価格ラベルを介してエントリ価格を描画する実装に移りましょう。前のブロックとの違いは、まず第一に、オブジェクトの一意の名前にあります。先頭に「name_open_」を追加します。もう1つの違いはclrWhiteSmokeの線の色です。この色はグラフ上で目立ちすぎませんが、それ以外はすべて同じです。
//--- draw entry price string name_open = "name_open_"+IntegerToString(tickeT); // assign the name ObjectCreate(handlE,name_open,OBJ_ARROW_LEFT_PRICE,0,timE,opeN); // create the left label object ObjectSetInteger(handlE,name_open,OBJPROP_COLOR,clrWhiteSmoke); // add color ObjectSetInteger(handlE,name_open,OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS); // set visibility ChartRedraw(handlE); // redraw
取引の開始価格ラベルと終了価格ラベルを結ぶ線は同じ色で表示されます。線の種類が異なります。ObjectCreate()メソッドパラメータでオブジェクトを作成するときに、トレンドラインを作成するために、ENUM_OBJECT列挙のOBJ_TREND値を3番目のパラメータとして渡します。チャート上にトレンドラインを正しく配置するには、2つのポイントの位置に関する追加のパラメータを指定する必要があります。各ポイントには、価格と時間という2つの属性があります。これをおこなうには、以下に示すように、始値と終値(opeNとclosE)を、終値と始値(timeEとtime_closE変数)とともに後続のパラメータに渡します。
//--- deal line string name_deal = "name_deal_"+IntegerToString(tickeT); // assign the name ObjectCreate(handlE,name_deal,OBJ_TREND,0,timE,opeN,time_closE,closE); // create the left label object ObjectSetInteger(handlE,name_deal,OBJPROP_COLOR,clrWhiteSmoke); // add color ObjectSetInteger(handlE,name_deal,OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS); // set visibility ChartRedraw(handlE); // redraw
チャート上に取引を完全に表示するには、取引を終了するための価格ラベルを描画する必要があります。これを実現するために、適切な価格ラベルを使用して、情報がより視覚的に見やすい形で印刷画面に表示されるようにします。正しいラベルを描画するには、ObjectCreate()メソッドは3番目のパラメータとしてOBJ_ARROW_RIGHT_PRICE値(ENUM_OBJECT列挙からの正しい価格ラベルを意味する)を受け取る必要があります。残りの描画では価格と時間だけが必要で、以下に示すように対応するtime_closE、closE変数を介して渡します。
//--- draw exit price string name_close = "name_close"+IntegerToString(tickeT); // assign the name ObjectCreate(handlE,name_close,OBJ_ARROW_RIGHT_PRICE,0,time_closE,closE);// create the left label object ObjectSetInteger(handlE,name_close,OBJPROP_COLOR,clrWhiteSmoke); // add color ObjectSetInteger(handlE,name_close,OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS); // set visibility ChartRedraw(handlE); // redraw
これで、ポジションのエントリラインとエグジットラインを描画するためのカスタムpaintDeal()メソッドの説明は完了です。ここで、paintPanel()メソッドで完全な取引情報のパネルを描画する方法の説明に進みます。
パネルを描画する方法を説明するには、OBJ_LABELタイプのテキストラベル、ENUM_OBJECT列挙、およびユーザーグラフィカルインターフェイスを作成および設計するためのOBJ_RECTANGLE_LABELオブジェクトなど、テキストラベルの描画を担当するより複雑なメソッド構造が必要になります。テキストラベルを作成するためのLabelCreate()と長方形のラベルを作成するためのRectLabelCreate()という対応するカスタムメソッドを宣言しましょう。まず補助メソッドの説明から始め、次に補助メソッドを使用するメインのpaintPanel()メソッドの説明に移ります。
一般に、スクリプトメソッドの構造は図2のようになります。
図2:グラフィックスを描画するためのカスタムメソッドの構造
次のシグネチャをパラメータとしてLabelCreate()メソッドを宣言します。
bool LabelCreate(const long chart_ID=0, // chart ID const string name="Label", // label name const int sub_window=0, // subwindow number const long x=0, // X coordinate const long y=0, // Y coordinate const ENUM_BASE_CORNER corner=CORNER_LEFT_UPPER, // chart corner for anchoring const string text="Label", // text const string font="Arial", // font const int font_size=10, // font size const color clr=clrRed, // color const double angle=0.0, // text angle const ENUM_ANCHOR_POINT anchor=ANCHOR_LEFT_UPPER, // anchor type const bool back=false, // in the background const bool selection=false, // select to move const bool hidden=true, // hidden in the list of objects const long z_order=0) // priority for clicking with a mouse
chart_IDパラメータは、オブジェクトを描画する必要があるチャートのハンドルを受け取ります。「name」は一意のオブジェクト名であり、sub_windowパラメータの値が0の場合は、オブジェクトをメインチャートウィンドウに描画することを意味します。オブジェクトの左上隅の座標は、それぞれXパラメータとYパラメータを介して渡されます。対応する値を「corner」パラメータに渡すことで、オブジェクトのコーナーとチャートのバインディングを標準の左コーナーから変更できますが、ここではデフォルト値のANCHOR_LEFT_UPPERのままにします。表示する情報の文字列値をtextパラメータに渡します。フォントの種類やサイズ、色、角度などの表示タイプは、対応する「font」、「font_size」、「clr」、「angle」パラメータで渡されます。また、「selection」および「hidden」パラメータを使用して、オブジェクトをユーザーのオブジェクトリスト内で非表示にし、マウスで選択できないようにします。z_orderパラメータは、マウスクリックの優先順位を決定します。
メソッドの説明を始める前に、まずエラー変数をリセットして、定義済み端末関数ResetLastError()を通じて将来オブジェクトを作成する結果を正しく制御できるようにします。OBJ_LABEL型のオブジェクト作成結果は、以下に示すように、ObjectCreate()関数を呼び出すときにif'論理演算子によって処理されます。オブジェクトが作成されない場合は、EAログでそのことをユーザーに通知し、通常どおりreturnステートメントを介してメソッドの実行を中断します。
//--- reset the error value ResetLastError(); //--- create a text label if(!ObjectCreate(chart_ID,name,OBJ_LABEL,sub_window,0,0)) { Print(__FUNCTION__, ": failed to create the text label! Error code = ",GetLastError()); return(false); }
オブジェクトが正常に作成された場合は、ObjectSetInteger()、ObjectSetString()、およびObjectSetDouble()の定義済み端末関数を使用してオブジェクトのプロパティフィールドを初期化し、必要な外観を実現します。ObjectSetInteger()関数を使用して、対応する座標、オブジェクトアンカー角度、フォントサイズ、オブジェクトアンカーメソッド、色、表示モード、およびユーザーに対するオブジェクトの可視性に関連するプロパティの値を設定します。ObjectSetDouble()関数を使用してフォント角度の値を設定し、ObjectSetString()関数を使用して渡されたテキストの内容と表示するフォントタイプを定義します。メソッド本体の完全な実装を以下に示します。
//--- reset the error value ResetLastError(); //--- create a text label if(!ObjectCreate(chart_ID,name,OBJ_LABEL,sub_window,0,0)) { Print(__FUNCTION__, ": failed to create the text label! Error code = ",GetLastError()); return(false); } //--- set label coordinates ObjectSetInteger(chart_ID,name,OBJPROP_XDISTANCE,x); ObjectSetInteger(chart_ID,name,OBJPROP_YDISTANCE,y); //--- set the chart's corner, relative to which point coordinates are defined ObjectSetInteger(chart_ID,name,OBJPROP_CORNER,corner); //--- set the text ObjectSetString(chart_ID,name,OBJPROP_TEXT,text); //--- set the text font ObjectSetString(chart_ID,name,OBJPROP_FONT,font); //--- set font size ObjectSetInteger(chart_ID,name,OBJPROP_FONTSIZE,font_size); //--- set the text angle ObjectSetDouble(chart_ID,name,OBJPROP_ANGLE,angle); //--- set anchor type ObjectSetInteger(chart_ID,name,OBJPROP_ANCHOR,anchor); //--- set the color ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr); //--- display in the foreground (false) or background (true) ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back); //--- enable (true) or disable (false) the mode of moving the label by mouse ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection); ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection); //--- hide (true) or display (false) graphical object name in the object list ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden); //--- set the priority for receiving the event of a mouse click on the chart ObjectSetInteger(chart_ID,name,OBJPROP_ZORDER,z_order); //--- successful execution return(true);
オブジェクト作成パラメータとして次のシグネチャを持つRectLabelCreate()メソッドを宣言します。
bool RectLabelCreate(const long chart_ID=0, // chart ID const string name="RectLabel", // label name const int sub_window=0, // subwindow number const int x=19, // X coordinate const int y=19, // Y coordinate const int width=150, // width const int height=20, // height const color back_clr=C'236,233,216', // background color const ENUM_BORDER_TYPE border=BORDER_SUNKEN, // border type const ENUM_BASE_CORNER corner=CORNER_LEFT_UPPER, // chart corner for anchoring const color clr=clrRed, // flat border color (Flat) const ENUM_LINE_STYLE style=STYLE_SOLID, // flat border style const int line_width=1, // flat border width const bool back=true, // 'true' in the background const bool selection=false, // select to move const bool hidden=true, // hidden in the list of objects const long z_order=0) // priority for clicking with a mouse
RectLabelCreate()メソッドのパラメータは、前のメソッドオブジェクトのデータを表示するための背景として機能する長方形のラベル境界の追加設定を除いて、以前に宣言したLabelCreate()メソッドのパラメータと非常に似ています。オブジェクトの境界線を構成するための追加パラメータは、border(ENUM_BORDER_TYPE列挙体で定義される境界線タイプで、デフォルト値はBORDER_SUNKEN)、style(ENUM_LINE_STYLE列挙体で定義される境界線スタイルで、デフォルト値はSTYLE_SOLID)、line_width(境界線の線幅(整数値))です。
メソッド本体の定義は前のものと似ており、同様に、以下に示すように、オブジェクトの作成と、対応する定義済み端末メソッドによるそのプロパティの定義という2つのグローバルセクションで構成されます。
//--- reset the error value ResetLastError(); // reset error //--- create a rectangle label if(ObjectCreate(chart_ID,name,OBJ_RECTANGLE_LABEL,sub_window,0,0)) // create object { //--- set label coordinates ObjectSetInteger(chart_ID,name,OBJPROP_XDISTANCE,x); // assign x coordinate ObjectSetInteger(chart_ID,name,OBJPROP_YDISTANCE,y); // assign y coordinate //--- set label size ObjectSetInteger(chart_ID,name,OBJPROP_XSIZE,width); // width ObjectSetInteger(chart_ID,name,OBJPROP_YSIZE,height); // height //--- set the background color ObjectSetInteger(chart_ID,name,OBJPROP_BGCOLOR,back_clr); // background color //--- set border type ObjectSetInteger(chart_ID,name,OBJPROP_BORDER_TYPE,border); // border type //--- set the chart corner, relative to which point coordinates are defined ObjectSetInteger(chart_ID,name,OBJPROP_CORNER,corner); // anchor corner //--- set flat border color (in Flat mode) ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr); // frame //--- set flat border line style ObjectSetInteger(chart_ID,name,OBJPROP_STYLE,style); // style //--- set flat border width ObjectSetInteger(chart_ID,name,OBJPROP_WIDTH,line_width); // width //--- display in the foreground (false) or background (true) ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back); // default is background //--- enable (true) or disable (false) the mode of moving the label by mouse ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection); // is it possible to select ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection); // //--- hide (true) or display (false) graphical object name in the object list ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden); // is it visible in the list //--- set the priority for receiving the event of a mouse click on the chart ObjectSetInteger(chart_ID,name,OBJPROP_ZORDER,z_order); // no events //--- successful execution } return(true);
すべての補助メソッドについて説明したので、パネル全体を描画するメインメソッドの本体であるpaintPanel()を定義しましょう。入力には、以下に示すように、ユーザーに完全な取引情報を表示するために必要なフィールドが含まれます。
void paintPanel(long handlE, ulong tickeT, double stop_losS, double take_profiT, double opeN, double closE, datetime timE, datetime time_closE, int magiC, string commenT, string externalIDD, double volumE, double commissioN, ENUM_DEAL_REASON reasoN, double swaP, double profiT, double feE, string symboL, int digitS )
前のメソッドと同様に、最初のパラメータはチャートのハンドルを定義します。このハンドル上に、情報パネルに関連付けられたすべてのオブジェクトが作成されます。他のすべてのパラメータは、履歴取引オブジェクトのフィールドを繰り返します。
以下に示すように、パネルのサイズを格納する変数と、表示される情報の名前と以前に取得した値で列を固定するための座標を定義することにより、取引データパネルを描画するためのメソッドの実装を開始します。
int height=20, max_height =0, max_width = 0; // column height and max values for indent int x_column[2] = {10, 130}; // columns X coordinates int y_column[17]; // Y coordinates
height変数には、各行が均等に描画されるように各列の高さとして静的な値20が格納され、max_height値とmax_width値には、均等に描画されるように各列の最大値が保存されます。X軸とY軸に沿った必要な座標は、それぞれx_column[]配列とy_column[]配列に格納されます。
ここで、ヘッダー列と値列を表示するための行の値を格納する2つの配列を宣言する必要があります。次のコードに示すように、文字列型のデータ配列を介してヘッダー列を宣言します。
string column_1[17] = { "Symbol", "Position ID", "External ID", "Magic", "Comment", "Reason", "Open", "Close", "Time open", "Time close", "Stop loss", "Take profit", "Volume", "Commission", "Swap", "Profit", "Fee" };
パネルは変更されず、データは常に同じ順序で表示されるため、すべての配列値は静的に宣言および初期化されます。これは、さまざまな取引に関する情報を閲覧することに慣れるという点で便利なはずです。値が含まれていないデータやゼロに等しいデータをパネルから除外できる機能を実装することは可能ですが、情報をすばやく検索するときには視覚的に不便です。毎回列の値を確認するよりも、よく知られた表示パターンで情報を検索する方がまだ馴染みがあります。
同じデータシーケンスで、上記の配列で宣言された列の値がすでに含まれている2番目の配列を宣言します。配列の宣言は次のように記述します。
string column_2[17] = { symboL, IntegerToString(tickeT), externalIDD, IntegerToString(magiC), commenT, EnumToString(reasoN), DoubleToString(opeN,digitS), DoubleToString(closE,digitS), TimeToString(timE), TimeToString(time_closE), DoubleToString(stop_losS,digitS), DoubleToString(take_profiT,digitS), DoubleToString(volumE,2), DoubleToString(commissioN,2), DoubleToString(swaP,2), DoubleToString(profiT,2), DoubleToString(feE,2) };
配列はメソッドレベルでローカルに宣言され、対応する定義済み端末関数を使用して、メソッドパラメータから直接フィールドが同時に初期化されます。
必要なデータを含むコンテナを宣言したので、各セル座標のアンカー値を計算して、各セルの最大値を検索する必要があります。これは次のコードで実装できます。
int count_rows = 1; for(int i=0; i<ArraySize(y_column); i++) { y_column[i] = height * count_rows; max_height = y_column[i]; count_rows++; int width_curr = StringLen(column_2[i]); if(width_curr>max_width) { max_width = width_curr; } } max_width = max_width*10; max_width += x_column[1]; max_width += x_column[0];
ここでは、行数をループし、各行に固定のY高さ値を掛けて、各オブジェクトのアンカー座標を見つけます。また、各値を最大幅の値と比較し、X座標を取得します。
すべての値とその座標を取得したら、以前に宣言したカスタムLabelCreate()メソッドを使用して情報の描画を開始できます。このメソッドは、以下に示すように、表示する行数に応じて周期的に呼び出されます。
color back_Color = clrWhiteSmoke; color font_Color = clrBlueViolet; for(int i=0; i<ArraySize(column_1); i++) { //--- draw 1 string name_1 = column_1[i]+"_1_"+IntegerToString(tickeT); LabelCreate(handlE,name_1,0,x_column[0],y_column[i],CORNER_LEFT_UPPER,column_1[i],"Arial",10,font_Color,0,ANCHOR_LEFT_UPPER,false); //--- draw 2 string name_2 = column_1[i]+"_2_"+IntegerToString(tickeT); LabelCreate(handlE,name_2,0,x_column[1],y_column[i],CORNER_LEFT_UPPER,column_2[i],"Arial",10,font_Color,0,ANCHOR_LEFT_UPPER,false); }
メソッドの最後では、以下に示すように、以前に宣言および説明したカスタムメソッドRectLabelCreate()を使用してこれらの値に背景を描画し、表示されるチャートを更新するだけです。
//--- draw the background RectLabelCreate(handlE,"RectLabel",0,1,height,max_width,max_height,back_Color); ChartRedraw(handlE);
これですべてのメソッドの説明は完了です。プロジェクトは組み立てて使用できる状態です。
その結果、スクリプトを使用した後のチャートファイルは図3のようになります。
図3:取引データを表示したスクリプト操作の結果
ご覧のとおり、取引に関するすべての情報が1つのチャートに一般化された形式で表示されるため、ユーザーが実行する取引操作の分析と評価がより便利になります。スクリプトはそのようなファイルを適切なフォルダに配置するため、ユーザーはいつでも口座履歴内の取引操作に関する必要な情報を検索することができます。
結論
この記事では、チャート上の取引を自動的に視覚化するスクリプトを作成するプロセスを完了しました。このソリューションを活用することで、エントリポイントを選定する際に起こり得るエラーを修正し、取引精度を大幅に向上させるだけでなく、適切な銘柄や価格変動の予測地点を選ぶことで、戦略全体の数学的期待値を高めることができます。さらに、このスクリプトを使用することで、チャートファイルの準備にかかる時間を大幅に短縮でき、その時間を分析や新しい取引アイデアの発掘に充てることが可能です。重要な点として、市場は常に変化しているため、安定した運用を維持するには、市場の動向を絶えず監視し、必要に応じて適応することが不可欠です。このツールは、そのプロセスをサポートします。読者の成功を祈っています。コメント欄にフィードバックをお寄せください。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/14961




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