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

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

第 2/3 篇

「用YOLOv8给MT5图表截图标形态」

官方文档里给的 ultralytics 快速上手代码现在跑不通,因为库里早就没了 YOLOvv8 这个写法,正确入口是 from ultralytics import YOLO,再加载本地 model.pt。把模型文件丢进工作目录下的 Models 子文件夹,predict 传图片名加 save=True,就能自动吐出框好形态的图。 直接调原生 predict 太单薄,撑不起批量扫 MT5 截图的需求。我们包一个 YOLOv8deploy 类,构造时吃进模型对象与截图文件夹路径,再挂一个 _get_images 扫指定后缀,返回(图片数, 文件名列表)元组,批量前先知道池子里有几张。 单张预测由 predict_image 负责:先 os.path.exists 拦掉不存在的文件,否则直接打印未找到并 return;通过后调 model.predict 拿 results,循环里取 result.boxes 就是检测框加置信度。hist=False 时走 cv2 弹窗看标注图,适合人工抽检。 实测取 EURUSD.PERIOD_H1 的 11 号截图跑通,预测结果和图都落在当前目录 runs\predict* 下。外汇和贵金属波动剧烈、杠杆高风险大,模型框出的形态只作概率参考,别当入场指令。

MQL5 / C++
from ultralytics class="kw">import YOLOvv8
model = YOLOvv8.from_pretrained("foduucom/stockmarket-pattern-detection-yolov8")
source = &class="macro">#x27;http:class=class="str">"cmt">//images.cocodataset.org/val2017/class="num">000000039769.jpg&class="macro">#x27;
model.predict( save=True)
from ultralytics class="kw">import YOLO
class="kw">import os
model = YOLO(os.path.join(&class="macro">#x27;Models&class="macro">#x27;,&class="macro">#x27;model.pt&class="macro">#x27;))
model.predict("image_name.png", save=True)
class YOLOv8deploy:
    
    def __init__(self, model: YOLO, images_folder: str):
        
        """A simple class for deploying YOLOv8 model for detecting trading patterns in chart images
        Args:
            model(YOLO): YOLO model object
            images_folder(str): A path where images will be imported from
        """
        
        self.model = model
        self.images_folder = images_folder
def _get_images(self, folder: str, img_extensions: list=[&class="macro">#x27;*.png&class="macro">#x27;, &class="macro">#x27;*.jpg&class="macro">#x27;, &class="macro">#x27;*.jpeg&class="macro">#x27;]) -> tuple:
    
   """ A function to help us detect the number of images present in a folder
   Args:
       folder(str): A path where images are located
       img_extensions(list, optional): Image filenames extensions. Defaults to [&class="macro">#x27;*.png&class="macro">#x27;, &class="macro">#x27;*.jpg&class="macro">#x27;, &class="macro">#x27;*.jpeg&class="macro">#x27;].
   Returns:
       tuple: Returns the number of images present in a folder and their names
   """
    
    image_files = []
    for ext in img_extensions:
        image_files.extend(glob.glob(os.path.join(folder, ext)))
    class="kw">return (len(image_files), image_files) # Get the number of images and their names
def predict_image(self, img_name: str, hist: class="type">bool=True):
        
        """This function predicts a single image
        Args:
            img_name(str): name of the image
            hist(class="type">bool, optional): When set to class="kw">false it means the function isn&class="macro">#x27;t predicting multiple instances and the outcome will be displayed.
            
            Defaults to True.
        """
        
        if os.path.exists(img_name) == False: # Check if an image exists
            print(f"Failed to detect patterns, {img_name} not found")
            class="kw">return
            
        results = self.model.predict( save=True) # Predict an image 
        # Loop through the results
        for result in results:
            boxes = result.boxes  # Contains bounding boxes and confidence

◍ 把检测回环跑起来并弹出标注图

这段脚本把 YOLOv8 的推理结果接到了本地文件循环:先拿 result.names 做类别索引到名称的映射,再逐个遍历 boxes,取出 cls_id 与 conf,用 f"{label} (confidence: {conf:.2f})" 打印出「Detected: 类名 (置信度: 两位小数)」。若 hist=False 且磁盘上真有保存图,就 cv2.imshow 弹窗看标注,waitKey(0) 卡住直到你按键。 路径写死在 C:\Users\Omega Joctan\...\MQL5\Files\Screenshots,实跑前必须改成你本机 Terminal 哈希目录下的 Files\Screenshots,否则 glob 不到图。示例里 symbol="EURUSD"、timeframe="PERIOD_H1"、imgs=100,单图调用传的是 EURUSD.PERIOD_H1.11.png。 predict_images() 用 _get_images 捞整个文件夹,对每张调一次 predict_image;外汇与贵金属波动剧烈、模型误检概率不低,弹窗看到的形态仅作辅助参考,不能直接当入场信号。 别把路径当摆设 MT5 每个 Terminal 实例的哈希串不同,复制代码后第一件事就是打印 os.listdir 确认 Screenshots 里有 png,再跑 detector,否则只会循环刷 No detections。

MQL5 / C++
names = result.names  # Class index to name mapping
if boxes is not None and len(boxes) > class="num">0:
    for box in boxes:
        cls_id = class="type">int(box.cls[class="num">0])  # class id
        conf = box.conf[class="num">0].item()  # confidence score
        label = names[cls_id]

        print(f"Detected: {label} (confidence: {conf:.2f})")

        # Open the saved image if this is a single(non-historical) run
        if not hist:
            base_name = os.path.splitext(os.path.basename(img_name))[class="num">0] + ".jpg"
            saved_path = os.path.join(result.save_dir, base_name)

            print("saved path: ",saved_path)

            if os.path.exists(saved_path):
                print(f"Opening detected image: {saved_path}")
                img = cv2.imread(saved_path)
                cv2.imshow("Detected Patterns", img)
                cv2.waitKey(class="num">0)
                cv2.destroyAllWindows()
else:
    print("No detections.")
images_path = r"C:\Users\Omega Joctan\AppData\Roaming\MetaQuotes\Terminal\F4F6C6D7A7155578A6DEA66D12B1D40D\MQL5\Files\Screenshots"  # Change this for to the right path on your pc :)
symbol = "EURUSD"
timeframe = "PERIOD_H1"
imgs = class="num">100
pattern_detector = YOLOv8deploy(model=model, images_folder=images_path)
pattern_detector.predict_image(img_name=os.path.join(images_path, f"{symbol}.{timeframe}.{class="num">11}.png"),
                              hist=False)
def predict_images(self):
    _, image_names = self._get_images(self.images_folder)  # Get all images from a folder
    for image_name in image_names:
        self.predict_image(img_name=image_name)
pattern_detector.predict_images()

YOLOv8识图的三道硬伤

模型在 MT5 上跑目标检测,第一道坎是图表呈现本身。K线配色要干净、背景和实体对比拉满;缩放比例直接决定输入质量——MT5 里把图表拉太近,吞没形态的右肩常被截掉,拉太远则杂波灌满画布,需要在缩放等级与截图尺寸间找平衡点。 第二道坎来自行情。剧烈波动会制造假突破和畸形 K 线,这类样本下模型几乎必然漏检或误检,外汇与贵金属的高波动时段风险尤其突出,别指望它替你兜底。 第三道坎是训练偏置。若某种市场特有形态在训练集里样本稀薄,检测器在该品种或周期上就歇菜。只把它丢在形态规整、样本充足的盘面上,才可能拿到能用的命中率。

「把YOLOv8标注图塞进MT5背景层」

MQL5原生读不了YOLOv8直接吐出的推理图片,但它认.BMP位图,也允许把图片对象嵌进图表背景层,这就给了落地窗口。思路不复杂:让Python端把标注好形态的JPG转成BMP,EA再定时把这张图铺到背景,同时把前景K线藏掉,视觉上就是一张带预测的图表在刷。 具体跑通时,Python侧 predict_image 负责单图推理并落盘。下面这段是核心保存逻辑,逐行拆一下:def predict_image 定义函数,入参是图片名和保存路径;先 os.path.exists 判缺失,没有就打印失败并返回;self.model.predict 执行推理,save=True 且 project/name 指向 MQL5\Files\YOLOv8 Images 目录;结果里取 boxes 和 names,随后 convert_jpg_to_bmp 把JPG转BMP供MT5用;最后遍历 boxes 取 cls_id 拿到形态类别。 EA侧用定时器每60秒截一次图存到 Screenshots,Python读图预测写回 YOLOv8 Images,EA再读BMP绘背景。showBars(false) 会把K线颜色设成和背景一致,元素全隐,避免前景叠图乱套。实测挂 EURUSD 1小时、定时器60秒,约60秒后图表收到模型图。 别把固定尺寸当理所当然 这套背景图法子比较糙,它默认图表尺寸在定时器周期内不变。你拖动调整窗口大小,绘图流程会断一小段时间;真要稳,得换更严谨的背景可视化方案,不然实盘盯盘时图片突然消失挺误事。外汇与贵金属波动剧烈、杠杆高风险大,任何视觉辅助都只是参考,不替代风控。

MQL5 / C++
def predict_image(self, img_name: str, save_path: str):
    """This function predicts a single image
    Args:
        img_name(str): name of the image
        hist(class="type">bool, optional): When set to class="kw">false it means the function isn&class="macro">#x27;t predicting multiple instances and the outcome will be displayed.
        Defaults to True.
    """
    if os.path.exists(img_name) == False: # Check if an image exists
        print(f"Failed to detect patterns, {img_name} not found")
        class="kw">return
    
    results = self.model.predict(
                                save=True,
                                project=save_path,
                                name="YOLOv8 Images",
                                exist_ok=True
                                ) # Predict an image 
                                
    # Loop through the results
    for result in results:
        boxes = result.boxes  # Contains bounding boxes and confidence
        names = result.names  # Class index to name mapping
        # Convert a jpg image to bmp suitable for MQL5 diplay purposes
        
        base_name = os.path.splitext(os.path.basename(img_name))[class="num">0] + ".jpg"
        saved_path = os.path.join(result.save_dir, base_name)
        convert_jpg_to_bmp(saved_path, os.path.join(result.save_dir, os.path.splitext(os.path.basename(img_name))[class="num">0] + &class="macro">#x27;.bmp&class="macro">#x27;))
        if boxes is not None and len(boxes) > class="num">0:
            for box in boxes:
                cls_id = class="type">int(box.cls[class="num">0])  # class id

◍ 把图表截图喂给模型做定时识别

上面这段 Python 脚本把 MT5 终端 Files 目录下的 Screenshots 子目录当作图像源,用 YOLOv8 部署类对 EURUSD 的 H1 截图做形态推断。注意路径写死在 C:\Users\Omega Joctan\AppData\Roaming\MetaQuotes\Terminal\F4F6C6D7A7155578A6DEA66D12B1D40D\MQL5\Files,你本机 Terminal 哈希不同必须改,否则读不到图。 文件名拼法里用了 now.weekday()+1 当星期序号、再用 %H 的 24 小时制字符串,组合成 EURUSD.PERIOD_H1.日.星期.小时.png。若你截图脚本没按这个命名落盘,predict_image 会直接报找不到文件,不会静默跳过。 convert_jpg_to_bmp 这个函数值得单独留着:它把任意模式(CMYK / 灰度)的 JPG 先转 RGB 再存 24 位 BMP。MT5 的 FileOpen 读裸图有时挑格式,留这道转换能少踩坑。外汇和贵金属杠杆高,模型识别出的形态只作概率参考,别拿它当进场指令。 最后用 schedule.every(1).minutes.do(scheduledYOLOv8Run) 把识别挂成每分钟跑一次。实测中若截图本身每秒才更新,1 分钟粒度足够覆盖,且不会把 CPU 吃满;想更密就改这个数,但注意 Terminal 共享目录的 IO 竞争。

MQL5 / C++
conf = box.conf[class="num">0].item()  # confidence score
label = names[cls_id]
print(f"Detected: {label} (confidence: {conf:.2f})")
else:
    print("No detections.")
def convert_jpg_to_bmp(jpg_path, bmp_path):
    """
    Convert a JPG image to class="num">24-bit RGB BMP format
    
    Args:
        jpg_path(str): Path to input JPG file
        bmp_path(str): Path to save output BMP file
    """
    try:
        # Open the JPG image
        with Image.open(jpg_path) as img:
            # Convert to RGB if not already(handles CMYK, grayscale, etc.)
            if img.mode != &class="macro">#x27;RGB&class="macro">#x27;:
                img = img.convert(&class="macro">#x27;RGB&class="macro">#x27;)
            
            # Save as class="num">24-bit BMP
            img.save(bmp_path, &class="macro">#x27;BMP&class="macro">#x27;)
            
            print(f"Successfully converted {jpg_path} to {bmp_path}")
            class="kw">return True
    
    except Exception as e:
        print(f"Conversion failed: {str(e)}")
        class="kw">return False
files_path = r"C:\Users\Omega Joctan\AppData\Roaming\MetaQuotes\Terminal\F4F6C6D7A7155578A6DEA66D12B1D40D\MQL5\Files"
images_path = os.path.join(files_path, "Screenshots") # Change this for to the right path on your pc :)
# ....
# ....
pattern_detector = YOLOv8deploy(model=model, images_folder=images_path)
pattern_detector.predict_image(img_name=image_filename, save_path=files_path)
files_path = r"C:\Users\Omega Joctan\AppData\Roaming\MetaQuotes\Terminal\F4F6C6D7A7155578A6DEA66D12B1D40D\MQL5\Files"
images_path = os.path.join(files_path, "Screenshots") # Change this for to the right path on your pc :)
symbol = "EURUSD"
timeframe = "PERIOD_H1"
def scheduledYOLOv8Run():
    now = class="type">class="kw">datetime.now() # Get the current local date and time
    # Extract current day and hour
    date = now.day
    current_day = now.weekday()  # e.g., &class="macro">#x27;Wednesday&class="macro">#x27;
    current_hour = now.strftime("%H")  # e.g., &class="macro">#x27;class="num">14&class="macro">#x27; for class="num">2 PM in class="num">24-hour format
    image_filename = os.path.join(images_path, f"{symbol}.{timeframe}.{date}.{current_day+class="num">1}.{current_hour}.png")
    pattern_detector = YOLOv8deploy(model=model, images_folder=images_path)
    pattern_detector.predict_image(img_name=image_filename,
                                  save_path=files_path)
    print(f"Processed image at {class="type">class="kw">datetime.now().strftime(&class="macro">#x27;%Y-%m-%d %H:%M:%S&class="macro">#x27;)}")

# Schedule the pattern detection after every minute(s)
schedule.every(class="num">1).minutes.do(scheduledYOLOv8Run)

常见问题

用平台自带的图表截图函数按固定周期存图,再写脚本把图片路径传给YOLOv8推理接口,定时触发即可。
可以,把模型输出的带框图片转成背景资源加载到图表层,注意调低透明度避免遮挡K线。
可以,小布能定时拉取标注图并提炼出疑似形态清单推给你,省去自己跑回环看图的麻烦。
这是常见硬伤:训练集噪声、尺度敏感和背景干扰都会导致误判,建议人工复核低置信度框。
会,固定间隔截图必然有盲区,把间隔压到1~2分钟并叠加事件触发能缓解漏检。