某策略的回测结果
2021-07-19 20:42:13.439225 起始资金: 10,000,000.00
2021-07-19 20:42:13.439261 结束资金: 15,559,877.94
2021-07-19 20:42:13.439272 总收益率: 55.60%
2021-07-19 20:42:13.439280 年化收益: 79.90%
2021-07-19 20:42:13.439287 最大回撤: -3,004,434.58
2021-07-19 20:42:13.439295 百分比最大回撤: -17.23%
初始资金1000w
策略以固定手数进行交易
策略算得最大回撤是百分比大约是17%
但最大回撤值是300w
实际上是不是最大回撤值更有参考意义
如果从发生最大回撤前的高点开始交易
测得的最大回撤百分比将可能是30%
通过脚本启动,导入 matplotlib 错误
后来发现直接导入 matplotlib 也不错
以 i 铁矿为例
在我们开发阶段时大都使用 i99 (指数)或i888 (连续合约,前复权平滑)
在实盘时我们是不是应该继续使用i99或i888计算指标,而交易的是实际的标的如 i2201
因为发现有个问题,主力刚换月时,如果用主力的合约的数据去计算指标
得到指标有点奇怪,因为新主力之前的交易量太小,走势也不明显
欢迎发表意见~
例如
def on_init(self):
"""
Callback when strategy is inited.
"""
self.write_log("策略初始化")
self.load_bar(10)
会在数据回放时刚好漏掉达到10时的数据
例如:
2010-01-14 14:58:00+08:00
2010-01-14 14:59:00+08:00
2021-07-31 22:35:04.157915 策略初始化完成
2021-07-31 22:35:04.159658 开始回放历史数据
[这里缺少了2010-01-15 09:00:00+08:00]
2010-01-15 09:01:00+08:00
源码:backtesting_data少取了刚好达到阈值时的数据
# Use the rest of history data for running backtesting
backtesting_data = self.history_data[ix + 1:]
if not backtesting_data:
self.output("历史数据不足,回测终止")
return
没有找到 load_ticks函数
from datetime import datetime, time
from config.common import log
from config.const import WEEK_DICT
DAY_START = time(8, 45)
DAY_END = time(15, 0)
NIGHT_START = time(20, 45)
NIGHT_END = time(2, 45)
WEEKEND_START = 6
today = datetime.now().weekday() + 1
def is_trading_time():
""""""
current_time = datetime.now().time()
trading = False
if (today < WEEKEND_START):
if (
(current_time >= DAY_START and current_time <= DAY_END)
or (current_time >= NIGHT_START)
or (current_time <= NIGHT_END)
):
trading = True
time = current_time.strftime("%H:%M:%S")
log.info("time:" + time + ' trading:' + str(trading))
return trading
def run_child():
"""
Running in the child process.
"""
SETTINGS["log.file"] = True
SETTINGS.update()
event_engine = EventEngine()
main_engine = MainEngine(event_engine)
main_engine.add_gateway(CtpGateway)
cta_engine = main_engine.add_app(CtaStrategyApp)
main_engine.write_log("主引擎创建成功")
log_engine = main_engine.get_engine("log")
event_engine.register(EVENT_CTA_LOG, log_engine.process_log_event)
main_engine.write_log("注册日志事件监听")
main_engine.connect(ctp_setting, "CTP")
main_engine.write_log("连接CTP接口")
sleep(10)
cta_engine.init_engine()
main_engine.write_log("CTA策略初始化完成")
cta_engine.init_all_strategies()
sleep(60) # Leave enough time to complete strategy initialization
main_engine.write_log("CTA策略全部初始化")
cta_engine.start_all_strategies()
main_engine.write_log("CTA策略全部启动")
while True:
sleep(60)
trading = time_util.is_trading_time()
if not trading:
cta_engine.stop_all_strategies()
log.info("关闭子进程")
sleep(60)
main_engine.close()
sleep(30)
sys.exit(0)
if name == "main":
run_child()
/Users/fourdirections_vincenttang/opt/miniconda3/lib/python3.7/site-packages/vnpy/app/cta_strategy/backtesting.py:472: RuntimeWarning: divide by zero encountered in double_scalars
return_drawdown_ratio = -total_return / max_ddpercent
遇到从由 tick 数据合成 分钟 bar 时,bar 数据没有成功取得最高价的问题
从 vnpy.trader.utility.py 中提取的代码
if new_minute:
self.bar = BarData(
symbol=tick.symbol,
exchange=tick.exchange,
interval=Interval.MINUTE,
datetime=tick.datetime,
gateway_name=tick.gateway_name,
open_price=tick.last_price,
high_price=tick.last_price,
low_price=tick.last_price,
close_price=tick.last_price,
open_interest=tick.open_interest
)
else:
self.bar.high_price = max(self.bar.high_price, tick.last_price)
if tick.high_price > self.last_tick.high_price:
self.bar.high_price = max(self.bar.high_price, tick.high_price)
self.bar.low_price = min(self.bar.low_price, tick.last_price)
if tick.low_price < self.last_tick.low_price:
self.bar.low_price = min(self.bar.low_price, tick.low_price)
self.bar.close_price = tick.last_price
self.bar.open_interest = tick.open_interest
self.bar.datetime = tick.datetime
按当前算法,当第一个 tick的 last_price < high_price ,并且该high_price是一分钟内最大值时,
合成的分钟 bar 的 high_price 将会小于 该一分钟内实际的最大值
同理,当第一个 tick 的 last_price < low_price ,并且该low_price是一分钟内的最小值时,
合成的分钟 bar 的 low_price 将会大于该一分钟内实际的最小值
自带的DualThrustStrategy
使用
if last_bar.datetime.date() != bar.datetime.date():
作为新的一天的 k 线的判断条件,是否和平常使用的不一样
平时新的一天的 k 线都是以从夜盘开始的
尝试把CincoStrategy 用 tick 回测
on_tick 没有回调
反而 on_bar 有回调,但传过来的是DbTickData的对象
回测异常:
AttributeError: 'DbTickData' object has no attribute 'close_price'
回测程序如果下:
def run_backtesting(strategy_class, setting, vt_symbol, interval, start, end, rate, slippage, size, pricetick, capital):
engine = BacktestingEngine()
engine.set_parameters(
vt_symbol=vt_symbol,
interval=interval,
start=start,
end=end,
rate=rate,
slippage=slippage,
size=size,
pricetick=pricetick,
mode=BacktestingMode.TICK,
capital=capital
)
engine.add_strategy(strategy_class, setting)
engine.load_data()
engine.run_backtesting()
df = engine.calculate_result()
engine.calculate_statistics()
engine.show_chart()
return df
if name == "main":
df = run_backtesting(
strategy_class=CincoStrategy,
setting={},
vt_symbol="IF1912.CFFEX",
interval=Interval.TICK,
start=datetime(2019, 11, 25),
end=datetime(2019, 11, 26),
rate=0.3 / 10000,
slippage=0.2,
size=300,
pricetick=0.2,
capital=1_000_000,
)
组合策略怎么实现停止单
portfolio_strategy 包的 template 没有提供 stop 参数选项
请问simnow的结果有多大的参考价值
看FAQ时看到:停止单到触发价时会以市价发单以实现最大的可能成交
问题一:
停止单有提供非市价发单的接口吗? 还是要自己实现
问题二:
比如从rqdata 使用1m 的数据回测
回测时使用停止单,这是不是和实盘情况有较大的差别,
回测时会以开盘成交对吧,实盘时会在k线内触发
simonw 行情中断过,然后的逻辑的就乱了(似乎bar的时间与pos的值都不对)
策略是此前分享的R_Breaker ,在update_bar 会调用cancel_all
问题
1:过程中连续开了两个1手多单,
2:有委托没找到
3:在最后收盘时也没有平仓
这些问题在实盘中可能遇到吗
self.tend_high, self.tend_low = am.donchian(self.donchian_window)
if bar.datetime.time() < self.exit_time:
if self.pos == 0:
self.intra_trade_low = bar.low_price
self.intra_trade_high = bar.high_price
if self.tend_high > self.sell_setup:
long_entry = max(self.buy_break, self.day_high)
self.buy(long_entry, self.fixed_size, stop=True)
self.short(self.sell_enter, self.multiplier * self.fixed_size, stop=True)
elif self.tend_low < self.buy_setup:
short_entry = min(self.sell_break, self.day_low)
self.short(short_entry, self.fixed_size, stop=True)
self.buy(self.buy_enter, self.multiplier * self.fixed_size, stop=True)
elif self.pos > 0:
self.intra_trade_high = max(self.intra_trade_high, bar.high_price)
long_stop = self.intra_trade_high * (1 - self.trailing_long / 100)
self.sell(long_stop, abs(self.pos), stop=True)
elif self.pos < 0:
self.intra_trade_low = min(self.intra_trade_low, bar.low_price)
short_stop = self.intra_trade_low * (1 + self.trailing_short / 100)
self.cover(short_stop, abs(self.pos), stop=True)
else:
if self.pos > 0:
self.sell(bar.close_price 0.99, abs(self.pos))
elif self.pos < 0:
self.cover(bar.close_price 1.01, abs(self.pos))