KB / ARTICLE

案例:铁矿石预测螺纹钢(线性回归)

机器学习 · 期货版修订 R4发布于 2026-10-16

本文提供期魔方【经典机器学习模型-线性回归】的完整示例:用前一天的铁矿石价格预测当天螺纹钢收盘价,涵盖数据获取、特征工程、训练评估、可视化、保存加载全过程。各环节的详细讲解见机器学习流程详解。

案例背景

本案例使用机器学习中的线性回归模型来预测螺纹钢期货价格。线性回归是一种监督学习算法,用于建立一个或多个自变量(特征)与因变量(目标变量)之间的线性关系。

特征与目标变量

什么是特征(X,自变量)

在这个模型中,特征是指用于预测目标变量的输入变量。具体来说:

  • 铁矿石的收盘价:这是模型中唯一的特征,用符号 X 表示。
  • 数据来源:通过期魔方平台数据接口获取的铁矿石主连合约的收盘价。
  • 数据处理:将前一天铁矿石的收盘价作为输入特征,用于预测当天螺纹钢的收盘价。

什么是目标变量(y,因变量)

目标变量是指模型试图预测的输出变量。在这个模型中:

  • 螺纹钢的收盘价:这是模型的目标变量,用符号 y 表示。
  • 数据来源:通过期魔方平台数据接口获取的螺纹钢主连合约的收盘价。
  • 数据处理:将螺纹钢的收盘价作为模型的输出目标,用于评估模型的预测性能。

完整示例代码

"""
文件类型:机器学习
帮助文档:https://qmfquant.com/static/doc/code/modelEdit.html
期魔方,为您提供专业的量化服务
模型类型:经典机器学习模型-线性回归
"""

import asyncio
import os

import joblib
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import qmf_model_sdk
import seaborn as sns
from qmf_data import load_kline
from scipy import stats
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score

# 获取数据配置
symbol = "rb888"
begin_time = "2020-01-01"
end_time = "2025-05-01"
symbol_cycle = "1天"

asyncio.run(qmf_model_sdk.get_futures_data(symbol, begin_time, end_time, symbol_cycle))
symbol = "i888"
asyncio.run(qmf_model_sdk.get_futures_data(symbol, begin_time, end_time, symbol_cycle))

# 设置图形、中文及负号显示
matplotlib.use("QtAgg")  # 期魔方平台图形显示必须要有这行代码
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False


# 获取螺纹钢主连和铁矿石主连的数据
def get_futures_data(symbol, start_time="2023-01-01 00:00:00", end_time="2025-05-01 00:00:00"):
    data = load_kline(product=symbol, cycle="1D", start_time=start_time, end_time=end_time)
    data["date"] = pd.to_datetime(data["date"], errors="coerce")
    data.rename(columns={"close": "收盘"}, inplace=True)
    data.reset_index(inplace=True, drop=True)
    return data


# 获取保存模型文件的地址
def get_save_model_path(file_name):
    current_file_path = os.path.abspath(__file__)
    filename_without_ext = os.path.splitext(os.path.basename(current_file_path))[0]
    return f"{os.path.dirname(current_file_path)}\\{filename_without_ext}__{file_name}"


# 获取数据
rbm_data = get_futures_data("rb888")  # 螺纹钢主连
im_data = get_futures_data("i888")  # 铁矿石主连

# 合并数据,基于日期,这样可以确保铁矿石和螺纹钢数据对齐
df = pd.merge(
    rbm_data[["date", "收盘"]],
    im_data[["date", "收盘"]],
    on="date",
    how="inner",
    suffixes=("_螺纹钢", "_铁矿石"),
)

# 创建前一天的铁矿石价格列
df["前一天铁矿石收盘"] = df["收盘_铁矿石"].shift(1)

# 删除第一行,因为它没有前一天的数据
df = df.dropna()

# 建立预测模型,利用前一天铁矿石的价格预测当天螺纹钢的收盘价
X = df[["前一天铁矿石收盘"]].values
y = df["收盘_螺纹钢"].values

# 确保数据按时间排序
df = df.sort_values("date")

# 按时间顺序划分训练集和测试集(80%训练,20%测试)
train_size = int(len(df) * 0.8)
train_data = df.iloc[:train_size]
test_data = df.iloc[train_size:]

# 准备特征和目标变量
X_train = train_data[["前一天铁矿石收盘"]].values
y_train = train_data["收盘_螺纹钢"].values

X_test = test_data[["前一天铁矿石收盘"]].values
y_test = test_data["收盘_螺纹钢"].values

# 创建并训练模型
model = LinearRegression()
model.fit(X_train, y_train)

# 在训练集和测试集上进行预测
y_train_pred = model.predict(X_train)
y_test_pred = model.predict(X_test)


# 计算各种评估指标
def calculate_metrics(y_true, y_pred):
    r2 = r2_score(y_true, y_pred)
    mse = mean_squared_error(y_true, y_pred)
    rmse = np.sqrt(mse)
    mae = mean_absolute_error(y_true, y_pred)
    mape = np.mean(np.abs((y_true - y_pred) / y_true)) * 100
    return r2, rmse, mae, mape


# 计算训练集和测试集的评估指标
train_metrics = calculate_metrics(y_train, y_train_pred)
test_metrics = calculate_metrics(y_test, y_test_pred)

# 打印评估结果
print("\n模型评估结果:")
print("训练集:")
print(f"R²分数: {train_metrics[0]:.4f}")
print(f"RMSE: {train_metrics[1]:.2f}")
print(f"MAE: {train_metrics[2]:.2f}")
print(f"MAPE: {train_metrics[3]:.2f}%")

print("\n测试集:")
print(f"R²分数: {test_metrics[0]:.4f}")
print(f"RMSE: {test_metrics[1]:.2f}")
print(f"MAE: {test_metrics[2]:.2f}")
print(f"MAPE: {test_metrics[3]:.2f}%")

# 创建评估图
plt.figure(figsize=(20, 15))

# 实际值vs预测值图
plt.subplot(2, 2, 1)
plt.scatter(y_train, y_train_pred, c="blue", alpha=0.5, label="训练集")
plt.scatter(y_test, y_test_pred, c="red", alpha=0.5, label="测试集")
plt.plot([y.min(), y.max()], [y.min(), y.max()], "k--", lw=2)
plt.xlabel("实际值")
plt.ylabel("预测值")
plt.title("实际值 vs 预测值")
plt.legend()

# 残差图
residuals_train = y_train - y_train_pred
residuals_test = y_test - y_test_pred

plt.subplot(2, 2, 2)
plt.scatter(y_train_pred, residuals_train, c="blue", alpha=0.5, label="训练集")
plt.scatter(y_test_pred, residuals_test, c="red", alpha=0.5, label="测试集")
plt.axhline(y=0, color="k", linestyle="--")
plt.xlabel("预测值")
plt.ylabel("残差")
plt.title("残差分布图")
plt.legend()

# 残差直方图
plt.subplot(2, 2, 3)
sns.histplot(residuals_train, color="blue", alpha=0.5, label="训练集", bins=30)
sns.histplot(residuals_test, color="red", alpha=0.5, label="测试集", bins=30)
plt.xlabel("残差")
plt.ylabel("频数")
plt.title("残差直方图")
plt.legend()

# Q-Q图
plt.subplot(2, 2, 4)
stats.probplot(residuals_train, dist="norm", plot=plt)
plt.title("残差Q-Q图")

plt.tight_layout()
plt.show()

# 添加时间序列预测图
plt.figure(figsize=(15, 7))
plt.plot(train_data["date"], train_data["收盘_螺纹钢"], 'b-', label='训练集实际值')
plt.plot(train_data["date"], y_train_pred, 'g--', label='训练集预测值')
plt.plot(test_data["date"], test_data["收盘_螺纹钢"], 'r-', label='测试集实际值')
plt.plot(test_data["date"], y_test_pred, 'y--', label='测试集预测值')
plt.axvline(x=train_data["date"].iloc[-1], color='k', linestyle='--', label='训练/测试分割点')
plt.xlabel('日期')
plt.ylabel('螺纹钢收盘价')
plt.title('时间序列预测结果')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()

# 添加散点图显示铁矿石价格与螺纹钢价格的关系
plt.figure(figsize=(10, 6))
plt.scatter(train_data["前一天铁矿石收盘"], train_data["收盘_螺纹钢"],
           c="blue", alpha=0.5, label="训练集")
plt.scatter(test_data["前一天铁矿石收盘"], test_data["收盘_螺纹钢"],
           c="red", alpha=0.5, label="测试集")

# 添加回归线
x_range = np.linspace(
    min(df["前一天铁矿石收盘"]),
    max(df["前一天铁矿石收盘"]),
    100
).reshape(-1, 1)
plt.plot(x_range, model.predict(x_range), 'k-', lw=2)

plt.xlabel('前一天铁矿石收盘价')
plt.ylabel('螺纹钢收盘价')
plt.title('前一天铁矿石价格与螺纹钢价格关系')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()

# 保存模型(文件名需与实盘策略中 load_model 的名称一致)
model_filename = get_save_model_path("model_lr.joblib")
joblib.dump(model, model_filename)
print(f"模型已保存至: {model_filename}")

# 加载模型示例
loaded_model = joblib.load(model_filename)

# 使用加载的模型进行预测
test_value = np.array([[800]])  # 假设前一天铁矿石价格为 800 时
predicted_price = loaded_model.predict(test_value)[0]
print("使用加载的模型预测:")
print(f"当铁矿石前一日价格为 800 时,螺纹钢当日预测价格为: {predicted_price:.2f}")

代码结构说明

本案例代码按工作流程可分为六个部分:

  1. 数据获取:通过 qmf_model_sdk 下载螺纹钢、铁矿石历史数据,再用 load_kline 读取
  2. 特征工程:按日期合并对齐两个品种数据,构建"前一天铁矿石收盘"特征,划分训练集/测试集
  3. 模型训练:LinearRegression 拟合训练集,并在训练集、测试集上分别预测
  4. 模型评估:计算并打印 R²、RMSE、MAE、MAPE 指标
  5. 可视化分析:绘制实际值vs预测值、残差分布、残差直方图、Q-Q 图、时间序列预测图、价格关系散点图
  6. 模型保存与加载:joblib 保存模型到本地并重新加载,用新数据(铁矿石 800 元)验证预测

相关阅读

已复制到剪贴板
机器学习 · 期货版修订 R4

返回机器学习 · 期货版板块

评论