English Русский
preview
初心者からエキスパートへ:MQL5を使ったアニメーションニュース見出し(I)

初心者からエキスパートへ:MQL5を使ったアニメーションニュース見出し(I)

MetaTrader 5 |
16 0
Clemence Benjamin
Clemence Benjamin

内容


はじめに

本記事では、MetaTrader 5ターミナルにおける経済ニュースおよびカレンダーイベントのアクセス方法における一般的な制約、特にチャート分析中の制限に焦点を当てます。

MetaTrader 5には、ツールボックスウィンドウ内に[ニュース]タブと[指標カレンダー]タブが用意されています。これらのタブでは、信頼できるニュース提供元から取得した重要な情報が提供されます。しかし、両者の違いを理解しておくことが重要です。

  • [ニュース]タブ:すでに公開された見出しやニュース更新情報を表示します。
  • [指標カレンダー]タブ:今後予定されている経済イベントのスケジュールを日付、時刻、重要度ごとに提供し、事前の計画に役立ちます。

どちらのツールも市場分析において不可欠です。経験豊富なトレーダーは、政策金利の発表や非農業部門雇用者数(NFP)などの経済指標が、市場に大きな影響を与えることを理解しています。適切に準備すれば収益機会になりますが、予想外の値動きが起これば大きなリスクも伴います。

MetaTrader 5でニュースとカレンダーにアクセスする

MetaTrader 5でのニュースとカレンダーへのアクセス

先ほど共有したスクリーンショットでは、MetaTrader 5が単一の統合環境内でニュースと経済指標カレンダーの両方へのアクセスを提供していることがわかります。しかし、ここで一つの制約が明らかになります。それは、これらの情報を閲覧するにはユーザーが手動でツールボックスウィンドウに移動する必要があるという点です。

ツールボックスに入ると、情報は表形式で提供され、行と列にニュース項目や予定イベントの詳細が表示されます。ユーザーはニュースフィードをスクロールして見出しを確認したり、カレンダーを参照して今後の経済リリースを確認したりできます。ツールボックスパネルを拡張して表示内容を増やすことも可能ですが、その場合チャートウィンドウの表示領域が縮小され、価格の動きやインジケーター、グラフィカルオブジェクトが見えにくくなる可能性があります。

本記事で提案するソリューションでは、ニュース見出しやカレンダーイベントをチャート上に直接表示することで、チャートの表示を妨げることなく情報にアクセスできるようにします。これにより、視認性と状況認識を向上させつつ、チャートをすっきりと機能的に保つことができます。

本ミニシリーズの導入部分では、まずMQL5の経済指標カレンダーを利用して、今後のニュースイベントの見出しをチャート上に表示する方法を実装します。このアプローチは、テレビ画面下部のニューススクロールや、ソーシャルメディアの動画広告でよく見られる表示形式を参考にしています。

続くセクションでは、MQL5を用いてNews Headline EAを構築する手法を紹介します。設計戦略の概要を示し、主要な実装上の判断について解説した後、コードベースを詳細に解説します。

最後に、テスト結果や観察事項をまとめ、本システムがMetaTrader 5上でリアルタイムニュースを表示する際の開発ライフサイクルを一通り提示します。


コンセプト

このアイデアを実現するために、MQL5標準ライブラリを活用します。このライブラリは、グラフィカルインターフェース開発のための強力なツール群を提供しています。具体的には、MQL5\Include\Canvas\Canvas.mqhに含まれるCCanvasクラスを使用します。このクラスにより、長方形で透明な描画面を作成でき、ニュース見出しや経済イベントの更新情報など、動的なコンテンツをチャート上に重ねて表示するのに最適です。

私たちの実装は、チャートの機能を妨げることなく、関連情報を連続的に表示するスクロールティッカー表示システムに重点を置いています。このコンセプトは、テレビニュース放送、金融系ウェブサイト、ソーシャルメディア動画コンテンツなどで見られる、画面上を流れる注目見出しに着想を得たものです。

見出しの定義(Google検索

ここで、なぜこのシステムを「News Headline EA」と呼ぶのか疑問に思うかもしれません。Headlineとは、重要な情報を簡潔かつ目立つ形でまとめた短文のことです。読者がすぐに理解できるよう設計されています。本EAでは、市場の時間敏感なデータ(例:ニュースイベントや経済指標発表)を視覚的に分かりやすく表示するためのコンパクトな手段として機能します。

News Headlineのコンセプト

コンセプト:MQL5 APIを使用して経済指標カレンダーデータを取得し、カスタムNews Headline EAを通じて今後のイベントをチャート上に直接表示する

開発の初期段階では、以下の2つの主要コンポーネントに焦点を当てます。

  • MQL5のCCanvasクラスの理解と活用:チャート上にカスタムグラフィック描画をおこなうための基盤
  • MQL5経済指標カレンダーの実装:組み込み関数を使用して今後のイベントを取得し、横スクロールで表示

横スクロール表示を採用するのは意図的で、チャートの垂直スペースを節約しつつ、時間敏感な情報をインラインかつ目立たずに表示することが目的です。

将来的な拡張として、外部ニュースAPIの統合も予定しています。これにより、経済指標カレンダーの下段に、リアルタイムの市場ニュースを別のスクロールレーンで表示できるようになります。現段階では、ニュースフィードの代わりにプレースホルダテキストを挿入して、実装の土台を作ります。

このシステムを使用するトレーダーは、以下を瞬時におこなうことができます。
  • 重要な今後のイベントを即座に把握する
  • イベント発生までの残り時間(時間・分単位)を確認する
  • 予想される影響度を認識し、より慎重または情報に基づいた戦略を立てる

これで基本的なコンセプトの説明は完了です。次のステップとして、実装の詳細に踏み込んで解説します。


実装

Canvasヘッダを理解する

MQL5のCCanvasクラスは、MetaTrader 5のチャート上でカスタムグラフィカルインターフェースやビジュアル要素を作成、管理するための、強力かつ汎用性の高いユーティリティです。CCanvasの中心的な機能は、メモリ内ビットマップサーフェスを作成し、OBJ_BITMAPやOBJ_BITMAP_LABELといったチャートオブジェクトを通じて描画することにあります。これにより、チャート上に動的なグラフィックコンテンツ(線、図形、多角形、テキストなど)を表示しつつ、既存のチャート要素に干渉することなく描画できます。CCanvasは、ピクセルバッファ(m_pixels[])を通じた低レベルの描画制御を提供し、CreateBitmapLabel()で初期化、Update()で出力を更新できます。

 また、リソース管理も強力で、ビットマップの読み込みと保存、チャートへのキャンバスのアタッチやデタッチ、PixelSet()やPixelGet()によるピクセル単位の操作が可能です。これにより、パフォーマンス重視のアプリケーションにも最適です。柔軟性も高く、カラー形式(ARGB、XRGB、透過対応)、多角形描画(非凸ポリゴンの塗りつぶしも含む)、階層化されたチャートインターフェースなど、多彩な表現が可能です。

実際の運用では、CCanvasはMetaTrader 5上での高度なUI開発を可能にします。滑らかに塗りつぶされたオーバーレイやインタラクティブなトレンドライン、取引可視化ダッシュボード、ボタンやスライダー付きのフル機能パネル、さらにはスクロールニュースティッカーなど、幅広く利用されます。CPU負荷の高い超高解像度環境では制約がありますが、ピクセル単位の精度と完全なカスタマイズ性により、ハイエンドのチャートインターフェースには欠かせない存在です。

経済指標カレンダーを理解する

MetaTrader 5の経済指標カレンダーを扱う際、まず理解すべきは各イベントが特定の国(または経済圏)に紐づくという点です。MQL5では、MqlCalendarCountry構造体でこれが表され、ISO 3166-1コード(id)、国名、2文字コード、通貨コードと銘柄、URL用国名などの情報を保持します。カレンダーの国リストを一度取得すれば、地域別にイベントをフィルタリング・グループ化するための属性がすべて揃います。各カレンダーイベントは、MqlCalendarEvent構造体内のcountry_idを通じて所属国を参照します。

MqlCalendarEvent構造体は、繰り返し発生するイベントタイプの一般的な特性を表します。イベント名、重要度、経済分野(GDP、雇用、物価など)、周期(毎日、毎月、四半期)、値の単位などです。重要なのは、これは単一の発生を表すものではなく、イベントの「テンプレート」や定義(例:「米国CPI発表」)である点です。カレンダーサービスは、この定義に基づいて過去の履歴にわたり複数回スケジュールを作成します。

実際のスケジュールされたイベントは、MqlCalendarValue構造体のテーブルに格納されます。各レコードにはevent_id(テンプレートへのリンク)、正確な時刻・期間、およびactual_value、forecast_value、prev_value、revised_prev_valueの4つの数値フィールドが含まれます。HasActualValue()やGetActualValue()といったヘルパー関数により、カレンダー内部の「ppm」表現から自動的にスケーリングされた実際の値を簡単に確認・取得できます。

このリレーショナル設計(国 → イベントタイプ → 発生イベント)により、データの重複を避けつつ効率的に管理できます。たとえば四半期ごとのCPIは、すべて1つのCPI定義にリンクしており、重要度や単位、周期情報を保持しています。これらの構造と参照関係を理解することで、必要な今後のイベントだけを効率的かつ保守性の高いコードでフィルタリングや表示できます。

News Headline EA

ユーザーコントロールの設定

まず、トレーダーが調整できるパラメータを決めます。スクロール速度は各重要度レーンごとに設定可能です(InpSpeedHigh、InpSpeedMed、InpSpeedLow)、ニュースティッカーの速度(InpNewsSpeed)、フレーム間隔(InpTimerMs)も調整可能です。また、ティッカーをチャート上部または下部に表示するか(InpPositionTop)、チャート端からの距離(InpTopOffset)、表示するレーン(ShowHigh、ShowMed、ShowLow)も選択できます。これらを「User Inputs」ブロックにまとめることで、誰でも実装の詳細に触れずに挙動を簡単に調整できる設計にしています。

//+------------------------------------------------------------------+
//| 1) USER INPUTS                                                   |
//+------------------------------------------------------------------+
input int   InpSpeedHigh   = 4;    // px/frame for High-impact lane
input int   InpSpeedMed    = 2;    // px/frame for Medium-impact lane
input int   InpSpeedLow    = 1;    // px/frame for Low-impact lane
input int   InpNewsSpeed   = 5;    // px/frame for news ticker row
input int   InpTimerMs     = 50;   // ms between frames (~20 fps)
input bool  InpPositionTop = true; // true=top, false=bottom
input int   InpTopOffset   = 50;   // px offset from chart edge
input bool  ShowHigh       = true; // toggle High lane
input bool  ShowMed        = true; // toggle Medium lane
input bool  ShowLow        = true; // toggle Low lane

レイアウト定数の定義

次に、視覚レイアウトを管理する固定の間隔ルールを設定します。たとえば、残り時間ラベルと通貨シンボルの間のピクセル数(GapTimeToSym)、インライン重要度ボックスの周囲の余白(GapSymToRect、GapRectToName)、およびボックスのサイズ(RectSize)などです。これらの値を集中管理することで、描画コードを細かく探すことなく、全体の見た目や配置を一か所で微調整できるようになります。

//+------------------------------------------------------------------+
//| 2) DEVELOPER CONSTANTS                                           |
//+------------------------------------------------------------------+
static const int GapTimeToSym = 10;  // px gap after “[1h]”
static const int GapSymToRect = 5;   // px gap before inline box
static const int RectSize     = 8;   // width & height of inline box
static const int GapRectToName= 10;  // px gap after inline box

状態バッファと描画バッファの保存

次に、チャート幅(canvW)、行の高さ(lineH)、ニュースレーン用のプレースホルダテキスト、カレンダーの重複クエリを避けるためのタイムスタンプ(lastReloadDay)を保持するためのグローバル変数を宣言します。また、CCanvasオブジェクトを2つ生成します。1つは3つのイベントレーン用、もう1つはニュースティッカー用です。さらに、CEventクラスと、重要度別にカレンダーイベントを格納するための3つの動的配列(highArr、medArr、lowArr)を定義します。各レーンの現在のスクロールオフセット(offHighなど)も設定し、EAの稼働中に維持すべき状態がここで完成します。

//+------------------------------------------------------------------+
//| 3) GLOBALS                                                       |
//+------------------------------------------------------------------+
int        lineH      = 16;           // row height in px
int        canvW;                     // chart width
string     placeholder =              // news ticker text
           "News feed coming soon – stay tuned with the calendar";
datetime   lastReloadDay = 0;         // daily reload guard

CCanvas    eventsCanvas, newsCanvas;  // two layers

// Event struct and arrays
class CEvent : public CObject
{
public:
  datetime time;
  string   sym, name;
  int      imp;
  CEvent(datetime t,const string &S,const string &N,int I)
    { time=t; sym=S; name=N; imp=I; }
};
CEvent *highArr[], *medArr[], *lowArr[];
int     offHigh, offMed, offLow, offNews;

配置と並べ替えのヘルパー関数

メインロジックをシンプルに保つために、2つの小さなヘルパー関数を切り出します。SetCanvas()は、ユーザー設定に応じて、キャンバスオブジェクトをチャートの上部または下部に配置します。SortArr()は各重要度配列をイベント時刻順に並べ替えるシンプルなバブルソートで、レーン内のイベントが常に正しい順序で表示されるようにします。

//+------------------------------------------------------------------+
//| Helper: position a canvas label                                  |
//+------------------------------------------------------------------+
void SetCanvas(string name,bool top,int yDist)
{
  ObjectSetInteger(0,name,OBJPROP_CORNER,    top?CORNER_LEFT_UPPER:CORNER_LEFT_LOWER);
  ObjectSetInteger(0,name,OBJPROP_XDISTANCE, 0);
  ObjectSetInteger(0,name,OBJPROP_YDISTANCE, yDist);
}

//+------------------------------------------------------------------+
//| Helper: sort events by time                                      |
//+------------------------------------------------------------------+
void SortArr(CEvent* &arr[])
{
  int n=ArraySize(arr);
  for(int i=0;i<n-1;i++)
    for(int j=i+1;j<n;j++)
      if(arr[i].time > arr[j].time)
      {
        CEvent *tmp=arr[i]; arr[i]=arr[j]; arr[j]=tmp;
      }
}

当日のイベントを取得する

ReloadEvents()関数は、データ取得とフィルタリングの中心的役割を担います。この関数は、MetaTraderの経済指標カレンダーから、本日0時から24時間以内に予定されているイベントをクエリします。既に過ぎたタイムスタンプのイベントはスキップされます。有効なイベントはすべてCEventオブジェクトにラップされ、その重要度に応じてhighArr、medArr、lowArrの各配列に格納されます。最後に、各レーンをソートして、スクロール内で最も早いイベントが先頭に表示されるようにします。

//+------------------------------------------------------------------+
//| ReloadEvents: load only *future* events for *today*              |
//+------------------------------------------------------------------+
void ReloadEvents()
{
  datetime srv = TimeTradeServer();
  // midnight today
  MqlDateTime dt; TimeToStruct(srv, dt);
  MqlDateTime m0 = {dt.year, dt.mon, dt.day,0,0,0};
  datetime today = StructToTime(m0);
  if(today == lastReloadDay) return;
  lastReloadDay = today;

  // clear previous
  for(int i=0;i<ArraySize(highArr);i++) delete highArr[i];
  for(int i=0;i<ArraySize(medArr); i++) delete medArr[i];
  for(int i=0;i<ArraySize(lowArr); i++) delete lowArr[i];
  ArrayResize(highArr,0); ArrayResize(medArr,0); ArrayResize(lowArr,0);

  // fetch events [today, today+24h)
  MqlCalendarValue vals[];
  int cnt = CalendarValueHistory(vals, today, today+86400);
  for(int i=0;i<cnt;i++)
  {
    if(vals[i].time <= srv) continue; // skip past
    MqlCalendarEvent e;
    if(!CalendarEventById(vals[i].event_id, e)) continue;
    MqlCalendarCountry c;
    if(!CalendarCountryById(e.country_id, c)) continue;
    string sym = "[" + c.currency + "]";
    CEvent *ev = new CEvent(vals[i].time, sym, e.name, e.importance);
    // classify
    if(e.importance==CALENDAR_IMPORTANCE_HIGH)
      { int s=ArraySize(highArr)+1; ArrayResize(highArr,s); highArr[s-1]=ev; }
    else if(e.importance==CALENDAR_IMPORTANCE_MODERATE)
      { int s=ArraySize(medArr)+1; ArrayResize(medArr,s); medArr[s-1]=ev; }
    else
      { int s=ArraySize(lowArr)+1;  ArrayResize(lowArr,s);  lowArr[s-1]=ev; }
  }
  SortArr(highArr); SortArr(medArr); SortArr(lowArr);
}

詳細には、ReloadEvents()が実行されると、まずCalendarValueHistory()を通じて本日のカレンダーエントリの全リストを取得します。しかし、各生データにはevent_idとcountry_idしか含まれていません。そこで、これらをMqlCalendarEventテーブルと照合します。このテーブルでは、各イベントタイプが名前、周期、経済分野、そして最も重要な重要度で定義されています。これにより、本当に市場に影響を与えるイベントだけを表示可能になります。さらに、MqlCalendarCountry構造体を使って、各見出しに正しい通貨ラベル(例:[USD])を付与します。これはISOコードや通貨シンボルから自動取得されます。この二段階のルックアップ(値 → イベントタイプ → 国)によって、EAは必要な情報だけを取得し、かつ正確に動作します。国やイベントの詳細をハードコーディングせず、MetaTrader自身の常に同期されたデータベースを活用しているためです。

重要度の定数(CALENDAR_IMPORTANCE_HIGH、..._MODERATE、..._LOW)は、各レーンのロジックの中核です。表示する重要度レベル(ShowHigh / ShowMed / ShowLow)を選択し、それぞれのインラインボックスに赤、オレンジ、白の色を付けることで、トレーダーに即座に視覚的な手がかりを提供します。赤は最も影響力の大きいリリース(例:FRBの利上げ決定、米雇用統計)、オレンジは中程度の影響(例:CPI、小売売上高)、白は軽微なもの(例:スピーチ、小規模データ)です。

実際の運用では、トレーダーはこれを一目で確認し、ストップを引き締めるか、あるいは自動売買戦略を一時停止するかの判断ができます。このフィルタリングと重要度に基づく色分け(MqlCalendarEventのimportanceフィールドによる)がなければ、数十件のイベントがスクロールリストに表示されるだけで、ノイズになってしまい、シグナルとして機能しません。

スクロールレーンのレンダリング

DrawLane()は、1本の横方向のレーンの描画ロジックをカプセル化しています。文字の幅を揃えるために等幅フォント(「Courier New」)を選択し、角括弧や数字を含むすべての文字が同じ幅になるようにして、整った配置を保証します。描画内容は以下の通りです。

  1. 残り時間ラベル(時間または分)
  2. 通貨シンボル
  3. インライン重要度ボックス(赤・オレンジ・白で色付け)
  4. イベント名(さらに後続のイベントがある場合は区切り記号を追加)

最後に、レーンのオフセットをレーン速度分だけ減算し、行全体が左端からスクロールアウトした場合は右端に巻き戻します。

//+------------------------------------------------------------------+
//| DrawLane: scroll one lane with inline importance box             |
//+------------------------------------------------------------------+
void DrawLane(CEvent* &arr[], int &offset, int y, int speed)
{
  int n=ArraySize(arr);
  if(n==0) return;

  // monospaced for alignment
  eventsCanvas.FontNameSet("Courier New");
  eventsCanvas.FontSizeSet(-100);

  int x = offset;
  datetime srv = TimeTradeServer();

  for(int i=0;i<n;i++)
  {
    CEvent *e = arr[i];
    // time-left “[1h]” or “[45m]”
    long diff = (long)e.time - (long)srv;
    string tl = (diff>=3600 ? IntegerToString(diff/3600)+"h"
                            : IntegerToString(diff/60)+"m");
    string part = "[" + tl + "]";
    eventsCanvas.TextOut(x,y,part,XRGB(255,255,255),ALIGN_LEFT);
    x += eventsCanvas.TextWidth(part) -20;

    // symbol “[USD]”
    eventsCanvas.TextOut(x,y,e.sym,XRGB(255,255,255),ALIGN_RIGHT);
    x += eventsCanvas.TextWidth(e.sym) + GapSymToRect;

    // inline importance box
    uint col = (e.imp==CALENDAR_IMPORTANCE_HIGH    ? XRGB(255,0,0) :
                e.imp==CALENDAR_IMPORTANCE_MODERATE? XRGB(255,165,0):
                                                     XRGB(255,255,255));
    eventsCanvas.FillRectangle(x, y + (lineH-RectSize)/2,
                               x+RectSize, y + (lineH-RectSize)/2 + RectSize,
                               col);
    x += RectSize + GapRectToName;

    // event name + separator
    eventsCanvas.TextOut(x,y,e.name,XRGB(255,255,255),ALIGN_RIGHT);
    x += eventsCanvas.TextWidth(e.name)+60;
    if(i+1<n)
    {
      eventsCanvas.TextOut(x,y,"|",XRGB(180,180,180),ALIGN_RIGHT);
      x += eventsCanvas.TextWidth("|") + 20;
    }
  }

  // scroll + wrap
  int totalW = x - offset;
  offset -= speed;
  if(offset + totalW < 0) offset = canvW;
}

すべてのレーンとニュースの行を編成する

DrawAll()では、まず3本のイベントレーンを縦に重ね、その下(または位置設定に応じて上)にニュース用のプレースホルダを配置します。イベントをeventsCanvasに描画した後、Update(false)を呼び出してチャートオブジェクトに反映させます。ニュースレーンは専用のnewsCanvasを使用し、テキストのみの簡易描画をおこなった後、Update(true)を呼び出して同期的に画面を更新します。

//+------------------------------------------------------------------+
//| DrawAll: render lanes + news row                                |
//+------------------------------------------------------------------+
void DrawAll()
{
  // clear events
  eventsCanvas.Erase(ARGB(180,0,0,0));
  int y=0;

  if(ShowHigh)
  {
    DrawLane(highArr, offHigh, y, InpSpeedHigh);
    y += lineH;
  }
  if(ShowMed)
  {
    DrawLane(medArr, offMed, y, InpSpeedMed);
    y += lineH;
  }
  if(ShowLow)
  {
    DrawLane(lowArr, offLow, y, InpSpeedLow);
    y += lineH;
  }
  eventsCanvas.Update(false);

  // news placeholder
  newsCanvas.Erase(ARGB(170,0,0,0));
  newsCanvas.FontNameSet("Tahoma");
  newsCanvas.FontSizeSet(-120);
  int yOff = (lineH - newsCanvas.TextHeight(placeholder)) / 2;
  newsCanvas.TextOut(offNews, yOff, placeholder, XRGB(255,255,255), ALIGN_LEFT);
  offNews -= InpNewsSpeed;
  if(offNews + newsCanvas.TextWidth(placeholder) < -20) offNews = canvW;
  newsCanvas.Update(true);
}

初期化、タイマー、クリーンアップ

最後に、OnInit()ではキャンバスを作成・設定し、ReloadEvents()を初回実行します。すべてのオフセットをcanvWに設定し、InpPositionTopとInpTopOffsetに基づいて2つのキャンバスの位置を決定します。その後、最初のフレームを描画し、ミリ秒タイマーを開始します。 

OnTimer()では、キャンバスの再配置(ユーザーがInpPositionTopをライブで変更できるように)、1日1回のイベント再取得、チャートサイズ変更への対応をおこない、再びDrawAll()を呼び出して描画します。OnDeinit()では、キャンバスをクリーンアップし、割り当てられたCEventオブジェクトを削除します。

//+------------------------------------------------------------------+
//| OnInit: setup canvases, initial load & position                 |
//+------------------------------------------------------------------+
int OnInit()
{
  // force reload Today
  lastReloadDay = 0;

  // clear arrays
  ArrayResize(highArr,0);
  ArrayResize(medArr,0);
  ArrayResize(lowArr,0);

  // chart width
  canvW = (int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS);

  // create events canvas (4 rows tall)
  eventsCanvas.CreateBitmapLabel("EvCanvas",0,0,canvW,4*lineH,COLOR_FORMAT_ARGB_RAW);
  eventsCanvas.TransparentLevelSet(150);

  // create news canvas (1 row tall)
  newsCanvas.CreateBitmapLabel("NwCanvas",0,0,canvW,lineH,COLOR_FORMAT_ARGB_RAW);
  newsCanvas.TransparentLevelSet(0);

  // load data + init offsets
  ReloadEvents();
  offHigh = offMed = offLow = offNews = canvW;

  // initial positioning
  {
    int rows = (ShowHigh?1:0)+(ShowMed?1:0)+(ShowLow?1:0);
    int yOff = InpTopOffset + (InpPositionTop ? 0 : rows*lineH);
    SetCanvas("EvCanvas", InpPositionTop, InpTopOffset);
    SetCanvas("NwCanvas", InpPositionTop, yOff + (InpPositionTop ? rows*lineH : 0));
  }

  // first draw & timer
  DrawAll();
  EventSetMillisecondTimer(InpTimerMs);
  return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| OnTimer: reposition, daily reload, redraw                       |
//+------------------------------------------------------------------+
void OnTimer()
{
  // reposition every tick
  int rows = (ShowHigh?1:0)+(ShowMed?1:0)+(ShowLow?1:0);
  if(InpPositionTop)
  {
    SetCanvas("EvCanvas", true,  InpTopOffset);
    SetCanvas("NwCanvas", true,  InpTopOffset + rows*lineH);
  }
  else
  {
    SetCanvas("EvCanvas", false, InpTopOffset);
    SetCanvas("NwCanvas", false, InpTopOffset + lineH);
  }

  // reload once per day
  ReloadEvents();

  // adapt width
  int wNew = (int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS);
  if(wNew != canvW)
  {
    canvW = wNew;
    ObjectSetInteger(0,"EvCanvas",OBJPROP_WIDTH,canvW);
    ObjectSetInteger(0,"NwCanvas",OBJPROP_WIDTH,canvW);
  }

  // redraw
  DrawAll();
}

//+------------------------------------------------------------------+
//| OnDeinit: cleanup                                               |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
  EventKillTimer();
  eventsCanvas.Destroy(); ObjectDelete(0,"EvCanvas");
  newsCanvas.Destroy();   ObjectDelete(0,"NwCanvas");
  for(int i=0;i<ArraySize(highArr);i++) delete highArr[i];
  for(int i=0;i<ArraySize(medArr); i++) delete medArr[i];
  for(int i=0;i<ArraySize(lowArr); i++) delete lowArr[i];
}

この段階が完了すると、チャート上でコードをテストできる状態になります。開発中には多くのコンパイルエラーを解消し、最終的にクリーンで構造の整ったバージョンを作成しました。上記のコンポーネントをコンパイルすることで、チャート上で動作可能なNews Headline EAが完成しました。次のセクションでは、私のテスト体験について詳しく紹介します。


テスト

MetaTrader 5ターミナルで、EAのセクションに移動し、News Headline EAをチャートにドラッグします。正常に追加されると、EAはデフォルトでチャート上部に表示され、縦方向のオフセットは50ピクセルに設定されています。このオフセットにより、板情報や取引パネルボタン、右上のEA名表示と重ならないようになっています。

オフセットは自由に調整可能で、ニュースヘッドラインキャンバスをチャート上の任意の縦位置に配置できます。EAは4本のレーンを備えており、各レーンは最適化されたスクロール速度で、フレームレート20 FPSで動作します。近日公開予定のニュースイベントは、重要度に応じた色分けされた長方形で表示されます。

TNews Headline EA.mq5のテスト

News Headline EAのテスト

上図は、News Headline EAをチャートに設置した様子です。意図どおりに動作し、すべての近日公開ニュースイベントがスムーズにスクロール表示されています。


結論

これでまた一つ、トレーダーと開発者の双方に実用的かつ示唆に富んだツールを提供する開発解説が完了しました。Canvasクラスの力を活用し、効率的なレンダリングと視覚的明瞭性を実現しました。この手法は、インターフェース開発における有用な近道としても役立ちます。

このプロジェクトを通じて、経済指標カレンダーデータの取得方法と、それをトレーダーにとって意味のある形式で表示する方法を学びました。その結果、チャート上に近日公開のニュースイベントをクリーンでミニマルな形で表示できるようになり、ニュースを基にした取引ツールに長らく存在した課題を解決しました。

今後の展望として、このEAの第2版ではリアルタイムニュースフィードのAPIアクセスを組み込み、さらに動的な更新を可能にする予定です。また、Canvasは他の取引関連データを表示するために再利用できるため、このアプローチは非常に汎用性があります。

高重要度のニュースがチャート上で明確に表示されることで、トレーダーはより情報に基づいた判断を下せるようになります。たとえば、取引に参加するか、待機するかを選択できます。ベストプラクティスとして、主要ニュース発表の前後数時間は取引を控えることで、リスクや急激な価格変動を避けることが推奨されます。

コメント欄でご意見や質問を共有いただけます。また、この記事の下に添付ファイルもありますので、ご参照ください。

ファイル名 説明
NewsTicker.mq5 チャート上でCCanvasクラスを使用して直接実装された、3本レーンのスクロール式経済指標カレンダーとニュースプレースホルダティッカーを表示するメインEAのソースコード。各レーンは個別のスクロール速度を持ち、インラインの重要度ボックスやリアルタイムカウントダウンを備えている。

MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/18299

添付されたファイル |
EAのサンプル EAのサンプル
一般的なMACDを使ったEAを例として、MQL4開発の原則を紹介します。
知っておくべきMQL5ウィザードのテクニック(第70回): 指数カーネルネットワークにおけるSARとRVIのパターンの使用 知っておくべきMQL5ウィザードのテクニック(第70回): 指数カーネルネットワークにおけるSARとRVIのパターンの使用
前回の記事では、SARとRVIのインジケーターペアを紹介しました。今回は、このインジケーターペアを機械学習によってどのように拡張できるかを検討します。SARとRVIは、それぞれトレンドとモメンタムを補完し合う関係にあります。本機械学習アプローチでは、畳み込みニューラルネットワーク(CNN)を使用し、カーネルとチャネルのサイズを指数関数的に拡大・調整することで、このインジケーターペアの予測を微調整します。この処理は、常にMQL5ウィザードと連携してエキスパートアドバイザー(EA)を組み立てるカスタムシグナルクラスファイル内でおこなわれます。
エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法 エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
この記事では、MT4において複数のEAの衝突をさける方法を扱います。ターミナルの操作、MQL4の基本的な使い方がわかる人にとって、役に立つでしょう。
MQL5で自己最適化エキスパートアドバイザーを構築する(第8回):複数戦略分析 MQL5で自己最適化エキスパートアドバイザーを構築する(第8回):複数戦略分析
複数の戦略をどのように組み合わせれば、最も効果的に強力なアンサンブル戦略を構築できるでしょうか。本記事では、3種類の戦略を1つの取引アプリケーションに統合する方法について検討します。トレーダーは通常、ポジションのエントリーとクローズに特化した戦略を用いますが、私たちは機械がこのタスクをより優れた形で遂行できるかどうかを探ります。最初の議論として、ストラテジーテスターの機能と、本タスクで必要となるオブジェクト指向プログラミング(OOP)の原則に慣れていきます。