backtrader:基于信号的策略开发及参数优化

原创文章第73篇,专注“个人成长与财富自由、世界运作的逻辑, AI量化投资”。

今天继续讲backtrader的交易。

bt在易用性上确实是下足了功夫,我们先来看一下“极简”的策略开发。

01 “基于信号的策略”

它不需要写strategy。

直接定义信号即可,信号同自定义指标一样,比如多头信号是 close>sma(30),退出信号是sma5<sma30。

我们只需要给大脑添加这两个信号:

# 添加交易信号1
cerebro.add_signal(bt.SIGNAL_LONG, MySignal, period=30)
# 添加交易信号2
cerebro.add_signal(bt.SIGNAL_LONGEXIT, SMAExitSignal, p1=5, p2=30)

注意这里信号的逻辑:

signal指标取值大于0 → 对应多头 long 信号;

signal指标取值小于0 → 对应空头 short 信号;

signal指标取值等于0 → 不发出指令;

bt.SIGNAL_LONGSHORT:

多头信号和空头信号都会作为开仓信号;

对于多头信号,如果之前有空头仓位,会先对空仓进行平仓close,再开多仓;

空头信号也类似,会在开空仓前对多仓进行平仓close。

bt.SIGNAL_LONG:

多头信号用于做多,空头信号用于平仓close;

如果系统中同时存在LONGEXIT 信号类型,SIGNAL_LONG 中的空头信号将不起作用,将会使用 LONGEXIT中的空头信号来平仓多头,如上面的多条交易信号的例子。

bt.SIGNAL_SHORT:

空头信号用于做空,多头信号用于平仓;如果系统中同时存在SHORTEXIT 信号类型,SIGNAL_SHORT 中的多头信号将不起作用,将会使用 SHORTEXIT中的多头信号来平仓空头。

import backtrader as bt


# 自定义信号指标
class MySignal(bt.Indicator):
    lines = ('signal',) # 声明 signal 线,交易信号放在 signal line 上
    params = (('period', 30),)

    def __init__(self):
        self.lines.signal = self.data - bt.indicators.SMA(period=self.p.period)


# 定义交易信号1
class SMACloseSignal(bt.Indicator):
    lines = ('signal',)
    params = (('period', 30),)

    def __init__(self):
        self.lines.signal = self.data - bt.indicators.SMA(period=self.p.period)


# 定义交易信号2
class SMAExitSignal(bt.Indicator):
    lines = ('signal',)
    params = (('p1', 5), ('p2', 30),)

    def __init__(self):
        sma1 = bt.indicators.SMA(period=self.p.p1)
        sma2 = bt.indicators.SMA(period=self.p.p2)
        self.lines.signal = sma1 - sma2


# 实例化大脑
cerebro = bt.Cerebro()

import pandas as pd
def load_data(name):
    df = pd.read_csv('../data/csv/{}.csv'.format(name))
    df['date'] = df['date'].apply(lambda x: str(x))
    df.set_index('date', inplace=True)
    df.sort_index(ascending=True, inplace=True)
    df.dropna(inplace=True)
    return df


df = load_data('000300.SH')
#df_spx = load_data('spx')
def to_backtrader_dataframe(df):
    df.index = pd.to_datetime(df.index)
    df['openinterest'] = 0
    if 'amount' not in df.columns:
        df['amount'] = 0
    df = df[['open', 'high', 'low', 'close', 'volume','amount', 'openinterest']]
    return df


df = to_backtrader_dataframe(df)
# 加载数据
cerebro.adddata(bt.feeds.PandasData(dataname=df))
# 添加交易信号1
cerebro.add_signal(bt.SIGNAL_LONG, MySignal, period=30)
# 添加交易信号2
cerebro.add_signal(bt.SIGNAL_LONGEXIT, SMAExitSignal, p1=5, p2=30)

# 添加分析指标
# 返回年初至年末的年度收益率
cerebro.addanalyzer(bt.analyzers.AnnualReturn, _name='_AnnualReturn')
# 计算最大回撤相关指标
cerebro.addanalyzer(bt.analyzers.DrawDown, _name='_DrawDown')
# 计算年化收益:日度收益
cerebro.addanalyzer(bt.analyzers.Returns, _name='_Returns', tann=252)
# 计算年化夏普比率:日度收益
cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='_SharpeRatio', timeframe=bt.TimeFrame.Days, annualize=True, riskfreerate=0) # 计算夏普比率
cerebro.addanalyzer(bt.analyzers.SharpeRatio_A, _name='_SharpeRatio_A')
# 返回收益率时序
cerebro.addanalyzer(bt.analyzers.TimeReturn, _name='_TimeReturn')

result = cerebro.run()

# 提取结果
print("--------------- AnnualReturn -----------------")
print(result[0].analyzers._AnnualReturn.get_analysis())
print("--------------- DrawDown -----------------")
print(result[0].analyzers._DrawDown.get_analysis())
print("--------------- Returns -----------------")
print(result[0].analyzers._Returns.get_analysis())
print("--------------- SharpeRatio -----------------")
print(result[0].analyzers._SharpeRatio.get_analysis())
print("--------------- SharpeRatio_A -----------------")
print(result[0].analyzers._SharpeRatio_A.get_analysis())

02  策略分析器

策略结果怎么样?风险、收益如何评价。

bt框架里专门负责回测收益评价指标计算的模块analyzers,大家可以将其称为“策略分析器”。

第一步:通过addanalyzer(ancls, _name, *args, **kwargs) 方法将分析器添加给大脑,ancls 对应内置的分析器类 ;

第二步:分别基于results = cerebro.run() 返回的各个对象results[x],提取该对象analyzers属性下的各个分析器的计算结果,并通过get_analysis() 来获取具体值。

由于结果是保存在OrderedDict里,需要显示的话,可以自行根据需要的字段提取。

03  自定义分析器

Backtrader支持继承bt.Analyzer,然后实现自己的分析器。

最基础只需要实现stop和get_analysia两个函数即可。

# 官方提供的 SharpeRatio 例子
class SharpeRatio(Analyzer):
    params = (('timeframe', TimeFrame.Years), ('riskfreerate', 0.01),)

    def __init__(self):
        super(SharpeRatio, self).__init__()
        self.anret = AnnualReturn()

    def start(self):
        # Not needed ... but could be used
        pass

    def next(self):
        # Not needed ... but could be used
        pass

    def stop(self):
        retfree = [self.p.riskfreerate] * len(self.anret.rets)
        retavg = average(list(map(operator.sub, self.anret.rets, retfree)))
        retdev = standarddev(self.anret.rets)
        self.ratio = retavg / retdev

    def get_analysis(self):
        return dict(sharperatio=self.ratio)

04 优化参数

把add_strategy改为optstrategy,即可以对参数区间进行批量回测:

# 添加优化器
cerebro1.optstrategy(TestStrategy, period1=range(5, 25, 5), period2=range(10, 41, 10))

# 添加分析指标
# 返回年初至年末的年度收益率
cerebro1.addanalyzer(bt.analyzers.AnnualReturn, _name='_AnnualReturn')
# 计算最大回撤相关指标
cerebro1.addanalyzer(bt.analyzers.DrawDown, _name='_DrawDown')
# 计算年化收益
cerebro1.addanalyzer(bt.analyzers.Returns, _name='_Returns', tann=252)
# 计算年化夏普比率
cerebro1.addanalyzer(bt.analyzers.SharpeRatio_A, _name='_SharpeRatio_A')
# 返回收益率时序
cerebro1.addanalyzer(bt.analyzers.TimeReturn, _name='_TimeReturn')

# 启动回测
result = cerebro1.run()


# 打印结果
def get_my_analyzer(result):
    analyzer = {}
    # 返回参数
    analyzer['period1'] = result.params.period1
    analyzer['period2'] = result.params.period2
    # 提取年化收益
    analyzer['年化收益率'] = result.analyzers._Returns.get_analysis()['rnorm']
    analyzer['年化收益率(%)'] = result.analyzers._Returns.get_analysis()['rnorm100']
    # 提取最大回撤(习惯用负的做大回撤,所以加了负号)
    analyzer['最大回撤(%)'] = result.analyzers._DrawDown.get_analysis()['max']['drawdown'] * (-1)
    # 提取夏普比率
    analyzer['年化夏普比率'] = result.analyzers._SharpeRatio_A.get_analysis()['sharperatio']

    return analyzer


ret = []
for i in result:
    ret.append(get_my_analyzer(i[0]))

pd.DataFrame(ret)

小结:

到此为止,backtrader框架用于量化回测的命题都覆盖了。

剩下的就看你的策略逻辑了。

看到华中科技大学副研究员郇真在顶级数学刊物上发表天才作品,而她的背景,确称不上神童。有人总结道:想做好一件事:

1、中等智商。——这个大部分人都有。

2、正确的方向和方法。——选择大于努力,但其实用心点也不难找到,而且很多事情,是你做了才对的,而不是反过来。

3、持续努力。——这个时代最缺的是这个。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
backtrader是一个功能强大的开源框架,可以用于构建和测试量化交易策略。它提供了参数优化的功能,可以帮助改进策略的性能。下面是使用backtrader进行参数优化的步骤: 1. 定义策略类:首先,你需要定义一个继承自backtrader.Strategy的策略类。在这个类中,你可以定义策略的逻辑和需要优化参数。 2. 定义参数:在策略类中,你可以使用backtrader的Params类来定义需要优化参数。你可以指定参数的名称、取值范围和步长。 3. 创建Cerebro对象:接下来,你需要创建一个Cerebro对象,它是backtrader的主要组件,用于管理策略和执行回测。 4. 添加数据:在Cerebro对象中,你需要添加数据源。backtrader支持多种数据源,包括CSV文件、Pandas DataFrame、实时数据等。 5. 添加策略:在Cerebro对象中,你需要添加之前定义的策略类。 6. 添加参数优化使用Cerebro对象的addanalyzer方法,你可以添加参数优化的分析器。backtrader提供了多种分析器,包括参数优化分析器。 7. 运行回测:调用Cerebro对象的run方法,可以运行回测并进行参数优化。回测结果将包含每个参数组合的性能指标。 8. 获取最佳参数:通过分析回测结果,你可以获取最佳参数组合。backtrader提供了一些方法来帮助你分析和选择最佳参数。 下面是一个使用backtrader进行参数优化的示例代码: ```python import backtrader as bt class MyStrategy(bt.Strategy): params = ( ('param1', 10), ('param2', 20), ) def __init__(self): # 策略初始化逻辑 def next(self): # 策略每个时间步的逻辑 cerebro = bt.Cerebro() # 添加数据源 data = bt.feeds.YourDataFeed() cerebro.adddata(data) # 添加策略 cerebro.addstrategy(MyStrategy) # 添加参数优化分析器 cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe') # 运行回测 results = cerebro.run() # 获取最佳参数 best_params = results[0].analyzers.sharpe.get_analysis() print("Best params:", best_params) ``` 这是一个基本的示例,你可以根据自己的需求和策略进行修改和扩展。希望对你有帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI量化投资实验室

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值