用于回测目标的连续期货合约
在之前关于 QuantStart 的文章中,我们研究了怎样从 Quandl 下载免费的期货数据。在本文中,我们将从回测的角度讨论期货合约的特征,这些特征带来了数据挑战。特殊是“连续合约”和“滚动收益”的概念。我们将概述期货的重要困难,并利用 pandas 提供 Python 实现,可以部分缓解这些题目。
期货合约简介
期货是双方签署的一种合约,约定在未来某一特定日期购买或出售一定命量的标的资产。该日期称为交割日或到期日。当该日期到来时,买方必须按照合约签署日商定的价格向卖方交付实物标的资产(或现金等价物)。
实际上,期货是在交易所交易的(与场外交易相反*)*,交易标的物的数目和质量都是标准化的。价格每天都按时价计算。期货流动性极强,被大量用于投机目标。虽然期货通常用于对冲农产物或工业品的价格,但期货合约可以针对任何有形或无形标的物形成,例如股票指数、外汇价值的利率。
在 CSI 数据网站上可以找到各个交易所期货合约利用的全部符号代码的具体列表:期货情况说明书。
期货合约和股票全部权之间的重要区别在于,期货合约由于到期日而具有有限的可用窗口。在任何时候,都会有多种以相同标的为基础的期货合约,它们的到期日各不相同。到期日最近的合约称为近期合约。作为量化交易者,我们面临的题目是,在任何时间点,我们都可以选择多种合约进行交易。因此,我们处理的是一组重叠的时间序列,而不是像股票或外汇那样的连续流。
本文的目标是概述从这组多个系列中构建连续合同流的各种方法,并强调与每种技术相干的权衡。
形成连续期货合约
试图从具有不同交割量的底层合约生成连续合约的重要困难在于,这些合约通常不会以相同的价格交易。因此,会出现它们无法提供从一个合约到下一个合约的平滑衔接的情况。这是由于期货溢价和现货溢价效应。有多种方法可以解决这个题目,我们现在将对此进行讨论。
常见方法
不幸的是,金融行业没有单一的“标准”方法来归并期货合约。终极,选择的方法将在很大程度上取决于采用合约的策略和实行方法。只管没有单一的方法,但仍有一些常见的方法:
退却/前进(“巴拿马”)调整
这种方法通过转移每份合约,使个别交割可以或许顺利地衔接相邻合约,从而缓解了多份合约之间的“差距”。这样,到期时前几份合约的开盘价/收盘价就匹配了。
巴拿马方法的关键题目在于引入趋势偏差,这将导致价格大幅波动。这可能导致具有足够历史意义的合约出现负数据。此外,由于价值的绝对变化,相对价格差异会淘汰。这意味着回报计算起来很复杂(大概完全不正确)。
比例调整
比例调整方法类似于处理股票分割的调整方法。不是对连续合约进行绝对调整,而是利用旧结算价与新开盘价的比率来按比例调整历史合约的价格。这允许连续流,而不会中断百分比回报的计算。
比例调整的重要题目是,任何依靠绝对价格程度的交易策略也必须进行类似调整才能实行正确的信号。这是一个有题目且轻易出错的过程。因此,这种连续流通常仅适用于汇总统计分析,而不是直接回溯测试研究。
展期/永世系列
Python 和 Pandas 中的 Roll-Return 格式
本文的别的部分将会集讨论怎样实施永续序列法,由于这种方法最适合回溯测试。这是开展策略管道研究的一种有效方法。
我们将把 WTI 原油“近”和“远”期货合约(代码 CL)拼接起来,以生成连续的价格序列。在撰写本文时(2014 年 1 月),近合约为CLF2014(1 月),远合约为CLG2014(2 月)。
为了下载期货数据,我利用了Quandl插件。确保在系统上设置正确的 Python 虚拟情况,并通过在终端中输入以下内容来安装 Quandl 包:
现在 Quandl 包已经安装完毕,我们需要利用 NumPy 和 pandas 来实行 roll-returns 构造。假如您尚未安装 NumPy 或 pandas,请按照此处的教程利用。创建一个新文件并输入以下导入语句:
- import datetime
- import numpy as np
- import pandas as pd
- import Quandl
复制代码 重要工作在函数中进行futures_rollover_weights。它需要一个开始日期(近期合约的第一个日期)、合约结算日期字典(expiry_dates)、合约符号以及合约展期的天数(默以为五天)。下面的注释解释了代码:
- def futures_rollover_weights(start_date, expiry_dates, contracts, rollover_days=5):
- """This constructs a pandas DataFrame that contains weights (between 0.0 and 1.0)
- of contract positions to hold in order to carry out a rollover of rollover_days
- prior to the expiration of the earliest contract. The matrix can then be
- 'multiplied' with another DataFrame containing the settle prices of each
- contract in order to produce a continuous time series futures contract."""
- # Construct a sequence of dates beginning from the earliest contract start
- # date to the end date of the final contract
- dates = pd.date_range(start_date, expiry_dates[-1], freq='B')
- # Create the 'roll weights' DataFrame that will store the multipliers for
- # each contract (between 0.0 and 1.0)
- roll_weights = pd.DataFrame(np.zeros((len(dates), len(contracts))),
- index=dates, columns=contracts)
- prev_date = roll_weights.index[0]
- # Loop through each contract and create the specific weightings for
- # each contract depending upon the settlement date and rollover_days
- for i, (item, ex_date) in enumerate(expiry_dates.iteritems()):
- if i < len(expiry_dates) - 1:
- roll_weights.ix[prev_date:ex_date - pd.offsets.BDay(), item] = 1
- roll_rng = pd.date_range(end=ex_date - pd.offsets.BDay(),
- periods=rollover_days + 1, freq='B')
- # Create a sequence of roll weights (i.e. [0.0,0.2,...,0.8,1.0]
- # and use these to adjust the weightings of each future
- decay_weights = np.linspace(0, 1, rollover_days + 1)
- roll_weights.ix[roll_rng, item] = 1 - decay_weights
- roll_weights.ix[roll_rng, expiry_dates.index[i+1]] = decay_weights
- else:
- roll_weights.ix[prev_date:, item] = 1
- prev_date = ex_date
- return roll_weights
复制代码 现在已经生成了加权矩阵,可以将其应用于单个时间序列。主函数下载近端和远端合约,为两者创建单个 DataFrame,构建展期加权矩阵,然后终极生成两个价格的连续序列,并适当加权:
- if __name__ == "__main__":
- # Download the current Front and Back (near and far) futures contracts
- # for WTI Crude, traded on NYMEX, from Quandl.com. You will need to
- # adjust the contracts to reflect your current near/far contracts
- # depending upon the point at which you read this!
- wti_near = Quandl.get("OFDP/FUTURE_CLF2014")
- wti_far = Quandl.get("OFDP/FUTURE_CLG2014")
- wti = pd.DataFrame({'CLF2014': wti_near['Settle'],
- 'CLG2014': wti_far['Settle']}, index=wti_far.index)
- # Create the dictionary of expiry dates for each contract
- expiry_dates = pd.Series({'CLF2014': datetime.datetime(2013, 12, 19),
- 'CLG2014': datetime.datetime(2014, 2, 21)}).order()
- # Obtain the rollover weighting matrix/DataFrame
- weights = futures_rollover_weights(wti_near.index[0], expiry_dates, wti.columns)
- # Construct the continuous future of the WTI CL contracts
- wti_cts = (wti * weights).sum(1).dropna()
- # Output the merged series of contract settle prices
- wti_cts.tail(60)
复制代码 输出如下:
- 2013-10-14 102.230
- 2013-10-15 101.240
- 2013-10-16 102.330
- 2013-10-17 100.620
- 2013-10-18 100.990
- 2013-10-21 99.760
- 2013-10-22 98.470
- 2013-10-23 97.000
- 2013-10-24 97.240
- 2013-10-25 97.950
- ..
- ..
- 2013-12-24 99.220
- 2013-12-26 99.550
- 2013-12-27 100.320
- 2013-12-30 99.290
- 2013-12-31 98.420
- 2014-01-02 95.440
- 2014-01-03 93.960
- 2014-01-06 93.430
- 2014-01-07 93.670
- 2014-01-08 92.330
- 长度:60,数据类型:float64
- 1 98.420
- 2014-01-02 95.440
- 2014-01-03 93.960
- 2014-01-06 93.430
- 2014-01-07 93.670
- 2014-01-08 92.330
- 长度:60,数据类型:float64
复制代码 可以看出,该序列现在在两个合约中是连续的。下一步是根据您的回溯测试需求,在不同年份对多个交割进行此利用。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |