
您应当知道的 MQL5 向导技术(第 11 部分):数字墙
概述
对于少量时间序列,可以依据序列中出现的先前值为序列中的下一个值设计一个公式。数字墙允许遵照交叉规则以矩阵的形式,初步生成“数字墙”来达成这一点。在生成这个矩阵时,主要目标是确立问题中的序列是否收敛,且数字墙交叉规则算法能愉快地回答这个问题,如果在应用几行之后,矩阵中的后续行只是零。
在这篇已发表论文中,展示了这些概念,Laurent Power 级数又名形式化 Laurent 级数(FLS),已当作框架,在使用柯西(Cauchy)乘积时以多项式格式依其算术表示这些序列。
通常,LFSR 序列满足递归关系,如此这般连续项的线性组合始终为零,概括如以下等式:
其中,序列 Sn 是一个线性重复、或线性反馈移位寄存器(LFSR),并且存在一个长度 r + 1 的 |Ji|(关系式)非零向量。
这意味着一个向量关系,其中 x 系数(常数是 x 的系数到幂零的系数)构成其元素。根据定义,该向量的量级至少为 2。
为了概括这一点,我们可以研究一对简单的例子,第一个是立方数字序列。我们都知道立方数字从零开始列举,每个值都是其索引位置的立方;如果我们将这些馈送到数字墙矩阵,初始表示将如下所示:
序列行上方所示的 0 和 1 始终是隐含的,它们有助于应用交叉规则,如此即可在矩阵内传播所有缺失值。在应用交叉规则时,对于矩阵中采用简单交叉格式的任意 5 个值,外部垂直数值的乘积、与外部水平数值的乘积相加时,应始终等于中心值的平方。
如果我们将此规则应用于上面的立方数字的基本序列,我们将得到如下所示的矩阵:
如此,为了概括交叉规则,如果我们研究上面序列中的数字 216,取它之前和之后的数字 125 乘以 343,当加到 7057 乘以 1 时,即分别是 343 下面的数字和上面的数字,结果给出我们 216 的二次幂。
我们能够如此快速地获得零值行的事实表明,这个序列确实是收敛的,并且可以很容易地推导出其后续值的公式。
但在我们查看公式之前,我们再研究一个例子,斐波那契(Fibonacci)级数。如果我们要在矩阵中应用这个级数,并应用上述长交叉规则,我们可以更迅速地得到零值行!
这当然看似很奇怪,因为人们会期望斐波那契级数比立方数字更复杂,因此需要更长久才能收敛,但其实不然!它在第 2 行就收敛了。
为了推导出收敛级数的公式,例如上面研究的两个示例,我们将在数字墙矩阵中将序列替换为公式格式,其会把序列中的任何数值替换为相同的数值减去先验值乘以 x。它取以下形状:
从此处,我们如上面的序列所做,简单地应用的我们交叉规则,并为随后的行生成多项式格式数值。有趣的是,如果一个序列是收敛的,即使按多项式数值,在长交叉规则的几次传播之后,我们仍然可能以零值行结束。下面概括了斐波那契数列。
那么之后我们如何处理这些多项式数值呢?好吧,事实证明,如果我们将最后一个方程等同于 0,并求解 x 的最高幂,那么我们在对侧留下的 x 系数是序列中前一个数值的倍数,即数值合计等于下一个数值。
故此,对于斐波那契数列的最后一个方程,如果我们求解 x^2,最高幂我们会留下:1 + x;
请记住,1 代表 x^0,实际上也是系数为 x 的序列号之前那个序列号的系数。简而言之,在斐波那契级数中,任何数字都是序列中两个先验数字之和。
立方序列的多项式方程是什么样的?如上所示,收敛需要更多的行,因此它更复杂,而非您所期望的将方程式表示为立方。
非收敛级数会如何,在这些场景下会生成什么样的矩阵?为了概括这一点,也许我们可以通过查看正规外汇对的价格序列来跳到正确位置,以 EURUSD 为例。如果我们尝试生成一个数字墙矩阵(没有公式)来测试 2024 年前 5 个交易日的 EURUSD 每日收盘价序列的收敛性,我们将为前 5 行得出一个类似于此的墙。
不言而喻,我们在第一行中没有得到收敛,事实上,我们是否曾得到真的严格收敛还远不清楚,尽管墙的趋势朝向于零,如此这应该令人欣慰。对于我们的意图,这确实削弱了数字墙的适用性,事实上,这个初步检查过程也是典型的计算密集型,这就是 Toeplitz 矩阵发挥作用的地方。
-如果我们创建一个矩阵,其中所有行都以某种方式相互关联,借此我们使用序列行的滑动重复,如果序列是收敛的,那么这个矩阵的行列式将为零。这是一种计算效率更高的收敛测试方法,而且它不仅如此,基于行列式的量级,其可按照我们可能令序列收敛的程度来“量化”。
故此,我们可以在任何序列的公式矩阵上传播交叉规则,并使用行列式的大小来“贴现”公式的预测值。要不然,我们可以有一个绝对阈值行列式数值,如果超过该值意味着我们将忽略公式结果。
所有这些都是可能的非收敛金融和其它时间序列的应对手段,它们当然不完美,但我们来验证当利用 MQL5 实现时它们有什么潜力。
MQL5 实现
为了以 MQL5 描绘这些思路,我们将在 EA 尾随类的实例中实现它们,该实例与内置的 动量震荡指标类配对,以便创建一个简单的智能系统。尾随类的实例将使用数字墙来判定尾随止损和止盈价位的量级大小。
当利用 MQL5 实现时,鉴于它们的额外函数和最低的代码要求,我们将大量使用矩阵和向量的内置数据类型。我们可以通过构建一个典型的(非公式)数字墙,并检查我们是否得出一行零,来预筛选一个序列的收敛性,但考虑到金融时间序列的性质和复杂性,给定的矩阵是不会收敛的,因此我们最好按公式计算序列中下一个数值,该值是从传播后最后一列的底行得到的。
为了传播数字墙,我们使用向量存储 x 的系数。在求解未知行的过程中,将任意两个这样的向量相乘等效于互相关,而生成的向量值将是 x 系数,其中较高的索引位置表示 x 的指数较高。这个函数是内置的,但是当涉及到除法时,我们需要调整两个商向量的大小,从而确保它们与任何大小的差值匹配,这只是意味着它们并不与 x 指数匹配。
在判定持仓的止盈和止损调整多少时,我们的数字墙的输入序列将是移动平均线的指标值。可以使用任何指标,尽管这个指标或布林带、或轨迹线样式指标可能更适合调整尾随止损。
一旦定义了句柄,MQL5 向量就可以轻松复制和加载指标值。我们查看下面的源码,典型的检查尾随停止代码(可用于多头和空头两者)
//+------------------------------------------------------------------+ //| Checking trailing stop and/or profit for long position. | //+------------------------------------------------------------------+ bool CTrailingLFSR::CheckTrailingStopLong(CPositionInfo *position, double &sl, double &tp) { //--- check ... //--- vector _t, _p; _p.Init(2); _t.CopyIndicatorBuffer(m_ma.Handle(), 0, 0, 2); double _s = 0.0; for(int i = 1; i >= 0; i--) { _s = 0.0; _p[i] = GetOutput(i, _s); } double _o = SetOutput(_t, _p); //--- ... }
我们可以看到,我们的决策制定是围绕两个函数进行的,即 GetOutput 和 SetOutput。GetOutput 是主要的,因为它构建了数字墙,并输出方程的多项式系数,以便查找序列中的下一个数值。GetOutput 清单,如下所示:
//+------------------------------------------------------------------+ //| LFSR Output. | //+------------------------------------------------------------------+ double CTrailingLFSR::GetOutput(int Index, double &Solution) { double _output = 0.0; vector _v; _v.CopyIndicatorBuffer(m_ma.Handle(), 0, Index, m_length); Solution = Solvability(_v); _v.Resize(m_length + 1); for(int i = 2; i < 2 + m_length; i++) { for(int ii = 2; ii < 2 + m_length; ii++) { if(i == 2) { vector _vi; _vi.Init(1); _vi[0] = _v[m_length - ii + 1]; m_numberwall.row[i].column[ii] = _vi; } else if(i == 3) { vector _vi; _vi.Init(2); _vi[0] = m_numberwall.row[i - 1].column[ii][0]; _vi[1] = -1.0 * m_numberwall.row[i - 1].column[ii - 1][0]; m_numberwall.row[i].column[ii] = _vi; } else if(ii < m_length + 1) { m_numberwall.row[i].column[ii] = Get(m_numberwall.row[i - 2].column[ii], m_numberwall.row[i - 1].column[ii - 1], m_numberwall.row[i - 1].column[ii + 1], m_numberwall.row[i - 1].column[ii]); } } } vector _u = Set(); vector _x; _x.CopyIndicatorBuffer(m_ma.Handle(), 0, Index, m_length); _u.Resize(fmax(_u.Size(),_x.Size())); _x.Resize(fmax(_u.Size(),_x.Size())); vector _y = _u * _x; _output = _y.Sum(); return(_output); }
我们上面的共享清单基本上是 2 部分。构造数字墙以获得方程系数,并使用方程依据当前序列读数进行投影。在第一部分中,get 函数是乘法生成方程的关键,并求解下一行的方程。清单如下:
//+------------------------------------------------------------------+ //| Get known Value | //+------------------------------------------------------------------+ vector CTrailingLFSR::Get(vector &Top, vector &Left, vector &Right, vector &Center) { vector _cc, _lr, _cc_lr, _i_top; _cc = Center.Correlate(Center, VECTOR_CONVOLVE_FULL); _lr = Left.Correlate(Right, VECTOR_CONVOLVE_FULL); ulong _size = fmax(_cc.Size(), _lr.Size()); _cc_lr.Init(_size); _cc.Resize(_size); _lr.Resize(_size); _cc_lr = _cc - _lr; _i_top = 1.0 / Top; vector _bottom = _cc_lr.Correlate(_i_top, VECTOR_CONVOLVE_FULL); return(_bottom); }
同样,set 函数使用含有最后传播系数的底部向量来求解下一个序列值,其清单如下:
//+------------------------------------------------------------------+ //| Set Unknown Value | //+------------------------------------------------------------------+ vector CTrailingLFSR::Set() { vector _formula = m_numberwall.row[m_length + 1].column[m_length + 1]; vector _right; _right.Copy(_formula); _right.Resize(ulong(fmax(_formula.Size() - 1, 1.0))); double _solver = -1.0 * _formula[int(_formula.Size() - 1)]; if(_solver != 0.0) { _right /= _solver; } return(_right); }
现在,在检查尾随函数之内,我们调用两次 GetOutput 函数,因为我们不仅需要获取当前预测,还需要获取先验结果,来辅助常规化我们的输出,这是因为由于大多数序列,尤其是金融时间序列不会收敛,如概述中所言,原始输出预测定然与输入序列值在含义上格格不入。获得的数值比典型序列值的量级大若干倍,甚至获得负值的情况并不少见,而显然此处只需要正值。
故此,在常规化时,我们调用非常简短的 SetOutput 函数,下面分享:
//+------------------------------------------------------------------+ //| Normalising Output to match Indicator Value | //+------------------------------------------------------------------+ double CTrailingLFSR::SetOutput(vector &True, vector &Predicted) { return(True[1] - ((True[0] - True[1]) * ((Predicted[0] - Predicted[1]) / fmax(m_symbol.Point(), fabs(Predicted[0]) + fabs(Predicted[1]))))); }
我们所做的只是简单地将预测值常规化,令其量级在序列值范围内,这是针对预测值和真实值利用序列变化来实现这一点。
此外,在典型的尾随停止检查中,我们会在 GetOutput 函数开始时测量可解性。该度量用作阈值过滤器,来判定我们是否应该忽略预测,其论点是较大的行列式值(我们称之为可解性)表示更高的收敛能力。可以说,即使含有小行列式的矩阵也不会收敛,不过我们假设,当提供更多行数来构建数字墙时,它比具有较高行列式的矩阵更有可能收敛。
故此,把这些放在一起,我们得到了文章末尾附带的尾随类的实例,尽管初步测试确实表明它是计算密集型的,而在我看来,这个思路可以改进,甚至可以与各种策略配对,从而开发出一款更健壮的交易系统。
附带代码是以 MQL5 实现的尾随类,可以很容易地由向导汇同到一起,从而根据所选信号类和资金管理,创建各式各样的智能系统如常,此处的帮助可作为如何执行该操作的指南。
附加提示
在共享代码和上述概括中到目前为止研究的数字墙用到的整个证券价格不包括任何零值。举例,如果我们想预测证券价格的变化,而不仅仅是原始价格,我们在理想情况下会有一个价格变化的输入序列。一旦我们开始应对与绝对价格相反的变化,我们不仅会遇到负值,还会且肯定会经常遇到出现几个零值。
最初,零值也许无害,但是当我们构建数字墙时,它们确实在判定下一行的数值时带来了挑战。参考以下示例:
配合我们求解未知值的基本交叉规则,我们会遭遇一个问题,由于一个已知值为零,因此当与我们的未知值相乘时,我们会空手而归。为了绕过这一点,长交叉规则就可上手了。如果我们拓展上面的图像,此处是它的表示形式:
我们能够有一定信心处置当前行上方的零值,即执行以下公式:
我所提到的“有一定信心”,因为在包含零值时,数字墙具有非常独特的属性。事实证明,在任何数字中,如果有零,则它们就会以单数或平方出现,这意味着它们可以是 1 x 1(单数)、或 2 x 2,或 3 x 3,依此类推。这基本上意味着如果我们在任何行上的两个数字之间遇到一个零,那么它下面的数字就不是零。任何人都可以看到,当我们应用长交叉规则时,我们有一个额外的未知数,即数字墙中最低的外部值。不过这不是问题,因为它乘以我们已知的零,其允许我们无需输入其值即可求解方程。
由长交叉规则解决问题适用于严格收敛的数字墙,正如我们在上面的概括中所见,这种情况很少出现在金融时间序列当中。如此,这值得参考吗?坦诚地说,这应该根据交易系统所关注的时间序列逐个序列判定。如果“可解性”或 Toeplitz 矩阵行列式满足必要的阈值,有些人甚至可以选择将其应用于金融序列,其他人可以终止构建数字墙,并使用他们于该点处(当它们为零时)的向量系数来构建预测多项式方程。有这些,也许还有一些其它选项,选择权决定于交易者的具体情况。
如果在传播数字墙时只遇到一个零,那么长交叉规则足够机敏,不过若是零位于一个更大的方块中(因为零总是在数字墙上采用 n x n 格式),那么它不会有太大用处,在这些实例中,通常被认为是马蹄规则。按该规则,典型情况一旦您有一个大的零值方块,该方块的边界序列就会按特定因子缩放其值。
这四个因子(方块的每条边各一个)具有一个独特的属性,可以用下面的公式来总结:
因此,当遇到一大堆零时,与这些零接壤的顶部和侧面数字已经是已知的,并且由于我们知道零值方块的宽度,我们本质上知道其高度,这意味着我们知道在何处估算未知的边界值。从上面的方程式中,下面的直接边界值可以通过计算底部比例因子,并计算其数字(通常从左到右)来获得。
然而,从这一点开始,使用普通的交叉规则仍然很困难,因为方块中的零值令其难以判定刚刚求解的边界行之下的行。解决这个问题代表了马蹄铁规则的“第二部分”,它依赖于以下有点冗长的公式:
关于这个主题,上面所引用的论文,除了佐证此处分享的许多观点外,还谈到了宝塔猜想。在最简单的形式中,它是一组数字集的总和,每个数字集都包含一组相同大小的数字,因此,如果将每个包含的集合视为一个多边形,其每个顶点代表其集合中的一个数字,那么这些多边形可以粘合在一起以形成一个更大的复杂格子,前提是在任何连接的顶点中多边形的所有顶点都具有相同的数字。当然,这是假设任何多边形的每个顶点数字在该集合中都是唯一的。
首先,形成宝塔的三个数字集合形成有趣的后果,因为事实证明,当每组数字按顺序排列时,可以在所传播的数字墙上观察到非常有趣的重复形态,在论文中分享了其中一些图像。
尽管出于交易目的,这种“新颖”的分类方式确实提供了另一种看待金融时间序列的途径,坦诚地说,这应该需要一篇单独的文章,可以说,我们足以归纳出宝塔序列可为我们所用的几种方式。
如果我们打算随心所欲地应用它,谨慎的做法是坚持使用三角形宝塔,而非更高维度的形式,因为它们往往会带来更多的可能性,而如何联合它们就很复杂。如果我们能接受这一点,那么我们的任务就是首先把我们的金融序常规化,以便容纳数值的重复,这是定义宝塔时强调的需求,且这种常规化的程度需要仔细检查,因为不同的迭代必然会产生不同的结果。
其次,一旦我们对某个常规化阈值感到满意,我们就需要设置宝塔中的三角形数量,即组大小。记住,随着组大小的提升,所有三角形直接链接的需求会减少,故在 3 个三角形宝塔中,所有三角形都是链接的,但随着这个数字的增加,对于任何三角形,它可拥有的最多链接数是 3 个,这意味着比如说一个 6 个三角形宝塔,只有底部的中心三角形在其所有顶点上都有链接,而所有其它三角形仅在两个顶点上有链接。
随着组大小的增加,三角形连接的复杂性不断增加,这可能表明判定最优组大小应该与设置常规化阈值同时进行,因为后者为我们提供了重复数据,这是建立三角形之间链接的关键。
结束语
为了总结我们已查看的数字墙,即依据正在检查的一连串时间序列生成的数字网格,并在共享代码示例中看到了如何在预测时正确设置持仓的止盈和止损。此外,我们还研究了宝塔猜想的相关概念,关于数字墙是这篇论文的亮点,并提出了一些思路,阐述它们如何能成为金融时间序列分类的另一种途径。
后记
向导组装智能系统的比较测试如下所示。它们都采用动量振荡器信号,且原则上具有类似的输入,如下所示:
它们之间的区别在于,一款智能系统使用抛物线 sar 来尾随和平仓,而另一款智能系统使用本文中讲述的数字墙算法。然而,尽管信号相同,但它们依据过去一年的 H1 时间帧、EURUSD 进行测试时,它们的报告却有所不同。首先,下面是抛物线 sar 尾随停止智能系统。
数字墙报告也如下所示:
结果的总体差异并不明显,但若测试和尝试时,不仅覆盖较长区间,且有不同的智能系统类别(如资金管理、甚或信号生成),则可能至关重要。
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/14142


