CTA:策略实现
策略是模板类的子类
一个策略就是一个继承自模板类的子类。每个策略放在单独的.py
脚本文件中。
策略文件可以放在两个地方:
vnpy_ctastrategy -> strategies
working_diretory -> strategies
下面以最简单的双均线策略做一个示例。
示例:双均线策略
package
from vnpy_ctastrategy import (
CtaTemplate,
StopOrder,
TickData,
BarData,
TradeData,
OrderData,
BarGenerator,
ArrayManager
)
from vnpy.trader.object import Interval
此处导入了两个相当重要的东西:
BarGenerator
:K线合成器ArrayManager
:K线池
他们在vnpy_ctastrategy -> __init__.py
中被导入,实际定义在vnpy.trader.utility
中。
策略设置
class DoubleMaStrategy(CtaTemplate):
"""双均线策略"""
author = "ZhuZhihao"
fast_window = 5
slow_window = 10
fixed_size = 1
fast_ma0 = 0.0
fast_ma1 = 0.0
slow_ma0 = 0.0
slow_ma1 = 0.0
parameters = ["fast_window", "slow_window", "fixed_size"]
variables = ["fast_ma0", "fast_ma1", "slow_ma0", "slow_ma1"]
在__init__
之前,定义了parameters
和variables
两组变量。它们的区别在于:
parameters
需要设置,是策略的参数variables
是参数运行时的中间重要变量
在本例中
parameters = ["fast_window", "slow_window", "fixed_size"]
是设置双均线的时间窗口。variables = ["fast_ma0", "fast_ma1", "slow_ma0", "slow_ma1"]
则定义了一组变量,来保存双均线的上个和上上个值。这些变量可以在策略运行中被查看。
def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
""""""
super().__init__(cta_engine, strategy_name, vt_symbol, setting)
self.bar_generator = BarGenerator(self.on_bar)
self.array_manager = ArrayManager(2*max(self.fast_window, self.slow_window))
在__init__
函数中,定义了BarGenerator
和ArrayManager
的实例。
def on_init(self) -> None:
"""
Callback when strategy is inited.
"""
self.write_log("策略初始化")
self.load_bar(2*max(self.fast_window, self.slow_window), use_database=True)
策略初始化后,触发回调函数on_init
。它会自动初始化K线池。
load_bar
函数追溯如下:vnpy_ctastrategy.template.load_bar() -> vnpy_ctastrategy.backtesting.load_bar()
def load_bar(
self,
days: int,
interval: Interval = Interval.MINUTE,
callback: Callable = None,
use_database: bool = False
) -> None:
"""
Load historical bar data for initializing strategy.
"""
if not callback:
callback: Callable = self.on_bar
bars: List[BarData] = self.cta_engine.load_bar(
self.vt_symbol,
days,
interval,
callback,
use_database
)
for bar in bars:
callback(bar)
当中self.cta_engine.load_bar()
实际把数据读进来保存在bars
中,再对每一根K线调用回调函数callback
。若策略以K线为回测频率,则回调函数设为on_bar
,见:callback: Callable = self.on_bar
。
策略逻辑
策略逻辑写在回调函数中。
def on_tick(self, tick: TickData):
"""
Callback of new tick data update.
"""
self.bar_generator.update_tick(tick)
on_tick()
函数的作用是接收Tick数据,并将Tick数据合成为K线数据。此处可以自定义,灵活实现各种频率上的K线。在K线合成器的update_tick()
中,合成K线后,会调用策略类的回调函数on_bar()
。
def on_bar(self, bar: BarData) -> None:
"""
Callback of new bar data update.
"""
array_manager = self.array_manager
array_manager.update_bar(bar)
if not array_manager.inited:
return
承接上文,对每一根K线调用回调函数时,第一步是更新K线池。这样,就维护了一个始终含有最新信息的K线池。
fast_ma = array_manager.sma(self.fast_window, array=True)
self.fast_ma0 = fast_ma[-1]
self.fast_ma1 = fast_ma[-2]
slow_ma = array_manager.sma(self.slow_window, array=True)
self.slow_ma0 = slow_ma[-1]
self.slow_ma1 = slow_ma[-2]
# 金叉
cross_over = self.fast_ma0 > self.slow_ma0 and self.fast_ma1 < self.slow_ma1
# 死叉
cross_below = self.fast_ma0 < self.slow_ma0 and self.fast_ma1 > self.slow_ma1
ArrayManager
内置了技术指标的计算方法,用talib
实现。
双均线策略逻辑为:
- 分别计算双均线前一个、前两个时刻的值
- 金叉:快均线上穿慢均线,做多
- 死叉:慢均线上穿快均线,做空
if cross_over:
if self.pos == 0:
self.buy(bar.close_price, self.fixed_size)
elif self.pos < 0:
self.cover(bar.close_price, abs(self.pos))
self.buy(bar.close_price, self.fixed_size)
elif cross_below:
if self.pos == 0:
self.short(bar.close_price, self.fixed_size)
elif self.pos > 0:
self.sell(bar.close_price, abs(self.pos))
self.short(bar.close_price, self.fixed_size)
self.put_event()