English Русский Español Deutsch 日本語
preview
数据科学与机器学习(第四十一部分):基于YOLOv8的外汇与股票市场图表形态检测

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

MetaTrader 5交易系统 |
303 0
Omega J Msigwa
Omega J Msigwa

内容


引言

在机器学习与人工智能(AI)领域,金融市场形态检测是一项极具挑战性的任务。尽管对人类而言这看似轻而易举,但要让机器自动检测并解读这些形态,却需要大量工作。这是因为交易中常用的是表格型二维数据,而形态检测需要处理二维图像数据(如.png、.jpg等格式)。

市场中有大量交易者的策略依赖价格行为与特定图表形态,例如:

  • 上升与下降阶梯形态
  • 上升三角形
  • 下降三角形
  • 对称三角形
  • 旗形 
  • 楔形
  • 双顶
  • 双底
  • 头肩
  • 圆弧顶或圆弧底
  • 杯柄
  • 以及其他更多形态

在编程实现中,K线形态、指标信号这类模式无需复杂代码即可识别,而上文提到的图表技术形态则复杂得多。

哪怕只是检测一个简单的W底(双底),也需要编写复杂、良好且优化的代码,那为何不直接使用AI帮我们完成这项繁琐的工作呢?

为了利用人工智能来解决这一问题,本文将介绍一个我在Hugging Face平台上发现的、非常出色的模型 —— YOLOv8

该模型能够以不错的精度从图片和图表中检测出各类金融图表形态。

想要完全理解本文内容,需要具备机器学习Python编程语言的基础知识。


什么是YOLOv8?

根据其官方文档:

YOLOv8s是基于YOLO(You Only Look Once,即“你只看一次”)框架的目标检测模型。专为实时检测股票交易数据截图中的各类图表形态而设计。

模型通过自动化分析图表形态,为交易者和投资者提供及时的参考,帮助其做出更有依据的决策。

该模型在多样化数据集上进行了微调优化,能够在实盘交易场景中高精度地检测并分类股票市场形态。

到目前为止,该模型已能精准预测多种形态,其中包括:

头肩顶:

头肩顶

形态简要说明

该形态表明市场可能出现反转。

交易者认为,当出现三组峰谷结构,且中间峰值最高时,意味着股价即将开始下跌。

颈线表示看跌交易者开始卖出的价位。了解更多。

头肩底:

这是头肩顶的反转形态。

M头形态:

M头形态

形态简要说明

该形态也被称为双顶,由两个连续的圆弧顶组合而成。

这类圆弧顶通常出现在长期上涨趋势之后,是看跌反转的信号。

当双顶形态形成时,第二个顶部通常略低于第一个顶部的高点,意味着此处存在强阻力,看涨动能已衰竭。

双顶形态并不常见,其出现往往表明投资者正在上涨趋势中获取最后一波利润。了解更多。

W底形态:

w底

该形态原理与M头一致,但方向完全相反。了解更多。

Stock Line形态:

我在文档和网络上均未找到相关参考资料,我们暂时跳过这一项。

三角形形态:

这类形态一旦有效确认,可作为持续形态;如果形态失效,则会成为强烈的反转形态。

交易者利用三角形形态,来精准找到个股或品种在上涨或下跌趋势之后、波动区间不断收窄的时机。了解更多。


从MetaTrader 5中获取图表形态

由于YOLOv8是基于图像(图片数据)工作的,我们需要提取大量高质量的图表图片,用于模型测试和演示。

幸运的是,MetaTrader 5平台及MQL5语言提供了一项功能,可以对任意图表和品种进行截图。我们用一个脚本来采集一批图片。

文件:ChartScreenshots.mq5

#property script_show_inputs 

input uint BarsToCapture = 1000; //Total Bars
input uint BarsShift = 50; //Bars Shift
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
{
//--- Save current chart position

    long firstVisibleBar = ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);
    long chartShift = ChartGetInteger(0, CHART_SHIFT);
    double priceMax = ChartGetDouble(0, CHART_PRICE_MAX);
    double priceMin = ChartGetDouble(0, CHART_PRICE_MIN);
    
//--- Set chart properties for clean screenshots

    ChartSetInteger(0, CHART_SHOW_PRICE_SCALE, true);
    ChartSetInteger(0, CHART_SHOW_DATE_SCALE, true);
    ChartSetInteger(0, CHART_SHOW_GRID, false); // Disable grid for cleaner images
    ChartSetInteger(0, CHART_SHOW_VOLUMES, false);
    ChartSetInteger(0, CHART_SHOW_TRADE_HISTORY, false);
    ChartSetInteger(0, CHART_AUTOSCROLL, false);  // prevent scrolling  
    
    int steps = (int)MathCeil((double)BarsToCapture / BarsShift);
    
    for(int i = 0; i < steps; i++)
    {
        // Shift chart view
        int shift = i * (int)BarsShift;
        ChartNavigate(0, CHART_END, -shift);
        
        // Wait a moment for the chart to update
        Sleep(500);
        
        // Take screenshot
        string filename = StringFormat("Screenshots\\%s.%s.%d.png", Symbol(), EnumToString(Period()), i+1);
        
        FileDelete(filename); //we delete a previous screenshot with the same name
        if(!ChartScreenShot(0, filename, 640, 480, ALIGN_CENTER))
         {
            printf("Failed to take screenshot #:%d Error = %d", i+1, GetLastError());
            continue;
         }
        else
         {
            printf("Screenshot saved: %s", filename);
         }
    }

//--- Restore original chart position

    ChartNavigate(0, CHART_END, -(int)firstVisibleBar);
    ChartSetDouble(0, CHART_PRICE_MAX, priceMax);
    ChartSetDouble(0, CHART_PRICE_MIN, priceMin);
    ChartSetInteger(0, CHART_SHIFT, chartShift);
}

该函数会对当前图表截取多张截图,默认覆盖1000根K线,每张截图相比上一张向后平移50根K线。

在调用截图函数之前,我们必须确保图表界面尽可能简洁。哪怕是少量干扰元素,比如网格线、成交量等,都可能干扰模型检测图表上的主要形态。

    ChartSetInteger(0, CHART_SHOW_PRICE_SCALE, true);
    ChartSetInteger(0, CHART_SHOW_DATE_SCALE, true); // Showing the timescale on a chart
    ChartSetInteger(0, CHART_SHOW_GRID, false); // Disable grid for cleaner images
    ChartSetInteger(0, CHART_SHOW_VOLUMES, false); //Prevent displaying the tick volumes
    ChartSetInteger(0, CHART_SHOW_TRADE_HISTORY, false); //Prevent drawing arrows that displays trading history
    ChartSetInteger(0, CHART_AUTOSCROLL, false);  // prevent scrolling  

所有图片将保存至路径:MQL5\Files\Screenshots


基于YOLOv8进行形态检测

    根据官方文档,以下是运行该模型并获取预测结果所需的最简代码。

    安装

    $pip install ultralytics

    快速上手:

    from ultralytics import YOLOvv8
    
    model = YOLOvv8.from_pretrained("foduucom/stockmarket-pattern-detection-yolov8")
    source = 'http://images.cocodataset.org/val2017/000000039769.jpg'
    model.predict(source=source, save=True)

    但是,这段代码在您的环境中会报错,因为当前ultralytics库中并没有YOLOvv8这个写法(官方文档有点过时了),正确的实现方式如下:

    首先,我们需要导入YOLO对象,并加载针对该任务训练好的模型。

    from ultralytics import YOLO
    import os
    
    model = YOLO(os.path.join('Models','model.pt'))

    文件model.pt可以在此处下载。下载完成后,将其保存到当前工作目录下名为Models的子文件夹中。

    之后,我们只需要调用模型对象的predict方法,并传入图片名称即可,您就能直接得到预测结果啦:)

    model.predict("image_name.png", save=True)

    当设置save=True时,模型会自动保存标注出高亮形态的结果图片。

    然而,这样实现过于简单,无法满足实际需求。让我们为这个预测器创建一个专用类,确保代码可靠、健壮,能够批量分析并预测文件夹中的多张MT5截图。

    同时,我们还需要用最优方式处理预测结果并进行可视化展示。

    单张图片的形态检测

    我们的类构造函数会接收YOLO模型对象,以及存放所有MT5截图的文件夹路径。

    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=['*.png', '*.jpg', '*.jpeg']) -> 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 ['*.png', '*.jpg', '*.jpeg'].
    
       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)))
    
       return (len(image_files), image_files) # Get the number of images and their names

    让我们来扩展这个预测函数,使其能够对单张图片进行预测;同时,这个函数还需要能输出检测到的形态,以及对应形态的置信度。

    def predict_image(self, img_name: str, hist: bool=True):
            
       """This function predicts a single image
    
       Args:
           img_name (str): name of the image
           hist (bool, optional): When set to false it means the function isn'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")
           return
            
       results = self.model.predict(source=img_name, save=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
    
           if boxes is not None and len(boxes) > 0:
               for box in boxes:
                   cls_id = int(box.cls[0])  # class id
                   conf = box.conf[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))[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(0)
                       cv2.destroyAllWindows()
           else:
               print("No detections.")
    

    YOLOv8提供的predict函数会返回一个包含检测框信息的字典 —— 该框会显示在图像中,精准框出检测到的特定图表形态,并附带该形态的置信度水平。

    提取这些信息后,我们将其打印到Python控制台或命令提示符(CMD)中。

    在函数的最后,当hist被设置为False时,该函数会通过cv2模块在窗口对话框中显示预测结果。

    当您需要可视化查看模型生成的、包含检测结果的图像时,这一功能非常实用。

    示例:

    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 = 100
    
    pattern_detector = YOLOv8deploy(model=model, images_folder=images_path)
    
    pattern_detector.predict_image(img_name=os.path.join(images_path, f"{symbol}.{timeframe}.{11}.png"), 
                                   hist=False)
    

    在Screenshots文件夹中有一张取自EURUSD.PERIOD_H1(欧元兑美元1小时周期)图片,文件编号标记为11,我们准备对其进行可视化展示。 

    原始图片。

    模型运行预测函数后,在独立窗口对话框中生成的结果图片如下。

    批量检测多张图片的形态

    从Screenshots文件夹获取所有图片后,我们对其进行遍历,并将每张图片传入刚才编写好的predict_image函数中。

    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()
    输出:

    模型生成的所有预测结果与图片,均可在当前工作目录下的runs\predict*路径中找到。

    如上可见,该模型实现了不错的预测效果,但和所有已构建的机器学习模型一样,它也存在一些不足与局限。以下是其中几点:


    YOLOv8的偏差、风险和局限性

    01:模型性能可能会受图表样式、屏幕分辨率以及市场行情的影响。

    为获得更好的效果,请务必确保图表上的K线颜色简洁清晰,且背景色与K线颜色应形成明显的区分。

    图表的缩放比例同样关键:在MetaTrader 5中,图表缩放过大可能导致形态显示不完整,而过小则会引入过多噪声。

    您需要在图表缩放比例与图片尺寸之间找到合适的平衡点。

    02:市场剧烈波动与交易数据中的噪声会影响识别准确率。

    剧烈波动会引起虚假突破和异常形态,模型在这类图表和行情中必然会出现识别错误。

    03:如果某类市场特有的形态在训练数据中样本不足,就会给检测带来困难。

    避免在形态不明确的市场与时间周期上使用,应当将模型部署在合适的市场,才能发挥其最佳效果。


    基于YOLOv8在MetaTrader 5中检测图表模式

    重申一下,YOLOv8模型基于图片进行推理,并以图片格式输出预测结果,而原生MQL5语言无法直接读取和解析这类图片。然而,MQL5支持处理.BMP位图格式的图片,这是一个很好的实现起点。

    由于MQL5支持创建图片对象,并可以将图片嵌入MT5图表的背景层,因此已有不少现成方案可供实现。让我们把YOLOv8生成的、已标注好图表形态的图片,在MetaTrader 5里打开并放到图表背景中,就像在本文中实现的那样。

    将图片添加到图表背景后,我们可以隐藏前景中正在刷新的K线,把整张图表以带K线的图片形式呈现并更新。

    要实现这一点,我们需要在Python类中修改几处内容:首先是优化预测函数,使得所有带预测结果的图片保存到指定的同一个文件夹中。

    文件名:deploy.py

    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 (bool, optional): When set to false it means the function isn'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")
           return
            
       results = self.model.predict(source=img_name, 
                                    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))[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))[0] + '.bmp'))
    
           if boxes is not None and len(boxes) > 0:
               for box in boxes:
                   cls_id = int(box.cls[0])  # class id
                   conf = box.conf[0].item()  # confidence score
                   label = names[cls_id]
                        
                   print(f"Detected: {label} (confidence: {conf:.2f})")
           else:
               print("No detections.")
    
    

    借助预测函数将图片保存至指定文件夹的功能,我们可以将所有文件统一存放在MQL5系统文件目录下新建的YOLOv8 Images子文件夹中。

    YOLOv8输出的图片为JPEG或JPG格式,如果要在MetaTrader 5中使用,就必须先将其转换为BMP位图格式。实现该转换功能的函数如下:

    def convert_jpg_to_bmp(jpg_path, bmp_path):
        """
        Convert a JPG image to 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 != 'RGB':
                    img = img.convert('RGB')
                
                # Save as 24-bit BMP
                img.save(bmp_path, 'BMP')
                
            print(f"Successfully converted {jpg_path} to {bmp_path}")
            return True
        
        except Exception as e:
            print(f"Conversion failed: {str(e)}")
            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)

    我们可以将这一流程自动化。设置为每隔数分钟,脚本就从MQL5\Files\Screenshots中读取图片,完成形态预测,并将结果图片保存至MQL5\Files\YOLOv8 Images中。

    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 = datetime.now() # Get the current local date and time
    
        # Extract current day and hour
    
        date = now.day
        current_day = now.weekday()  # e.g., 'Wednesday'
        current_hour = now.strftime("%H")  # e.g., '14' for 2 PM in 24-hour format
    
        image_filename = os.path.join(images_path, f"{symbol}.{timeframe}.{date}.{current_day+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 {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
        
    
    # Schedule the pattern detection after every minute(s)
    
    schedule.every(1).minutes.do(scheduledYOLOv8Run)
    
    print("Scheduler started. Press Ctrl+C to stop.")
    
    # Run forever
    while True:
        schedule.run_pending()
        time.sleep(1)

    现在,让我们来配置合适的环境,以便将生成好的Bitmap(.BMP)格式图片部署到MetaTrader 5图表中。

    首先,我们初始化定时器处理函数,用于自动实现截图、并用标注好预测形态的图片更新图表这一整套流程。

    文件名: YOLOv8 EA.mq5

    input uint chart_scale = 3;
    input uint timer_seconds = 60;
    
    int chart_width, chart_height;
    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //---
       
       if (!EventSetTimer(timer_seconds))
         {
           printf("%s failed to set the timer, Error = %d",__FUNCTION__,GetLastError());
           return INIT_FAILED;
         }
    
        showBars(true);   
             
    //---
    
       return(INIT_SUCCEEDED);
      }

    名为showBars的函数,用于清理并配置图表环境,为截图操作做好准备。

    void showBars(bool show=true)
     { 
    //--- Cleaning the chart
    
        ChartSetInteger(0, CHART_SHOW_PRICE_SCALE, true);
        ChartSetInteger(0, CHART_SHOW_DATE_SCALE, true);
        ChartSetInteger(0, CHART_SHOW_GRID, false); // Disable grid for cleaner images
        ChartSetInteger(0, CHART_SHOW_VOLUMES, false);
        ChartSetInteger(0, CHART_SHOW_TRADE_HISTORY, false);
        ChartSetInteger(0, CHART_AUTOSCROLL, true);  // prevent scrolling  
        ChartSetInteger(0, CHART_SHIFT, true);
        
        if (ChartGetInteger(0, CHART_SCALE) != chart_scale)
             ChartSetInteger(0, CHART_SCALE, chart_scale);
       
       if (show)
         {
             ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrWhite);
             ChartSetInteger(0, CHART_COLOR_FOREGROUND, clrBlack);
             ChartSetInteger(0, CHART_COLOR_CHART_UP, clrTomato);
             ChartSetInteger(0, CHART_COLOR_CANDLE_BULL, clrTomato);
             ChartSetInteger(0, CHART_COLOR_CHART_DOWN, clrLightSeaGreen);
             ChartSetInteger(0, CHART_COLOR_CANDLE_BEAR, clrLightSeaGreen);
             ChartSetInteger(0, CHART_SHOW_ASK_LINE, true);
             ChartSetInteger(0, CHART_SHOW_BID_LINE, true);
             ChartSetInteger(0, CHART_COLOR_ASK, clrTurquoise);
         }
       else
         {
             ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrWhite);
             ChartSetInteger(0, CHART_COLOR_FOREGROUND, clrBlack);
             
             ChartSetInteger(0, CHART_COLOR_CHART_UP, clrWhite);
             ChartSetInteger(0, CHART_COLOR_CANDLE_BULL, clrWhite);
             ChartSetInteger(0, CHART_COLOR_CHART_DOWN, clrWhite);
             ChartSetInteger(0, CHART_COLOR_CANDLE_BEAR, clrWhite);
             ChartSetInteger(0, CHART_SHOW_ASK_LINE, true);
             ChartSetInteger(0, CHART_SHOW_BID_LINE, true);
             ChartSetInteger(0, CHART_COLOR_ASK, clrTurquoise);
         }
         
       ChartRedraw();
     }

    当以false作为参数调用该函数时,就会将图表的所有显示属性(包括K线颜色)设置为与图表背景完全相同,从而让所有元素不可见。

    这一步是必要的,因为我们要在当前图表背景上加载图片,不希望前景K线叠加在图片上,从而造成视觉混乱。

    将.BMP格式转换为图表对象,并将其设置为当前图表背景的函数如下:

    //+------------------------------------------------------------------+
    //|       Function to set a BMP image as chart background            |
    //+------------------------------------------------------------------+
    bool chartBackGroundSet(string filename, int width, int height)
    {
        string obj_name = "background-img";
            
       if(!ObjectCreate(0,obj_name,OBJ_BITMAP_LABEL,0,0,0)) 
         { 
          printf("%s failed to create a bitmap in the chart window! Error = %s",__FUNCTION__,ErrorDescription(GetLastError())); 
          return(false); 
         } 
         
    //--- set the path to the image file 
       if(!ObjectSetString(0,obj_name,OBJPROP_BMPFILE, filename)) 
         { 
          printf("%s failed to load the image! Error = %s",__FUNCTION__,ErrorDescription(GetLastError())); 
          return(false); 
         } 
    
    //--- Position the image to cover the entire chart
    
        ObjectSetInteger(0, obj_name, OBJPROP_XDISTANCE, 0);
        ObjectSetInteger(0, obj_name, OBJPROP_YDISTANCE, 0);
        ObjectSetInteger(0, obj_name, OBJPROP_XSIZE, width);
        ObjectSetInteger(0, obj_name, OBJPROP_YSIZE, height);
        
    //--- Send the image to the background
    
        ObjectSetInteger(0, obj_name, OBJPROP_BACK, true);
        ObjectSetInteger(0, obj_name, OBJPROP_ZORDER, -1);
        
    //--- Make sure the object is visible
        ObjectSetInteger(0, obj_name, OBJPROP_SELECTABLE, false);
        ObjectSetInteger(0, obj_name, OBJPROP_HIDDEN, true);
        
    //--- Redraw the chart to see changes
    
        ChartRedraw(0);    
    //---
       return true;
    }

    最后,我们自动化整个流程:自动截取图表图片并保存至Screenshots目录中,以供Python脚本读取;自动从YOLOv8 Images文件夹中读取已完成预测的图片;再自动将图片绘制显示在MetaTrader 5图表上。

    void OnTimer(void)
      {   
    //---
    
        showBars(true); //explicitly show the bars
        
        // Clear the objects before taking a screenshot
        ObjectsDeleteAll(0);
        ObjectsDeleteAll(0,0);
        
        if (takeScreenShot())
         {
           Print("Screen shot taken: ",TimeCurrent());
           Sleep(100);
         }
         
        chart_width = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
        chart_height = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
               
    //--- Take screenshot
        
        MqlDateTime time_struct;
        TimeToStruct(TimeLocal(), time_struct);
       
        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);
        string fileshort_name = filename;
        
        bool checkfile = false;
        if (StringReplace(fileshort_name, "\\Files\\","")>0)
          checkfile = true; //If the parent folder was removed we can proceed to check if a file exists before drawing an object
          
        if (checkfile)
         while (!FileIsExist(fileshort_name)) 
           {
             printf("%s not found",fileshort_name);
             return;
           }
            
    //--- Set the image with patterns detected to a chart
    
        if (!chartBackGroundSet(filename, chart_width, chart_height))
            return;
            
        showBars(false);
      }

    最后,我们将这款智能交易系统(EA)加载到欧元兑美元1小时周期图表上(与Python脚本保持一致)。Python脚本保持运行,将定时器设置为60秒。

    在欧元兑美元1小时图表上等待60秒后,MetaTrader 5就会接收到来自模型的检测结果图片。


    太棒了!

    以上使用的这种将图片作为图表背景的方法比较粗糙,其假定图表尺寸是固定的,或者在一段时间内(至少在定时器设定的时长内)保持不变。

    因此,调整图表尺寸会在一段时间内中断在图表上绘制图片的流程。我推荐使用此处介绍的方法,以实现更稳健的图表背景图片的可视化方案。


    总结

    在我看来,YOLOv8是一款令人印象深刻的模型。和其他机器学习模型一样,它也存在局限与不足,但在检测上文提到的各类图表形态时,它的确能准确识别出不少形态。向其开发者致敬,因为我过去也曾尝试为该任务构建类似模型,效果却很不理想。

    尽管我们常使用AI模型来自动化交易策略,但就目前而言,该模型仅输出人类可识别的图片,并不适用于算法自动交易。它更适合作为手动交易者的形态检测辅助工具。如果要将其用于程序化策略,可以通过建立Python与MetaTrader之间的通信,将模型predict 方法输出的状态与预测结果以文本或JSON格式进行传输。

    此致敬礼。

    欢迎持续关注,并通过此GitHub仓库,为MQL5语言的机器学习算法开发贡献力量。


    附件表


    文件名与路径
    说明/用法
    Experts\YOLOv8 EA.mq5 用于截取图表截图,并将YOLOv8预测后的图片加载到当前图表的一款EA。
    Include\errordescription.mqh 用于将MetaTrader 5和MQL5生成的错误代码转换为人们可读文本的一个库。
    Scripts\ChartScreenshots.mq5 用于从MetaTrader 5指定图表中批量截取历史多帧截图的一款脚本。
    YOLOv8 Proj\test.py 用于在多张图片上批量测试YOLOv8模型的一款Python脚本。
    YOLOv8 Proj\deploy.py 用于持续部署并运行YOLOv8模型的一款Python脚本。
    YOLOv8 Proj\requirements.txt 包含项目所用的全部Python依赖库及其版本号。


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

    附加的文件 |
    Attachements.zip (1023.22 KB)
    外汇套利交易:汇率关系评估面板 外汇套利交易:汇率关系评估面板
    本文介绍了在 MQL5 中开发套利分析面板的过程。如何通过不同方式在外汇交易中获得公允的汇率?制定一个指标,以获取市场价格与公允汇率之间的偏差,并评估一种货币兑换为另一种货币的套利方式(如三角套利)的收益。
    基于马尔可夫状态转移矩阵的神经网络自学习型EA 基于马尔可夫状态转移矩阵的神经网络自学习型EA
    基于状态矩阵与神经网络的自训练智能交易系统(EA)我们将马尔可夫链与基于ALGLIB MQL5库开发的多层感知器(MLP)神经网络相结合。马尔可夫链与神经网络如何结合应用于外汇预测?
    神经网络在交易中的应用:多元时间序列的双重聚类(DUET) 神经网络在交易中的应用:多元时间序列的双重聚类(DUET)
    DUET 框架提供了一种创新的时间序列分析方法,该方法结合了时间和通道聚类,以揭示分析数据中的隐藏模式。这使得模型能够随着时间的推移而适应变化,并通过消除噪声来提高预测质量。
    神经网络在交易中的应用:将混沌理论融入时间序列预测(终篇) 神经网络在交易中的应用:将混沌理论融入时间序列预测(终篇)
    我们继续将 Attraos 框架的作者提出的方法整合到交易模型中。让我提醒您,这个框架利用混沌理论的概念来解决时间序列预测问题,将其解释为多维混沌动态系统的投影。