English Русский Español Deutsch 日本語 Português
preview
MQL5中的范畴论(第18部分):自然性四边形

MQL5中的范畴论(第18部分):自然性四边形

MetaTrader 5测试者 | 18 四月 2024, 10:19
340 0
Stephen Njuki
Stephen Njuki

概述

范畴论在MQL5中对交易员的影响必然是主观的,到目前为止,在这些系列中,我们使用了其全系统的方法,强调态射而非对象,以对金融数据进行预测和分类。

自然变换(Natural transformations)是范畴论中的一个关键概念,通常被视为函子之间的简单映射。这种平淡无奇的观点虽然没有错,但如果你考虑到函子链接了两个对象,就可能会导致一些混乱,因为问题变成了自然变换链接哪些对象?简单的答案是函子的两个共域对象,在本文中,我们将尝试展示导致这个定义的构建,并包括一个使用这个态射来预测波动性变化的EA交易跟踪类的实例。

在说明自然变换时用作示例的范畴将是两个,这是用于定义自然变换的一对函子的最小数量。第一个将由两个对象组成,这两个对象由归一化的指标值组成。我们将使用的指标是ATR和布林带的值。第二个范畴将作为共域范畴,因为两个函子将引导它,它将包括四个对象,这些对象将捕获我们想要预测的值的价格柱范围。


了解范畴

在我们的文章中提到指标值范畴只是为了帮助理解这里概述的概念。最终,它在预测我们感兴趣的波动性方面发挥的作用微乎其微,因为我们将主要依靠自然性四边形(naturality square)来实现这一点。它仍然是基础。关于自然性四边形,网上没有太多明确的信息,但这篇文章对于那些在这里分享的内容之外寻找更多资源的人来说,可能是一篇有趣的读物

回到我们的领域范畴,如前所述,它有两个对象,一个具有ATR值,另一个具有布林带值。这些值被规范化,使得对象具有固定的基数(大小)。每个对象中表示的值是对指标值的相应变化。这些更改以10%的步长记录,从负100%到正100%,这意味着每个对象的基数为21。因此,它们包括以下值:

{

-100, -90, -80, -70, -60, -50, -40, -30, -20, -10,

 0,

 10, 20, 30, 40, 50, 60, 70, 80, 90, 100

}

链接这些元素相同对象的态射将基于它们是否同时注册来对值进行配对,从而提供两个指标值的当前变化记录。

这些指标变化值可能来自任何其他与波动性相关的指标。原则保持不变。指标值的变化除以上一次和当前指标读数的绝对值之和,得到小数。然后将这个小数乘以10,四舍五入到小数点后没有小数。然后,它再次乘以10,并根据其等效值在上面列出的对象中指定一个索引。

价格柱范围范畴将包括四个对象,这四个对象将是我们在进行投影时使用的自然性四边形的主要焦点。由于我们的域范畴(具有指标变化)由两个对象组成,并且我们有两个函子从它通向这个共域,因此它遵循这些函子中的每个都映射到一个对象。映射到的对象并不总是必须不同,然而,在我们的情况下,为了帮助澄清我们的概念,我们让域范畴中映射的每个对象在价格范围范畴中都有自己的共域对象。因此,2个对象乘以2个函子将产生4个端点对象,即我们的共域范畴的成员。

由于我们有四个对象,并且不希望具有双重性,因此每个对象都将记录一组不同的价格柱范围变化。为了帮助实现这一点,这两个函数将表示不同的预测增量。一个函子将把价格柱范围映射到一个柱之后,而另一个函子将把价格范围变化映射到两个柱之后。此外,ATR对象的映射将用于单个柱形图上的价格范围,而布林带对象的映射则用于两个柱形图的价格范围。这可以通过下面实现的代码来概括:

      CElement<string> _e;
      for(int i=0;i<m_extra_training+1;i++)
      {
         double _a=((m_high.GetData(i+_x)-m_low.GetData(i+_x))-(m_high.GetData(i+_x+1)-m_low.GetData(i+_x+1)))/((m_high.GetData(i+_x)-m_low.GetData(i+_x))+(m_high.GetData(i+_x+1)-m_low.GetData(i+_x+1)));
         double _c=((m_high.GetData(i+_x)-m_low.GetData(i+_x))-(m_high.GetData(i+_x+2)-m_low.GetData(i+_x+2)))/((m_high.GetData(i+_x)-m_low.GetData(i+_x))+(m_high.GetData(i+_x+2)-m_low.GetData(i+_x+2)));
         double _b=((fmax(m_high.GetData(i+_x),m_high.GetData(i+_x+1))-fmin(m_low.GetData(i+_x),m_low.GetData(i+_x+1)))
                     -(fmax(m_high.GetData(i+_x+2),m_high.GetData(i+_x+3))-fmin(m_low.GetData(i+_x+2),m_low.GetData(i+_x+3))))
                     /((fmax(m_high.GetData(i+_x),m_high.GetData(i+_x+1))-fmin(m_low.GetData(i+_x),m_low.GetData(i+_x+1)))
                     +(fmax(m_high.GetData(i+_x+2),m_high.GetData(i+_x+3))-fmin(m_low.GetData(i+_x+2),m_low.GetData(i+_x+3))));
         double _d=((fmax(m_high.GetData(i+_x),m_high.GetData(i+_x+1))-fmin(m_low.GetData(i+_x),m_low.GetData(i+_x+1)))
                     -(fmax(m_high.GetData(i+_x+3),m_high.GetData(i+_x+4))-fmin(m_low.GetData(i+_x+3),m_low.GetData(i+_x+4))))
                     /((fmax(m_high.GetData(i+_x),m_high.GetData(i+_x+1))-fmin(m_low.GetData(i+_x),m_low.GetData(i+_x+1)))
                     +(fmax(m_high.GetData(i+_x+3),m_high.GetData(i+_x+4))-fmin(m_low.GetData(i+_x+3),m_low.GetData(i+_x+4))));
         
         ...
      }


这些对象将是单一大小的,因为它们只记录当前变化。它们之间的态射将以四边形方式从单柱价格commute投影到两个柱之前的两柱范围预测价格。当我们在下面正式定义自然变换时,我们将分享更多关于这一点的信息。

价格柱范围和来源市场数据之间的关系也显示在我们上面的来源中。记录在每个对象中的变化并不像指标值那样标准化,而是将范围中的变化除以当前柱形图范围和先前柱形图范围的总和,以产生未舍入的十进制值。


函子:将指标值与价格柱范围相关联

函子早在我们的第四篇系列文章中就已经介绍过了,但在这里,它们被视为两类中的一对。回忆函子不仅映射对象,还映射态射,因此与此保持一致,因为我们的指标值的域范畴有两个对象和一个态射,这意味着在我们的共域范畴中有三个输出点,每个函子有两个来自对象,一个来自态射。对于两个函子,这使得我们的共域中有六个端点。

标准化指标整数值到十进制价格条范围变化的映射,即记录为分数而非原始值的变化,可以在多层感知器的帮助下完成,正如我们在前两篇文章中所探讨的那样。在这些系列中,还有许多其他方法尚未探索,例如随机森林

此处的图示仅为完整起见。正确地展示什么是自然转变及其所有先决条件。作为交易员,当面临新概念时,关键问题总是它的应用和好处是什么?这就是为什么我一开始就说,为了我们的预测目的,我们的重点将是自然性四边形,它完全由同域范畴中的四个对象决定。因此,在这里提到域范畴及其对象只是有助于定义自然变换,而对本文的特定应用程序没有帮助。


自然变换:弥合差距

有了这一点,现在可以看看自然变换公理,以便继续应用。形式上,函子之间的自然变换

F: C --> D

以及

G: C --> D

是一个态射族

ηA: F(A) --> G(A)

对于范畴C中的所有对象A,使得对于所有态射

f: A --> B

C范畴中,下图进行了commute:


 

网上有大量关于自然变换的材料,但同样有助于找到一个更具说明性的定义,从而得出自然性四边形。为此,我们假设有两个范畴C&D,范畴C有两个对象X和Y,定义如下:

X = {5, 6, 7}

以及

Y = {Q, R, S}

我们还假设这些对象之间存在态射,f定义为:

f: X à Y

所以 f(5) = S, f(6) = R, and f(7) = R.

对于这个例子,范畴C和D之间的两个函子F和G将做两件简单的事情。分别准备一个列表和一个列表的列表。因此,函子F在应用于X时将输出:

[5, 6, 5, 7, 5, 6, 7, 7]

类似地,函子G(列表的列表)将给出:

[[5, 6], [5, 7, 5, 6, 7], [7]]

如果我们将这些函子类似地应用于对象Y,我们将在共域范畴D中得到4个对象。它们表示如下:



请注意,我们现在只关注范畴D中的四个对象。由于我们的域范畴C中有两个对象,我们还将分别对C中的一个对象进行两个自然变换。它们表示如下:


上面的表示是自然性的四边形。正如你从箭头所示,它可以 commute。因此,两个水平箭头是我们关于C中每个对象的自然变换(NT),垂直箭头是当应用于函子f和G的范畴C中的态射f时的函子输出。

保护结构和关系的重要性是NT的一个关键方面,这一点可能会被忽视,但即使它如此简单,它也是至关重要的。为了表明我们的观点,让我们考虑一下烹调/烹饪领域的一个例子。假设两位著名的厨师,让我们称他们为A和B,他们各自有一种独特的方法,用一套标准的食材烹制同一道菜。我们将食材视为更广泛的食材类型范畴中的一个对象,每个厨师制作的两道菜也属于另一个更广泛的菜肴类型范畴。现在,我们的厨师A和B制作的两道菜之间的自然变换将记录配料,以及将厨师A制作的菜修改为厨师B制作的菜所需的额外烹饪准备工作。通过这种方法,我们正在记录更多信息,事实上可以检查,看看厨师C的菜是否也需要如此相似的NT来匹配厨师B的菜,或者如果不需要,需要多大程度的NT?但除了比较之外,NT申请获得厨师B的菜还需要厨师A的食谱、烹饪风格和方法。这意味着他们受到保护和尊重。这种保存对记录很重要,但也可以作为开发新食谱的一种手段,甚至可以根据某人的饮食限制检查现有食谱。


应用程序:预测波动性

有了这些,我们现在可以研究在预测中的可能应用。预测价格柱范围的下一个变化是我们在这些系列中探讨了很多的事情,因此初步解释可能并不奇怪。但概括一下,我们首先使用这个预测来确定我们是否需要调整未平仓合约的跟踪止损,其次是我们需要调整多少。

自然平方作为关键工具的实现将在多层感知器(MLP)的帮助下进行,就像我们上两篇文章中的情况一样,这里的区别是这些MLP是围绕平方换相(square commutation)组成的。这使我们能够检查我们的预测,因为任何两个阶段都可能产生预测。正方形的四个角反映了在未来某个时候对我们价格柱范围变化的不同预测。当我们向D角移动时,我们越是展望未来,A角只投影下一个小节的范围变化。这意味着,如果我们能够使用最近价格柱的范围变化来训练连接所有四个角落的MLP,我们就可以在一个价格柱之外做出更进一步的预测。

下面的代码突出显示了应用NT获取预测所涉及的步骤:

//+------------------------------------------------------------------+
//| NATURAL TRANSFORMATION CLASS                                     |
//+------------------------------------------------------------------+
class CTransformation
   {
      protected:
      
      public:
      
      CDomain<string>               domain;  //codomain object of first functor
      CDomain<string>               codomain;//codomain object of second functor
      
      uint                          hidden_size;
      CMultilayerPerceptron         transformer;
      CMLPBase                      init;
      
      void                          Transform(CDomain<string> &D,CDomain<string> &C)
                                    {
                                       domain=D;
                                       codomain=C;
                                       
                                       int _inputs=D.Cardinality(),_outputs=C.Cardinality();
                                       
                                       if(_inputs>0 && _outputs>0)
                                       {
                                          init.MLPCreate1(_inputs,hidden_size+fmax(_inputs,_outputs),_outputs,transformer);
                                       }
                                    }
      
      //
      void                          Let()
                                    {
                                       this.codomain.Let();
                                       this.domain.Let();
                                    };
      
                                    CTransformation(void){ hidden_size=1; };
                                    ~CTransformation(void){};
   };

首先,我们在上面列出了NT类。从随意的定义来看,你会期望它包括它所链接的两个函子的实例,但这虽然适用,但不够简洁。NT的关键是函子映射到的两个域,这两个域是突出显示的。

//+------------------------------------------------------------------+
//| NATURALITY CLASS                                                 |
//+------------------------------------------------------------------+
class CNaturalitySquare
   {
      protected:
      
      public:
      
      CDomain<string>               A,B,C,D;
      
      CTransformation               AB;
      
      uint                          hidden_size_bd;
      CMultilayerPerceptron         BD;
      
      uint                          hidden_size_ac;
      CMultilayerPerceptron         AC;
      
      CTransformation               CD;
      
      CMLPBase                      init;
      
      
                                    CNaturalitySquare(void){};
                                    ~CNaturalitySquare(void){};
   };

自然性四边形的图如上图所示,它的类也将用NT类的实例表示。它的四个角由A、B、C和D表示,是域类捕获的对象,并且它的态射中只有两个是直接MLP,其他两个被识别为NT。


MQL5中的实际实现

考虑到我们对MLP的使用,MQL5中的实际实现势必面临挑战,主要是在我们如何训练和存储所学内容(网络权重)方面。对于这篇文章,与前两篇不同的是,训练中的权重根本没有存储在每个新条上——四个MLP中的每一个的新实例都会生成并训练。这是通过如下所示的刷新函数实现的:

//+------------------------------------------------------------------+
//| Refresh function for naturality square.                          |
//+------------------------------------------------------------------+
double CTrailingCT::Refresh()
   {
      double _refresh=0.0;
      
      m_high.Refresh(-1);
      m_low.Refresh(-1);
      
      int _x=StartIndex();
      
      // atr domains capture 1 bar ranges       
      // bands' domains capture 2 bar ranges    
      // 1 functors capture ranges after 1 bar  
      // 2 functors capture ranges after 2 bars 
      
      int _info_ab=0,_info_bd=0,_info_ac=0,_info_cd=0;
      CMLPReport _report_ab,_report_bd,_report_ac,_report_cd;
      CMatrixDouble _xy_ab;_xy_ab.Resize(m_extra_training+1,1+1);
      CMatrixDouble _xy_bd;_xy_bd.Resize(m_extra_training+1,1+1);
      CMatrixDouble _xy_ac;_xy_ac.Resize(m_extra_training+1,1+1);
      CMatrixDouble _xy_cd;_xy_cd.Resize(m_extra_training+1,1+1);
      
      CElement<string> _e;
      for(int i=0;i<m_extra_training+1;i++)
      {
         ...
         
         if(i<m_extra_training+1)
         {
            _xy_ab[i].Set(0,_a);//in
            _xy_ab[i].Set(1,_b);//out
            
            _xy_bd[i].Set(0,_b);//in
            _xy_bd[i].Set(1,_d);//out
            
            _xy_ac[i].Set(0,_a);//in
            _xy_ac[i].Set(1,_c);//out
            
            _xy_cd[i].Set(0,_c);//in
            _xy_cd[i].Set(1,_d);//out
         }
      }
      
      m_train.MLPTrainLM(m_naturality_square.AB.transformer,_xy_ab,m_extra_training+1,m_decay,m_restarts,_info_ab,_report_ab);
     
      ...

      //
      if(_info_ab>0 && _info_bd>0 && _info_ac>0 && _info_cd>0)
      {
         ...
      }
      
      return(_refresh);
   }

上面的刷新函数仅在最近的价格柱上训练用随机权重初始化的MLP。这显然不足以用于其他交易系统或共享代码的实现,但是,出于我们的测试目的,可以向上调整默认值为零的输入参数“m_extra_training”,以在做出预测之前提供更全面的测试。

在额外训练中使用该参数势必会给专家带来超负荷的表现,事实上,这也说明了为什么在本文中避免了阅读和书写训练中的权重。


优点和局限性

如果我们在2022.08.01至2023.08.01的每日时间范围内对 EURUSD 进行测试,我们的最佳运行之一会产生以下报告:

r_1


l_1

如果我们在非优化期(在我们的情况下是测试范围前的一年)使用这些相同设置进行测试,我们会得到负面结果,这些结果并不能反映我们在上述报告中获得的良好性能。可以看出,所有的利润都来自于止损。

与我们在本系列早期使用的方法相比,在预测波动性时,这种方法无疑是一种资源密集型方法,显然需要对自然性四边形中的四个对象的定义方式进行修改,以实现在非优化时期的前瞻测试。


结论

总之,这里提出的关键概念是自然变换。它们通过捕捉连接范畴的平行函子对之间的差异,在连接范畴方面具有重要意义。这里探讨的应用是通过利用自然性四边形来预测波动性,然而其他可能的应用确实包括进场和退出信号的生成以及头寸大小。此外,值得一提的是,对于本文和整个系列,我们没有对获得的优化设置进行任何前瞻运行。因此,它们很可能不会开箱即用(即,当提供代码时),但一旦进行了修改,例如通过将这些想法与读者可能使用的其他策略配对,它们可能会起作用。这就是为什么MQL5向导类的使用非常方便,因为它无缝地允许这样做。


参考

Wikepedia 和 stack exchange 分享了超链接。


附件说明

请将文件'SignalCT_16_.mqh'放在文件夹'MQL5\include\Expert\Signal\'中,文件'ct_16.mqh'可以放在'MQL5\include\'文件夹中。

此外,您可能需要按照指南了解如何使用向导组装 EA 交易,因为您需要将它们作为 EA 交易的一部分进行组装。正如文章中所述,我对资金管理使用了无跟踪止损和固定保证金,这两种方法都是MQL5库的一部分。和往常一样,本文的目标不是向您展示圣杯,而是一个您可以根据自己的策略进行定制的思路。



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

附加的文件 |
ct_18.mqh (37.71 KB)
TrailingCT_18_.mqh (17.66 KB)
MQL5中的范畴论(第19部分):自然性四边形归纳法 MQL5中的范畴论(第19部分):自然性四边形归纳法
我们继续通过探讨自然性四边形归纳法来研究自然变换。对于使用MQL5向导构建的EA交易来说,对多货币实现的轻微限制意味着我们正在通过脚本展示我们的数据分类能力。所考虑的主要应用是价格变化分类及其预测。
MQL5 中的范畴论 (第 16 部分):多层感知器函子 MQL5 中的范畴论 (第 16 部分):多层感知器函子
本文是我们系列文章的第 16 篇,继续考察函子以及如何使用人工神经网络实现它们。我们偏离了迄今为止在该系列中所采用的方式,这涉及预测波动率,并尝试实现自定义信号类来设置入仓和出仓信号。
神经网络变得轻松(第五十四部分):利用随机编码器(RE3)进行高效研究 神经网络变得轻松(第五十四部分):利用随机编码器(RE3)进行高效研究
无论何时我们研究强化学习方法时,我们都会面对有效探索环境的问题。解决这个问题通常会导致算法更复杂性,以及训练额外模型。在本文中,我们将看看解决此问题的替代方法。
如何利用 MQL5 创建简单的多币种智能交易系统(第 1 部分):基于 ADX 指标的信号,并结合抛物线 SAR 如何利用 MQL5 创建简单的多币种智能交易系统(第 1 部分):基于 ADX 指标的信号,并结合抛物线 SAR
本文中的多币种智能交易系统是交易机器人,它只能在单一品种图表中运营,但可交易(开单、平单和管理订单)超过一个品种对。