重思经典策略(第八部分):基于美元兑加元(USDCAD)探讨外汇市场与贵金属(基础篇)
📘

重思经典策略(第八部分):基于美元兑加元(USDCAD)探讨外汇市场与贵金属(基础篇)

第 1/3 篇

「AI 策略系列的开场定位」

这套连载想把人工智能在交易里的落点讲清楚,而不是堆概念。重点会放在外汇与贵金属这类高波动品种上,美元兑加元(USDCAD)是首篇拿来拆解的标的。 原文示例发布于 2025 年 4 月 22 日 08:17,MetaTrader 5 环境,页面统计显示 637 次浏览、4 条互动。这个数字说明即便小众 AI 策略文,在 MT5 社区也有稳定读者盘。 核心立场很直接:任何基于 AI 的策略,都只是帮你缩小决策范围,不替你承担外汇/贵金属的高风险。系列目标是在你投钱前,先能按自己的风险承受度筛掉不合适的玩法。

贵金属与货币对的隐性联动

做外汇和贵金属,不能只盯一个盘面。黄金、钯金这类贵金属既是工业原料也是避险载体,其价格摆动会顺着贸易链传导到相关国家的产出与汇率。 加拿大和美国都是黄金与汽车的主要出口国,而一台汽车里超过 20 个部件要用到含钯金的催化转换器。钯金或黄金若持续走高,出口国生产端成本承压,对本币汇率存在潜在拖累倾向。 历史上黄金与美元多呈反向:美元走弱时资金常平仓美元多头转投黄金对冲。但进入量化宽松周期后,这条负相关明显钝化,拿来直接反推汇率容易踩坑。外汇与贵金属杠杆高,联动只是概率倾向,开 MT5 拉 AUDCAD、USDCAD 与 XAUUSD 叠加图自己验证最实在。

◍ 让机器自己找跨市场关系

做贵金属和外汇联动,人容易先入为主——只信直觉上顺眼的规律。让 MT5 上的脚本自己跑数据,反而可能挖出一辈子看不出的关联,或者戳破你原以为靠谱的策略边界。 我们拿了三组因子去预测 USDCAD:单用 USDCAD 自身报价、叠 XAUUSD 与 XPDUSD 报价、以及前两组组合。数据全用 MQL5 定制脚本从终端直抽,落 CSV 进 Python 洗。相关性上,XAUUSD 对 USDCAD 是 -0.5,XPDUSD 对 USDCAD 是 -0.66,两种金属之间只有 0.37,中等偏弱。 散点图和 3D 散点都试过,肉眼完全分不出簇,数据像是一团搅在一起的雾。模型侧用 5 折时间序列交叉验证(不打乱)算平均 RMSE 选优;线性模型单因子组最强,但没参数可调,于是落地选了次优的线性支持向量回归(LSVR)。 调参后 LSVR 在未见数据上压过默认版,说明没白拟合,但验证集 RMSE 仍没追上纯线性。最后把定制 LSVR 转 ONNX,用 MQL5 写了带 AI 的 EA——外汇贵金属杠杆高,跨市场信号失效也快,跑之前先想清楚滑点和重训频率。

「用脚本把图表数据倒成CSV」

想在 Python 里做价格行为分析,第一步是把 MT5 图表上的 OHLC 历史拽出来。挂一个脚本到目标图表就能自动导出 CSV,省去手动另存的麻烦。 脚本里有个关键属性 #property script_show_inputs,漏写的话你在 MT5 里右键调不了输入参数,只能改代码重编译。默认 size = 100000,代表向外取 10 万根柱线,不够就调小,太多可能写文件偏慢。 导出的文件名会拼成 Market Data [符号].csv,首行写表头 Time/Open/High/Low/Close,后面按当前周期逐根回写。这个 Time 列是后面在 Python 里多品种按日期内连接(merge)的唯一键,只保留共同日期的行,错位数据直接丢掉。 别把正态当圣经:CSV 里的时间是经纪商服务器时间,和 Python 端 pandas 的时区若不一致,合并时可能静默丢数据,先 print(df['Time'].head()) 核对再 join。 下面这段是脚本本体,逐行看逻辑: #property script_show_inputs 让输入面板可见;input int size=100000 定义抓取根数;OnStartFileOpen 以写/ANSI/CSV 逗号分隔打开文件;for 从 size 递减到 0,首轮写表头,之后每轮用 iTime/iOpen/iHigh/iLow/iClose 取对应柱面写入。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  ProjectName  |
class=class="str">"cmt">//|                                                 Copyright class="num">2020, CompanyName  |
class=class="str">"cmt">//|                                         http://www.companyname.net   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Gamuchirai Zororo Ndawana"
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.00"
class="macro">#class="kw">property script_show_inputs
class=class="str">"cmt">//---Amount of data requested
class="kw">input class="type">int size = class="num">100000; class=class="str">"cmt">//How much data should we fetch?
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnStart()
  {
class=class="str">"cmt">//---File name
   class="type">class="kw">string file_name = "Market Data " + Symbol() + ".csv";
class=class="str">"cmt">//---Write to file
   class="type">int file_handle=FileOpen(file_name,FILE_WRITE|FILE_ANSI|FILE_CSV,",");
   for(class="type">int i= size;i>=class="num">0;i--)
     {
     if(i == size)
     {
     FileWrite(file_handle,"Time","Open","High","Low","Close");
     }
     else
     {
     FileWrite(file_handle,iTime(Symbol(),PERIOD_CURRENT,i),
                   iOpen(Symbol(),PERIOD_CURRENT,i),
                   iHigh(Symbol(),PERIOD_CURRENT,i),
                   iLow(Symbol(),PERIOD_CURRENT,i),
                   iClose(Symbol(),PERIOD_CURRENT,i));
     }
     }
  }
class=class="str">"cmt">//+------------------------------------------------------------------+

把三品种 CSV 并成一张可训练表

做跨品种预测前,先得把 MT5 导出的散装 CSV 收拢成单一数据帧。这里用的是 Pandas 2.2.1 与 Numpy 1.26.4,读取路径直接指向 MT5 的 MQL5\Files 目录,USDCAD、XAUUSD、XPDUSD 三个文件各自按时间列设索引后做左连接合并。 合并完并不是直接跑模型。代码里先把预测视界定为 20 根 K 线(look_ahead = 20),也就是用当下已知量去推 20 根之后的收盘位。预测因子分两组:USDCAD 自身的 OHLC,加上黄金和钯金的八个价格字段,共 12 个输入。 标签这块做了三层。Target 是 USDCAD 收盘的 20 根后移位值;Binary Target 把它压成 0/1 涨跌二分类;另外单独给 XAU 和 XPD 各标了自身涨跌标签,方便后面看跨市场传导。最后 dropna 把移位产生的空行清掉,表才干净。外汇与贵金属杠杆高,跨品种相关性随时变,这套标签只反映历史样本,实盘信号概率会漂移。 下面这段是可直接抄去跑的清理脚本,路径按你本机 MT5 装法改一下就能用。

MQL5 / C++
class="macro">#Import the libraries we need
class="kw">import pandas as pd
class="kw">import numpy as np
class="macro">#Display library versions
print(f"Pandas version {pd.__version__}")
print(f"Numpy version {np.__version__}")
class="macro">#Read in the data we need
usdcad = pd.read_csv("\home\volatily\.wine\drive_c\Program Files\MetaTrader class="num">5\MQL5\Files\Market Data USDCAD.csv")
usdcad = usdcad[::-class="num">1]
xauusd = pd.read_csv("\home\volatily\.wine\drive_c\Program Files\MetaTrader class="num">5\MQL5\Files\Market Data XAUUSD.csv")
xauusd = xauusd[::-class="num">1]
xpdusd = pd.read_csv("\home\volatily\.wine\drive_c\Program Files\MetaTrader class="num">5\MQL5\Files\Market Data XPDUSD.csv")
xpdusd = xpdusd[::-class="num">1]
class="macro">#Set the time column as the index
usdcad.set_index("Time",inplace=True)
xauusd.set_index("Time",inplace=True)
xpdusd.set_index("Time",inplace=True)
class="macro">#Let&class="macro">#x27;s merge the data
merged_data = usdcad.merge(xauusd,suffixes=(&class="macro">#x27;&class="macro">#x27;,&class="macro">#x27; XAU&class="macro">#x27;),left_index=True,right_index=True)
merged_data = merged_data.merge(xpdusd,suffixes=(&class="macro">#x27;&class="macro">#x27;,&class="macro">#x27; XPD&class="macro">#x27;),left_index=True,right_index=True)
merged_data
class="macro">#Define the forecast horizon
look_ahead = class="num">20
class="macro">#Define the predictors and target
ohlc_predictors = [&class="macro">#x27;Open&class="macro">#x27;,&class="macro">#x27;High&class="macro">#x27;,&class="macro">#x27;Low&class="macro">#x27;,&class="macro">#x27;Close&class="macro">#x27;]
new_predictors = [&class="macro">#x27;Open XAU&class="macro">#x27;,&class="macro">#x27;High XAU&class="macro">#x27;,&class="macro">#x27;Low XAU&class="macro">#x27;,&class="macro">#x27;Close XAU&class="macro">#x27;,&class="macro">#x27;Open XPD&class="macro">#x27;,&class="macro">#x27;High XPD&class="macro">#x27;,&class="macro">#x27;Low XPD&class="macro">#x27;,&class="macro">#x27;Close XPD&class="macro">#x27;]
predictors = ohlc_predictors + new_predictors
class="macro">#Let&class="macro">#x27;s add labels to the data
merged_data["Target"] = merged_data["Close"].shift(-look_ahead)
class="macro">#Let&class="macro">#x27;s also add labels to help us visualize the relationships
merged_data["Binary Target"] = np.nan
merged_data["XAU Target"] = np.nan
merged_data["XPD Target"] = np.nan
class="macro">#Define the target values
class="macro">#Changes in the USDCAD Exchange rate
merged_data.loc[merged_data["Close"] > merged_data["Target"],"Binary Target"] = class="num">0
merged_data.loc[merged_data["Close"] < merged_data["Target"],"Binary Target"] = class="num">1
class="macro">#Changes in the price of Gold
merged_data.loc[merged_data["Close XAU"] > merged_data["Close XAU"].shift(-look_ahead),"XAU Target"] = class="num">0
merged_data.loc[merged_data["Close XAU"] < merged_data["Close XAU"].shift(-look_ahead),"XAU Target"] = class="num">1
class="macro">#Changes in the price of Palladium
merged_data.loc[merged_data["Close XPD"] > merged_data["Close XPD"].shift(-look_ahead),"XPD Target"] = class="num">0
merged_data.loc[merged_data["Close XPD"] < merged_data["Close XPD"].shift(-look_ahead),"XPD Target"] = class="num">1
class="macro">#Drop any NA values
merged_data.dropna(inplace=True)

◍ 先把合并数据拉平再画图

做探索性分析前,得先把前面 join 好的 merged_data 索引重置掉,否则 seaborn 按索引画图会把行号当维度,坐标轴直接乱掉。 代码里先引 seaborn 和 matplotlib,顺手把 sns.__version__ 打出来确认环境。当前用的是 Seaborn 0.13.2,老版本某些绘图 API 参数名不一样,跑之前最好核一下。 merged_data.reset_index(inplace=True) 是原地改对象,执行后原 df 的旧索引变成普通列,行序回到 0、1、2… 这种连续整数,后面 pairplot / heatmap 才不会报 index 错。

MQL5 / C++
class="macro">#Explorartory data analysis
class="kw">import seaborn as sns
class="kw">import matplotlib.pyplot as plt
class="macro">#Display library version
print(f"Seaborn version: {sns.__version__}")
class="macro">#Reset the index
merged_data.reset_index(inplace=True)

常见问题

可用脚本把两个品种的历史数据导出为 CSV,再按时间合并到一张表,机器比对比肉眼快得多。
先把多品种 CSV 按时间戳对齐并拉平,再画散点或相关图,能直观看到统计层面的强弱关系而非感觉。
小布可读取你导出的合并数据,直接给出相关性诊断和异常时点提示,省去自己写脚本比对。
以主交易品种时间为基准做左连接,缺失段填向前值或留空,保证合并表每行都有统一时间戳。
历史关系可能随央行政策断裂,结论仅作概率参考,实盘前需用近期样本重验避免过拟合。