机器学习(二):商品期货中的VAR模型应用

Author: ianzeng123, Created: 2023-11-17 15:17:26, Updated: 2024-02-28 21:49:36

img

在商品期货市场中,预测价格走势和风险管理是两个核心任务。VAR模型作为一种强大的统计工具,能够同时在这两个方面发挥重要作用。本文将详细解释VAR模型的概念、原理,以及如何使用FMZ本地回测引擎在Python中应用VAR模型,并探讨VAR模型在商品期货中的应用意义。

VAR模型简介

VAR,全称Vector Autoregression,即向量自回归模型,是一种用于处理多个时间序列数据的统计模型。它通过构建一个包含多个变量的时间序列模型,以捕捉变量之间的动态关系,从而预测未来的价格走势。

VAR模型原理

VAR模型的原理基于这样一个假设:一组时间序列变量可以由其历史数据和滞后值来预测。具体来说,VAR模型通过最小化预测误差平方和,找出变量之间的最优线性组合。这种组合能够最大限度地解释变量之间的相互关系,从而预测未来。

VAR模型在Python中的应用方法

Python作为一种强大的编程语言,拥有丰富的库来支持VAR模型的实现。其中,最为常用的是statsmodels库。以下是使用Python进行VAR模型估计的基本步骤:

Step 1: 导入必要的库

# 数据处理
import pandas as pd
import numpy as np

# 时间序列分析
from statsmodels.tsa.api import VAR
from statsmodels.tsa.stattools import adfuller

# 数据可视化
import matplotlib.pyplot as plt
%matplotlib inline
plt.style.use('seaborn-darkgrid')

# 忽略警告
import warnings
warnings.filterwarnings('ignore')

Step 2: 导入数据

这次的数据我们根据在聚类分析中的能源类簇,一共包含7个品种,分别是’低硫燃料油’, ‘原油’, ‘液化石油气’, ‘燃料油’, ‘石油沥青’, ‘精对苯二甲酸’, ‘苯乙烯’,来构建一个VAR模型。

  • 定义目标品种
import pandas as pd  
  
# 将商品信息存储在一个列表中  
data = [  
    ["黄大豆2号", "b", "DCE", "大商所"],  
    ["聚丙烯", "pp", "DCE", "大商所"],  
    ["聚氯乙烯", "v", "DCE", "大商所"],  
    ["豆粕", "m", "DCE", "大商所"],  
    ["铁矿石", "i", "DCE", "大商所"],  
    ["棕榈油", "p", "DCE", "大商所"],  
    ["黄大豆1号", "a", "DCE", "大商所"],  
    ["焦煤", "jm", "DCE", "大商所"],  
    ["黄玉米", "c", "DCE", "大商所"],  
    ["玉米淀粉", "cs", "DCE", "大商所"],  
    ["豆油", "y", "DCE", "大商所"],  
    ["冶金焦炭", "j", "DCE", "大商所"],  
    ["苯乙烯", "eb", "DCE", "大商所"],  
    ["液化石油气", "pg", "DCE", "大商所"],  
    ["生猪", "lh", "DCE", "大商所"],  
    ["鲜鸡蛋", "jd", "DCE", "大商所"],  
    ["乙二醇", "eg", "DCE", "大商所"],  
    ["线型低密度聚乙烯", "l", "DCE", "大商所"],  
    ["原油", "sc", "INE", "上能源"],  
    ["低硫燃料油", "lu", "INE", "上能源"],  
    ["国际铜", "bc", "INE", "上能源"],  
    ["铜", "cu", "SHFE", "上期所"],  
    ["不锈钢", "ss", "SHFE", "上期所"],  
    ["石油沥青", "bu", "SHFE","上期所"],  
    ["锡", "sn","SHFE","上期所"],  
    ["锌","zn","SHFE","上期所"],  
    ["铅","pb","SHFE","上期所"],  
    ["热轧卷板","hc","SHFE","上期所"],  
    ["燃料油","fu","SHFE","上期所"],  
    ["白银","ag","SHFE","上期所"],  
    ["铝","al","SHFE","上期所"],  
    ["螺纹钢","rb","SHFE","上期所"],  
    ["黄金","au","SHFE","上期所"],  
    ["镍","ni","SHFE","上期所"],  
    ["天然橡胶","ru","SHFE","上期所"],  
    ["漂针浆","sp","SHFE","上期所"],  
    ["精对苯二甲酸","TA","CZCE","郑商所"],  
    ["甲醇","MA","CZCE","郑商所"],  
    ["硅铁","SF","CZCE","郑商所"],  
    ["玻璃","FG","CZCE","郑商所"],  
    ["尿素","UR","CZCE","郑商所"],  
    ["白砂糖","SR","CZCE","郑商所"],  
    ["锰硅","SM","CZCE","郑商所"],  
    ["纯碱","SA","CZCE","郑商所"]  
]  
  
# 创建DataFrame  
df = pd.DataFrame(data, columns=["商品名称", "代码", "交易所代码", "交易所"])  

groupContract = ['低硫燃料油', '原油', '液化石油气', '燃料油', '石油沥青', '精对苯二甲酸', '苯乙烯']

# 使用loc方法根据商品名称筛选出对应的代码  
codeDf = df.loc[df['商品名称'].isin(groupContract),]
codeDf.reset_index(drop=True, inplace=True)  
print(codeDf)
  • 获取目标品种k线数据
'''backtest
start: 2023-01-03 09:00:00
end: 2023-11-01 15:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES","depthDeep":20}]
'''
from fmz import *
task = VCtx(__doc__) 

dataDf = pd.DataFrame(columns=["InstrumentId", "Instrument", "Time", "Close"])

for i in range(len(codeDf["代码"])):
    rlist = []
    rlisttime = []
    prebartime = 0
    
    mainId = codeDf["代码"][i] + '888'
    print(mainId)
    
    codeId =codeDf["商品名称"][i]
    
    while True:
        try:
            exchange.SetContractType(mainId)
            r = exchange.GetRecords(PERIOD_D1)
            if r[-1].Time != prebartime:
                for j in range(len(r) - 1):
                    if r[j].Time not in rlisttime:
                        rlist.append([mainId, codeId, r[j].Time, r[j].Close])
                        rlisttime.append(r[j].Time)
                prebartime = r[-1].Time
            else:
                continue
            new_rows = pd.DataFrame(rlist, columns=["InstrumentId", "Instrument", "Time", "Close"])
            dataDf = dataDf.append(new_rows, ignore_index=True)
        except Exception as e:
            print(mainId + '数据读取完成')
            break

接下来,我们需要确保VAR是平稳的。为了达到这个目的,我们需要平稳的时间序列。我们对每个品种价格应用ADF测试。如果它们不是平稳的,我们对其一阶差分应用相同的ADF测试。

如果一阶差分仍然不是平稳的,我们对其二阶差分应用ADF测试,以此类推,直到找到积分的阶数。在这个过程中,我们创建一个DataFrame来保存应用于每个品种的所有测试的p值。

Step 3: 创建每个时间序列的一阶和二阶差分

pivot_df = dataDf.pivot_table(index=dataDf.index, columns='InstrumentId', values='Close')

Step 4: 确定每个品种的差分阶数

tickers = codeDf.代码 + '888'

for ticker in tickers:
    pivot_df[f'{ticker}_dif'] = pivot_df[f'{ticker}']-pivot_df[f'{ticker}'].shift(1)
    pivot_df[f'{ticker}_dif2'] = pivot_df[f'{ticker}_dif']-pivot_df[f'{ticker}_dif'].shift(1)
pivot_df.dropna(inplace=True)

adf_table = pd.DataFrame(index=tickers, columns=['prices_pvalue','dif_pvalue', 'dif2_pvalue','Integration_order'])

# ADF 测试
for ticker in tickers:
    adf_table.loc[ticker,'prices_pvalue'] = adfuller(pivot_df[f'{ticker}'],autolag='aic',regression='c')[1]
    adf_table.loc[ticker,'dif_pvalue'] = adfuller(pivot_df[f'{ticker}_dif'],autolag='aic',regression='n')[1]
    adf_table.loc[ticker,'dif2_pvalue'] = adfuller(pivot_df[f'{ticker}_dif2'],autolag='aic',regression='n')[1]
    
# 确定差分阶数
for ticker in tickers:
    if adf_table.loc[ticker,'prices_pvalue']<0.05:
        adf_table.loc[ticker,'Integration_order'] = 0
    elif adf_table.loc[ticker,'dif_pvalue']<0.05:
        adf_table.loc[ticker,'Integration_order'] = 1
    elif adf_table.loc[ticker,'dif2_pvalue']<0.05:
        adf_table.loc[ticker,'Integration_order'] = 2

Step 5: 输出表格显示每个品种的差分阶数

print(adf_table)

ADF (Augmented Dickey-Fuller) 测试是一种常用的单位根检验方法,用于确定一个时间序列数据是否具有单位根,即是否是非平稳的。p值小于某个显著性水平(例如0.05),我们就可以拒绝原假设(存在单位根),认为该序列是平稳的。

根据结果显示,除pg888,sc888和fu888 ,其他品种在未差分的情况下都是平稳的,而这三个品种在一阶差分下,成为平稳序列。

	prices_pvalue	dif_pvalue	dif2_pvalue	Integration_order
代码				
eb888	0.010119	3.49337e-29	3.19182e-13	0
pg888	0.313131	3.69196e-29	1.34249e-16	1
sc888	0.253303	3.50433e-29	1.76681e-16	1
lu888	0.0260044	4.78851e-15	6.19499e-13	0
bu888	0.00232234	4.03646e-29	7.68858e-15	0
fu888	0.0686547	4.72012e-29	2.3965e-22	1
TA888	0.00618636	0	        1.43402e-14	0

这些结果可能意味着,pg888,sc888和fu888原始序列可能包含一些趋势或者季节性因素,而其他品种可能没有这些因素或者这些因素的影响较小。在进行时间序列分析时,平稳性是一个重要的前提条件,因为非平稳序列可能会产生虚假的结果。因此,对于非平稳序列,我们通常会进行差分或其他转换以消除趋势或季节性因素。

Step 6: 估计一个VAR模型


# 创建var_data
var_data = pd.DataFrame(data=[np.log(pivot_df[f'{ticker}']/pivot_df[f'{ticker}'].shift(1)) for ticker in tickers]).T

var_data.dropna(inplace=True)

model = VAR(var_data)

# 创建var(1)模型
results = model.fit(1)

# 输入aic & bic结果
print("AIC:", results.aic)
print("BIC:", results.bic)

在fit括号内,我们编写了数字1,表示我们将创建一个具有每个时间序列一个滞后的VAR模型,然后我们来进行比较,接着我们打印了AIC和BIC的结果。

AIC: -58.07461469288411
BIC: -57.42556952600262

Step 7: VAR模型预测

我们创建了使用VAR模型拟合了数据并设置了滞后阶数为1,现在可以使用forecast方法来进行未来步数的预测。

# 获取最后一个时间点的观测值
last_observation = var_data.iloc[-lags:].values

# 进行未来预测,假设预测未来1步
forecast_period = 1
forecast = results.forecast(last_observation, steps=forecast_period)

# 将预测结果转换为数据框
forecast_df = pd.DataFrame(forecast, columns=var_data.columns)

# 打印预测结果
print(forecast_df)

这段代码将获取你的数据中的最后一个时间点的观测值,并使用VAR模型的forecast方法预测未来一步的收益率。

      eb888     pg888     sc888     lu888     bu888     fu888     TA888
0  0.003276 -0.001379  0.000621  0.002426  0.002708  0.003034  0.004318

Step 8: VAR模型策略构建

根据预测的结果,我们可以进行策略的构建,首先建立var模型,然后预测下一个时间点的结果,如果是正数,我们进行平空开多;如果是负数,我们进行平多开空。如果正负性保持一致,我们保持原有的仓位。大家有兴趣可以研究一下,如果想要实现的话,请留言评论区。我们也将会为大家进行呈现。

最后总结一下,VAR模型作为一种有效的统计工具,能够处理多个时间序列数据,并用于预测价格走势和风险管理。在Python中实现VAR模型可以充分发挥其作用。在商品期货市场中,VAR模型的应用有助于提高预测准确性、降低风险以及制定有效的投资策略。因此,VAR模型将成为商品期货市场的重要工具之一。

本系列课程旨在为大家介绍机器学习技术在商品期货量化交易中的应用,其他相关文章请点击下面链接:


更多内容