利用Backtrader进行期权回测之三:导入期权数据

在前面的例子里,我使用的是从雅虎财经获取的股票数据。要回测期权的话,需要导入从通达信获取的数据。如何下载和解析通达信期权数据请参考:利用BackTrader进行期权回测之一:获取期权数据

为了加载自己的数据,Backtrader提供了两种常用方法:

  • 从CSV文件加载
  • 从Pandas DataFrame加载数据

从DataFrame加载数据

如果我已经有了如下图这样一个dataframe:
图1:期权数据dataframe
注意,backtrader要求至少有6列数据,包括:日期,最高价,最低价,开盘价,收盘价,成交量。

有个这个dataframe以后,加载数据就可以通过创建一个PandasData实例来实现:

from backtrader.feeds import PandasData

# 从dataframe加载数据
data = PandasData(dataname=df, datetime=-1)

# 添加数据到回测引擎
cerebro.adddata(data)

在创建PandasData的时候,参数datetime=-1表示“交易日期”通过列名自动识别。如果在dataframe中日期列的名称不是“datetime”,除了修改dataframe列名以外,还可以使用下面的方法。

  • 通过一个字符串指定日期列的名称:
# 日期列的名称是“col_name”
data = PandasData(dataname=df, datetime='col_name')
  • 或通过一个整数指定日期列在第几列:
# 日期列在第1列
data = PandasData(dataname=df, datetime=1)

如果datetime参数没有指定(相当于datetime=None),PandasData会尝试从dataframe的索引中获取日期。

其它列与之类似,可以使用标准的列名high,low,open,close,volume;也可以指定列的名称和位置。如果没有指定,则会自动根据列的名称去匹配。

加载数据以后,就可以在策略类中访问数据了。

添加额外的数据指标

对于期权来说,通常除了价格和成交量以外,还需要增加一些重要的指标,比如期权到期时间等。那么应该怎么办呢?

通过扩展一个新的数据加载类,可以很方便地在这个类中定义新增的数据。

现在我dataframe中新增了两列数据,分别是ttm(到期时间)和impvol(隐含波动率),如下图所示:
图2:dataframe数据扩展

扩展后的数据加载类的代码如下:

class ETFOptionPandasData(PandasData):
	# 新增两条数据线
    lines = ('ttm', 'impvol',)   
    
   	# 新增数据在dataframe中的位置,分别是第6列和第7列
	params = (('ttm', 6), ('impvol',7), )

Backtrader把一列数据叫做一个line,所以新增两列数据需要增加两个line。

在Backtrader中添加数据特别方便的地方在于:只需要定义数据名称和位置,不用编写实现代码。

定义好扩展数据加载类以后,就可以创建类实例来加载数据了:

data = ETFOptionPandasData(dataname=df, datetime=-1)
cerebro.adddata(data)

新增数据在策略类中可以通过以下方式引用:

class SimpleStratedy(bt.Stratedy):
	...
	def next(self):
		# 显示当天的收盘价
		print(self.datas[0].close[0])

在Backtrader中,next方法会在每个交易日调用,close[0]表示“close”列在当天的值,close[-1]表示前一个交易日,close[-2]表示往前两个交易日,依次类推。

其中datas[0]的含义我在下面解释。

加载多个期权的数据

可能有些读者已经看出来了:dataframe数据里没有期权代码!那么要怎么知道交易哪个期权呢?

在Backtrader中,每只股票(或期权)的数据是一个数据流(data feed),如果有多个期权,那么就要为每个期权创建一个数据流。假设包含期权代码的dataframe数据如下:

图3:包含期权代码的dataframe

加载多个数据流的代码是:

for opt in list(set(df[‘code’])):
	# 筛选出每个期权的数据并去掉“代码”列(第一列)
	d = df[ df[‘code’] == opt ].iloc[:, 1:]    
	
	# 加载数据并添加到回测引擎
	data = ETFOptionPandasData(dataname=d, datetime=-1)
	cerebro.adddata(data, name=opt)

cerebro.adddata方法中的name=opt参数是给每个数据流一个名称,这里我使用期权代码作为数据流的名称。然后在程序里可以通过数据流名称来获取对应的数据流。

在交易的时候,在策略类里指定交易标的:

class SimpleStrategy(bt.Strategy):def next(self):
		for d in self.datas:       # 循环处理每个期权的数据
			if d.impvol[0] < 0.1:  
				self.buy(d, size=10000)  # 指定买入期权和买入数量
			elif d.impvol[0] > 0.5:  
				self.sell(d, size=10000)  # 卖出标的和数量

这里的self.datas是一个列表,列表里的每个元素是一个数据流(data feed),也就是通过cerebro.adddata()添加的一个期权的数据。第1次调用adddata添加的是self.datas[0],第2次调用添加的是self.datas[1], 依次类推。所以前面提到的self.datas[0].close[0]就是第1个期权当天的收盘价。

除了通过序号访问data feed以外,也经常需要通过名称来访问,这可以通过self.getdatabyname()来实现。

class SimpleStrategy(bt.Strategy):
	def next(self):
		d = self.getdatabyname('option_name')		# 通过名称获取data feed
		self.buy(d, size=10000)						# 使用获得的data feed买入期权 

期权开始日期不一致问题

加载多个期权数据之后,出现了一个新问题:策略的next()方法只从最晚创建的期权开始交易的那一天才会被调用到。假设我们有3个数据流(Data Feed),其中:

  • 期权0从1月23日开始交易
  • 期权1从2月23日开始交易
  • 期权2从3月23日开始交易

那么Backtrader引擎在回测的时候,只会从3月23日开始调用next()方法,而在此之前的日期则不会调用。

Backtrader这样的设计会带来一个大问题,期权是滚动创建的,这意味回测只能从data feed中最晚的期权开始交易那一天开始。这显然不是我想要的结果。怎么解决呢?

研究backtrader文档发现,一个策略的生命周期是这样的:

  • 策略实例化:调用一次init()方法。
  • 策略开始:调用一次start()方法
  • 策略数据准备:每天调用prenext()方法。有些数据需要一定的准备时间,在这段时间里,每天会调用prenext()方法。比如前面提到的5日均线,需要5个交易日才能够产生第1个均线的值,因此在前4个交易日会每天调用prenext()。在数据准备完成,开始调用next()方法之后,prenext()不会再被调用。
  • 策略执行:每天调用next()方法。在数据准备完成以后每天调用,主要实现策略的交易逻辑。
  • 策略停止:调用一次stop()方法。

根据上面的说明,我可以在prenext()中调用next(),这样就可以在每个交易日都执行next()了。类似下面这样:

class SimpleStrategy(bt.Strategy):def prenext(self):
		# 调用next()方法
        self.next()
        
	def next(self):
		for d in self.datas:
			# 如果期权还未创建,跳过这条期权
			if len(d) == 0 :	
				continue

由于现在每个交易日都会调用next(),调用的时候会出现有些期权有数据,而另一些期权还没有创建所以没有数据的情况。为了解决这个问题,可以在next()方法中用len(d)来判断期权是否有数据,如果期权还未开始则d的长度为0。

好了,现在解决了期权数据的加载问题,终于可以开始实现一个真正的期权策略了。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值