如之前文章《创新技术阁:量化交易系统CryptoTrader使用说明》所说,用户可以根据策略模板,自己开发策略,只需要把开发的策略放到可执行文件的统计目录strategies中,即可在CryptoTrader里面使用。本文就详细介绍如何开发自己的策略。
1. 注意事项
1.1 命名规范
- 策略文件名采用下划线模式,例如:turtle_strategy.py
- 策略类名采用驼峰式,例如:TurtleStrategy
1.2 类名冲突
自建策略的类名不得重复,如果发生重名,图形界面只会显示一个策略类名。
2. 策略开发的步骤
2.1 载入内部组件
在编写策略逻辑前,需在策略文件顶部导入所需的内部组件。
from core.trader.utility import round_to, get_decimal_places_via_decimal_lib
from app.app_novastrategy import (
StrategyTemplate,
BarData, TickData,
TradeData, OrderData,
ArrayManager, BarGenerator,
Interval, datetime
)
组件说明
- StrategyTemplate: CTA策略模板基类
- TickData, BarData, TradeData, OrderData: 数据容器,分别存储Tick、K线、成交和委托信息
- BarGenerator: K线生成模块,用于合成K线数据
- ArrayManager: K线时间序列管理模块,支持技术指标计算
2.2 定义策略参数与变量
在策略类中,可定义策略的作者(author)、参数(parameters)和变量(variables)。
class TurtleStrategy(StrategyTemplate):
"""经典海龟交易策略"""
author = "创新技术阁"
# 策略参数
entry_window = 90 # 入场窗口
exit_window = 60 # 离场窗口
atr_window = 12 # ATR计算窗口
risk_level = 5000.0 # 风险水平
# 策略变量
trading_size = 0.0 # 交易手数
trading_target = 0.0 # 目标仓位
trading_pos = 0.0 # 当前仓位
entry_up = 0.0 # 入场上限
entry_down = 0.0 # 入场下限
exit_up = 0.0 # 离场上限
exit_down = 0.0 # 离场下限
atr_value = 0.0 # ATR值
long_entry = 0.0 # 多头入场价
short_entry = 0.0 # 空头入场价
long_stop = 0.0 # 多头止损价
short_stop = 0.0 # 空头止损价
# 参数与变量列表
parameters = ["entry_window", "exit_window", "atr_window", "risk_level"]
variables = [
"trading_size", "trading_target", "trading_pos",
"entry_up", "entry_down", "exit_up", "exit_down", "atr_value"
]
2.2.1 参数与变量的区别
- 策略参数: 由交易员外部指定,固定不变
- 策略变量: 随交易过程动态变化,初始化为默认值(如0或0.0)
2.2.2 显示与保存
若需在CTA引擎UI界面显示参数和变量,并在数据刷新或停止策略时保存数值,需将其名称以字符串形式添加到parameters和variables列表中。
注意: 列表仅支持str、int、float和bool类型,其他类型需在__init__函数中定义。
2.3 类的初始化
2.3.1 函数名称__init__
- 入参: cta_engine: Any, strategy_name: str, vt_symbol: str, setting: dict
- 出参: 无
def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
super().__init__(cta_engine, strategy_name, vt_symbol, setting)
# 初始化K线生成器
self.bg = BarGenerator(
on_bar=lambda bar: None,
window=1,
on_window_bar=self.on_window_bar,
interval=Interval.HOUR
)
# 初始化K线序列管理器
self.am = ArrayManager()
# 加载历史数据
self.load_bars(5, Interval.MINUTE)
策略类的构造函数__init__,需与StrategyTemplate保持一致。初始化分三步:
- 继承模板: 通过super()传入CTA引擎、策略名称、vt_symbol和参数设置(支持实盘与回测引擎)
- K线生成: 使用BarGenerator将Tick数据合成为1小时K线
- 序列管理: 使用ArrayManager将K线数据转化为时间序列,支持技术指标计算
2.4 回调函数
StrategyTemplate中的on_开头的函数为回调函数,由CTA引擎在特定事件触发时自动调用。按功能分为三类:
2.4.1 策略状态控制
on_init
- 功能: 初始化策略
- 示例:
def on_init(self):
self.trading_symbol = self.vt_symbols[0]
self.bar_dt = None
self.bg = BarGenerator(
on_bar=lambda bar: None,
window=1,
on_window_bar=self.on_window_bar,
interval=Interval.HOUR
)
self.am = ArrayManager()
self.load_bars(5, Interval.MINUTE)
self.write_log("策略初始化完成")
- 说明: 调用后inited状态变为True,此时仅计算指标,不发交易信号。基于Tick回测需调用load_tick。
on_start
- 功能: 启动策略
- 示例:
def on_start(self):
self.write_log("策略启动")
- 说明: 调用后trading状态变为True,可发交易信号。
on_stop
- 功能: 停止策略
- 示例:
def on_stop(self):
self.write_log("策略停止")
- 说明: 调用后trading状态变为False,停止交易信号。
2.4.2 数据接收与交易信号
on_tick
- 功能: 处理Tick数据更新
- 示例:
def on_tick(self, tick: TickData):
self.bg.update_tick(tick)
if not tick.extra or not tick.extra.get("bar"):
return
bar = tick.extra["bar"]
bar_dt = bar.datetime
if self.bar_dt and bar_dt == self.bar_dt:
return
self.bar_dt = bar_dt
bars = {bar.vt_symbol: bar}
self.on_bars(bars)
- 说明: 将Tick数据推进BarGenerator合成K线。
on_bars
- 功能: 处理K线数据更新并生成交易信号
- 示例:
def on_bars(self, bars: dict[str, BarData]):
self.cancel_all()
bar = bars[self.trading_symbol]
self.bg.update_bar(bar)
if not self.am.inited:
return
min_volume = self.get_min_volume(self.trading_symbol)
decimal_num = get_decimal_places_via_decimal_lib(min_volume)
if not self.trading_target: # 无仓位
self.atr_value = self.am.atr(self.atr_window)
self.trading_size = round(self.risk_level / self.atr_value, decimal_num)
if bar.high_price >= self.entry_up:
self.trading_target = self.trading_size
elif bar.low_price <= self.entry_down:
self.trading_target = -self.trading_size
elif self.trading_target > 0: # 多头
if bar.low_price <= self.exit_down:
self.trading_target = 0
elif self.trading_target < 0: # 空头
if bar.high_price >= self.exit_up:
self.trading_target = 0
pricetick = self.get_pricetick(self.trading_symbol)
trading_volume = round_to(self.trading_target - self.trading_pos, min_volume)
if trading_volume > 0:
buy_price = round_to(bar.close_price * 1.01, pricetick)
self.buy(self.trading_symbol, buy_price, abs(trading_volume))
elif trading_volume < 0:
short_price = round_to(bar.close_price * 0.99, pricetick)
self.short(self.trading_symbol, short_price, abs(trading_volume))
self.put_event()
- 说明: 基于1分钟K线数据进行交易决策。
on_window_bar
- 功能: 处理窗口K线更新
- 示例:
def on_window_bar(self, bar: BarData):
self.am.update_bar(bar)
if not self.am.inited:
return
self.entry_up, self.entry_down = self.am.donchian(self.entry_window)
self.exit_up, self.exit_down = self.am.donchian(self.exit_window)
self.put_event()
- 说明: 基于1小时K线计算入场和离场点。
2.4.3 委托状态更新
on_trade
- 功能: 处理成交回报
- 示例:
def on_trade(self, trade: TradeData):
trade.strategy = self.strategy_name
self.trading_pos = self.get_pos(self.trading_symbol)
self.put_event()
on_order
- 功能: 处理委托回报
- 示例:
def on_order(self, order: OrderData):
pass
2.5 交易函数
- buy: 买入开仓
- sell: 卖出平仓
- short: 卖出开仓
- cover: 买入平仓
- 入参: price: float, volume: float, stop: bool = False, lock: bool = False, net: bool = False
- 出参: vt_orderids: List[str] 或 无
2.6 委托与撤单
- send_order: 发送委托(由引擎调用)
- cancel_order: 撤销指定委托
- cancel_all: 撤销所有委托
注意: 仅在trading状态为True时可发送或撤销委托。