English Русский Español Deutsch 日本語 Português
preview
外汇套利交易:汇率关系评估面板

外汇套利交易:汇率关系评估面板

MetaTrader 5交易系统 |
297 13
Yevgeniy Koshtenko
Yevgeniy Koshtenko

概述

我在算法外汇交易领域的探索已经持续多年 — 历经多年的尝试、错误、无数个不眠之夜,以及虽少却难忘的胜利时刻。在此期间,我尝试了从简单的指标到复杂的机器学习系统等各种方法。但有一个想法一直萦绕在我心头:如果市场并不像表面看起来那样完美,会怎样?如果报价波动背后确实存在可被识别的裂缝,我们是否就有机会看清其本质并从中获利?这就是我如何开始研究套利分析的,这种方法对我来说是一次真正的发现,几乎是一种启示。

今天我想分享我的作品,这是一个用于分析公允货币价格的系统,附带套利评估功能,使用 MetaTrader 5 的 MQL5 语言编写。这一切都始于一个让我彻夜难眠的简单问题:如果我们能计算出“正确”的汇率 — 不是市场给出的汇率,而是本应存在的汇率,那会怎么样?出于这种好奇心,一个面板应运而生,该面板不仅会突出价格与其“理想”价值的偏差,还会寻找三角套利路径 — 即市场暂时出现效率低下的时刻,而我们可以利用这一时机。

将理论转化为代码,结果证明是一次真正的冒险 — 充满了陷阱、无数次的迭代,以及无数次想要放弃一切的时刻。让我来告诉你们,枯燥的计算是如何变成一个生机勃勃的系统的,市场是如何一次又一次地考验我,以及我是如何学会将结果可视化,让它们自己说话的。您将看到面板如何运作的真实示例 — 包括数字、信号和套利三角形。我会真诚地分析其优缺点,分享我适应不同策略的经验 — 从单一交易到多币种网格交易,这对我来说已成为一个真正的实验领域。

我的目标并非只是为了炫耀代码或在公众面前卖弄。我想给你一个工具 — 一些你可以拿去使用、调整、定制、完善的实实在在的东西。我希望这个面板不仅能激发你们的新想法,还能帮助你们从不同的角度看待市场 — 不是将其视为混乱,而是将其视为一个可以解决的谜题。



套利对冲基金与外汇套利交易的规模

当我们谈论外汇市场中的套利行为时,不可避免地要提到这一领域的主要参与者 — 那些专业的对冲基金,他们利用对差异的数学分析,将这一领域打造成了一个价值数十亿美元的产业。他们的经验和运营规模揭示了,我们这个看似普通的套利指标在合适的人手中能发挥多么重要的作用。

货币套利巨头

在套利对冲基金中,有几家主要基金尤为突出。例如,管理资产超过 500 亿美元的 Millennium Management 采用多策略方法,其中外汇市场的统计套利是关键领域之一。他们的量化分析师团队正在开发与我们的公允价格矩阵在概念上相似的模型,但其数学框架和计算能力要复杂得多。

Citadel Securities、Renaissance Technologies 和 DE Shaw 等其他巨头也积极利用货币套利。他们的优势不仅在于资金,还在于技术:直接接入银行间市场,拥有延迟极低的服务器(共置),以及关键交易平台之间的专用通信渠道。一些基金已投资数亿美元于基础设施,将订单执行延迟缩短至微秒级,遗憾的是,在这一领域,我们的 MetaTrader 5 指标无法与之竞争。



公允汇率分析与套利的理论基础

当我想到:如果市场有时会出错怎么办?时,分析公允价格的想法就吸引了我。多年的外汇交易经验告诉我,汇率并非只是屏幕上的随机数字。这是一个复杂的关联网络,每一个货币对都像巨大画布上的线索一样牵动着其他货币对。如果这幅画布上某处出现了一个洞 — 即存在差异 — 那么就有机会赚钱。

事实上,公允价格即货币对的“理想”汇率,如果市场完全理性,那么汇率就应该是公允的。对于像 EURUSD 这样的直接货币对,一切似乎都很简单:取买入价和卖出价,取其平均值,这就是你的参考价。但外汇交易不仅仅是直接报价。有些交叉货币对,例如 EURJPY,可以使用基础货币对进行计算。然后我想起了三角套利,这是一个古老但仍然活跃的概念:

EUR/USD * USD/JPY = EUR/JPY

如果实际的 EURJPY 汇率与此计算结果不符,则说明市场低估或高估了某些因素。我记得我第一次手动检查它的时候 — 我坐在电脑前,手边放着一个计算器,正在对比模拟账户上的报价。差异虽小,只有不足 1% 的细微偏差,但它们的存在本身却点燃了我心中的火花。这就像在墙壁上发现了一个无人注意的小裂缝,但通过它,你可以窥见内部。

然后我继续深入研究。直接报价只是冰山一角。要理解价格的“公允性”,有必要构建一个包含所有货币关系的矩阵。想象一下:你有 8 种货币 — EUR、USD、GBP、JPY 等等。它们中的每一个都可以通过可用的配对来相互表达。例如,如果我有 EURUSD 和 GBPUSD 货币对,我可以这样计算 EURGBP:

EURGBP = EURUSD / GBPUSD

如果再加上像 USDJPY 和 JPYUSD 这样的反向货币对,那么就可以无限地改进计算。我尤其对“动态校准”这个概念着迷。如果我们不依赖单一货币对的报价,而是将所有可用的报价“平均”到一个系统中,结果会怎样呢?例如,对于 EURJPY,我不仅可以使用 EURUSD 和 USDJPY,还可以使用其他路径:EURGBP 和 GBPJPY,甚至可以通过 CHF 或 AUD 等更长的货币链。这就像拼一幅拼图,每个细节都必须完美地拼合到位。 

但是,如何从所有包含相同货币的其他货币对中计算出一对货币对呢?以 EURUSD 为例,我有一个直接汇率 — 比如说 1.10。但我可以通过交叉盘来查看:通过 GBP(EURGBP 和 GBPUSD),通过 JPY(EURJPY 和 USDJPY),通过 CHF 等等。理论上,所有这些路径都应该给出相同的值。若结果不一致,这本身就是一个信号。我开始思考:如果我们构建一个8x8的矩阵,其中每个单元格代表一种货币相对于另一种货币的汇率,并迫使这个矩阵“收敛”到一个唯一的解,会怎么样呢?这就像解一个方程组,其中每一对数字都是一个方程,而变量则是货币汇率。示例: 

EURUSD = 1. 10

USDJPY = 150

EURJPY = EURUSD * USDJPY = 165

如果 EURJPY 的实际汇率是 166,那么其中肯定存在偏差。根据配对的数量,这样的“方程”有几十个。你可以计算所有路径的平均值,可以寻找中位数,甚至还可以像解线性方程组那样,使用迭代方法来找到“最优”价格。 

套利已成为解决这一难题的第二块拼图。三角套利是指通过三个货币对(EURUSD -> USDJPY -> EURJPY)的循环交易,最终实现盈利。但我在想:如果我们不仅寻找三角形,还寻找更长的路径,那会怎么样?例如,四五对?理论认为,路径越长,噪声就越多,但研究结果也越有趣。每个概念都经过了实践检验:我在终端运行了数据,查看了计算结果与实际情况存在差异的地方,并从中吸取教训。 

对我而言,它已经不仅仅是一套公式了。这是一种哲学理念:市场是一个充满小错误的有机体,如果你学会去发现这些小错误,你就能与之和谐共处。希望我的想法能激励你更深入地挖掘,并看到同样的机会。


套利面板的实现:从构思到代码

将理论转化为代码始终是一项挑战。以下是我在 MQL5 中实现套利面板的方法:

void CalculateFairValues() {
    GetCurrentMarketRates();
    InitializeCurrencyMatrix();
    FillCurrencyMatrixFromDirectRates();
    CalculateCrossRatesArbitrage();
    CalculateDiscrepancies();
    FindArbitrageOpportunities();
    GenerateSignals();
    UpdateDisplay();
}
此函数是系统的核心。它会收集当前报价,建立公允汇率矩阵,并寻找机会。我记得我曾长时间苦苦挣扎于 CalculateCrossRatesArbitrage 函数 — 市场并不总能提供货币对的直接汇率,我不得不通过倒数来计算:
if(!g_market_rates[i].is_direct) {
    double temp_bid = 1.0 / g_market_rates[i].ask;
    double temp_ask = 1.0 / g_market_rates[i].bid;
    g_market_rates[i].bid = temp_bid;
    g_market_rates[i].ask = temp_ask;
}
我为套利实现了一个独立的逻辑:
double CalculateArbitrageProfit(int pair1_idx, int pair2_idx, int pair3_idx, double amount) {
    double result = amount;
    // Calculation logic through Bid/Ask for a triangle
    return result;
}

这种方法是从一个循环中计算利润,例如 EURUSD->USDJPY->EURJPY。起初,结果很混乱 — 点差吞噬了所有收益。为了过滤掉噪声,我必须添加一个 InpArbitrageThreshold 阈值。

接下来是公允汇率矩阵。InitializeCurrencyMatrix 将对角线上的单位设置为 (EUR/EUR = 1),而 FillCurrencyMatrixFromDirectRates 用直接报价填充它。但真正的奥妙在于 CalculateCrossRatesArbitrage 函数。

void CalculateCrossRatesArbitrage() {
    for(int iterations = 0; iterations < 3; iterations++) {
        for(int i = 0; i < g_currencies_count; i++) {
            for(int j = 0; j < g_currencies_count; j++) {
                if(i == j) continue;
                for(int k = 0; k < g_currencies_count; k++) {
                    if(k == i || k == j) continue;
                    if(g_currency_matrix[i][k] != 0 && g_currency_matrix[k][j] != 0) {
                        double triangleRate = g_currency_matrix[i][k] * g_currency_matrix[k][j];
                        if(g_currency_matrix[i][j] == 0) g_currency_matrix[i][j] = triangleRate;
                        else g_currency_matrix[i][j] = (g_currency_matrix[i][j] * 0.7 + triangleRate * 0.3);
                        g_currency_matrix[j][i] = 1.0 / g_currency_matrix[i][j];
                    }
                }
            }
        }
    }
}

在这里,我通过三角形指定交叉汇率,对货币进行了三次迭代。思路很简单:如果我有 EURUSD 和 USDJPY,我就计算 EURJPY。如果已经有直接的 EURJPY 汇率,我会取加权平均值 — 旧汇率的 70%,新汇率的 30%。三次迭代是在准确性和速度之间取得的折衷方案。我记得在 EURJPY 货币对上测试过:最初差异为 0.1%,三个周期后差异为 0.01%。但市场不会等待,所以我止步于此。

但是,如何才能从所有其货币对中获得其中一对呢?假设我想要一个“公允”的 EURUSD 汇率。我拥有的是:

  • EURGBP 和 GBPUSD:EURUSD = EURGBP * GBPUSD
  • EURJPY 和 USDJPY:EURUSD = EURJPY / USDJPY
  • EURCHF 和 USDCHF:EURUSD = EURCHF / USDCHF

我可以选择所有这些路径,并计算其平均值或中位数。或者更进一步,采用迭代收敛的方法,就像解方程组时使用的高斯-赛德尔(Gauss-Seidel)方法一样。示例:

double CalculatePairFairValue(string base, string quote) {
    int baseIdx = GetCurrencyIndex(base);
    int quoteIdx = GetCurrencyIndex(quote);
    double sum = 0.0;
    int count = 0;
    for(int k = 0; k < g_currencies_count; k++) {
        if(k == baseIdx || k == quoteIdx) continue;
        if(g_currency_matrix[baseIdx][k] != 0 && g_currency_matrix[k][quoteIdx] != 0) {
            sum += g_currency_matrix[baseIdx][k] * g_currency_matrix[k][quoteIdx];
            count++;
        }
        if(g_currency_matrix[k][baseIdx] != 0 && g_currency_matrix[quoteIdx][k] != 0) {
            sum += g_currency_matrix[quoteIdx][k] / g_currency_matrix[k][baseIdx];
            count++;
        }
    }
    return (count > 0) ? sum / count : g_currency_matrix[baseIdx][quoteIdx];
}

每一段代码都是对现实的挑战。但当我看到第一个信号和三角形时,我意识到:它奏效了。


图形界面:特征可视化

这段代码只会显示一串数字,没有清晰的可视化效果。我倾注了全部心血来设计界面:

bool CreateGraphics() {
    ObjectCreate(0, "FVPanel_Main", OBJ_RECTANGLE_LABEL, 0, 0, 0);
    ObjectSetInteger(0, "FVPanel_Main", OBJPROP_XDISTANCE, 20);
    ObjectSetInteger(0, "FVPanel_Main", OBJPROP_YDISTANCE, 20);
    ObjectSetInteger(0, "FVPanel_Main", OBJPROP_XSIZE, 900);
    ObjectSetInteger(0, "FVPanel_Main", OBJPROP_YSIZE, 560);
    ObjectSetInteger(0, "FVPanel_Main", OBJPROP_BGCOLOR, InpBgColor);
    CreateText("FVPanel_Header", 220, 30, "ANALYSIS OF FAIR CURRENCY PRICES", InpHeaderTextColor, 12, true);
    CreateText("FVPanel_Signals", 60, 60, "SIGNALS OF DEVIATION FROM FAIR PRICE", InpBuySignalColor, 10, true);
    CreateText("FVPanel_Arbitrage", 460, 60, “ARBITRAGE OPPORTUNITIES", InpArbitrageColor, 10, true);
    return true;
}

该面板分为三个区域:偏差信号、套利路径和汇率矩阵。诸如“EURUSD:+0.04% — SELL”之类的信号会以绿色或红色突出显示,而诸如“EURUSD->USDJPY->EURJPY:+0.02%”之类的套利三角形会立即显示交易方向。

汇率矩阵可以说是我个人最着迷的部分。表格中列出了全部 8 种货币,其中偏离公允价格的部分用颜色突出显示。我记得当时花了几个小时挑选字体和缩进,让一切看起来整洁美观。

它是通过定时器更新的:

void OnTimer() {
    g_timer_counter++;
    if(g_timer_counter >= InpUpdateInterval) {
        CalculateFairValues();
        g_timer_counter = 0;
    }
}

这使得系统充满活力 — 数据实时流动,就像市场脉搏一样。


工作成果与诚实评价

我还没有用真实数据测试过这个系统。我正在基于它制作一个机器人,这大约是我第五、六次尝试制作一个能正常工作的套利机器人了。以下是系统运行的视频:



关于公允价格和矩阵的思考

如何从矩阵中获得公允的价格?它成了我的执念。8x8 矩阵就像一张市场地图。每个单元格代表一种货币兑另一种货币的汇率。但数据存在噪声:经纪商不提供所有交易对,报价波动,点差干扰。我开始做实验。

方法一:路径平均值

对于 EURUSD,我选择所有交叉盘:

  • EURGBP * GBPUSD
  • EURJPY / USDJPY
  • EURCHF / USDCHF

计算平均值。虽然方法很简单,但它忽略了权重,因为 EURJPY 的流动性可能比 EURCHF 更好。

方法二:迭代收敛

就像高斯-赛德尔迭代法一样:我不断更新矩阵中的每个单元格,直到变化变得极小。上面的代码(CalculateCrossRatesArbitrage)是一个简化版本。但我想得更远:如果我们根据交易量来增加权重呢?或者,如果我们考虑到该货币对的波动性呢?

方法三:减少误差

我们可以设定一个优化任务:最小化当前报价与矩阵之间差异的平方和。示例:

Error = Σ (MarketRate[i] - MatrixRate[i])^2

通过梯度下降或类似奇异值分解(SVD)的方法进行求解。对于 MQL5 来说,这可能需要很长时间,但在 Python 中,我会尝试一下。

方法四:长路径

为什么只有三角形?我可以通过 EURGBP->GBPCHF->CHFUSD 链计算 EURUSD。路径越多,结果越准确,但同时也会产生更多噪音。在实践中,长链被淹没在点差和费用中,就像被淹没在沼泽中一样。

这些思考目前还只是理论上的。真实的市场并非教科书,理想价格仍是一个梦想。但即便只是接近它们,也已经是向前迈出了一步。


结论

我本以为只是提出公允价格的想法,没想到竟然得到了这样一个系统。这是一段不断试错的过程,从矩阵中的漏洞到套利中的挫折。但每一步都教会了我一些东西。

市场并非混乱无序,而是一个复杂的机制。公允价格和套利就像放大镜,透过放大镜你可以看到它的各个部分。可视化使数据鲜活起来,适应性使其灵活运用。这个面板并不完美,但它已经给了我信心。

我有很多计划,从机器学习到实际测试。外汇市场不会停滞不前,我也不会。我希望我的经验能激励你深入市场表象之下,发现你所认为的“不公”

本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/17422

附加的文件 |
最近评论 | 前往讨论 (13)
pivomoe
pivomoe | 22 3月 2025 在 22:21
Yevgeniy Koshtenko #:

与标准偏差相比,合成值与实际值之间的差异往往是巨大的....

您能举例说明对数的巨大差异吗?

pivomoe
pivomoe | 23 3月 2025 在 11:36
Yevgeniy Koshtenko #:

关于这一点,将有第二篇文章

我查看了第二篇文章中的代码。它与第一篇文章没有任何关系。代码中甚至没有任何地方试图输入低于/高于合理价格。它只是一个普通的网络程序,将一张张订单抛给命运的摆布。

Khai Nguyen
Khai Nguyen | 29 1月 2026 在 10:38
您的原始代码版本存在很多问题,因此我认为您故意不拍摄屏幕截图,包括结果表的特殊部分。
我同意你的想法,但仍在努力将其转化为现实的交易

希望你能对所附的结果发表意见,并帮助将其转化为一个有效的系统。
2pay2pay
2pay2pay | 16 5月 2026 在 02:42

我过去曾创建过类似的三角套利模型。

它在回溯测试中效果很好。

然而,当我在现实生活中运行它时,我发现它从未进行过一次交易。

这是因为 HFT 和 MM 以 1/1000000s 秒的速度消除扭曲。

这不是个人交易者所能达到的速度。

神经网络在交易中的应用:多元时间序列的双重聚类(DUET) 神经网络在交易中的应用:多元时间序列的双重聚类(DUET)
DUET 框架提供了一种创新的时间序列分析方法,该方法结合了时间和通道聚类,以揭示分析数据中的隐藏模式。这使得模型能够随着时间的推移而适应变化,并通过消除噪声来提高预测质量。
数据科学与机器学习(第四十一部分):基于YOLOv8的外汇与股票市场图表形态检测 数据科学与机器学习(第四十一部分):基于YOLOv8的外汇与股票市场图表形态检测
金融市场的形态检测极具挑战性,因为它需要“看懂图表画面”,而MQL5受限于图像处理能力,很难实现这一点。本文将介绍一个基于Python构建的合适的模型,它能让我们轻松高效地检测图表上的各类形态。
您应该了解的MQL5向导技巧(第六十六部分):结合点积核使用FrAMA与强力指数形态 您应该了解的MQL5向导技巧(第六十六部分):结合点积核使用FrAMA与强力指数形态
分形自适应移动平均线(FrAMA)指标与强力指数震荡指标分别属于趋势类和成交量类工具,两者搭配使用可用于开发智能交易系统(EA)。本文承接前一篇对该指标组合的介绍,进一步探讨如何将机器学习应用到该组合中。我们将使用一种搭载点积核的卷积神经网络,并以这两个指标的数据作为输入进行预测。相关实现封装在一个自定义信号类文件中,可配合MQL5向导直接生成EA。
基于马尔可夫状态转移矩阵的神经网络自学习型EA 基于马尔可夫状态转移矩阵的神经网络自学习型EA
基于状态矩阵与神经网络的自训练智能交易系统(EA)我们将马尔可夫链与基于ALGLIB MQL5库开发的多层感知器(MLP)神经网络相结合。马尔可夫链与神经网络如何结合应用于外汇预测?