English Русский Deutsch 日本語
preview
日志记录精通指南(第三部分):探索日志处理器(Handlers)实现方案

日志记录精通指南(第三部分):探索日志处理器(Handlers)实现方案

MetaTrader 5示例 |
29 0
joaopedrodev
joaopedrodev

概述

在该系列首篇文章—— 《日志记录精通指南(第一部分):MQL5基础概念与第一步》中,我们开启了为智能交易系统(EA)开发量身定制日志库的创建之旅。我们探讨了构建这一核心工具的契机:突破MetaTrader 5原生日志的局限,为MQL5生态带来健壮、可定制且功能强大的解决方案。

回顾要点,我们通过以下基础规范奠定了日志库的基石:

  1. 健壮的架构 :采用 单例(Singleton)模式确保代码组件间的一致性。
  2. 高级持久化:支持数据库存储日志,提供可追溯的历史记录以支持深度审计与分析。
  3. 灵活的输出方式:支持日志在控制台、文件、终端界面或数据库中的便捷存储与展示。
  4. 日志级别分类:区分信息性消息、关键警报和错误日志。
  5. 输出格式定制满足不同开发者或项目的个性化需求。

基于这一稳固的基础,我们开发的日志框架已超越了简单事件记录的范畴,将成为实时解读、监控和优化EA行为的战略工具。

在第三篇文章中,我们将深入探讨处理器这一关键概念。如果格式化器(formatters)负责组织数据,那么处理器则决定日志的流向。它们如同"管道工",将消息导向文件、控制台、数据库甚至通知系统等目标。本文将解析处理器的工作逻辑,通过实战案例演示其在不同场景的应用,并探索与格式化器的集成方法。在本篇结束时,您将掌握构建高度可定制化高效日志流的所有必要工具。让我们开始吧!


什么是处理器?

处理器是定义日志消息发送目标的核心组件。可将其视为"消息调度员":从日志记录器(Logger)接收信息,并转发至适当目的地(控制台、文件、邮件或远程服务器)。

想象您正在管理一家工厂。产品(日志消息)需要被运送到不同目的地:一些进入仓库,一些发往发货区,还有一些作为历史记录存档。调度员负责决定每件产品的去向,而处理器正是扮演这一角色的“调度员”。

每个处理器都可以配置特定参数,如:严重级别(比如仅发送错误消息)、输出格式(比如是否包含时间戳)和目标地址。

这些组件对于实现日志消息的分流与智能路由至关重要,尤其在中大型应用中。处理器可实现一些核心功能,例如:支持开发者在控制台实时调试错误、存储详细日志以供未来深度分析、在紧急事件发生时发送关键邮件警报,以及将监控信息转发至中央服务器进行集中管理。所有功能均可同步实现,且无需复杂配置。


处理器如何工作

为深入理解处理器的实际运作机制,我们通过日志库中的示例进行解析。请参考下方图表,该图展示了日志消息处理的基础流程:

在当前流程中,CLogify类的主函数是Append方法,其职责为接收日志数据,包括日志级别、消息内容、来源及时间戳。基于这些数据创建MqlLogifyModel类型的变量,最终通过MetaTrader 5原生Print函数将日志输出至终端控制台。

该流程虽能运行,但存在明显局限:所有日志仅能显示在控制台,并且无法灵活处理或存储日志到别处。

引入处理器后,流程得到显著优化。请参考下方更新后的架构图:

在新流程中:

  1. Append方法仍负责接收日志信息(级别、消息、来源、时间等)。
  2. 其创建相同的MqlLogifyModel变量来封装数据。
  3. 与之前直接输出到控制台不同,现在日志会传递给由处理器数组维护的处理器列表。

每个处理器都能独立处理数据,实现日志消息的多目标路由。

以下是三种本系统使用的基础处理器示例:

  • HandlerTerminal: 在MetaTrader 5终端直接显示日志,适用于实时诊断与调试。
  • HandlerFile: 将日志保存为.txt或.log文件,适合存储执行历史或生成分析报告。
  • HandlerDatabase: 将日志存入数据库(如SQLite),支持高级查询,如趋势分析、复杂审计等。

我将为您提供一个实际示例,以展示处理器的实用性。假设您开发了一个需要高效日志系统的交易机器人。您可以按如下方式配置处理器:

  • 仅保存DEBUG和INFO级别日志,记录所有操作(如开仓和平仓信号)。
  • 实时输出WARN和ERROR级别日志,便于即时监控跟踪问题。
  • 将关键错误存入数据库,供工程师后续分析。

此架构使日志系统更健壮、有序且高效。借助处理器,您可以完全掌控日志的处理与存储方式及位置。


实现处理器基类

接下来,我们将实现一个名为CLogifyHandler的基类,作为后续所有处理器的父类。在库的根目录下新建Handlers文件夹。在其中创建LogifyHandler.mqh文件。在创建的文件中,我们将定义CLogifyHandler类。初始版本仅包含基础构造函数和析构函数,代码如下所示:

//+------------------------------------------------------------------+
//|                                                LogifyHandler.mqh |
//|                                                     joaopedrodev |
//|                       https://www.mql5.com/en/users/joaopedrodev |
//+------------------------------------------------------------------+
#property copyright "joaopedrodev"
#property link      "https://www.mql5.com/en/users/joaopedrodev"
//+------------------------------------------------------------------+
//| class : CLogifyHandler                                           |
//|                                                                  |
//| [PROPERTY]                                                       |
//| Name        : CLogifyHandler                                     |
//| Heritage    : No heritage                                        |
//| Description : Base class for all log handlers.                   |
//|                                                                  |
//+------------------------------------------------------------------+
class CLogifyHandler
  {
public:
                     CLogifyHandler(void);
                    ~CLogifyHandler(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CLogifyHandler::CLogifyHandler(void)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CLogifyHandler::~CLogifyHandler(void)
  {
  }
//+------------------------------------------------------------------+

此时,CLogifyHandler类仅作为基础“框架”(骨架),后续将逐步扩展其功能。

我们需要引入日志模型定义文件LogifyModel.mqh,该文件包含了所有处理器共用的日志数据结构。

//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "../LogifyModel.mqh"

该文件定义了MqlLogifyModel类/结构体,用于封装每条日志消息的核心数据,例如:日志级别、消息内容、来源等。

接下来,我们将为基类添加两个受保护属性:

  • m_name:存储处理器名称,便于调试或报告时识别。
  • m_level:定义处理器将处理的严重性级别,例如:DEBUG、INFO、ERROR。

此外,我们将创建公共方法来设置和检索这些值。

class CLogifyHandler
  {
protected:
   string            m_name;
   ENUM_LOG_LEVEL    m_level;
   
public:
   //--- Set/Get
   void              SetLevel(ENUM_LOG_LEVEL level);
   string            GetName(void);
   ENUM_LOG_LEVEL    GetLevel(void);
  };
//+------------------------------------------------------------------+
//| Set level                                                        |
//+------------------------------------------------------------------+
void CLogifyHandler::SetLevel(ENUM_LOG_LEVEL level)
  {
   m_level = level;
  }
//+------------------------------------------------------------------+
//| Get name                                                         |
//+------------------------------------------------------------------+
string CLogifyHandler::GetName(void)
  {
   return(m_name);
  }
//+------------------------------------------------------------------+
//| Get level                                                        |
//+------------------------------------------------------------------+
ENUM_LOG_LEVEL CLogifyHandler::GetLevel(void)
  {
   return(m_level);
  }
//+------------------------------------------------------------------+

m_name 属性仅允许通过派生类的构造函数进行设置,以此强化安全性与封装性。因此未提供SetName方法。

所有处理器都必须实现的三大基本方法:

  1. Emit(MqlLogifyModel &data): 处理日志消息并发送至目标(文件/控制台/数据库等)
  2. Flush(): 终止或清空所有待处理操作。
  3. Close():关闭处理器并释放关联资源。

当前,这些方法将被声明为(virtual)函数,并默认为空值。这样的设计可使每个子类根据实际需求自定义具体行为。

class CLogifyHandler
  {
public:
   //--- Handler methods
   virtual void      Emit(MqlLogifyModel &data);         // Processes a log message and sends it to the specified destination
   virtual void      Flush(void);                        // Clears or completes any pending operations
   virtual void      Close(void);                        // Closes the handler and releases any resources
  };
//+------------------------------------------------------------------+
//| Processes a log message and sends it to the specified destination|
//+------------------------------------------------------------------+
void CLogifyHandler::Emit(MqlLogifyModel &data)
  {
  }
//+------------------------------------------------------------------+
//| Clears or completes any pending operations                       |
//+------------------------------------------------------------------+
void CLogifyHandler::Flush(void)
  {
  }
//+------------------------------------------------------------------+
//| Closes the handler and releases any resources                    |
//+------------------------------------------------------------------+
void CLogifyHandler::Close(void)
  {
  }
//+------------------------------------------------------------------+

当前阶段,这些方法暂不实现具体功能,每个处理器的具体行为将通过子类(如HandlerFileHandlerDatabase等)实现。


处理器的实现

在完成基类CLogifyHandler的开发后,我们可以开始创建继承自它的专用处理器类。这种设计严格遵循面向对象编程的核心原则,例如继承和多态,为代码带来了显著的模块化与灵活性优势。每个专用处理器都将负责以特定方式处理日志:它们共用基类的统一结构,但在Emit、Flush 和Close方法中实现各自的专属逻辑。

在实现具体处理器之前,让我们先规范项目。请在Handlers目录下创建以下三个新文件:分别是:LogifyHandlerConsole.mqh、LogifyHandlerDatabase.mqh和LogifyHandlerFile.mqh。最终结构示例如下:

在LogifyHandlerConsole.mqh文件中,我们将创建CLogifyHandlerConsole类,该类继承自CLogifyHandler基类的所有属性和方法。在类的构造函数中,首要任务是设置基类的m_name变量为 "console"。这一修改将使处理器在运行时可通过名称被清晰识别。以下是该类的初始定义:

//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "LogifyHandler.mqh"
//+------------------------------------------------------------------+
//| class : CLogifyHandlerConsole                                    |
//|                                                                  |
//| [PROPERTY]                                                       |
//| Name        : CLogifyHandlerConsole                              |
//| Heritage    : CLogifyHandler                                     |
//| Description : Log handler, inserts data into terminal window.    |
//|                                                                  |
//+------------------------------------------------------------------+
class CLogifyHandlerConsole : public CLogifyHandler
  {
public:
                     CLogifyHandlerConsole(void);
                    ~CLogifyHandlerConsole(void);
   
   virtual void      Emit(MqlLogifyModel &data);         // Processes a log message and sends it to the specified destination
   virtual void      Flush(void);                        // Clears or completes any pending operations
   virtual void      Close(void);                        // Closes the handler and releases any resources
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CLogifyHandlerConsole::CLogifyHandlerConsole(void)
  {
   m_name = "console";
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CLogifyHandlerConsole::~CLogifyHandlerConsole(void)
  {
  }
//+------------------------------------------------------------------+

Emit函数的核心职责是处理日志消息并将其发送至指定目标。对于控制台而言,这仅仅意味着在MetaTrader终端中显示已格式化的消息。以下是该方法的实现过程:

//+------------------------------------------------------------------+
//| Processes a log message and sends it to the specified destination|
//+------------------------------------------------------------------+
void CLogifyHandlerConsole::Emit(MqlLogifyModel &data)
  {
   if(data.level >= this.GetLevel())
     {
      Print("Console handler: ",data.formated);
     }
  }
//+------------------------------------------------------------------+

请注意,在显示消息之前,我们会先检查日志级别(data.level)是否与处理器中配置的级别相匹配。这样确保只有重要或相关的消息才会被显示出来。

遵循与控制台处理器相同的原理,我们可以创建其他专用处理器,例如数据库和文件处理器。需要修改的文件是LogifyHandlerDatabase.mqh和LogifyHandlerFile.mqh。虽然这些处理器可能共享相同的基本逻辑,但它们对Emit方法的具体实现会有所不同。

该处理器旨在将日志存储到数据库中,不过目前为了演示目的,我们仅向控制台输出一条消息。Emit函数的代码如下:

//+------------------------------------------------------------------+
//| Processes a log message and sends it to the specified destination|
//+------------------------------------------------------------------+
void CLogifyHandlerDatabase::Emit(MqlLogifyModel &data)
  {
   if(data.level >= this.GetLevel())
     {
      Print("Database handler: ",data.formated);
     }
  }
//+------------------------------------------------------------------+

LogifyHandlerFile处理器用于将日志写入指定文件。以下是Emit的初始实现:

//+------------------------------------------------------------------+
//| Processes a log message and sends it to the specified destination|
//+------------------------------------------------------------------+
void CLogifyHandlerFile::Emit(MqlLogifyModel &data)
  {
   if(data.level >= this.GetLevel())
     {
      Print("File handler: ",data.formated);
     }
  }
//+------------------------------------------------------------------+

尽管我们在基类中定义了Flush与Close方法,但并非所有处理器都需要立即实现这两种方法。

  • Flush 方法对复杂处理器(如文件写入或实时流)尤为实用。
  • Close 方法则用于释放资源,例如关闭数据库连接或输出流。

对于控制台处理器,这两种方法留空即可,因为无需额外操作。请记住文中仅展示代码段,完整版代码可在文章末尾下载。


将处理器集成到CLogify类

现在处理器已经单独运行正常,接下来要把它们整合至日志库的主类CLogify中。首先导入基类CLogifyHandler,它包含所有处理器所需的通用结构:

#include "Handlers/LogifyHandler.mqh"
#include "Handlers/LogifyHandlerConsole.mqh"
#include "Handlers/LogifyHandlerDatabase.mqh"
#include "Handlers/LogifyHandlerFile.mqh"

CLogify类的实现中,我们将添加一个私有成员,用于存储待使用的处理器。我选用CLogifyHandler类型的指针数组,以便动态管理这些处理器。

此外,该类具有其专属的管理处理器的方法:

  • AddHandler:向数组新增处理器。
  • HasHandler:按名称检查某处理器是否已存在。
  • GetHandler:按数组名称或索引获取处理器。
  • SizeHandlers:返回列表中的处理器总数。

以下是更新后的CLogify类代码,已包含上述方法:

class CLogify
  {
private:
   CLogifyHandler    *m_handlers[];
   
public:
   //--- Handler
   void              AddHandler(CLogifyHandler *handler);
   bool              HasHandler(string name);
   CLogifyHandler    *GetHandler(string name);
   CLogifyHandler    *GetHandler(int index);
   int               SizeHandlers(void);
  };
//+------------------------------------------------------------------+
//| Add handler to handlers array                                    |
//+------------------------------------------------------------------+
void CLogify::AddHandler(CLogifyHandler *handler)
  {
   int size = ArraySize(m_handlers);
   ArrayResize(m_handlers,size+1);
   m_handlers[size] = GetPointer(handler);
  }
//+------------------------------------------------------------------+
//| Checks if handler is already in the array by name                |
//+------------------------------------------------------------------+
bool CLogify::HasHandler(string name)
  {
   int size = ArraySize(m_handlers);
   for(int i=0;i<size;i++)
     {
      if(m_handlers[i].GetName() == name)
        {
         return(true);
        }
     }
   return(false);
  }
//+------------------------------------------------------------------+
//| Get handler by name                                              |
//+------------------------------------------------------------------+
CLogifyHandler *CLogify::GetHandler(string name)
  {
   int size = ArraySize(m_handlers);
   for(int i=0;i<size;i++)
     {
      if(m_handlers[i].GetName() == name)
        {
         return(m_handlers[i]);
        }
     }
   return(NULL);
  }
//+------------------------------------------------------------------+
//| Get handler by index                                             |
//+------------------------------------------------------------------+
CLogifyHandler *CLogify::GetHandler(int index)
  {
   return(m_handlers[index]);
  }
//+------------------------------------------------------------------+
//| Gets the total size of the handlers array                        |
//+------------------------------------------------------------------+
int CLogify::SizeHandlers(void)
  {
   return(ArraySize(m_handlers));
  }
//+------------------------------------------------------------------+

借助处理器管理方法,我们接下来修改Append函数,使其在记录日志时调用这些处理器。

现在,Append会遍历数组中的所有处理器,并逐一调用它们的Emit方法,从而把日志发送到对应目标(控制台、数据库等)。

以下是更新后的Append方法代码:

//+------------------------------------------------------------------+
//| Generic method for adding logs                                   |
//+------------------------------------------------------------------+
bool CLogify::Append(ENUM_LOG_LEVEL level,string msg, string origin = "", string args = "",string filename="",string function="",int line=0)
  {
   //--- If the formatter is not configured, the log will not be recorded.
   if(m_formatter == NULL)
     {
      return(false);
     }
   
   //--- Textual name of the log level
   string levelStr = "";
   switch(level)
     {
      case LOG_LEVEL_DEBUG: levelStr = "DEBUG"; break;
      case LOG_LEVEL_INFOR: levelStr = "INFOR"; break;
      case LOG_LEVEL_ALERT: levelStr = "ALERT"; break;
      case LOG_LEVEL_ERROR: levelStr = "ERROR"; break;
      case LOG_LEVEL_FATAL: levelStr = "FATAL"; break;
     }
   
   //--- Creating a log template with detailed information
   datetime time_current = TimeCurrent();
   MqlLogifyModel data("",levelStr,msg,args,time_current,time_current,level,origin,filename,function,line);
   data.formated = m_formatter.FormatLog(data);
   
   //--- Call handlers
   int size = this.SizeHandlers();
   for(int i=0;i<size;i++)
     {
      m_handlers[i].Emit(data);
     }
   
   return(true);
  }
//+------------------------------------------------------------------+

说明如下:

  • 格式化后的日志保存在data.formatted中,确保所有处理器都能获得完整信息。
  • 每个处理器独立处理日志,调用各自的Emit方法。

最后要做的调整是在类析构函数中释放数组指针:

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CLogify::~CLogify()
  {
   //--- Delete formatter
   if(m_formatter != NULL)
     {
      delete m_formatter;
     }
   
   //--- Delete handlers
   int size_handlers = ArraySize(m_handlers);
   for(int i=0;i<size_handlers;i++)
     {
      delete m_handlers[i];
     }
  }
//+------------------------------------------------------------------+


测试处理器

本例将使用之前提到的测试文件LogifyTest.mq5。目标是演示如何配置并启用两个日志处理器,它们分别具有不同的日志级别,以及如何基于所配置的过滤器记录日志。

首先创建两个处理器,负责在不同位置和级别记录日志:

  • 控制台:通过配置CLogifyHandlerConsole实例,记录DEBUG级别消息(即所有消息,从最详细到关键的错误)。
  • 文件:通过配置CLogifyHandlerFile实例,仅捕获INFO及以上级别消息,过滤掉DEBUG消息。

配置这些处理器的代码如下:

int OnInit()
  {
   //--- Console
   CLogifyHandler *handler_console = new CLogifyHandlerConsole();
   handler_console.SetLevel(LOG_LEVEL_DEBUG);
   
   //--- File
   CLogifyHandler *handler_file = new CLogifyHandlerFile();
   handler_file.SetLevel(LOG_LEVEL_INFOR);
   
   return(INIT_SUCCEEDED);
  }

配置好处理器后,下一步是将它们加入基础类CLogify中,由该类统一管理应用的所有日志。

此外,我们还将添加一个 formatter,用于定义日志的显示格式。本例中的格式化器将采用以下模式:"时:分:秒, [日志级别], 消息"

配置完处理器和格式化器后,我们将发出三条不同级别的日志消息。以下是此步骤的完整代码:

int OnInit()
  {
   //--- Console
   CLogifyHandler *handler_console = new CLogifyHandlerConsole();
   handler_console.SetLevel(LOG_LEVEL_DEBUG);
   
   //--- File
   CLogifyHandler *handler_file = new CLogifyHandlerFile();
   handler_file.SetLevel(LOG_LEVEL_INFOR);
   
   //--- Config
   logify.SetFormatter(new CLogifyFormatter("hh:mm:ss","{date_time} [{levelname}] {msg}"));
   logify.AddHandler(handler_console);
   logify.AddHandler(handler_file);
   
   //--- Logs
   logify.Debug("Debug Message");
   logify.Infor("Information Message");
   logify.Error("Error Message");
   
   return(INIT_SUCCEEDED);
  }

当运行上述代码时,我们将在控制台中看到如下输出:

Console handler: 03:20:05 [DEBUG] Debug Message
Console handler: 03:20:05 [INFOR] Information Message
File handler: 03:20:05 [INFOR] Information Message
Console handler: 03:20:05 [ERROR] Error Message
File handler: 03:20:05 [ERROR] Error Message

结果解析:

  • 控制台处理器( handler_console):该处理器捕获所有日志消息,涵盖从DEBUG到 ERROR的全部级别。因此,控制台共记录了3条日志(对应每次调用的输出)。
  • 文件处理器(handler_file):该处理器仅配置记录INFO级别及以上的消息。因此,忽略了DEBUG日志,最终在日志文件中仅记录了2条日志(INFO和ERROR各一条)。


结论

通过本文,我们在构建MQL5日志库的道路上迈出了关键的一步。我们深入探讨了处理器(handlers) 的概念,掌握它们作为日志消息“指挥家”的核心作用——将消息导向不同的目标。同时,我们学习了处理器如何与格式化器(formatters)协同工作,共同构成一个模块化、可扩展的日志处理系统。

在实践中,我们已构建好处理器的基础结构——定义了一个抽象类,作为所有后续实现的基石。同时开发了三大初始处理器:控制台数据库文件,分别负责将日志导向控制台、数据库和文件。目前它们均用Print()函数做模拟,但这一坚实基础让我们能在未来文章中扩展并专业化各类,赋予其最终功能。

测试环节验证了处理器与日志库的集成,展示了其灵活添加与使用的方式。整个过程展示了处理器作为模块化组件的潜力,可按需定制以满足不同日志需求。


本文中使用的所有代码都附在下方。下表列出了库中各文件的说明:

文件名
描述
Experts/Logify/LogiftTest.mq5
测试日志库功能的文件,包含一个实用示例
Include/Logify/Formatter/LogifyFormatter.mqh
负责格式化日志记录的类,将占位符替换为具体值
Include/Logify/Handlers/LogifyHandler.mqh
日志处理器管理基类,包含日志级别设置与消息发送功能
Include/Logify/Handlers/LogifyHandlerConsole.mqh
MetaTrader终端控制台日志处理器,直接输出格式化日志至终端
Include/Logify/Handlers/LogifyHandlerDatabase.mqh
数据库日志处理器(当前仅实现打印功能,后续将支持SQLite数据库存储)
Include/Logify/Handlers/LogifyHandlerFile.mqh
文件日志处理器(当前仅实现打印预览,后续将支持真实文件存储)
Include/Logify/Logify.mqh
日志管理核心类,集成日志级别、输出模型与格式化功能
Include/Logify/LogifyLevel.mqh
Logify库日志级别定义文件,支持精细化日志控制
Include/Logify/LogifyModel.mqh
日志记录数据结构模型,涵盖级别、消息、时间戳及内容信息
在后续的文章中,我们将完整实现各类处理器,深入探讨如何将日志存入文件、把数据保存到数据库,以及在终端中配置特定输出。


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

附加的文件 |
LogifyfPart3a.zip (11.96 KB)
在MQL5中实现基于经济日历新闻事件的突破型智能交易系统(EA) 在MQL5中实现基于经济日历新闻事件的突破型智能交易系统(EA)
重大经济数据发布前后市场波动率通常显著上升,为突破交易策略提供了理想的环境。在本文中,我们将阐述基于经济日历的突破策略的实现过程。我们将全面覆盖从创建用于解析和存储日历数据的类,到利用这些数据开发符合实际的回测系统,最终实现实盘交易执行代码的完整流程。
迁移至 MQL5 Algo Forge(第 3 部分):在您自己的项目中使用外部仓库 迁移至 MQL5 Algo Forge(第 3 部分):在您自己的项目中使用外部仓库
让我们探索如何开始将 MQL5 Algo Forge 存储中任何仓库的外部代码集成到您自己的项目中。在本文中,我们最后转向这个有前景但更复杂的任务:如何在 MQL5 Algo Forge 中实际连接和使用来自第三方仓库的库。
新手在交易中的10个基本错误 新手在交易中的10个基本错误
新手在交易中会犯的10个基本错误: 在市场刚开始时交易, 获利时不适当地仓促, 在损失的时候追加投资, 从最好的仓位开始平仓, 翻本心理, 最优越的仓位, 用永远买进的规则进行交易, 在第一天就平掉获利的仓位,当发出建一个相反的仓位警示时平仓, 犹豫。
基于隐马尔可夫模型的趋势跟踪波动率预测 基于隐马尔可夫模型的趋势跟踪波动率预测
隐马尔可夫模型(HMMs)是强大的统计工具,可通过分析可观测的价格波动来识别潜在的市场状态。在交易领域,隐马尔可夫模型通过建模和预测市场状态的转变,可提升波动率预测的准确性,并为趋势跟踪策略提供依据。在本文中,我们将完整介绍一种趋势跟踪策略的开发流程,该策略利用隐马尔可夫模型预测波动率,并将其作为交易信号的过滤条件。