WhaleQuant第七章——量化回测

第七章

7.0 学习资源

学习链接

  1. pandas计算策略评估指标:
    本节介绍了如何使用Python的pandas库来计算量化策略的关键评估指标,如年化收益率、波动率和最大回撤。这些指标对于全面评估策略的表现至关重要。

  2. 聚宽平台量化回测实践:
    介绍了聚宽(JoinQuant)平台,这是一个提供量化交易工具和服务的平台。本节通过一个具体的双均线策略示例,展示了如何在聚宽平台上实现策略并进行回测,以及如何分析回测结果。

  3. Backtrader量化回测框架实践:
    Backtrader是一个基于Python的开源量化回测框架。本节提供了一个双均线策略的Backtrader实现,并详细描述了如何在该框架中添加数据、设置策略、运行回测并分析结果。

  4. BigQuant量化框架实战:
    BigQuant是一个人工智能量化投资平台。本节介绍了如何在BigQuant上实现双均线策略,包括策略的初始化、数据处理、交易逻辑和回测结果的分析。

  5. 手写回测代码 - 手把手实现一个傻瓜式量化回测框架:
    本节介绍了一个简化的量化回测框架,称为“傻瓜式量化回测框架”。这个框架通过纯函数模式简化了策略的编写和回测过程,使得策略开发更加直观和便捷。同时,介绍了如何使用贝叶斯超参数优化方法来优化策略参数。

7.1 pandas计算策略评估指标

读取数据

import pandas as pd  
import numpy as np
import matplotlib.pyplot as plt
import tushare as ts
%matplotlib inline   

# 无视warning
import warnings
warnings.filterwarnings("ignore")

# 正常显示画图时出现的中文和负号
from pylab import mpl
mpl.rcParams['font.sans-serif']=['SimHei']
mpl.rcParams['axes.unicode_minus']=False

#起始和结束日期可以自行输入,否则使用默认
def get_data(code, start_date, end_date):
    # 配置 tushare token
    my_token = '039660346bc8f4ad98c1b7b28fbbbb7750a6c6eea62573ff80a42593'
    pro = ts.pro_api(my_token)

    df = pro.daily(ts_code=code, start_date=start_date, end_date=end_date)
    df.index = pd.to_datetime(df.trade_date)

    return df.close

#以上证综指、贵州茅台、工商银行、中国平安为例
stocks={
    '600519.SH':'贵州茅台',
    '601398.SH':'工商银行',
    '601318.SH':'中国平安'

}

df = pd.DataFrame()
for code,name in stocks.items():
    df[name] = get_data(code, '20180101', '20221231')

# 按照日期正序
df = df.sort_index()

# 本地读入沪深300合并
df_base = pd.read_csv('./000300.XSHG_2018_2022.csv')
df_base.index = pd.to_datetime(df_base.trade_date)
df['沪深300'] = df_base['close']

绘制净值曲线

# 以第一交易日2018年1月1日收盘价为基点,计算净值并绘制净值曲线
df_worth = df / df.iloc[0]
df_worth.plot(figsize=(15,6))
plt.title('股价净值走势', fontsize=10)
plt.xticks(pd.date_range('20180101','20221231',freq='Y'),fontsize=10)
plt.show()

在这里插入图片描述
即如果从2018年持有贵州茅台,在2021年卖出,有350%的收益。

在这里插入图片描述

累计收益率

# 区间累计收益率(绝对收益率)
total_return = df_worth.iloc[-1]-1
total_return = pd.DataFrame(total_return.values,columns=['累计收益率'],index=total_return.index)
total_return

在这里插入图片描述

年化收益率

# 年化收益率
annual_return = pd.DataFrame((1 + total_return.values) ** (252 / 1826) - 1,columns=['年化收益率'],index=total_return.index)
annual_return

在这里插入图片描述
波动率

df_return = df / df.shift(1) - 1
df_return = ((df_return.iloc[1:] - df_return.mean()) ** 2)

volatility = pd.DataFrame(np.sqrt(df_return.sum() * 252 / (1826-1)),columns=['波动率'],index=total_return.index)
volatility

在这里插入图片描述
最大回测

def max_drawdown_cal(df):
    md = ((df.cummax() - df)/df.cummax()).max()
    return round(md, 4)

max_drawdown = {}

stocks={
    '600519.SH':'贵州茅台',
    '601398.SH':'工商银行',
    '601318.SH':'中国平安',
    '000300.XSHG': '沪深300'
}

for code,name in stocks.items():
    max_drawdown[name]=max_drawdown_cal(df[name])

max_drawdown = pd.DataFrame(max_drawdown,index=['最大回撤']).T
max_drawdown

在这里插入图片描述
Alpha系数和Beta系数

from scipy import stats

#计算每日收益率 收盘价缺失值(停牌),使用前值代替
rets=(df.iloc[:,:4].fillna(method='pad')).apply(lambda x:x/x.shift(1)-1)[1:]

#市场指数为x,个股收益率为y
x = rets.iloc[:,3].values
y = rets.iloc[:,:3].values
capm = pd.DataFrame()
alpha = []
beta = []
for i in range(3):
    b, a, r_value, p_value, std_err=stats.linregress(x,y[:,i])
    #alpha转化为年化
    alpha.append(round(a*250,3))
    beta.append(round(b,3))
    
capm['alpha']=alpha
capm['beta']=beta
capm.index=rets.columns[:3]
#输出结果:
capm

在这里插入图片描述
夏普比率

  • 夏普比率(sharpe ratio)表示每承受一单位总风险,会产生多少的超额报酬,该比率越高越好。
# 超额收益率以无风险收益率为基准 假设无风险收益率为年化3%
ex_return=rets - 0.03/250

# 计算夏普比率
sharpe_ratio=np.sqrt(len(ex_return))*ex_return.mean()/ex_return.std()
sharpe_ratio=pd.DataFrame(sharpe_ratio,columns=['夏普比率'])
sharpe_ratio

在这里插入图片描述

信息比率

  • 信息比率含义与夏普比率类似,只不过其参照基准不是无风险收益率,而是策略的市场基准收益率。
###信息比率
ex_return = pd.DataFrame() 
ex_return['贵州茅台']=rets.iloc[:,0]-rets.iloc[:,3]
ex_return['工商银行']=rets.iloc[:,1]-rets.iloc[:,3]
ex_return['中国平安']=rets.iloc[:,2]-rets.iloc[:,3]

#计算信息比率
information_ratio = np.sqrt(len(ex_return))*ex_return.mean()/ex_return.std()
#信息比率的输出结果
information_ratio = pd.DataFrame(information_ratio,columns=['信息比率'])
information_ratio

在这里插入图片描述

7.2聚宽实测

7.3Backtrader平台量化回测实践

  • 需要申请tushare平台的免费token
    链接

在Backtrader实现一个双均线策略(具体参照ch05择时策略),并且在该平台上进行回测, 来测试整体收益率。

  • 选择标的为:002594.XSHE 比亚迪
  • 选择基准为:000300.XSHG 沪深300
  • 策略为:当5日线金叉10日线,全仓买入;当5日线死叉10日线全仓卖出。
# 导入函数库
from __future__ import (absolute_import, division, print_function, unicode_literals) 
import datetime
import pymysql
import pandas as pd
import backtrader as bt
import tushare as ts
import numpy as np


# 数据获取(从Tushare中获取数据)
""" 
数据获取一般都是通过连接数据库从数据库中读取,对于不了解数据库来源的新手可以从Tushare中直接获取数据
"""
def get_data(stock_code):
    """
    stock_code:股票代码,类型: str
    return: 股票日线数据,类型: DataFrame
    """
    token = 'Tushare token'   # 可通过进入个人主页-接口TOKEN获得

    ts.set_token(token)
    pro = ts.pro_api(token)

    data_daily = pro.daily(ts_code = stock_code, start_date='20180101', end_date='20230101')
    data_daily['trade_date'] = pd.to_datetime(data_daily['trade_date'])
    data_daily = data_daily.rename(columns={'vol': 'volume'})
    data_daily.set_index('trade_date', inplace=True) 
    data_daily = data_daily.sort_index(ascending=True)
    dataframe = data_daily
    data_daily['openinterest'] = 0
    dataframe['openinterest'] = 0
    data = bt.feeds.PandasData(dataname=dataframe,
                               fromdate=datetime.datetime(2018, 1, 1),
                               todate=datetime.datetime(2023, 1, 1)
                               )

    return data


# 双均线策略实现
class DoubleAverages(bt.Strategy):

    # 设置均线周期
    params = (
        ('period_data5', 5),
        ('period_data10', 10)
    )

    # 日志输出
    def log(self, txt, dt=None):
        dt = dt or self.datas[0].datetime.date(0)
        print('%s, %s' % (dt.isoformat(), txt))

    def __init__(self):
        # 初始化数据参数

        self.dataclose = self.datas[0].close   # 定义变量dataclose,保存收盘价
        self.order = None   # 定义变量order,用于保存订单
        self.buycomm = None    # 定义变量buycomm,记录订单佣金
        self.buyprice = None    # 定义变量buyprice,记录订单价格

        self.sma5 = bt.indicators.SimpleMovingAverage(self.datas[0], period=self.params.period_data5)  # 计算5日均线
        self.sma10 = bt.indicators.SimpleMovingAverage(self.datas[0], period=self.params.period_data10)  # 计算10日均线

    def notify_order(self, order):
        if order.status in [order.Submitted, order.Accepted]:  # 若订单提交或者已经接受则返回
            return

        if order.status in [order.Completed]:
            if order.isbuy():
                self.log(
                    'Buy Executed, Price: %.2f, Cost: %.2f, Comm: %.2f' %
                    (order.executed.price, order.executed.value, order.executed.comm))

                self.buyprice = order.executed.price
                self.buycomm = order.executed.comm
            else:
                self.log('Sell Executed, Price: %.2f, Cost: %.2f, Comm: %.2f' %
                        (order.executed.price, order.executed.value, order.executed.comm))
            self.bar_executed = len(self)
        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            self.log('Order Canceled/Margin/Rejected')

        self.order = None


    def notify_trade(self, trade):
        if not trade.isclosed:   # 若交易未关闭则返回
            return
        self.log('Operation Profit, Total_Profit %.2f, Net_Profit: %.2f' %
                (trade.pnl, trade.pnlcomm))    # pnl表示盈利, pnlcomm表示手续费

    def next(self):   # 双均线策略逻辑实现
        self.log('Close: %.2f' % self.dataclose[0])   # 打印收盘价格

        if self.order:   # 检查是否有订单发送
            return

        if not self.position:   # 检查是否有仓位
            if self.sma5[0] > self.sma10[0]:
                self.log('Buy: %.2f' % self.dataclose[0])
                self.order = self.buy()

        else:
            if self.sma5[0] < self.sma10[0]:
                self.log('Sell: %.2f' % self.dataclose[0])
                self.order = self.sell()





if __name__ == '__main__':
    cerebro = bt.Cerebro()   # 创建策略容器
    cerebro.addstrategy(DoubleAverages)    # 添加双均线策略
    data = get_data('000001.SZ')
    cerebro.adddata(data)   # 添加数据
    cerebro.broker.setcash(10000.0)   # 设置资金
    cerebro.addsizer(bt.sizers.FixedSize, stake=100)   # 设置每笔交易的股票数量
    cerebro.broker.setcommission(commission=0.01)   # 设置手续费
    print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())   # 打印初始资金
    cerebro.run()   # 运行策略
    print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())   # 打印最终资金
    cerebro.plot()

在这里插入图片描述

  • 10000块只剩7847了,还亏钱了。 在这里插入图片描述
  • 修改买股数量200股,亏更多:
    在这里插入图片描述

7.4 BigQuant量化框架实战

BigQuant官网

7.5 手写回测代码 - 手把手实现一个傻瓜式量化回测框架

引用一个StupidHead.py文件

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值