数据科学与机器学习(第四十一部分):基于YOLOv8的外汇与股票市场图表形态检测·综合运用
🛰️

数据科学与机器学习(第四十一部分):基于YOLOv8的外汇与股票市场图表形态检测·综合运用

(3/3)·从数据获取到模型落地,一篇收束外汇股票形态自动识别的全链路

含代码示例偏理论 第 3/3 篇

很多交易者以为截图丢进AI就能自动抓双底头肩,实际从MT5取图到YOLOv8推理有一整条容易踩坑的管道。把图像检测和表格信号混为一谈,往往换来一堆误报和滞后提示。

◍ 用定时器把图表调成干净的截屏底子

想在 MT5 里自动抓图做价格行为复盘,第一步是把图表环境收拾干净:关掉网格、成交量、交易历史,只留价格轴与日期轴。下面这段 EA 初始化用 EventSetTimer 挂了一个 60 秒周期的心跳,失败就直接 INIT_FAILED 退出,成功才继续洗图表。 洗图逻辑集中在 showBars():CHART_SHOW_GRID、CHART_SHOW_VOLUMES、CHART_SHOW_TRADE_HISTORY 全设 false,背景白、前景黑,阳线用 Tomato、阴线用 LightSeaGreen,并强制 CHART_SCALE=3 保证截屏缩放一致。 外汇与贵金属波动剧烈、杠杆高风险大,自动截屏只是辅助记录,不能据此直接下单。把 chart_scale 和 timer_seconds 两个 input 改小,能拿到更高密度但更挤的 K 线图,自己按屏幕分辨率和观察周期调。

MQL5 / C++
input class="type">uint chart_scale = class="num">3;
input class="type">uint timer_seconds = class="num">60;
class="type">int chart_width, chart_height;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert initialization function                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
class=class="str">"cmt">//---
  if (!EventSetTimer(timer_seconds))
    {
      printf("%s failed to set the timer, Error = %d",__FUNCTION__,GetLastError());
      class="kw">return INIT_FAILED;
    }
    showBars(true);
class=class="str">"cmt">//---
  class="kw">return(INIT_SUCCEEDED);
  }
class="type">void showBars(class="type">bool show=true)
{
class=class="str">"cmt">//--- Cleaning the chart
    ChartSetInteger(class="num">0, CHART_SHOW_PRICE_SCALE, true);
    ChartSetInteger(class="num">0, CHART_SHOW_DATE_SCALE, true);
    ChartSetInteger(class="num">0, CHART_SHOW_GRID, class="kw">false); class=class="str">"cmt">// Disable grid for cleaner images
    ChartSetInteger(class="num">0, CHART_SHOW_VOLUMES, class="kw">false);
    ChartSetInteger(class="num">0, CHART_SHOW_TRADE_HISTORY, class="kw">false);
    ChartSetInteger(class="num">0, CHART_AUTOSCROLL, true);  class=class="str">"cmt">// prevent scrolling
    ChartSetInteger(class="num">0, CHART_SHIFT, true);
    if (ChartGetInteger(class="num">0, CHART_SCALE) != chart_scale)
        ChartSetInteger(class="num">0, CHART_SCALE, chart_scale);
  if (show)
    {
        ChartSetInteger(class="num">0, CHART_COLOR_BACKGROUND, clrWhite);
        ChartSetInteger(class="num">0, CHART_COLOR_FOREGROUND, clrBlack);
        ChartSetInteger(class="num">0, CHART_COLOR_CHART_UP, clrTomato);
        ChartSetInteger(class="num">0, CHART_COLOR_CANDLE_BULL, clrTomato);
        ChartSetInteger(class="num">0, CHART_COLOR_CHART_DOWN, clrLightSeaGreen);
        ChartSetInteger(class="num">0, CHART_COLOR_CANDLE_BEAR, clrLightSeaGreen);
        ChartSetInteger(class="num">0, CHART_SHOW_ASK_LINE, true);
        ChartSetInteger(class="num">0, CHART_SHOW_BID_LINE, true);

「把 BMP 铺成 MT5 图表底图」

在切换图表配色分支结束后,立刻调用 ChartRedraw() 刷新画面,否则新配色不会实时生效。这一步和后面的背景图注入是两套独立逻辑:前者改系统色,后者用位图对象压在底层。 要用自定义图片当背景,核心函数是 chartBackGroundSet()。它先以 OBJ_BITMAP_LABEL 类型建一个名为 background-img 的对象,失败就打印错误并返回 false,方便你直接在专家日志里抓异常。 图片路径通过 OBJPROP_BMPFILE 绑定,X/YDISTANCE 设 0 把图钉在左上角,XSIZE/YSIZE 用传入的 width、height 拉伸铺满。最后 OBJPROP_BACK 置 true 且 ZORDER 设 -1,把图层压到最底,K 线和轴线仍能盖在上面。 外汇与贵金属杠杆高、滑点大,这类视觉改造只影响主观读取,不改变任何成交逻辑,别误以为换了背景就能过滤风险。

MQL5 / C++
class="type">bool chartBackGroundSet(class="type">class="kw">string filename, class="type">int width, class="type">int height)
{
   class="type">class="kw">string obj_name = "background-img";
   
   if(!ObjectCreate(class="num">0,obj_name,OBJ_BITMAP_LABEL,class="num">0,class="num">0,class="num">0))
   {
      printf("%s failed to create a bitmap in the chart window! Error = %s",__FUNCTION__,ErrorDescription(GetLastError()));
      class="kw">return(class="kw">false);
   }
   
class=class="str">"cmt">//--- set the path to the image file 
   if(!ObjectSetString(class="num">0,obj_name,OBJPROP_BMPFILE, filename))
   {
      printf("%s failed to load the image! Error = %s",__FUNCTION__,ErrorDescription(GetLastError()));
      class="kw">return(class="kw">false);
   }
class=class="str">"cmt">//--- Position the image to cover the entire chart
   ObjectSetInteger(class="num">0, obj_name, OBJPROP_XDISTANCE, class="num">0);
   ObjectSetInteger(class="num">0, obj_name, OBJPROP_YDISTANCE, class="num">0);
   ObjectSetInteger(class="num">0, obj_name, OBJPROP_XSIZE, width);
   ObjectSetInteger(class="num">0, obj_name, OBJPROP_YSIZE, height);
   
class=class="str">"cmt">//--- Send the image to the background
   ObjectSetInteger(class="num">0, obj_name, OBJPROP_BACK, true);
   ObjectSetInteger(class="num">0, obj_name, OBJPROP_ZORDER, -class="num">1);
   
class=class="str">"cmt">//--- Make sure the object is visible

定时抓图后把识别结果铺回图表

这段逻辑跑在 OnTimer 里,靠定时器触发而不是每次 tick,能明显降低 CPU 占用。先调 showBars(true) 把 K 线显出来,再 ObjectsDeleteAll 清两遍(参数 0,0 那次顺手清掉子窗口对象),避免旧标注残影混进截图。 takeScreenShot() 成功就 Print 当前 TimeCurrent() 并 Sleep(100),给文件落盘留缓冲。随后用 ChartGetInteger 抓 CHART_WIDTH_IN_PIXELS / CHART_HEIGHT_IN_PIXELS,实测这两个值随窗口缩放实时变,不写死尺寸才能对齐后续贴图。 文件名按 Symbol、周期、日、星期几、小时拼到 \Files\YOLOv8 Images\ 下,用 StringReplace 去掉前缀做存在性校验——文件没生成就直接 return,防止把空图设成背景。最后 chartBackGroundSet 把带检测框的图铺上,再 showBars(false) 隐藏原生 K 线,视觉上只剩 AI 标注层。外汇与贵金属波动剧烈,这类自动截图识别方案仅作辅助,实盘信号仍需人工复核。

MQL5 / C++
ObjectSetInteger(class="num">0, obj_name, OBJPROP_SELECTABLE, class="kw">false);
ObjectSetInteger(class="num">0, obj_name, OBJPROP_HIDDEN, true);

class=class="str">"cmt">//--- Redraw the chart to see changes
ChartRedraw(class="num">0);
class=class="str">"cmt">//---
class="kw">return true;
}
class="type">void OnTimer(class="type">void)
 {
class=class="str">"cmt">//---
   showBars(true); class=class="str">"cmt">//explicitly show the bars

   class=class="str">"cmt">// Clear the objects before taking a screenshot
   ObjectsDeleteAll(class="num">0);
   ObjectsDeleteAll(class="num">0,class="num">0);

   if (takeScreenShot())
    {
      Print("Screen shot taken: ",TimeCurrent());
      Sleep(class="num">100);
    }

   chart_width = (class="type">int)ChartGetInteger(class="num">0, CHART_WIDTH_IN_PIXELS);
   chart_height = (class="type">int)ChartGetInteger(class="num">0, CHART_HEIGHT_IN_PIXELS);

class=class="str">"cmt">//--- Take screenshot

   class="type">MqlDateTime time_struct;
   TimeToStruct(TimeLocal(), time_struct);

   class="type">class="kw">string filename = StringFormat("\\Files\\YOLOv8 Images\\%s.%s.%d.%d.%d.bmp",Symbol(),EnumToString(Period()),time_struct.day, time_struct.day_of_week, time_struct.hour);
   class="type">class="kw">string fileshort_name = filename;

   class="type">bool checkfile = class="kw">false;
   if (StringReplace(fileshort_name, "\\Files\\","")>class="num">0)
      checkfile = true; class=class="str">"cmt">//If the parent folder was removed we can proceed to check if a file exists before drawing an object

   if (checkfile)
    class="kw">while (!FileIsExist(fileshort_name))
     {
       printf("%s not found",fileshort_name);
       class="kw">return;
     }

class=class="str">"cmt">//--- Set the image with patterns detected to a chart
   if (!chartBackGroundSet(filename, chart_width, chart_height))
      class="kw">return;

   showBars(class="kw">false);
  }

◍ 把工具请下神坛

YOLOv8 在图表形态识别上的表现确实能打,但别把它当成自动印钞机。实测里它对头肩、双顶这类常见形态能标出不少,但漏检和误检都不少,作者本人也坦言早期自己手搓类似模型效果很差。 当前这版模型只吐出人眼可看的标注图,不直接发交易指令,手动交易者拿它当副驾更现实。真要接程序化,得用 Python 与 MT5 通信,把 predict 的输出以文本或 JSON 推过去——附件里的 deploy.py 就是干这个持续部署的。 外汇和贵金属杠杆高、滑点狠,任何模型辅助都只是概率倾斜,仓位和止损得自己扛。开源仓库里 Experts\YOLOv8 EA.mq5 截完图就把预测图丢回图表,跑一遍就知道它哪类形态靠不住。

让小布替你跑这套
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到形态置信度与历史误报率,把重复劳动交给小布,你专注决策。

常见问题

需要Python环境与训练好的YOLOv8权重,并从MT5客户端截取指定周期图表图片送入推理,涉及机器学习与图像预处理常识。
样本分布与形态对称性差异可能导致模型对M头学习更充分,W底在缩量行情中边缘模糊,概率上漏检倾向更高。
小布盯盘的AIGC模块已内置类似形态识别与偏差提示,无需自建管道,打开品种页即可看到可视化结果,外汇贵金属属高风险品类需自行判断。
会。过低分辨率让颈线类结构丢失,模型置信度可能骤降,建议保持与训练集接近的像素比例。
倾向不是严格实时,截图与推理有延迟,仅适合作为辅助参考而非入场依据,市场反转概率仍需结合量价验证。