English Русский Deutsch 日本語
preview
从新手到专家:使用 MQL5 制作动画新闻标题(一)

从新手到专家:使用 MQL5 制作动画新闻标题(一)

MetaTrader 5示例 |
32 0
Clemence Benjamin
Clemence Benjamin

目录:


概述

今天,我们的目标是解决在 MetaTrader 5 终端中访问经济新闻和日历事件时的一个常见限制 —— 尤其是在进行图表分析时。

在 MetaTrader 5 中,“新闻” 和 “经济日历” 选项卡都位于 “工具箱” 窗口下。这些标签页提供来自知名新闻提供商的重要信息。然而,了解两者之间的区别至关重要:

  • 新闻标签页:显示已发布的新闻标题和更新。
  • 经济日历:提供即将发生的经济事件日程表,按日期、时间和重要性分类 —— 有助于提前规划。

这两种工具对于市场分析都至关重要。经验丰富的交易者都知道,一些经济数据发布 —— 例如利率决定或非农就业数据 —— 可能会对市场产生重大影响。只要准备充分,此类事件就能带来可以获利的交易机会。但是,如果交易结果与预期相反,它们也会带来相当大的风险。

在 MetaTrader 5 上访问新闻和日历

在 MetaTrader 5 上访问新闻和日历。

在之前分享的截图中,你会注意到 MetaTrader 5 在一个集成环境中提供了对新闻和经济日历的访问。然而,一个关键的限制变得明显:要查看这些功能中的任何一个,用户必须手动导航到“工具箱”窗口。

进入工具箱后,信息以表格形式呈现 —— 行和列显示各种新闻项目和预定事件,以及相关详细信息。用户可以滚动浏览新闻提要以阅读头条新闻,或浏览日历以查看即将发布的经济新闻。虽然工具箱面板可以扩展以显示更多内容,但这样做是以减小图表窗口大小为代价的,这可能会阻碍图表上的价格行为、指标或图形对象。

我们提出的解决方案通过将新闻标题和日历事件直接投影到图表上来解决这一限制,在不侵入图表空间或干扰交易对象的情况下保持完全可见性。目标是提高可访问性和态势感知能力,同时保持干净实用的图表环境。

在本系列介绍部分,我将主要介绍如何实现 MQL5 经济日历,以显示即将发生的新闻事件的标题。这里的解决方案借鉴了新闻标题通常在电视屏幕底部显示的方式,或者广告通常在社交媒体视频上以这种方式播放的方式。

在下一节中,我们将介绍使用 MQL5 构建新闻标题 EA 的方法。我们将概述设计策略,浏览关键的实现决策,然后详细研究代码库。

最后,我们将以测试结果和观察结果结束,为 MetaTrader 5 中的实时新闻显示系统提供完整的端到端开发生命周期视图。


概念

为了将这个想法变为现实,我们将利用 MQL5 标准库,它为图形界面开发提供了强大的工具。具体来说,我们将使用位于 MQL5\Include\Canvas\Canvas.mqh 文件中的 CCanvas 类。这个类允许我们创建矩形的透明绘图表面 —— 非常适合将新闻标题和经济事件更新等动态内容直接叠加到图表上。

我们的实现方案以滚动式信息显示屏系统为核心,该系统在不中断图表功能的情况下连续显示相关信息。这一概念的灵感来自电视新闻广播、金融网站甚至社交媒体视频内容中的真实界面,在这些界面中,吸引眼球的标题会在屏幕上移动,提供实时更新。

标题的定义片段来自谷歌搜索

此时,您可能会想知道为什么我们特意将其称为“新闻标题”EA 交易。标题一词是指对关键信息的简短、突出的总结,旨在便于查看和快速理解。在我们的案例中,它作为一种紧凑的方式,以易于访问和视觉吸引力的格式传达对时间敏感的市场数据(例如新闻事件或经济新闻)。

新闻标题概念

概念:使用 MQL5 API 获取经济日历数据,并通过我们自定义的新闻标题 EA 直接在图表上显示即将发生的事件。 

为了简化这一开发阶段,我们将首先关注两个核心组件:

  • 了解和使用 MQL5 中的 CCanvas 类,该类支持在图表上进行自定义图形绘制。
  • 实现 MQL5 经济日历,使用内置函数检索即将发生的事件,并使用水平滚动效果显示它们。

这种水平滚动设计是特意设计的 —— 它节省了图表上宝贵的垂直空间,并以内联、不干扰的方式显示对时间敏感的信息。

作为一项前瞻性改进措施,我们还计划在未来的迭代中集成外部新闻 API。这将使我们能够在日历事件下方的单独滚动通道中显示实时市场新闻。现在,我们将通过插入占位符文本来表示新闻提要来奠定基础。

使用此系统的交易者可以直观地看到:
  • 立即识别即将发生的重要事件。
  • 看到它们会在多久内发生(例如,以小时或分钟为单位)。
  • 认识到它们的预期影响水平 —— 允许交易者采取更谨慎或更明智的策略。

在介绍了核心概念之后,我们现在转向对实现细节的更深入探索。


实现

了解 Canvas 头文件

MQL5 中的 CCanvas 类是一个功能强大且用途广泛的实用程序,旨在直接在 MetaTrader 5 图表中创建和管理自定义图形界面和视觉元素。CCanvas 的核心功能是允许开发者创建内存中的位图表面,这些位图表面通过 OBJ_BITMAP 和 OBJ_BITMAP_LABEL 等图表对象进行渲染。这使得在图表上动态绘制图形内容(包括线条、形状、多边形和文本)成为可能,而不会干扰原生图表元素。该类通过像素缓冲区 (m_pixels[]) 提供对渲染的底层控制,以及 CreateBitmapLabel() 等用于初始化的函数和 Update() 等用于刷新输出的函数。

 CCanvas 提供强大的资源管理:开发人员可以加载或保存位图,将画布附加/分离到图表中,并使用 PixelSet() 或 PixelGet() 直接操作像素 —— 这使其成为对性能敏感的应用程序的理想选择。它的灵活性体现在颜色格式(ARGB、XRGB,带透明度)、多边形渲染(包括非凸填充)和分层图表界面等方面。

实际上,CCanvas 为 MetTrader 5 中复杂的 UI 开发打开了大门。它通常用于自定义指标(如平滑或阴影叠加)、交互式工具(如带有自定义上限的趋势线)、交易可视化面板以及带有按钮、滑块甚至滚动新闻条的全功能图表面板。尽管 CPU 限制的渲染限制了它在超高分辨率环境中的应用,但它的像素级精度和完全的可定制性使其成为高端图表界面不可或缺的一部分。

了解经济日历

使用 MetaTrader 5 经济日历时,首先要了解的是,每个事件都通过唯一的国家标识符与特定国家(或经济联盟)相关联。在 MQL5 中,这由 MqlCalendarCountry 结构表示,其中包括 id(ISO 3166-1 代码)、国家/地区名称、两位字母代码、货币代码和交易品种,甚至 URL 友好的国家/地区名称等字段。通过查询一次日历的 MqlCalendarCountry 条目列表,您可以访问按地区过滤或分组事件所需的所有属性。然后,每个日历事件通过 MqlCalendarEvent 结构中的 country_id 字段引用回其国家。

该结构本身描述了重复事件类型的一般特征 —— 其名称、重要性、经济领域(GDP、就业、价格等)、周期性(每日、每月、每季度)以及其值的单位。至关重要的是,它并不代表单次事件,而是事件的“模板”或定义(例如,“美国 CPI 发布”),日历服务可以在其发布的历史记录中多次安排该事件。

这些事件类型的实际计划发生情况(包括时间戳、实际值和预测值以及任何修订)保存在单独的 MqlCalendarValue 结构表中。每个 MqlCalendarValue 记录都包含一个 event_id(链接到 MqlCalendarEvent 中的模板)、一个精确的时间和周期,以及四个数值字段(actual_value、forecast_value、prev_value、revious_prev_value),这些字段可能尚未填充。HasActualValue() 和 GetActualValue() 等辅助方法可以轻松检查和检索真实世界的值(自动从日历的内部“ppm”表示缩减)。

这种关系设计 —— 国家、事件类型,然后是事件发生 —— 确保数据不会不必要地重复:例如,季度 CPI 条目都指向同一个 CPI 定义,该定义包含其重要性级别、单位和频率。通过理解这些结构以及它们如何相互引用,我们可以精确地过滤、格式化和显示我们关心的即将发生的事件,同时保持代码的高效性和可维护性。

新闻标题 EA

设置用户控件

首先,我们决定交易者应该能够调整哪些参数。我们公开了每个重要通道的滚动速度(InpSpeedHigh、InpSpeedMed、InpSpeedLow)、新闻滚动速度(InpNewsSpeed)和帧间隔(InpTimerMs)。我们还允许用户选择滚动位于顶部还是底部(InpPositionTop),它应该距离图表边缘多远(InpTopOffset),以及要显示哪些通道(ShowHigh、ShowMed、ShowLow)。通过将这些内容隔离到一个简洁的“用户输入”模块中,任何人都可以快速调整行为,而无需深入了解实现细节。

//+------------------------------------------------------------------+
//| 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 对象 —— 一个用于三个事件通道,一个用于新闻滚动条。最后,我们定义了 CEvent 类和三个动态数组(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;

定位和排序辅助功能

为了保持主要逻辑的简洁性,我们提取出两个小的辅助函数。SetCanvas() 根据用户设置,将 canvas 对象放置在图表的顶部或底部。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 的经济日历,查找今天午夜至 24 小时后发生的事件。我们会跳过时间戳已经过去的任何事件。每个有效事件都会被包装在一个 CEvent 对象中,然后根据其重要性放入高/中/低(high/medium/low)数组中。最后,我们对每个通道进行排序,使最早的事件出现在该通道的滚动条的最前面。

//+------------------------------------------------------------------+
//| 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),并将每个内联框涂成红色、橙色或白色,我们可以立即给交易员一个视觉提示:红色表示影响最大的发布(例如美联储利率决定、非农就业数据),橙色表示影响中等的发布(例如消费者物价指数、零售销售数据),白色表示影响较小的发布(例如次要讲话、低级别数据)。

实际上,这有助于交易者一眼看出在重要事件临近时是否需要收紧止损,甚至暂停自动交易策略。如果没有该过滤器和颜色编码标签(由 MqlCalendarEvent 中的重要性字段提供支持),数十个条目的滚动列表很快就会变成噪音而不是信号。

渲染滚动通道

DrawLane() 封装了一个水平条带的逻辑。我们选择等宽字体(“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() 中,我们将三个事件通道垂直分层,然后将新闻占位符放在下方(或上方,取决于位置)。在将事件绘制到 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 定位两个画布。然后我们绘制第一帧并启动毫秒计时器。 

OnTimer() 只是简单地重新定位画布(以便用户可以实时翻转 InpPositionTop),每天重新加载一次事件,调整图表大小,并再次调用 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 具有四个通道,每个通道都能优化主速度,以 20 FPS 的帧速率运行。即将发生的新闻事件以不同颜色的矩形显示,颜色代表其重要性级别。

测试 News Headline EA.mq5

测试新闻标题 EA

上图显示了新闻标题 EA 的成功部署。它按预期显示,以流畅无缝的动画显示所有即将发生的新闻事件。


结论

这标志着另一场激动人心的开发讨论的结束,最终为交易者和开发人员提供了一个实用而富有洞察力的工具。我们成功地利用 Canvas 类的强大功能实现了高效的渲染和清晰的视觉效果 —— 这种方法也可以作为界面开发中的一条有价值的捷径。

在整个项目中,我们学习了如何检索经济日历数据,并以有意义、对交易者友好的格式显示。其结果是,对即将发生的新闻事件有了清晰、简约的看法,就在图表上 —— 解决了基于新闻的交易工具中长期存在的挑战。

展望未来,该 EA 的第二版将整合实时新闻源的 API 访问,从而实现更加动态的更新。此外,Canvas 还可以重新用于显示其他与交易相关的数据,使这种方法具有很高的灵活性。

图表上清晰地显示了具有重大影响的新闻,交易者可以做出更明智的决定 —— 选择是否参与市场或保持观望。作为最佳实践,通常建议在重大新闻发布前后几个小时避免交易,以降低风险并避免波动性飙升

欢迎您在评论区分享您的想法或提问。您还可以在本文下方找到附件。

文件名 描述
NewsTicker.mq5 主 EA 交易源代码,使用 CCanvas 类直接在图表上实现三通道滚动经济日历和新闻占位符滚动条,每个通速度可调,并带有内联重要性框和实时倒计时。

本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/18299

附加的文件 |
基于机器学习构建均值回归策略 基于机器学习构建均值回归策略
本文提出了另一种基于机器学习的原创交易系统构建方法,该方法运用聚类分析和交易标注来设计均值回归策略。
台球优化算法(BOA) 台球优化算法(BOA)
BOA方法灵感源自经典的台球运动,它将寻求最优解的过程模拟为一场游戏:球体致力于落入代表最佳结果的球袋之中。本文将探讨BOA的基本原理、数学模型及其在解决各类优化问题中的效率。
新手在交易中的10个基本错误 新手在交易中的10个基本错误
新手在交易中会犯的10个基本错误: 在市场刚开始时交易, 获利时不适当地仓促, 在损失的时候追加投资, 从最好的仓位开始平仓, 翻本心理, 最优越的仓位, 用永远买进的规则进行交易, 在第一天就平掉获利的仓位,当发出建一个相反的仓位警示时平仓, 犹豫。
MQL5 简介(第 17 部分):构建趋势反转 EA 交易 MQL5 简介(第 17 部分):构建趋势反转 EA 交易
本文教初学者如何在 MQL5 中构建一个基于图表形态识别的 EA 交易系统,该系统利用趋势线突破和反转进行交易。通过学习如何动态检索趋势线值并将其与价格走势进行比较,读者将能够开发出能够识别和交易图表形态(如上升和下降趋势线、通道、楔形、三角形等)的 EA 交易。