解方程
在机器学习方法和优化问题中,通常需要求解线性方程系统。MQL5 包含四个方法可根据矩阵类型求解该等方程。
- Solve 求解线性矩阵方程或线性代数方程系统
- LstSq 近似求解线性代码方程系统(对于非方形矩阵或退化矩阵)
- Inv 使用约当-高斯法计算相对于方形非奇异矩阵的乘法逆元矩阵
- PInv 使用摩尔-彭罗斯法计算伪逆矩阵
下面是方法原型。
vector<T> matrix<T>::Solve(const vector<T> b)
vector<T> matrix<T>::LstSq(const vector<T> b)
matrix<T> matrix<T>::Inv()
matrix<T> matrix<T>::PInv()
Solve 和 LstSq 方法表示求解 A*X=B 形式的方程系统,其中 A 是矩阵,B 是使用函数的值(“因变量”)通过一个参数传递的向量。
我们尝试应用 LstSq 方法求解方程系统,该系统是一个理想组合交易模型(在本例中,我们将分析主要外汇货币组合)。为此,对于给定数量的“历史”柱线,我们需要找出均线趋向于一条持续增长直线时的每种货币的手数。
我们将第 i 个货币对表示为 Si。其在 k 索引柱线处的报价等于 Si[k]。柱线编号从过去到将来编号,如同由 CopyRates 方法填充的矩阵和向量中那样。因此,收集的用于训练模型的报价开头对应于标记为编号 0 的柱线,但在时间线上,它将是(根据算法设置处理的)最老的历史柱线。它右边的柱线(将来方向)编号为 1、2,依此类推,直至用户要求计算的总柱线数。
在第 0 条柱线和第 N 条柱线之间的交易品种价格的变化决定截至第 N 条柱线时的益/损。
考虑到货币对,我们得到第 1 柱线的以下利润方程:
(S1[1] - S1[0]) * X1 + (S2[1] - S2[0]) * X2 + ... + (Sm[1] - Sm[0]) * Xm = B |
其中 m 是总字符数, Xi 是每个交易品种的手数,B 是浮动利润(若锁定利润则为条件性余额)。
为简洁起见,我们缩短表示法。我们从绝对值转到价格增量(Ai[k] = Si[k]-Si[0])。考虑跨柱线的移动,我们将获得若干虚拟余额曲线的表达式:
A1[1] * X1 + A2[1] * X2 + ... + Am[1] * Xm = B[1]
|
成功交易的特征为每条柱线的利润恒定,即,右手边向量 B 的模型是是一个单增函数,理想情况下是一条直线。
我们实施该模型并基于报价为其选择 X 系数。由于我们尚不知道应用程序 API,因此无法编写一个成熟的交易策略。我们只是使用来自标准头文件 Graphic.mqh 的 GraphPlot 函数构建一个虚拟余额图(我们已经使用它演示过 数学函数)。
新示例的完整源代码在 MatrixForexBasket.mq5 脚本中。
在输入参数中,让用户选择用于数据采样的总柱线数 (BarCount),以及该选择范围内的柱线偏移量 (BarOffset),该偏移量对应“假设过去”结束和“假设未来”开始的分界柱线编号。
将对“假设过去”构建一个模型(将求解上述线性方程系统),并对“假设未来”执行前向测试。
input int BarCount = 20; // BarCount (known "history" and "future")
|
为了以一个理想的余额填充向量,我们编写 ConstantGrow 函数:稍后初始化期间将使用这个函数。
void ConstantGrow(vector &v)
|
交易的金融工具(主要外汇对)列表在 OnStart 函数的开头硬设定,需进行编辑以适合你的要求和交易环境。
void OnStart()
|
我们创建将在其中添加交易品种报价的 rates 矩阵、具有所需余额曲线的 model 向量以及用于逐交易品种请求柱线收盘价的辅助 close 向量(它包含的数据将被复制到 rates 矩阵的列中)。
matrix rates(BarCount, size);
|
在一个交易品种循环中,我们将收盘价复制到 close 向量,计算价格增量,并将它们写入到 rates 矩阵的对应列中。
for(int i = 0; i < size; i++)
|
我们将在第五章中探讨一个价格点值(以存款货币计)的计算。
还有一点务必要注意,具有相同索引的柱线可能对不同的金融工具有不同的时间戳,例如,如果某个国家因而节假日而休市(外汇以外的交易品种在理论上可能具有不同的交易时段计划表)。为解决这一问题,我们需要对报价更深度的分析,在将它们插入 rates 矩阵之前,需考虑到柱线时间及其同步。在这里,为了简单起见,同时也因为外汇市场大多数时间根据相同规则运作,因此我们不这么做。
我们将矩阵拆分为两部分:初始部分将用于求解(模拟历史优化),后续部分将用于前向测试(计算后续余额变化)。
matrix split[];
|
获得解后,我们来构建样本的所有柱线的余额曲线(理想的“历史”部分将在开头,然后是“将来”部分,后者不会用于调整模型)。
vector balance = vector::Zeros(BarCount);
|
我们根据 R2 标准估算解的质量。
if(BarOffset > 0)
|
要在图表上显示余额曲线,你需要将数据从向量转移到数组。
double array[];
|
下面是通过在 EURUSD,H1 上运行该脚本获得的一个日志的示例。
Solution (lots per symbol):
|
下面是虚拟余额曲线的示例。

根据决定按“手数”交易货币组合的虚拟余额
左半部分的形状更均匀且 R2 更高,这并不奇怪,因为模型(X 变量)专门针对该部分进行调整。
出于兴趣,我们将训练和验证深度增加 10 倍,即我们将在参数中设置 BarCount = 200 和 BarOffset = 100。我们将得到一个新图片。

根据决定按“手数”交易货币组合的虚拟余额
“将来”部分看起来更平滑,我们甚至可以说,它能够继续增长只是凭运气,尽管这是一个如此简单的模型。原则上,在前向测试期间,虚拟余额曲线会大大退化,开始下滑。
务必需要注意的是,为了测试该模型,我们采用从系统的“原样”解获得的 X 值,而在实践中,我们需要将它们标准化为最小手数和手梯度,这将对结果造成负面影响,使它们更接近现实。