1Backtrader来了

#!/usr/bin/env python

coding: utf-8

In[1]:

import backtrader as bt
import pandas as pd
import numpy as np
import datetime
from copy import deepcopy

# 小试一下

In[2]:

实例化 cerebro

cerebro = bt.Cerebro()

打印初始资金

print(‘Starting Portfolio Value: %.2f’ % cerebro.broker.getvalue())

启动回测

cerebro.run()

打印回测完成后的资金

print(‘Final Portfolio Value: %.2f’ % cerebro.broker.getvalue())

# 一、数据准备

## 1.1 读取日度行情表

表内字段就是 Backtrader 默认情况下要求输入的 7 个字段: ‘datetime’ 、‘open’、‘high’、‘low’、‘close’、‘volume’、‘openinterest’,外加一个 ‘sec_code’ 股票代码字段。

In[3]:

daily_price = pd.read_csv(“./data/daily_price.csv”, parse_dates=[‘datetime’])
daily_price

In[4]:

daily_price.query(“sec_code==‘600466.SH’”)

In[5]:

以 datetime 为 index,类型为 datetime 或 date 类型,Datafeeds 默认情况下是将 index 匹配给 datetime 字段;

daily_price = daily_price.set_index([‘datetime’])

## 1.2 读取调仓信息表

表内数据说明:

+ trade_date: 调仓期(每月最后一个交易日);

+ sec_code:持仓成分股;

+ weight:持仓权重。

In[6]:

trade_info = pd.read_csv(“./data/trade_info.csv”, parse_dates=[‘trade_date’])
trade_info

# 二、 选股回测

选股策略:定期按持仓权重调仓 。

In[7]:

回测策略

class TestStrategy(bt.Strategy):
params = (
(‘buy_stocks’, None), # 传入各个调仓日的股票列表和相应的权重
)
def log(self, txt, dt=None):
‘’’ Logging function fot this strategy’‘’
dt = dt or self.datas[0].datetime.date(0)
print(‘{}, {}’.format(dt.isoformat(), txt))

def __init__(self):
     # 读取调仓日期,即每月的最后一个交易日,回测时,会在这一天下单,然后在下一个交易日,以开盘价买入
    self.trade_dates = pd.to_datetime(self.p.buy_stocks['trade_date'].unique()).tolist()
    self.buy_stock = self.p.buy_stocks # 保留调仓信息
    self.order_list = []  # 记录以往订单,在调仓日要全部取消未成交的订单
    self.buy_stocks_pre = [] # 记录上一期持仓

def next(self):
    # 获取当前的回测时间点
    dt = self.datas[0].datetime.date(0)
    # 打印当前时刻的总资产
    self.log('当前总资产 %.2f' %(self.broker.getvalue()))
    # 如果是调仓日,则进行调仓操作
    if dt in self.trade_dates:
        print("--------------{} 为调仓日----------".format(dt))
        #取消之前所下的没成交也未到期的订单
        if len(self.order_list) > 0:
            print("--------------- 撤销未完成的订单 -----------------")
            for od in self.order_list:
                # 如果订单未完成,则撤销订单
                self.cancel(od) 
             #重置订单列表
            self.order_list = [] 
            
        # 提取当前调仓日的持仓列表
        buy_stocks_data = self.buy_stock.query(f"trade_date=='{dt}'")
        long_list = buy_stocks_data['sec_code'].tolist()
        print('long_list', long_list)  # 打印持仓列表
        
        # 对现有持仓中,调仓后不再继续持有的股票进行卖出平仓
        sell_stock = [i for i in self.buy_stocks_pre if i not in long_list]
        print('sell_stock', sell_stock)
        if len(sell_stock) > 0:
            print("-----------对不再持有的股票进行平仓--------------")
            for stock in sell_stock:
                data = self.getdatabyname(stock)
                if self.getposition(data).size > 0 :
                    od = self.close(data=data)  
                    self.order_list.append(od) # 记录卖出订单

        # 买入此次调仓的股票:多退少补原则
        print("-----------买入此次调仓期的股票--------------")
        for stock in long_list:
            w = buy_stocks_data.query(f"sec_code=='{stock}'")['weight'].iloc[0] # 提取持仓权重
            data = self.getdatabyname(stock)
            order = self.order_target_percent(data=data, target=w*0.95) # 为减少可用资金不足的情况,留 5% 的现金做备用
            self.order_list.append(order)
            
        self.buy_stocks_pre = long_list  # 保存此次调仓的股票列表
    
#订单日志    
def notify_order(self, order):
    # 未被处理的订单
    if order.status in [order.Submitted, order.Accepted]:
        return
    # 已被处理的订单
    if order.status in [order.Completed, order.Canceled, order.Margin]:
        if order.isbuy():
            self.log(
                'BUY EXECUTED, ref:%.0f,Price: %.2f, Cost: %.2f, Comm %.2f, Size: %.2f, Stock: %s' %
                (order.ref,
                 order.executed.price,
                 order.executed.value,
                 order.executed.comm,
                 order.executed.size,
                 order.data._name))
        else:  # Sell
            self.log('SELL EXECUTED, ref:%.0f, Price: %.2f, Cost: %.2f, Comm %.2f, Size: %.2f, Stock: %s' %
                    (order.ref,
                     order.executed.price,
                     order.executed.value,
                     order.executed.comm,
                     order.executed.size,
                     order.data._name))

In[8]:

实例化大脑

cerebro_ = bt.Cerebro()

按股票代码,依次循环传入数据

for stock in daily_price[‘sec_code’].unique():
# 日期对齐
data = pd.DataFrame(index=daily_price.index.unique())
df = daily_price.query(f"sec_code==‘{stock}’“)[[‘open’,‘high’,‘low’,‘close’,‘volume’,‘openinterest’]]
data_ = pd.merge(data, df, left_index=True, right_index=True, how=‘left’)
data_.loc[:,[‘volume’,‘openinterest’]] = data_.loc[:,[‘volume’,‘openinterest’]].fillna(0)
data_.loc[:,[‘open’,‘high’,‘low’,‘close’]] = data_.loc[:,[‘open’,‘high’,‘low’,‘close’]].fillna(method=‘pad’)
data_.loc[:,[‘open’,‘high’,‘low’,‘close’]] = data_.loc[:,[‘open’,‘high’,‘low’,‘close’]].fillna(0)
datafeed = bt.feeds.PandasData(dataname=data_, fromdate=datetime.datetime(2019,1,2), todate=datetime.datetime(2021,1,28))
cerebro_.adddata(datafeed, name=stock)
print(f”{stock} Done !")

In[9]:

cerebro = deepcopy(cerebro_) # 深度复制已经导入数据的 cerebro_,避免重复导入数据

初始资金 100,000,000

cerebro.broker.setcash(100000000.0)

佣金,双边各 0.0003

cerebro.broker.setcommission(commission=0.0003)

滑点:双边各 0.0001

cerebro.broker.set_slippage_perc(perc=0.0001)

添加策略

cerebro.addstrategy(TestStrategy, buy_stocks=trade_info) # 通过修改参数 buy_stocks ,使用同一策略回测不同的持仓列表

添加分析器

cerebro.addanalyzer(bt.analyzers.TimeReturn, _name=‘pnl’) # 返回收益率时序数据
cerebro.addanalyzer(bt.analyzers.AnnualReturn, _name=‘_AnnualReturn’)
cerebro.addanalyzer(bt.analyzers.SharpeRatio, riskfreerate=0.003, annualize=True, _name=‘_SharpeRatio’)
cerebro.addanalyzer(bt.analyzers.DrawDown, _name=‘_DrawDown’)

添加观测器

cerebro.addobserver(bt.observers.Value) # 查看账户资产变动

启动回测

result = cerebro.run()

In[10]:

strat = result[0]
print(“--------------- AnnualReturn -----------------”)
print(strat.analyzers._AnnualReturn.get_analysis())
print(“--------------- SharpeRatio -----------------”)
print(strat.analyzers._SharpeRatio.get_analysis())
print(“--------------- DrawDown -----------------”)
print(strat.analyzers._DrawDown.get_analysis())

In[ ]:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值