使用 Python 分析天气对农业国家货币的影响·综合运用
(3/3)·从气象 API 到 MT5 行情同步,把五大金融中心的天气异常转成可交易的农产品货币信号
接上篇,我们已经跑通了单因子的天气—汇率关联,这一篇把数据管道、模型评估和分地区结论全部串起来。很多团队卡在气象数据与报价频率不对齐,导致回测里信号漂亮、实盘里全废,本篇直接给可复用的同步方案。
◍ 五折回测下的三类模型怎么打分
在 5 年历史样本上跑机器学习,最忌讳一次性拟合后直接信。我们用五折滑动窗口把数据切成 5 段轮流验证,每个市场区域都单独建三套模型:价格方向(分类)、价格变动幅度(回归)、波动率(回归)。外汇与贵金属杠杆高、跳空频繁,这种切窗法至少能压住一点过拟合的幻觉。 下面这段 Python 评估函数就是干这事的核心。它遍历所有区域,把方向预测的准确率、变动幅度的 RMSE、波动率的 RMSE 分别打印出来,并保留每折指标方便你算最大最小区间。
class="kw">import matplotlib.pyplot as plt class="kw">import seaborn as sns from sklearn.metrics class="kw">import confusion_matrix, classification_report def evaluate_model_performance(results, region_data): """ Comprehensive model evaluation across all regions """ print(f"\nEvaluating model performance for {len(results)} regions") evaluation = {} for region, models in results.items(): print(f"\nAnalyzing {region} performance:") region_metrics = { &class="macro">#x27;direction&class="macro">#x27;: { &class="macro">#x27;accuracy&class="macro">#x27;: models[&class="macro">#x27;direction&class="macro">#x27;][&class="macro">#x27;mean_metric&class="macro">#x27;], &class="macro">#x27;fold_metrics&class="macro">#x27;: models[&class="macro">#x27;direction&class="macro">#x27;][&class="macro">#x27;metrics&class="macro">#x27;], &class="macro">#x27;max_accuracy&class="macro">#x27;: max(models[&class="macro">#x27;direction&class="macro">#x27;][&class="macro">#x27;metrics&class="macro">#x27;]), &class="macro">#x27;min_accuracy&class="macro">#x27;: min(models[&class="macro">#x27;direction&class="macro">#x27;][&class="macro">#x27;metrics&class="macro">#x27;]) }, &class="macro">#x27;price_change&class="macro">#x27;: { &class="macro">#x27;rmse&class="macro">#x27;: models[&class="macro">#x27;price_change&class="macro">#x27;][&class="macro">#x27;mean_metric&class="macro">#x27;], &class="macro">#x27;fold_metrics&class="macro">#x27;: models[&class="macro">#x27;price_change&class="macro">#x27;][&class="macro">#x27;metrics&class="macro">#x27;] }, &class="macro">#x27;volatility&class="macro">#x27;: { &class="macro">#x27;rmse&class="macro">#x27;: models[&class="macro">#x27;volatility&class="macro">#x27;][&class="macro">#x27;mean_metric&class="macro">#x27;], &class="macro">#x27;fold_metrics&class="macro">#x27;: models[&class="macro">#x27;volatility&class="macro">#x27;][&class="macro">#x27;metrics&class="macro">#x27;] } } print(f"Direction prediction accuracy: {region_metrics[&class="macro">#x27;direction&class="macro">#x27;][&class="macro">#x27;accuracy&class="macro">#x27;]:.class="num">2%}") print(f"Price change RMSE: {region_metrics[&class="macro">#x27;price_change&class="macro">#x27;][&class="macro">#x27;rmse&class="macro">#x27;]:.4f}") print(f"Volatility RMSE: {region_metrics[&class="macro">#x27;volatility&class="macro">#x27;][&class="macro">#x27;rmse&class="macro">#x27;]:.4f}") evaluation[region] = region_metrics class="kw">return evaluation def plot_feature_importance(models, region): """ Visualize feature importance for each model type """ plt.figure(figsize=(class="num">15, class="num">10)) for target, model_info in models.items(): feature_importance = pd.DataFrame({ &class="macro">#x27;feature&class="macro">#x27;: model_info[&class="macro">#x27;model&class="macro">#x27;].feature_names_, &class="macro">#x27;importance&class="macro">#x27;: model_info[&class="macro">#x27;model&class="macro">#x27;].feature_importances_ }) feature_importance = feature_importance.sort_values(&class="macro">#x27;importance&class="macro">#x27;, ascending=False)
import 三行是拉画图与 sklearn 评估工具;evaluate_model_performance 函数先打印区域总数,再循环每个区域取三类模型的均值与每折指标,方向类额外算 max/min 准确率,最后返回嵌套字典。plot_feature_importance 则开 15x10 画布,把每个目标的 feature_importances_ 排降序,便于肉眼看哪些因子在盯盘里真正管用。
实操上,你把这个脚本接在自己 MT5 导出的 5 年 CSV 上,若某区域方向准确率均值低于 52%,大概率该品种噪声盖过信号,贵金属夜盘常出这情况,此时别硬上模型信号。
class="kw">import matplotlib.pyplot as plt class="kw">import seaborn as sns from sklearn.metrics class="kw">import confusion_matrix, classification_report def evaluate_model_performance(results, region_data): """ Comprehensive model evaluation across all regions """ print(f"\nEvaluating model performance for {len(results)} regions") evaluation = {} for region, models in results.items(): print(f"\nAnalyzing {region} performance:") region_metrics = { &class="macro">#x27;direction&class="macro">#x27;: { &class="macro">#x27;accuracy&class="macro">#x27;: models[&class="macro">#x27;direction&class="macro">#x27;][&class="macro">#x27;mean_metric&class="macro">#x27;], &class="macro">#x27;fold_metrics&class="macro">#x27;: models[&class="macro">#x27;direction&class="macro">#x27;][&class="macro">#x27;metrics&class="macro">#x27;], &class="macro">#x27;max_accuracy&class="macro">#x27;: max(models[&class="macro">#x27;direction&class="macro">#x27;][&class="macro">#x27;metrics&class="macro">#x27;]), &class="macro">#x27;min_accuracy&class="macro">#x27;: min(models[&class="macro">#x27;direction&class="macro">#x27;][&class="macro">#x27;metrics&class="macro">#x27;]) }, &class="macro">#x27;price_change&class="macro">#x27;: { &class="macro">#x27;rmse&class="macro">#x27;: models[&class="macro">#x27;price_change&class="macro">#x27;][&class="macro">#x27;mean_metric&class="macro">#x27;], &class="macro">#x27;fold_metrics&class="macro">#x27;: models[&class="macro">#x27;price_change&class="macro">#x27;][&class="macro">#x27;metrics&class="macro">#x27;] }, &class="macro">#x27;volatility&class="macro">#x27;: { &class="macro">#x27;rmse&class="macro">#x27;: models[&class="macro">#x27;volatility&class="macro">#x27;][&class="macro">#x27;mean_metric&class="macro">#x27;], &class="macro">#x27;fold_metrics&class="macro">#x27;: models[&class="macro">#x27;volatility&class="macro">#x27;][&class="macro">#x27;metrics&class="macro">#x27;] } } print(f"Direction prediction accuracy: {region_metrics[&class="macro">#x27;direction&class="macro">#x27;][&class="macro">#x27;accuracy&class="macro">#x27;]:.class="num">2%}") print(f"Price change RMSE: {region_metrics[&class="macro">#x27;price_change&class="macro">#x27;][&class="macro">#x27;rmse&class="macro">#x27;]:.4f}") print(f"Volatility RMSE: {region_metrics[&class="macro">#x27;volatility&class="macro">#x27;][&class="macro">#x27;rmse&class="macro">#x27;]:.4f}") evaluation[region] = region_metrics class="kw">return evaluation def plot_feature_importance(models, region): """ Visualize feature importance for each model type """ plt.figure(figsize=(class="num">15, class="num">10)) for target, model_info in models.items(): feature_importance = pd.DataFrame({ &class="macro">#x27;feature&class="macro">#x27;: model_info[&class="macro">#x27;model&class="macro">#x27;].feature_names_, &class="macro">#x27;importance&class="macro">#x27;: model_info[&class="macro">#x27;model&class="macro">#x27;].feature_importances_ }) feature_importance = feature_importance.sort_values(&class="macro">#x27;importance&class="macro">#x27;, ascending=False)
把模型解释拆开看:特征重要性与季节准确率
这段 Python 代码承担三件实事:画 Top10 特征柱状图、按月统计方向预测准确率、绘天气与价格相关性热力图。它不直接下单,但能告诉你哪类因子在拖模型后腿。 plt.subplot(3,1, list(models.keys()).index(target)+1) 把多模型排成纵向三栏,sns.barplot 取 feature_importance.head(10) 画每个模型的前 10 重要特征。若某贵金属模型里 'real_rate' 排第一,说明利率预期比季节性权重更大,调参应优先动它。 visualize_seasonal_patterns 里 monthly_accuracy 用 range(1,13) 建 12 个月索引,对每月预测与 (close.pct_change()>0) 的真实涨跌算 accuracy_score。曾经某外汇对 7 月准确率掉到 0.42、2 月升到 0.61,这种季节性塌陷靠肉眼看 equity 曲线很难察觉。 plot_correlation_heatmap 用 RdYlBu 配 center=0 画相关系数,annot=True 标到小数点后两位。若 EURUSD 与某天气变量的格子里写 0.71,说明两者同向性强,可作为小布 AIGC 筛选特征的旁路验证。外汇与贵金属杠杆高,这类统计关系只作概率参考,实盘前务必在 MT5 用历史数据复跑一遍。
plt.subplot(class="num">3, class="num">1, list(models.keys()).index(target) + class="num">1) sns.barplot(x=&class="macro">#x27;importance&class="macro">#x27;, y=&class="macro">#x27;feature&class="macro">#x27;, data=feature_importance.head(class="num">10)) plt.title(f&class="macro">#x27;{target.capitalize()} Model - Top class="num">10 Important Features&class="macro">#x27;) plt.tight_layout() plt.show() def visualize_seasonal_patterns(results, region_data): """ Create visualization of seasonal patterns in predictions """ for region, data in region_data.items(): print(f"\nVisualizing seasonal patterns for {region}") # Create monthly aggregation of accuracy monthly_accuracy = pd.DataFrame(index=range(class="num">1, class="num">13)) data[&class="macro">#x27;month&class="macro">#x27;] = data.index.month for month in range(class="num">1, class="num">13): month_predictions = results[region][&class="macro">#x27;direction&class="macro">#x27;][&class="macro">#x27;predictions&class="macro">#x27;][ data.index.month == month ] month_actual = (data[&class="macro">#x27;close&class="macro">#x27;].pct_change() > class="num">0)[ data.index.month == month ] accuracy = accuracy_score( month_actual, month_predictions ) monthly_accuracy.loc[month, &class="macro">#x27;accuracy&class="macro">#x27;] = accuracy # Plot seasonal accuracy plt.figure(figsize=(class="num">12, class="num">6)) monthly_accuracy[&class="macro">#x27;accuracy&class="macro">#x27;].plot(kind=&class="macro">#x27;bar&class="macro">#x27;) plt.title(f&class="macro">#x27;Seasonal Prediction Accuracy - {region}&class="macro">#x27;) plt.xlabel(&class="macro">#x27;Month&class="macro">#x27;) plt.ylabel(&class="macro">#x27;Accuracy&class="macro">#x27;) plt.show() def plot_correlation_heatmap(correlation_data): """ Create heatmap visualization of correlations """ plt.figure(figsize=(class="num">12, class="num">8)) sns.heatmap( correlation_data, cmap=&class="macro">#x27;RdYlBu&class="macro">#x27;, center=class="num">0, annot=True, fmt=&class="macro">#x27;.2f&class="macro">#x27; ) plt.title(&class="macro">#x27;Weather-Price Correlation Heatmap&class="macro">#x27;) plt.tight_layout() plt.show()
「三地气象模型对澳纽加元的交叉验证」
把小麦带、坎特伯雷、加拿大草原三处的天气信号分别接回 AUDUSD、NZDUSD 与 USDCAD 相关品种,方向预测平均准确率落在 56.92%–62.81% 区间,均明显脱离随机基准。其中新西兰坎特伯雷对 NZDUSD 的峰值准确率冲到 75.44%,但最低一折掉到 54.39%,说明气象因子在某些气候异常周会失灵。 价格变动预测的 RMSE 三地差异显著:加拿大草原仅 0.0159,澳大利亚小麦带 0.0303,新西兰 0.0281。波动率 RMSE 全部压在 0.0015–0.0023 窄带内,意味着模型对波动幅度的刻画比绝对价格位移更稳。 外汇与贵金属受宏观扰动极多,气象派生信号仅作辅助概率参考,实盘须自担高风险。开 MT5 把这三组 RMSE 当阈值,对比你本地回测的残差分布,若明显宽于 0.03 就该怀疑特征泄漏。
◍ 农业货币汇率的月度命中率窗口
把模型逐月准确率拉出来看,农业相关货币对存在明显的季节聚集。AUDUSD 在 12 月至次年 2 月命中率偏高,对应小麦灌浆到成熟的关键期;NZDUSD 在牛奶高产季更稳;USDCAD 则在草原作物旺盛生长阶段表现更好。 这种分布不是噪声,而是天气通过农产品供给预期传导到汇率的价格行为痕迹。外汇与贵金属杠杆高、波动剧烈,此类季节性只提高概率倾向,不构成方向保证。 下面这段 Python 把分地区结果按月份拆开算准确率并打印,再画图对比,思路可直接搬进 MT5 的 Python 桥接环境验证。
def analyze_model_seasonality(results, data): """ Analyze seasonal performance patterns of the models """ print("Starting seasonal analysis of model performance") seasonal_metrics = {} for region, region_results in results.items(): print(f"\nAnalyzing {region} seasonal patterns:") # Extract predictions and actual values predictions = region_results[&class="macro">#x27;direction&class="macro">#x27;][&class="macro">#x27;predictions&class="macro">#x27;] actuals = data[region][&class="macro">#x27;close&class="macro">#x27;].pct_change() > class="num">0 # Calculate monthly accuracy monthly_acc = [] for month in range(class="num">1, class="num">13): month_mask = predictions.index.month == month if month_mask.any(): acc = accuracy_score( actuals[month_mask], predictions[month_mask] ) monthly_acc.append(acc) print(f"Month {month} accuracy: {acc:.class="num">2%}") seasonal_metrics[region] = pd.Series( monthly_acc, index=range(class="num">1, class="num">13) ) class="kw">return seasonal_metrics def plot_seasonal_performance(seasonal_metrics): """ Visualize seasonal performance patterns """ plt.figure(figsize=(class="num">15, class="num">8)) for region, metrics in seasonal_metrics.items(): plt.plot(metrics.index, metrics.values, label=region, marker=&class="macro">#x27;o&class="macro">#x27;) plt.title(&class="macro">#x27;Model Accuracy by Month&class="macro">#x27;) plt.xlabel(&class="macro">#x27;Month&class="macro">#x27;) plt.ylabel(&class="macro">#x27;Accuracy&class="macro">#x27;) plt.legend() plt.grid(True) plt.show()
def analyze_model_seasonality(results, data): """ Analyze seasonal performance patterns of the models """ print("Starting seasonal analysis of model performance") seasonal_metrics = {} for region, region_results in results.items(): print(f"\nAnalyzing {region} seasonal patterns:") # Extract predictions and actual values predictions = region_results[&class="macro">#x27;direction&class="macro">#x27;][&class="macro">#x27;predictions&class="macro">#x27;] actuals = data[region][&class="macro">#x27;close&class="macro">#x27;].pct_change() > class="num">0 # Calculate monthly accuracy monthly_acc = [] for month in range(class="num">1, class="num">13): month_mask = predictions.index.month == month if month_mask.any(): acc = accuracy_score( actuals[month_mask], predictions[month_mask] ) monthly_acc.append(acc) print(f"Month {month} accuracy: {acc:.class="num">2%}") seasonal_metrics[region] = pd.Series( monthly_acc, index=range(class="num">1, class="num">13) ) class="kw">return seasonal_metrics def plot_seasonal_performance(seasonal_metrics): """ Visualize seasonal performance patterns """ plt.figure(figsize=(class="num">15, class="num">8)) for region, metrics in seasonal_metrics.items(): plt.plot(metrics.index, metrics.values, label=region, marker=&class="macro">#x27;o&class="macro">#x27;) plt.title(&class="macro">#x27;Model Accuracy by Month&class="macro">#x27;) plt.xlabel(&class="macro">#x27;Month&class="macro">#x27;) plt.ylabel(&class="macro">#x27;Accuracy&class="macro">#x27;) plt.legend() plt.grid(True) plt.show()
画得少,看得清
把天气变量接进汇率预测,回测给出的命中率并不玄学:AUDUSD 平均 62.67%、NZDUSD 62.81%、USDCAD 56.92%,且集中在农业周期的关键窗口。 实操上不用盯全年的气象图——AUDUSD 只在 12 至 2 月看风速与温度,NZDUSD 抓乳制品旺季做中短期,USDCAD 挑播种与收割季择机,信号密度够用就好。 模型精度依赖数据常更,尤其市场剧烈波动时旧样本会快速失效;外汇与贵金属杠杆高、回撤快,上述关联只是概率倾向,真要上 MT5 验证建议先用策略测试器跑一轮再谈仓位。