English Русский Español Deutsch 日本語
preview
数据科学与机器学习(第四十二部分):使用Python中的ARIMA模型进行外汇时间序列预测 —— 您需要了解的一切

数据科学与机器学习(第四十二部分):使用Python中的ARIMA模型进行外汇时间序列预测 —— 您需要了解的一切

MetaTrader 5交易系统 |
31 1
Omega J Msigwa
Omega J Msigwa

内容


什么是时间序列预测?

时间序列预测,是利用历史数据预测按时间排序的数据点未来取值的过程。这类数据通常按时间顺序排列,因此被称为时间序列

时间序列数据的核心变量 

尽管数据中可以包含任意多的特征变量,但任何用于时间序列分析或预测的数据,都必须包含以下两个变量:

  1. 时间

    作为自变量,表示各个数据点被观测到的具体时间。

  2. 目标变量

    即您希望基于历史观测值(以及可能的其他因素)预测的数值。(例如:每日股票收盘价、每小时气温、每分钟网站访问量)。

时间序列预测的目标,是挖掘数据中的历史规律与趋势,从而对未来数值做出可靠预测。

我们之前已经讨论过使用常规AI模型进行时间序列预测。在本文中,我们将改用专为时间序列问题设计的模型 —— ARIMA来进行时间序列预测。

可以将时间序列预测分为两类:

  1. 单变量时间序列预测
    这类预测仅用一个变量来预测其自身的未来值。例如,用股票当前收盘价预测未来收盘价。

    这正是ARIMA模型所能实现的预测类型。
  2. 多变量时间序列预测
    这类预测使用多个输入变量来预测未来的目标变量。

    与我们在另一篇文章中所做的方法类似。


ARIMA模型简介

ARIMA的全称是自回归积分移动平均模型。

它属于一类基于历史数据建模的方法,即利用时间序列的滞后项与滞后预测误差来解释当前序列。

其公式可直接用于预测未来数值。任何“非季节性”、存在规律模式、且不是纯随机白噪声的时间序列,都可以用ARIMA建模。

因此,简单来说,ARIMA是一种仅依靠时间序列的历史值就能预测未来值的预测算法。

ARIMA模型由三个阶数参数定义:(p, d, q),

其中:

  • p 为AR项的阶数
  • q为MA项的阶数
  • d为使时间序列达到平稳所需的差分阶数


ARIMA模型中p、d、q的含义

p的含义

p是自回归(AR)项的阶数。它表示将多少期滞后的Y值用作预测变量。

d的含义

ARIMA中的自回归意味着这是一个线性回归模型,并使用自身的滞后值作为预测因子。众所周知,线性回归模型在预测变量互不相关、彼此独立时效果最优,因此我们需要让时间序列平稳。

使序列平稳最常用的方法是差分法,即用当前值减去前一期的值。有时,根据序列的复杂程度,可能需要进行多次差分。

因此,d表示使序列平稳所需的最少差分次数。如果时间序列本身已经平稳,则d = 0。

q的含义

q是移动平均(MA)项的阶数。它表示将多少期滞后的预测误差纳入ARIMA模型中。


ARIMA模型的核心组成部分

要理解ARIMA,我们需要拆解它的基本结构。将各组成部分拆分后,就能更轻松地理解这种时序预测方法的整体工作原理。

ARIMA的名称可以拆分为三个部分:AR、I、MA,具体如下。

自回归部分AR(p)

自回归(AR)组件基于历史数值构建趋势。简单来说,自回归模型类似于线性回归模型,但它使用时间序列自身过去的滞后值作为预测因子。 

此部分通过以下公式计算:

AR公式

其中:

  •  表示时间序列在t时刻的当前取值。
  •  是常数项。
  •  φ₁至φₚ为自回归参数(系数),用于表示各滞后项对当前值的影响程度。
  •  y_(t-1)至 y_(t-p)为时间序列的各期滞后历史值。
  •  为t时刻的误差项。

积分部分I(d)

积分(I)部分涉及对时间序列进行差分处理,同时需保证时间序列的平稳性,即序列的均值和方差在一段时间内保持恒定。

简单来说,就是将一个观测值减去另一个观测值,以消除趋势与季节性的影响。通过差分操作,我们可以使序列变得平稳。这一步至关重要,因为它能让模型拟合数据本身,而非拟合噪声。

移动平均部分MA(q) 

移动平均(MA)组件关注观测值与滞后预测误差之间的关系。通过分析当前观测值与历史误差之间的关联,我们可以提取出数据中潜在趋势的有效信息。 

我们可以将滞后预测误差视为误差项,移动平均模型会估算这些误差对最新观测值的影响并将其纳入模型。这一方法尤其适用于捕捉数据中的短期波动与随机冲击。在时间序列的MA部分中,我们能获取序列行为的关键信息,从而实现更精准的预测。

其中:

  •  是常数值。
  •  是MA参数。
  •  是上一期的误差项。
  •  是当前的误差项。


基于Python的ARIMA模型

因此,ARIMA模型就是上述AR、I、MA三个部分的简单组合。其公式如下:

在ARIMA模型中,关键难点在于确定合适的参数(p、d、q取值)。因为这些参数决定了模型的运行效果,让我们来学习如何确定这些参数。


确定AR项的阶数(p)

我们可以通过观察偏自相关函数(PACF)图来确定所需的AR项数量。

偏自相关是指剔除中间滞后项的影响后,序列与它的滞后项之间的相关关系。因此,PACF反映的是某一滞后项与序列之间的纯相关度。通过这种方式,我们就能判断该滞后项是否需要纳入AR项中。

首先在命令提示符(CMD)中安装所有依赖库。本文末尾附有requirements.txt文件

pip install -r requirements.txt

导入:

# Importing required libraries
import pandas as pd
import numpy as np
import MetaTrader5 as mt5

# Use auto_arima to automatically select best ARIMA parameters

import seaborn as sns
import matplotlib.pyplot as plt
import warnings
import os

# Suppress warning messages for cleaner output
warnings.filterwarnings("ignore")

# Set seaborn plot style for better visualization
sns.set_style("darkgrid")

MetaTrader5中获取数据。

# Getting (EUR/USD OHLC data) from MetaTrader5

mt5_exe_file = r"c:\Users\Omega Joctan\AppData\Roaming\Pepperstone MetaTrader 5\terminal64.exe" # Change this to your MetaTrader5 path
if not mt5.initialize(mt5_exe_file):
    print("Failed to initialize Metatrader5, error = ",mt5.last_error)
    exit()

# select a symbol into the market watch
symbol = "EURUSD"
timeframe = mt5.TIMEFRAME_D1

if not mt5.symbol_select(symbol, True):
    print(f"Failed to select {symbol}, error = {mt5.last_error}")
    mt5.shutdown()
    exit()

rates = mt5.copy_rates_from_pos(symbol, timeframe, 1, 1000) # Get 1000 bars historically
df = pd.DataFrame(rates)

print(df.head(5))
print(df.shape)

PACF图。

from statsmodels.graphics.tsaplots import plot_pacf
import matplotlib.pyplot as plt

plt.figure(figsize=(6,4))
plot_pacf(series.diff().dropna(), lags=5)

plt.title("Partial Autocorrelation Plot")
plt.xlabel('Lag')  # X-axis label
plt.ylabel('PACF')  # Y-axis label

plt.savefig("pacf plot.png")
plt.show()

输出:

要确定合适的p值,我们需要在图中寻找PACF截断对应的滞后阶数(即相关系数降至接近0且不再显著)。该滞后阶数即为p的合理取值。 

由上图可见,合适的p值为0(0阶之后的所有滞后项均不显著)。

确定ARIMA模型中的差分阶数(d)

如前所述,对时间序列进行差分的目的是使其平稳,因为ARIMA模型假设序列是平稳的。但需要注意的是,应避免对时间序列差分不足或过度差分。

合适的差分阶数,是使序列达到近似平稳所需的最小差分次数。平稳序列会围绕一个固定均值波动,并且自相关函数(ACF)图会快速衰减至零附近。

如果序列在很多滞后阶数(10阶及以上)上自相关系数均为正值,说明序列需要进一步差分。反之,如果滞后1阶的自相关系数负值过大,则说明序列很可能已经过度差分。

当难以在两个差分阶数之间做选择时,我们应选择使差分后序列标准差更小的那个阶数。

让我们以欧元兑美元(EURUSD)的收盘价为例,确定合适的差分阶数。

首先,我们需要借助Python中statsmodels库提供的增广迪基 - 富勒检验(ADF检验),检查给定序列(本例中为收盘价)是否平稳。我们要检验平稳性,因为只有非平稳序列才需要计算差分阶数。

ADF检验的原假设(H₀)为时间序列是非平稳的。因此,如果检验的p值小于显著性水平(0.05),我们就拒绝原假设,并推断该时间序列确实是平稳的。

所以,在本例中,如果p值大于0.05,我们就需要继续计算合适的差分阶数。

甚至在进行ADF检验之前,我们仅通过观察折线图,就能判断欧元兑美元的收盘价是非平稳的。

plt.figure(figsize=(7,5))
sns.lineplot(df, x=df.index, y="Close")
plt.savefig("close prices.png")

输出:

检验平稳性。

from statsmodels.tsa.stattools import adfuller

series = df["Close"]
result = adfuller(series)

print(f'p-value: {result[1]}')

输出:

p-value: 0.3707268514544181

由此可见,p值远大于显著性水平(0.05)。让我们对序列做一次差分,再做二次差分,然后观察自相关图的变化。

# Original Series
fig, axes = plt.subplots(3, 2, sharex=True, figsize=(9, 9))

axes[0, 0].plot(series); axes[0, 0].set_title('Original Series')
plot_acf(series, ax=axes[0, 1])

# 1st Differencing
axes[1, 0].plot(series.diff().dropna()); axes[1, 0].set_title('1st Order Differencing')
plot_acf(series.diff().dropna(), ax=axes[1, 1])

# 2nd Differencing
axes[2, 0].plot(series.diff().diff()); axes[2, 0].set_title('2nd Order Differencing')
plot_acf(series.diff().diff().dropna(), ax=axes[2, 1])

plt.savefig("acf plots.png")
plt.show()

输出:

由图可见,一阶差分已经达到了平稳效果,二阶差分后的平稳性并没有显著提升。这一点可以再次通过ADF检验进行验证。

result = adfuller(series.diff().dropna())
print(f'p-value d=1: {result[1]}')

result = adfuller(series.diff().diff().dropna())
print(f'p-value d=2: {result[1]}')

输出:

p-value d=1: 0.0
p-value d=2: 0.0

确定MA项的阶数(q)

正如通过查看PACF图来确定AR项的数量一样,我们将借助ACF图以判断MA项的数量。重申一下,严格来说,MA项指的是滞后预测的误差值。

ACF图告诉我们,需要多少个MA项才能消除平稳序列中残留的自相关性。

plt.figure(figsize=(7,5))
plot_pacf(series.diff().dropna(), lags=20)

plt.title("Partial Autocorrelation Plot")
plt.xlabel('Lag')  # X-axis label
plt.ylabel('PACF')  # Y-axis label

plt.savefig("pacf plot finding q.png")
plt.show()

输出:

q的最佳值是0。

上述讨论的确定p、d、q值的方法比较原始且需要手动操作。现在,我们可以借助pmdarima库中名为 auto_arima的工具函数,自动完成这一过程,轻松获得这些参数。

from pmdarima.arima import auto_arima

model = auto_arima(series, seasonal=False, trace=True)
print(model.summary())

输出:

Performing stepwise search to minimize aic
 ARIMA(2,1,2)(0,0,0)[0] intercept   : AIC=-35532.282, Time=3.21 sec
 ARIMA(0,1,0)(0,0,0)[0] intercept   : AIC=-35537.068, Time=0.49 sec
 ARIMA(1,1,0)(0,0,0)[0] intercept   : AIC=-35537.492, Time=0.59 sec
 ARIMA(0,1,1)(0,0,0)[0] intercept   : AIC=-35537.511, Time=0.74 sec
 ARIMA(0,1,0)(0,0,0)[0]             : AIC=-35538.731, Time=0.25 sec
 ARIMA(1,1,1)(0,0,0)[0] intercept   : AIC=-35535.683, Time=1.22 sec

Best model:  ARIMA(0,1,0)(0,0,0)[0]          
Total fit time: 6.521 seconds

这样一来,我们得到了与手动分析完全一致的参数。


基于欧元兑美元构建ARIMA模型

既然已经确定了p、d、q的取值,我们就具备了拟合(训练)ARIMA模型所需的全部条件。

from statsmodels.tsa.arima.model import ARIMA

arima_model = ARIMA(series, order=(0,1,0))
arima_model = arima_model.fit()
print(arima_model.summary())

输出:

                               SARIMAX Results                                
==============================================================================
Dep. Variable:                  Close   No. Observations:                 4007
Model:                 ARIMA(0, 1, 0)   Log Likelihood               13987.647
Date:                Mon, 26 May 2025   AIC                         -27973.293
Time:                        16:59:38   BIC                         -27966.998
Sample:                             0   HQIC                        -27971.062
                               - 4007                                         
Covariance Type:                  opg                                         
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
sigma2      5.427e-05   7.78e-07     69.768      0.000    5.27e-05    5.58e-05
===================================================================================
Ljung-Box (L1) (Q):                   1.47   Jarque-Bera (JB):              1370.86
Prob(Q):                              0.22   Prob(JB):                         0.00
Heteroskedasticity (H):               0.49   Skew:                             0.09
Prob(H) (two-sided):                  0.00   Kurtosis:                         5.86
===================================================================================

Warnings:
[1] Covariance matrix calculated using the outer product of gradients (complex-step).

现在,我们用部分数据训练该模型,并利用它对样本外数据进行预测,这与使用传统机器学习模型的思路是一样的。

首先,将数据划分为训练集与测试集。

series = df["Close"]

train_size = int(len(series) * 0.8)
train, test = series[:train_size], series[train_size:]

将模型拟合到训练数据上。

from statsmodels.tsa.arima.model import ARIMA

arima_model = ARIMA(train, order=(0,1,0))
arima_model = arima_model.fit()
print(arima_model.summary())

对训练数据进行预测。

predicted = arima_model.predict(start=1, end=len(train))

可视化输出结果。

plt.figure(figsize=(7,4))
plt.plot(train.index, train, label='Actual')
plt.plot(train.index, predicted, label='Forecasted mean', linestyle='--')
plt.title('Actual vs Forecast')
plt.legend()
plt.show()

输出:

现在,我们有了真实值和对应的预测值,就可以选用任意评估方法或损失函数来评估模型效果。

但在此之前,让我们先学习如何用此ARIMA模型对样本外数据进行预测。


基于ARIMA进行样本外预测

与传统机器学习算法不同,这类经典时间序列预测模型在处理未见过的新数据时,采用的是完全不同的方法。

在常规机器学习框架和Python库中,调用predict方法并传入一组数据,模型就会预测/估计未来值;但ARIMA模块提供的predict函数功能却截然不同。

在ARIMA模型中,该方法并不直接预测未来值,而是仅适用于对样本内数据(即模型已见过的训练集数据)做出预测。

为了理解这一点,我们先区分一下预测和未来预测的差别。

预测泛指用模型估计未知值(可以是未来值或其他缺失值),而未来预测专指利用时间模式和时序依赖关系,预测时间序列未来的值。

预测可用于判断市场方向、估计下一期收盘价等任务;而未来预测专指利用时间模式和时序依赖关系,预测时间序列未来的值。

在ARIMA模型中,predict方法通常用于对模型已经学习过的历史数据做拟合预测,这也是它需要传入起始索引和结束索引的原因。它同样可以指定您想要回测(评估)的历史步长数。

predicted = arima_model.predict(start=1, end=len(train))
print(arima_model.predict(steps=10))

要预测未来值,我们必须使用名为forecast()的方法。 

如前所述,ARIMA这类传统时间序列模型依赖前序数据来预测下一时刻的值,这一点由图03中的公式可见

这就意味着我们需要不断用新数据更新模型,才能保证预测有效。例如,要用ARIMA预测欧元兑美元明日的收盘价,就必须向模型输入今日的收盘价;明日预测后天价格时也是同理。

这与传统机器学习的做法有很大区别。

现在,让我们开始对样本外数据进行预测。

# Fit initial model

model = ARIMA(train, order=(0, 1, 0)) 
results = model.fit()

# Initialize forecasts
forecasts = [results.forecast(steps=1).iloc[0]]  # First forecast

# Update with test data iteratively
for i in range(len(test)):
    # Append new observation without refitting
    results = results.append(test.iloc[i:i+1], refit=False)
    
    # Forecast next step
    forecasts.append(results.forecast(steps=1).iloc[0])

forecasts = forecasts[:-1] # remove the last element which is the predicted next value 

# Compare forecasts vs actual test data
plt.plot(test.index, test, label="Actual")
plt.plot(test.index, forecasts, label="Forecast", linestyle="--")
plt.legend()

append方法会将新的(当前)数据并入模型中。在本例中,使用欧元兑美元的当前收盘价来预测下一期收盘价。 

将refit设置为False,确保模型不会重新训练。这是更新ARIMA模型的高效方式。

让我们编写一个包含若干评估指标的函数,用于评估ARIMA模型的表现。

import sklearn.metrics as metric
from statsmodels.tsa.stattools import acf
from scipy.stats import pearsonr

def forecast_accuracy(forecast, actual):
    # Convert to numpy arrays if they aren't already
    forecast = np.asarray(forecast)
    actual = np.asarray(actual)
    
    metrics = {
        'mape': metric.mean_absolute_percentage_error(actual, forecast),
        'me': np.mean(forecast - actual),  # Mean Error
        'mae': metric.mean_absolute_error(actual, forecast),
        'mpe': np.mean((forecast - actual) / actual),  # Mean Percentage Error
        'rmse': metric.mean_squared_error(actual, forecast, squared=False),
        'corr': pearsonr(forecast, actual)[0],  # Pearson correlation
        'minmax': 1 - np.mean(np.minimum(forecast, actual) / np.maximum(forecast, actual)),
        'acf1': acf(forecast - actual, nlags=1)[1],  # ACF of residuals at lag 1
        "r2_score": metric.r2_score(forecast, actual)
    }

    return metrics
forecast_accuracy(forecasts, test)

输出:

{'mape': 0.0034114761554881936,
 'me': 6.360279441117738e-05,
 'mae': 0.0037872155688622737,
 'mpe': 6.825424905960248e-05,
 'rmse': 0.005018824533752777,
 'corr': 0.99656297100796,
 'minmax': 0.0034008221524469695,
 'acf1': 0.04637470541528736,
 'r2_score': 0.9931220697334551}

MAPE值为0.003,表明模型准确率约为99.996%,这一结果与R²决定系数基本一致。

测试集上真实值与预测值的对比图如下:


ARIMA模型的残差图

ARIMA提供了残差的可视化方法,便于更直观地理解模型效果。

results.plot_diagnostics(figsize=(8,8))
plt.show()

输出:

标准化残差

残差看起来围绕均值0上下波动,且方差基本均匀。

直方图

密度分布图显示残差近似服从正态分布,均值略微向右偏移。

理论分位数图

绝大多数点都与红色参考直线高度重合。如果出现明显偏离,则说明分布存在偏斜。

自相关图

自相关图(ACF图)表明残差序列不存在自相关性。如果ACF图显示残差仍存在某种模式,说明模型未能完全解释这些信息,需要引入更多自变量(预测因子)。

总体来看,该模型的拟合效果良好。


SARIMA模型

普通 ARIMA 模型的一个局限在于,它无法显式建模季节性。

季节性是指金融数据中按固定周期重复出现的规律,比如按小时、日、周、月、季度和年度循环的模式。

我们经常会看到交易品种呈现出某些重复性规律。例如,零售类股票通常在第四季度(假日购物季)上涨,部分能源股会随季节性天气变化波动;而在外汇品种中,某些交易时段内市场波动率会明显升高,等等。

如果时间序列数据存在明显或可确定的季节性,那么我们就应该选用季节性ARIMA 模型(简称SARIMA),因为它引入了季节性差分。

SARIMAX(p, d, q)x(P, D, Q, S) 模型组成

  • 自回归项(AR)

    如前所述,自回归通过时间序列的历史值来预测当前值。

  • 移动平均项(MA)

    移动平均项用于对历史预测误差进行建模。

  • 差分阶数(I)

    差分操作始终存在,目的是让时间序列变为平稳序列。

  • 季节性项(S)

    季节性项用于捕捉按固定周期重复出现的波动变化。

季节性差分与普通差分类似,但区别在于:普通差分是用当期值减去前一期值,而季节性差分是用当期值减去上一个季节周期对应的值。

在调用SARIMAX模型之前,我们先用auto_arima确定其最优参数。

from pmdarima.arima import auto_arima

# Auto-fit SARIMA (automatically detects P, D, Q, S)

auto_model = auto_arima(
    series,
    seasonal=True,          # Enable seasonality
    m=5,                    # Weeky cycle (5 days) for daily data
    trace=True,             # Show search progress
    stepwise=True,          # Faster optimization
    suppress_warnings=True,
    error_action="ignore"
)

print(auto_model.summary())

输出:

Performing stepwise search to minimize aic
 ARIMA(2,1,2)(1,0,1)[5] intercept   : AIC=-35529.092, Time=3.81 sec
 ARIMA(0,1,0)(0,0,0)[5] intercept   : AIC=-35537.068, Time=0.29 sec
 ARIMA(1,1,0)(1,0,0)[5] intercept   : AIC=-35536.573, Time=0.97 sec
 ARIMA(0,1,1)(0,0,1)[5] intercept   : AIC=-35536.570, Time=4.38 sec
 ARIMA(0,1,0)(0,0,0)[5]             : AIC=-35538.731, Time=0.21 sec
 ARIMA(0,1,0)(1,0,0)[5] intercept   : AIC=-35536.048, Time=0.67 sec
 ARIMA(0,1,0)(0,0,1)[5] intercept   : AIC=-35536.024, Time=0.87 sec
 ARIMA(0,1,0)(1,0,1)[5] intercept   : AIC=-35534.248, Time=0.92 sec
 ARIMA(1,1,0)(0,0,0)[5] intercept   : AIC=-35537.492, Time=0.37 sec
 ARIMA(0,1,1)(0,0,0)[5] intercept   : AIC=-35537.511, Time=0.55 sec
 ARIMA(1,1,1)(0,0,0)[5] intercept   : AIC=-35535.683, Time=0.57 sec

Best model:  ARIMA(0,1,0)(0,0,0)[5]          
Total fit time: 13.656 seconds
                               SARIMAX Results                                
==============================================================================
Dep. Variable:                      y   No. Observations:                 5009
Model:               SARIMAX(0, 1, 0)   Log Likelihood               17770.365
Date:                Tue, 27 May 2025   AIC                         -35538.731
Time:                        11:16:40   BIC                         -35532.212
Sample:                             0   HQIC                        -35536.446
                               - 5009                                         
Covariance Type:                  opg                                         
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
sigma2      4.846e-05   6.06e-07     80.005      0.000    4.73e-05    4.96e-05
===================================================================================
Ljung-Box (L1) (Q):                   2.42   Jarque-Bera (JB):              2028.68
Prob(Q):                              0.12   Prob(JB):                         0.00
Heteroskedasticity (H):               0.34   Skew:                             0.08
Prob(H) (two-sided):                  0.00   Kurtosis:                         6.11
===================================================================================

Warnings:
[1] Covariance matrix calculated using the outer product of gradients (complex-step).

虽然auto_arima会直接返回一个SARIMAX模型,无需再手动重新拟合,但手动再次拟合SARIMAX模型能够对结果拥有更多控制权,因此,我们还要再执行一次拟合。

from statsmodels.tsa.statespace.sarimax import SARIMAX

model = SARIMAX(
    train,
    order=auto_model.order,                  # Non-seasonal (p,d,q)
    seasonal_order=auto_model.order+(5,),      # Seasonal (P,D,Q,S)
    enforce_stationarity=False
)

results = model.fit()
print(results.summary())

输出:

                                     SARIMAX Results                                     
=========================================================================================
Dep. Variable:                             Close   No. Observations:                 4007
Model:             SARIMAX(0, 1, 0)x(0, 1, 0, 5)   Log Likelihood               12613.829
Date:                           Tue, 27 May 2025   AIC                         -25225.658
Time:                                   11:16:41   BIC                         -25219.364
Sample:                                        0   HQIC                        -25223.427
                                          - 4007                                         
Covariance Type:                             opg                                         
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
sigma2         0.0001   1.68e-06     63.423      0.000       0.000       0.000
===================================================================================
Ljung-Box (L1) (Q):                   3.42   Jarque-Bera (JB):               676.61
Prob(Q):                              0.06   Prob(JB):                         0.00
Heteroskedasticity (H):               0.48   Skew:                            -0.01
Prob(H) (two-sided):                  0.00   Kurtosis:                         5.01
===================================================================================

Warnings:
[1] Covariance matrix calculated using the outer product of gradients (complex-step).

尽管已经将季节性参数设置为true,但auto_model返回的阶数仍然仅适用于ARIMA(p,d,q)。在定义SARIMAX模型时,我们必须在元组末尾追加数值5,确保阶数格式为(p,d,q,s)。

在对真实值和预测值进行可视化展示与分析之前,我们需要剔除数组中前若干个数据,剔除数量等于季节性周期长度,因为这部分数据对应的信息不完整。

predicted = results.predict(start=1, end=len(train))

clean_train = train[5:]
clean_predicted = predicted[5:]

plt.figure(figsize=(7,4))
plt.plot(clean_train.index[5:], clean_train[5:], label='Actual')
plt.plot(clean_train.index[5:], clean_predicted[5:], label='Forecasted mean', linestyle='--')
plt.title('Actual vs Forecast')
plt.legend()
plt.savefig("sarimax train actual&forecast plot.png")
plt.show()

输出:

我们可以使用与评估ARIMA模型完全相同的方法来评估该SARIMA模型。

# Initialize forecasts
forecasts = [results.forecast(steps=1).iloc[0]]  # First forecast

# Update with test data iteratively
for i in range(len(test)):
    # Append new observation without refitting
    results = results.append(test.iloc[i:i+1], refit=False)
    
    # Forecast next step
    forecasts.append(results.forecast(steps=1).iloc[0])

clean_test = test[5:]
forecasts = forecasts[5:-1] # remove the last element which is the predicted next value and the first 5 items
forecast_accuracy(forecasts, clean_test)

输出:

{'mape': 0.004900183060803821,
 'me': -6.94082142749275e-06,
 'mae': 0.005432456867698095,
 'mpe': -7.226495372320155e-06,
 'rmse': 0.007127465498996785,
 'corr': 0.9931778828074744,
 'minmax': 0.004880027322298863,
 'acf1': 0.10724254539104018,
 'r2_score': 0.9864021833085908}

从R²指标来看,模型拟合表现较好,约为 0.986。这是一个相当不错的结果。

最后,我们可以使用ARIMA模型,根据从MetaTrader5中获取的数据进行实时预测。

首先,我们需要导入schedule库,以实现每日定时预测 ,因为该模型是基于日周期数据训练的。

import schedule
# Make realtime predictions based on the recent data from MetaTrader5

def predict_close():
    
    rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, 1) 
    if not rates:
        print(f"Failed to get recent OHLC values, error = {mt5.last_error}")
        time.sleep(60)
    
    rates_df = pd.DataFrame(rates)    
    
    global results # Get the variable globally, outside the function
    global forecasts
    
    # Append new observation to the model without refitting
    
    new_obs_value = rates_df["close"].iloc[-1]
    new_obs_index = results.data.endog.shape[0]  # continue integer index
    
    new_obs = pd.Series([new_obs_value], index=[new_obs_index]) # Its very important to continue making predictions where we ended on the training data
    results = results.append(new_obs, refit=False)
    
    # Forecast next step
    forecasts.append(results.forecast(steps=1).iloc[0])
    print(f"Current Close Price: {new_obs_value} Forecasted next day Close Price: {forecasts[-1]}")
    

通过schedule按日定时执行预测任务。

schedule.every(1).days.do(predict_close) # call the predict function after a given time

while True: 
    
    schedule.run_pending()    
    time.sleep(60)

mt5.shutdown()

输出:

Current Close Price: 1.1374900000000001 Forecasted next day Close Price: 1.1337899981049262
Current Close Price: 1.1372200000000001 Forecasted next day Close Price: 1.1447100065656721

根据这些预测的收盘价,您可以将其扩展为一套交易策略,并通过MetaTrader5-Python接口执行实际交易操作。



总结

ARIMA和SARIMA都是表现不错的传统时间序列模型,已被广泛应用于多个领域和行业。但您必须了解它们的局限与缺点,其中包括:

  • 它们要求序列平稳(差分后)

    但在实际应用中,我们并非总能拿到平稳的数据,且常常希望直接使用原始数据。对数据做差分处理,可能会破坏我们关注的原有数据结构与趋势。

  • 线性假设

    ARIMA本质上是线性模型,它假设未来值与历史滞后项、历史误差之间呈线性关系。这在金融和外汇市场的走势中并不成立,因为复杂的非线性形态才是常态,这意味着这类模型在某些场景下效果会很差。

  • 单变量模型

    这两种模型一次只能使用单一特征。众所周知,金融市场非常复杂,需要多个特征和多角度才能全面分析市场。这类模型只能从单一维度观察市场,会让我们遗漏其他有价值的信息。

    即便可以在SARIMAX模型中加入外生变量,但通常也远远不够。

尽管存在这些局限,但在参数合适、问题类型匹配、数据信息充分的前提下,简单的ARIMA 模型在时间序列预测上,甚至可以优于RNN这类复杂模型。

谨此结束。


来源与参考文献


附件表

文件名 说明/用法
forex_ts_forecasting_using_arima.py 包含本文探讨的所有案例的完整Python脚本
requirements.txt  包含Python依赖项及其版本号的文本文件


本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/18247

最近评论 | 前往讨论 (1)
Retail Trading Realities LTD
Philip Kym Sang Nelson | 22 3月 2026 在 15:16

令人惊叹的内容。

正是我想要的。

可能不会在交易中使用它,但非常有趣。

第一次听说 ARIMA 是在 Perry J Kaufman 的《交易系统与方法》一书中。

有人使用 ARIMA 成功交易过吗?

神经网络在交易中的应用:市场异常的自适应检测(DADA) 神经网络在交易中的应用:市场异常的自适应检测(DADA)
我们诚邀您了解 DADA 框架,这是一种用于检测时间序列异常的创新方法。它有助于区分随机波动和可疑偏差。与传统方法不同,DADA 具有灵活性,能够适应不同的数据。它没有采用固定的压缩级别,而是提供了多种选项,并为每种情况选择最合适的选项。
博弈论方法在交易算法中的应用 博弈论方法在交易算法中的应用
我们正在基于深度Q网络(DQN)机器学习技术,结合多维因果推理,开发一款自适应、自学习的交易 EA。该 EA 将能够同时成功交易 7 个货币对。不同货币对的智能体之间会相互交换信息。
新手在交易中的10个基本错误 新手在交易中的10个基本错误
新手在交易中会犯的10个基本错误: 在市场刚开始时交易, 获利时不适当地仓促, 在损失的时候追加投资, 从最好的仓位开始平仓, 翻本心理, 最优越的仓位, 用永远买进的规则进行交易, 在第一天就平掉获利的仓位,当发出建一个相反的仓位警示时平仓, 犹豫。
神经网络在交易中的应用:多元时间序列的双重聚类(终篇) 神经网络在交易中的应用:多元时间序列的双重聚类(终篇)
我们继续实现 DUET 框架作者提出的方法,该框架提供了一种创新的时间序列分析方法,结合时间和通道聚类来揭示分析数据中的隐藏模式。