English Русский Español Deutsch 日本語 Português
preview
您应当知道的 MQL5 向导技术(第 07 部分):树状图

您应当知道的 MQL5 向导技术(第 07 部分):树状图

MetaTrader 5EA交易 | 3 六月 2024, 10:09
648 0
Stephen Njuki
Stephen Njuki

概述

本文是有关使用 MQL5 向导系列文章的一部分,着眼于树状图。我们已经研究了一些对于交易者很实用的使用 MQL5 向导思路,例如:线性判别分析马尔可夫链傅里叶变换、和其它一些想法,本文旨在尽力深入探查利用 MetaQuotes 移植的大量 ALGLIB 代码的能力、配合内置的 MQL5 向导、熟练测试和开发新思路的途径。

集聚层次化分类听起来很拗口,但实际上却十分简单。简而言之,它是一种将数据集的不同部分关联起来的手段,首先研究基本的独立聚类,然后将它们系统化分组,一次一步,直至整个数据集可视作一个排序单元。该过程的输出是一个层次化示意图,通常引申为树状图。

本文将专注于如何使用这些成分聚类来评估和预测价格柱线范围,但不像过去我们这样做是为帮助调整尾随止损,我们于此的研究是出于资金管理或仓位规模目的。本文采用的风格会假设读者相对 MetaTrader 平台和 MQL5 编程语言是新手,故此我们也许会讨论一些主题和领域的细节,然对经验丰富的交易者而言这些都太幼稚。

准确预测价格区间的重要性在很大程度上是主观的。这是因为其明显更取决于交易者的策略和整体交易方式。什么时候不太重要?好吧,在某些状况下,可能如此;例如,若在您的交易设置中,首先您用最小的杠杆或没有杠杆,您还有一个明确的止损,且您倾向于长期持仓,可能会持续数月,以及您有固定保证金持仓规模(甚或固定手数方式)。在这种情况下,当您专注于筛选入场和离场信号时,价格柱线的波动性可能会被推至次要等级。另一方面,如果您是日内交易者,或者采用大杠杆的人,或不会持仓过周末的人,或者任何遇到市场披露消息时看中短线的人,那么价格柱线范围当然是您应当关注的。我们通过创建一个自定义的 “ExpertMoney” 类实例来研究如何在资金管理中使用它,但如果您认为理解和合理预测价格柱线范围的能力有助于决断何时加仓,及反之,何时减仓,那么它的应用可能会扩散到资金管理范围之外,甚至蕴含风险。


波动率

在交易的境况下,价格柱线范围(这就是我们在本文中量化波动性的方式)是交易品种价格在设定时间帧内的最高价和最低价之间的差值。故此,如果我们假设取日线时间帧,并且在一天内,交易品种的价格上涨到 H 且不高于 H,然后下降到 L,且一样不低于 L,那么就本文而言,我们的范围是:

H – L;

关注波动率可以说是重要的,因为通常被称为波动率聚类。这是一种现象,即高波动区间之后倾向于更多的波动性,相反,低波动性区间之后也会后随较低的波动性。若这是主观的,如上强调那样,那么对于大多数交易者(包括我认为的所有初学者)来说,知道如何运用杠杆进行交易从长远来看可能是一个加分项,因为正如大多数交易者所熟悉的那样,高波动性可能会令账户爆仓,并非由于入场信号是错误的,而是因为波动性太过分。大多数人也能意识到,即使您的持仓有一个不错的止损,有时止损价可能不可用,以 2015 年 1 月的瑞士法郎崩盘为例,在这种情况下,您的持仓将被券商以下一次最佳可用价格平仓,这通常比您的止损更糟糕。这是因为只有限价单才会保证价格,破位单和止损不会。

因此,价格柱线范围除了提供市场环境的整体感觉外,还有助于指导入场、甚至离场价位。同样,根据您的策略,举例,如果您做多某个特定品种,那么您的价格柱线范围展望(您预测的内容)的程度可以很容易地判定或至少指导您在哪里放置入场价格,以至止盈。

所冒风险听起来很平凡,它也许有助于突出一些非常基本的价格蜡烛类型,并勾勒它们各自的范围。更显要的类型是看跌看涨锤子墓碑长腿、和蜻蜓。当然还有更多类型,但可以说这些类型确实涵盖了人们在面对价格图表时最有可能遇到的那些。所有这些实例,如下图所示,价格柱线范围只是最高价减去最低价。


集聚层次化分类

集聚层次化分类(AHC) 是一种将数据分类为预设数量的聚类,然后通过所谓的树状图以系统层次化方式把这些聚类关联起来的手段。这样做的益处主要源于这样一个事实,即被分类的数据通常是多维的,因此在进行比较时,需要考虑单个数据点中的许多变量也许不太容易设法解决。例如,一家寻找基于来自客户的信息为他们进行评级的公司就可以利用这一点,因为这些信息必然涵盖客户生活的不同方面,譬如过去的消费习惯、他们的年龄、性别、地址、等等。AHC,通过量化每位客户的所有这些变量,从每个数据点的表观质心创建聚类。但比之更重要的是,这些聚类被分组到系统化关系的层次结构之中,由此如果分类需要 5 个聚类,那么 AHC 将按排序格式提供这 5 个聚类,这意味着您可以推断哪些聚类更相似,哪些更具差别。这种聚类比较虽然是次要的,但如果您需要比较多个数据点,且它们摆明位于不同的聚类中,那么它就可以派上用场。聚类排位将通过聚类各自之间的间隔幅度来告知两个点之间的差距有多大。

AHC 的分类是无监督的,这意味着它可以用于不同分类器下的预测。在我们的案例中,我们正在预测价格柱线范围,具有相同训练聚类的其他人可以用它们来预测收盘价的变化,或与他的交易相关的其它方面。这比在特定分类器上进行监督和训练具有更大的灵活性,因为在这种情况下,模型将仅对所分类的内容进行预测,这意味着若要出于其它目的进行预测,模型将需要依据该新数据集重新训练。


工具和库

MQL5 平台在其 IDE 的帮助下,确实允许从头开始开发自定义智能交易系统,为了展示这里共享的内容,我们可以假定采取这条路线。然而,该选项将涉及对交易系统做出许多决定,而在面对实施相同概念时,其他交易者可能会采取不同的决定。还有,这样实现的代码可能定制过度,且也许容易出错 — 很容易针对不同状况进行修改。这就是为什么将我们的想法作为由 MQL5 向导提供的其它“标准”智能系统类的一部分进行组装是一个令人信服的案例。我们不仅需要减少调试(即使在 MQL5 的内置类中也偶尔会出现错误,但不是很多),而且通过将其保存为标准类之一的实例,它可以与 MQL5 向导中的各种其它类一起使用和组合,以便得到不同的智能系统,从而提供更全面的测试平台。

MQL5 函数库代码提供了 AlgLib 类,这些类已在本系列的前面文章中引用,并在本文中再次使用。具体来说,在 “DATAANALYSIS.MQH“ 文件内,我们将依靠 ”CClustering“ 类和其它几个相关类来为我们的价格序列数据提供 AHC 分类。由于我们的主要兴趣是价格柱线范围,后续我们的训练数据将由前段时期的此类范围构成。当使用来自数据分析包含文件中的数据训练类时,典型用法将该数据放置在一个 “XY” 矩阵当中,其中 X 代表自变量,Y 代表模型训练所用的分类器或“标签”。两者通常位于同一矩阵之中。


准备训练数据

不过,就本文而言,由于我们进行的是无监督训练,因此我们的输入数据仅包含 X 自变量。这些将是历史价格柱线范围。同时,我们希望通过参考另一个相关数据流来做出预测,即最终的价格柱线范围。这相当于上面提到的 Y。为了将这两个数据集结合起来,同时保持无监督学习的灵活性,我们可以采用以下数据结构

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CMoneyAHC                  : public CExpertMoney
  {
protected:
   
   double                        m_decrease_factor;
   
   int                           m_clusters;                            // clusters
   int                           m_training_points;                     // training points
   int                           m_point_featues;                       // point featues


...


public:
                                 CMoneyAHC(void);
                                 ~CMoneyAHC(void);
                                 
   virtual bool                  ValidationSettings(void);
   //---
   virtual double                CheckOpenLong(double price,double sl);
   virtual double                CheckOpenShort(double price,double sl);
   //---
   void                          DecreaseFactor(double decrease_factor) { m_decrease_factor=decrease_factor;            }
   
   void                          Clusters(int value)                    { m_clusters=value;                             }
   void                          TrainingPoints(int value)              { m_training_points=value;                      }
   void                          PointFeatures(int value)               { m_point_featues=value;                        }

protected:

   double                        Optimize(double lots);
   
      
   double                        GetOutput();

   CClusterizerState             m_state;
   
   CAHCReport                    m_report;
   
   struct                        Sdata
                                 {
                                    CMatrixDouble  x;
                                    CRowDouble     y;
                                    
                                                   Sdata(){};
                                                   ~Sdata(){};
                                 };
                                 
   Sdata                         m_data;
   
   CClustering                   m_clustering;
   
   CRowInt                       m_clustering_index;
   CRowInt                       m_clustering_z;
      
  };


如此,将在每根新柱线开立时收集新一批的历史价格柱线范围。由 MQL5 向导生成的智能系统倾向于在每根新柱开立时执行交易决策,对于我们的测试目的来说,这已经足够了。确实有替代方式,诸如获得跨越数月甚至数年的大批次数据,然后据测试检验模型的聚类如何最终将低波动率价格柱线与高波动率价格柱线区分开来。还要记得,我们只用 3 个聚类,其中一个极端聚类用于波动剧烈的柱线,一个用于波动非常低的,一个用于中等波动。同样,我们可以用 5 个聚类进行调查举例,但就我们的目的而言,原理是相同的。将聚类按最高波动率到最低波动率的顺序放置,并识别我们当前数据点所在的聚类。


填充数据

每根新柱开立时,获取最新柱线范围,并填充自定义结构的代码如下所示:

      m_data.x.Resize(m_training_points,m_point_featues);
      m_data.y.Resize(m_training_points-1);
      
      m_high.Refresh(-1);
      m_low.Refresh(-1);
      
      for(int i=0;i<m_training_points;i++)
      {
         for(int ii=0;ii<m_point_featues;ii++)
         {
            m_data.x.Set(i,ii,m_high.GetData(StartIndex()+ii+i)-m_low.GetData(StartIndex()+ii+i));
         }
      }

训练点的数量定义了我们的训练数据集有多大。这是一个可自定义的输入参数,是为数据点特征参数。该参数定义了每个数据点具有的“维度”数量。因此,在我们的案例中,我们默认有 4 个,但这仅仅意味着我们使用最后 4 个价格柱线范围来定义任何给定的数据点。它类似于向量。


生成聚类

因此,一旦我们的自定义结构中有了数据,下一步就是调用 AlgLib AHC 模型生成器对其进行建模。该模型在代码清单中称为“状态”,为此,我们的模型最终被命名为 “m_state”。这是一个两步过程。首先,我们必须基于提供的训练数据生成模型点,然后运行 AHC 生成器。设置点可以看作是初始化模型,并确保所有关键参数都得到良好的定义。这在我们的代码中如下调用:

m_clustering.ClusterizerSetPoints(m_state, m_data.x, m_training_points, m_point_featues, 20);


第二个重要步骤是运行模型,以便定义训练数据集中每个所提供数据点的聚类。这是通过调用 “ClusterizerRunAHC” 函数来完成的,如下所示:

m_clustering.ClusterizerRunAHC(m_state, m_report);

从 AlgLib 端来看,这是生成我们所需聚类的肉和土豆。该函数执行一些简短的预处理,然后调用受保护的(专用)函数 “ClusterizerRunAHCInternal”,该函数执行繁琐的工作。所有这些源代码都在您的 “include\math\AlgLib\dataanalysis.mqh” 文件当中,从第 22463 行可见。这里值得注目的是在 “cidx” 输出数组中生成树状图。该数组巧妙地将大量聚类信息压缩到单个数组之中。此前,需要利用所有训练数据点的质心为它们生成距离矩阵。该数组捕获距离矩阵值到聚类索引的映射,第一个值直到训练点总数表示每个点的聚类,后续索引表示这些聚类的合并,形成树状图。

同样值得注意的大概就是生成距离矩阵所用的距离类型。有九个选项可供选择,从切比雪夫(Chebyshev)距离、欧几里得(Euclidean)距离、到长矛手等级相关性。这些备选方案中的每一个都被分配了一个索引,当我们调用上述设定点函数时,我们会建立该索引。距离类型的选择必然对所生成聚类的性质和类型非常敏感,因此应注意这一点。在设置 AHC 算法时,使用欧几里得距离(其索引为 2)可以更灵活地实现,因为 Ward 方法与其它距离类型的用法不同。


提取聚类

提取聚类也与生成集群一样简单。我们只需调用一个函数 “ClusterizerGetKClusters”,它就会从我们之前调用的聚类生成函数的输出报告中提取两个数组(运行 AHC)。这些数组是聚类索引数组和聚类 z 数组,它们不仅指导如何定义聚类,还指导如何据它们形成树状图。调用此函数简单地如下所示:

m_clustering.ClusterizerGetKClusters(m_report, m_clusters, m_clustering_index, m_clustering_z);


生成的聚类结构非常简单,因为在我们的案例中,我们仅将训练数据集分类为 3 个聚类。这意味着我们在树状图中的合并级别不超过三个。如果我们用到更多的聚类,那么我们的树状图肯定会更复杂,可能拥有 n-1 合并级别,其中 n 是模型用到的聚类数量。


标记数据点

训练数据点的后期标记,是为帮助预测接下来会是什么。我们对简单的数据集分类不感兴趣,但我们希望将它们投入使用,因此我们的“标记”将是每个训练数据点之后的最终价格柱线范围。我们正在每根新柱线产生时获取新数据集,其中包括当前数据点,其最终波动率未知。这就是为什么在标记时,我们跳过索引为 0 的数据点,如下图所示:

      for(int i=0;i<m_training_points;i++)
      {
         if(i>0)//assign classifier only for data points for which eventual bar range is known
         {
            m_data.y.Set(i-1,m_high.GetData(StartIndex()+i-1)-m_low.GetData(StartIndex()+i-1));
         }
      }

当然,该标记过程也可以用其它实现。例如,与其只关注下一根柱线的价格柱线范围,不如我们采取宏观视野,通过查看接下来的 5 或 10 根柱线的范围,将这些柱线的整体范围作为我们的 y 值。这种方式可以带来更“准确”和不稳定性更低的值,事实上,如果我们的标记是价格方向(收盘价的变化),那么可按相同的展望,我们将尝试预测更多的柱线,而不仅仅是一根。无论哪种方式,如同我们跳过第一个索引,因为我们没有其最终值,我们将跳过 n 根柱线(其中 n 是我们希望预测的往前面的柱线数量)。这种长远视野方式将导致相当大的滞后,因为 n 在另一方面变大,尽管大的滞后将允许与投影进行安全比较,因为请记住,滞后距离目标 y 值仅差一根柱线。


预测波动率

一旦我们完成了训练数据集的“标记”,我们就可以继续确立我们当前数据点属于模型中已定义聚类中的哪一个。这是通过轻松遍历建模报告的输出数组,并将当前数据点的聚类索引与其它训练数据点进行比较来完成的。如果它们匹配,则它们属于同一聚类。以下是其简单的清单:

      if(m_report.m_terminationtype==1)
      {
         int _clusters_by_index[];
         if(m_clustering_index.ToArray(_clusters_by_index))
         {
            int _output_count=0;
            for(int i=1;i<m_training_points;i++)
            {
               //get mean target bar range of matching cluster
               if(_clusters_by_index[0]==_clusters_by_index[i])
               {
                  _output+=(m_data.y[i-1]);
                  _output_count++;
               }
            }
            //
            if(_output_count>0){ _output/=_output_count; } 
         }
      }


一旦找到匹配项后,我们将继续并发计算该聚类内所有训练数据点的平均 Y 值。获得平均值会被认为较粗略,但这是一种途径。另一个可能是查找中位数,或也许是众数,无论选择哪个选项,都遵从相同的原则,即仅从其聚类中的其它数据点获取当前点的 Y 值。


使用树状图

到目前为止,我们通过共享源代码展示了如何用所创建的单个聚类进行分类和预测。那么树状图的作用是什么呢?为何量化每个聚类之间的差值很重要?为了回答这个问题,我们可以考虑比较两个训练数据点,而非像我们刚才所做的那样只有一个。在这种场景下,就波动率而言,我们可以在关键拐点从历史中获得一个数据点(如果您预测价格方向,这可能是价格波动的关键分形,但我们在本文中关注的是波动性)。由于我们有两个点的聚类,因此它们之间的距离将告诉我们当前数据点与过去拐点的接近程度。


案例研究

依据向导组装的智能系统运行了少量测试,其利用了资金管理类的自定义实例。我们的信号类基于函数库提供的动量振荡器,我们在 2022.10.01 到 2023.10.01 的 H4 时间帧上针对品种 EURUSD 运行,这产生了如下报告:

r1

作为对照,我们还采用上述相同的条件运行测试,除了所用的资金管理是函数库提供的固定保证金选项,其给了我们以下报告:

r2

我们从简短测试的两份报告中得到的启示是,依据品种的盛行波动性调整我们的交易量大有潜力。我们的智能系统和控制的设置分别如下所示。

s1

以及

s2

很明显,大多数情况下都采用类似设置,唯一的例外是在我们的智能系统中,我们不得不更多地采用自定义资金管理。


结束语

综上所述,我们已经探索了如何借助树状图的集聚层次化分类来帮助识别和调整不同的数据集,以及如何将这种分类用于进行预测。可以在此处找到有关该主题的更多信息,且与往常一样,共享的思路和源代码是为了测试,尤其是在与不同方式配对的设定中。这就是为何采用 MQL5 向导类的代码格式。


附件注意事项

附带代码旨在与 MQL5 向导一起组装,作为包含信号类文件和尾随类文件进行组装的一部分。就本文,信号文件是动量振荡器(SignalAO.mqh)。有关如何使用向导的更多信息,请参阅此处


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

附加的文件 |
MoneyWZ_7.mqh (11.68 KB)
神经网络变得简单(第 61 部分):离线强化学习中的乐观情绪问题 神经网络变得简单(第 61 部分):离线强化学习中的乐观情绪问题
在离线学习期间,我们基于训练样本数据优化了智能体的政策。成品政策令智能体对其动作充满信心。然而,这种乐观情绪并不总是正当的,并且可能会在模型操作期间导致风险增加。今天,我们要寻找降低这些风险的方法之一。
开发回放系统(第33部分):订单系统(二) 开发回放系统(第33部分):订单系统(二)
今天,我们将继续开发订单系统。正如您将看到的,我们将大规模重用其他文章中已经展示的内容。尽管如此,你还是会在这篇文章中获得一点奖励。首先,我们将开发一个可以与真实交易服务器一起使用的系统,无论是从模拟账户还是从真实账户。我们将广泛使用MetaTrader 5平台,该平台将从一开始就为我们提供所有必要的支持。
Scikit-Learn 库中的分类模型及其导出到 ONNX Scikit-Learn 库中的分类模型及其导出到 ONNX
在本文中,我们将探讨使用 Scikit-Learn 库中所有可用的分类模型来解决 Fisher 鸢尾花数据集的分类任务。我们将尝试把这些模型转换为 ONNX 格式,并在 MQL5 程序中使用生成的模型。此外,我们将在完整的鸢尾花数据集上比较原始模型与其 ONNX 版本的准确性。
神经网络实验(第 7 部分):传递指标 神经网络实验(第 7 部分):传递指标
传递指标至感知器的示例。本文讲述了一般概念,并展示了最简单的现成智能交易系统,后随其优化和前向验算结果。