whale-quant 学习 part7:量化回测

量化回测

  • 计算策略评估指标
  • 聚宽平台量化回测实践
    • 策略实现
  • 参考

计算策略评估指标

使用数据为:贵州茅台(600519.SH)、工商银行(601398.SH)、中国平安(601318.SH),策略基准是沪深300指数(000300.XSHG),策略采用最简单的方式:买入持有。持有周期为20180101 - 20221231,共1826个自然日。
数据获取

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 tokenmy_token = 'XXXXX'  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()
  • 年化收益率
    累计收益率
    R t = P r − P t P t P T 表示在期末资产的价格 P t 表示期初资产价格 R_t = \frac{P_r-P_t}{P_t} \\ P_T 表示在期末资产的价格 \\ P_t 表示期初资产价格 Rt=PtPrPtPT表示在期末资产的价格Pt表示期初资产价格
    年化收益率
    R p = ( 1 + R ) m n − 1 R_p = (1+R)^{\frac{m}{n}}-1 Rp=(1+R)nm1
    R表示期间总收益率,m是与n(可以是天数、周数、月数)相对应的计算周期,根据计算惯例,m=252、52、12分别指代日、周、月向年化的转换;n为期间自然日天数。(年化收益的一个直观的理解是,假设按照某种盈利能力,换算成一年的收益大概能有多少。这个概念常常会存在误导性,比如,这个月股票赚了5%,在随机波动的市场中,这是很正常的现象。如果据此号称年化收益为5%×12个月=60%,这就显得不太可信了,实际上每个月的收益不可能都这么稳定。所以在听到有人说年化收益的时候,需要特别留意一下具体的情况,否则很容易被误导。)
# 区间累计收益率(绝对收益率)
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
  • 波动率
    波动率是对收益变动的一种衡量,本质也是风险,波动率和风险,都是用来衡量收益率的不确定性的。我们用方差来表示,年波动率等于策略收益和无风险收益的标准差除以其均值,再除以交易日倒数的平方根,通常交易日取252天。
    V o l a t i l i t y = 252 n − 1 ∑ r i = 1 n ( r p − r p ^ ) 2 r p 表示策略每日收益率 r p ^ 表示策略每日收益率的平均值 n 表示策略执行天数 Volatility = \sqrt{\frac{252}{n-1}\sum^{n}_{ri=1}(r_p-\hat{r_p})^2} \\ r_p 表示策略每日收益率 \\ \hat{r_p} 表示策略每日收益率的平均值 \\ n表示策略执行天数 Volatility=n1252ri=1n(rprp^)2 rp表示策略每日收益率rp^表示策略每日收益率的平均值n表示策略执行天数
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
  • 最大撤回
    选定周期内任一历史时点往后推,于最低点时的收益率回撤幅度的最大值。最大回撤用来描述可能出现的最糟糕的情况。最大回撤是一个重要的风险指标,对于量化策略交易,该指标比波动率还重要。 P为某一天的净值,i为某一天,j为i后的某一天,Pi为第i天的产品净值,Pj则是Pi后面某一天的净值
    该资产的最大回撤计算如下:
    M a x D r a w d o w n = m a x ( P i − P j ) P i MaxDrawdown=\frac{max(P_i-P_j)}{P_i} MaxDrawdown=Pimax(PiPj)
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系数
  • Beta系数代表投资中的系统风险,而在投资中除了系统风险外还面临着市场波动无关的非系统性风险。 Alpha系数就代表投资中的非系统性风险,是投资者获得与市场波动无关的回报。
    可以使用资本资产定价模型(CAPM)来估计策略的beta和alpha值,CAPM模型为:
    E ( r i ) = r f + β ( E ( r m ) − r f ) E ( r i ) 表示投资组合的预期收益率 r f 表示无风险利率 r m 表示市场指数收益率 β 表示股市波动风险与投资机会中的结构性与系统性风险 C A P M 的计量模型可以表示为 r i = α + β r m + ϵ α ϵ α 表示随机扰动,即个体风险 E(r_i)=r_f+\beta(E(r_m)-r_f) \\ E(r_i) 表示投资组合的预期收益率 \\ r_f 表示无风险利率 \\ r_m 表示市场指数收益率 \\ \beta 表示股市波动风险与投资机会中的结构性与系统性风险 \\ CAPM的计量模型可以表示为 \\ r_i=\alpha+\beta r_m +\epsilon_{\alpha} \\ \epsilon_\alpha表示随机扰动,即个体风险 E(ri)=rf+β(E(rm)rf)E(ri)表示投资组合的预期收益率rf表示无风险利率rm表示市场指数收益率β表示股市波动风险与投资机会中的结构性与系统性风险CAPM的计量模型可以表示为ri=α+βrm+ϵαϵα表示随机扰动,即个体风险
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)表示每承受一单位总风险,会产生多少的超额报酬,该比率越高。夏普比率是在资本资产定价模型进一步发展得来的。
    S h a r p e R a t i o = R p − R f σ p R p 表示策略年化收益率 R F 表示无风险收益差 σ p 表示年化标准差 SharpeRatio=\frac{R_p-R_f}{\sigma_p} \\R_p 表示策略年化收益率 \\R_F 表示无风险收益差 \\ \sigma_p表示年化标准差 SharpeRatio=σpRpRfRp表示策略年化收益率RF表示无风险收益差σp表示年化标准差
# 超额收益率以无风险收益率为基准 假设无风险收益率为年化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
  • 信息比率
    信息比率含义与夏普比率类似,只不过其参照基准不是无风险收益率,而是策略的市场基准收益率。
    I n f o r m a t i o n R a t i o = R p − R f σ t R p 表示策略年化收益率 R F 表示无风险收益率 σ t 表示策略与基准每日收益率差值的年化标准差 InformationRatio=\frac{R_p-R_f}{\sigma_t} \\ R_p 表示策略年化收益率 \\ R_F表示无风险收益率 \\ \sigma_t 表示策略与基准每日收益率差值的年化标准差 InformationRatio=σtRpRfRp表示策略年化收益率RF表示无风险收益率σt表示策略与基准每日收益率差值的年化标准差
###信息比率
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

聚宽平台量化回测实践

聚宽(https://www.joinquant.com/) 成立于2015年5月,是一家量化交易平台,为投资者提供做量化交易的工具与服务,帮助投资者更好地做量化交易。 整体来看,聚宽具有以下几点优势

  1. 聚宽让做量化交易的成本极大降低
  2. 提供多种优质的便于取用的数据
  3. 提供投资研究功能,便于自由地统计、研究、学习等
  4. 提供多种的策略评价指标与评价维度
  5. 支持多种策略的编写、回测、模拟、实盘
  6. 具备丰富且活跃的量化社区,可以发帖、学习、比赛等。

策略实现

本部分将介绍如何在聚宽平台实现一个双均线策略(具体参照ch05择时策略),并且在聚宽平台上进行回测, 来测试整体收益率。

策略代码如下,核心点有:

  • 选择标的为:002594.XSHE 比亚迪
  • 选择基准为:000300.XSHG 沪深300
  • 策略为:当5日线金叉10日线,全仓买入;当5日线死叉10日线全仓卖出。
# 导入函数库
from jqdata import *# 初始化函数,设定基准等等
def initialize(context):# 设定沪深上证作为基准set_benchmark('000300.XSHG')# 开启动态复权模式(真实价格)set_option('use_real_price', True)# 输出内容到日志 log.info()log.info('初始函数开始运行且全局只运行一次')# 过滤掉order系列API产生的比error级别低的log# log.set_level('order', 'error')### 股票相关设定 #### 股票类每笔交易时的手续费是:买入时佣金万分之三,卖出时佣金万分之三加千分之一印花税, 每笔交易佣金最低扣5块钱set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')## 运行函数(reference_security为运行时间的参考标的;传入的标的只做种类区分,因此传入'000300.XSHG'或'510300.XSHG'是一样的)# 开盘前运行run_daily(before_market_open, time='before_open', reference_security='000300.XSHG')# 开盘时运行run_daily(market_open, time='open', reference_security='000300.XSHG')# 收盘后运行run_daily(after_market_close, time='after_close', reference_security='000300.XSHG')## 开盘前运行函数
def before_market_open(context):# 输出运行时间log.info('函数运行时间(before_market_open):'+str(context.current_dt.time()))# 给微信发送消息(添加模拟交易,并绑定微信生效)# send_message('美好的一天~')# 要操作的股票:比亚迪(g.为全局变量)g.security = '002594.XSHE'## 开盘时运行函数
def market_open(context):log.info('函数运行时间(market_open):'+str(context.current_dt.time()))security = g.security# 获取股票的收盘价close_data5 = get_bars(security, count=5, unit='1d', fields=['close'])close_data10 = get_bars(security, count=10, unit='1d', fields=['close'])# close_data20 = get_bars(security, count=20, unit='1d', fields=['close'])# 取得过去五天,十天的平均价格MA5 = close_data5['close'].mean()MA10 = close_data10['close'].mean()# 取得上一时间点价格#current_price = close_data['close'][-1]# 取得当前的现金cash = context.portfolio.available_cash# 五日均线上穿十日均线if (MA5 > MA10) and (cash > 0):# 记录这次买入log.info("5日线金叉10日线,买入 %s" % (security))# 用所有 cash 买入股票order_value(security, cash)# 五日均线跌破十日均线elif (MA5 < MA10) and context.portfolio.positions[security].closeable_amount > 0:# 记录这次卖出log.info("5日线死叉10日线, 卖出 %s" % (security))# 卖出所有股票,使这只股票的最终持有量为0for security in context.portfolio.positions.keys():order_target(security, 0)## 收盘后运行函数
def after_market_close(context):log.info(str('函数运行时间(after_market_close):'+str(context.current_dt.time())))#得到当天所有成交记录trades = get_trades()for _trade in trades.values():log.info('成交记录:'+str(_trade))log.info('一天结束')log.info('##############################################################')

在聚宽上回测策略结果如下,虽然策略整体具备较好的收益,但需要提示的是该策略并不稳定。

  1. 该策略带入了后验知识。因为我们大致知道2018-2020年左右比亚迪处于上涨周期,该周期内基本上五日线在10日线以上。
  2. 该策略会有很强的回撤。例如回测的后半段,该策略已经开始较大幅度回撤,因此需要结合其他策略来进行止盈止损。
  3. 该策略回测周期不够长。本策略仅回测了两年,并且处于较强周期内,因此不具备较强的回测意义。
    在这里插入图片描述

参考

Datawhale 202401 whale-quant 开源学习(更多内容Backtrader、BigQuant等量化实战 微信搜索“Datawhale”)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/664156.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

正则表达式与文本处理工具

目录 引言 一、正则表达式基础 &#xff08;一&#xff09;字符匹配 1.基本字符 2.特殊字符 3.量词 4.边界匹配 &#xff08;二&#xff09;进阶用法 1.组与引用 2.选择 二、命令之-----grep &#xff08;一&#xff09;基础用法 &#xff08;二&#xff09;高级用…

MySQL中去除重复(十一)

MySQL中去除重复(十一) 一、相同的行 我们要去除相同行要使用DISTINCT关键字 SELECT DISTINCT 列名 FROM 表名; distinct 是针对查询的结果集合进行去重而不是针对某一行或者某一列。 二、查询中的行选择 用 WHERE 子句限制从查询返回的行。一个 WHERE 子句包含一个 必须满…

智能分析网关V4+EasyCVR视频融合平台——高速公路交通情况的实时监控和分析一体化方案

随着2024年春运帷幕的拉开&#xff0c;不少人的返乡之旅也即将开启&#xff0c;从这几日的新闻来看&#xff0c;高速上一路飘红。伴随恶劣天气&#xff0c;加上激增的车流&#xff0c;极易导致高速瘫痪&#xff0c;无法正常使用。为解决此问题&#xff0c;助力高速高效运营&…

一些你可能用到的函数和头文件

对于排序想必大家应该挺熟悉的&#xff0c;如果要是给一连串打乱的整数让你由小到大排序&#xff0c;常见的方法有冒泡排序法和选择排序法等&#xff0c;今天我就给大家介绍一个十分好用的方法&#xff0c;就是使用 sort 函数来进行快排。 sort 函数是位于头文件 #include <…

R语言分析任务:

有需要实验报告的可CSDN 主页个人私信 《大数据统计分析软件&#xff08;R语言&#xff09;》 实 验 报 告 指导教师&#xff1a; 专 业&#xff1a; 班 级&#xff1a; 姓 名&#xff1a; 学 …

Three.js 纹理贴图 - 环境贴图 - 纹理贴图 - 透明贴图 - 高光贴图

文章目录 Three.js 纹理贴图纹理贴图 map属性纹理贴图的映射方式 texture.Mapping纹理贴图的色彩空间 texture.colorSpace中途更新纹理的色彩空间 texture.needsUpdate 纹理加载器 THREE.TextureLoader监听单个材质监听多个材质 - LoadingManager类 1. 颜色贴图与材质的颜色2.渲…

Linux——存储管理

文章目录 基本分区磁盘简介磁盘分类linux的磁盘命名磁盘的分区方式 管理磁盘虚拟机添加硬盘查看磁盘信息磁盘分区流程创建分区创建文件系统挂载mount查看挂载信息 剩余空间继续分区MBR如何划分更多的分区为什么只能有4个主分区扩展分区的引入 逻辑卷LVM是什么特点术语创建LVMVG…

数据图表方案,企业视频生产数据可视化

在信息爆炸的时代&#xff0c;如何将复杂的数据转化为直观、生动的视觉信息&#xff0c;是企业在数字化转型中面临的挑战。美摄科技凭借其独特的数据图表方案&#xff0c;为企业在数据可视化领域打开了一扇全新的大门。 一、数据图表方案的优势 1、高效便捷&#xff1a;利用数…

vscode实时预览markdown效果

安装插件 Markdown Preview Enhanced 上面是搜索框 启动预览 右键->Open Preview On the Side 效果如下&#xff1a; 目录功能 目录功能还是使用gitee吧 push后使用gitee&#xff0c;gitee上markdown支持侧边生成目录

Android矩阵Matrix裁切setRectToRect拉伸Bitmap替代Bitmap.createScaledBitmap缩放,Kotlin

Android矩阵Matrix裁切setRectToRect拉伸Bitmap替代Bitmap.createScaledBitmap缩放&#xff0c;Kotlin class MyImageView : AppCompatImageView {private var mSrcBmp: Bitmap? nullprivate var testIV: ImageView? nullconstructor(ctx: Context, attrs: AttributeSet) :…

一步到位:用Python实现PC屏幕截图并自动发送邮件,实现屏幕监控

在当前的数字化世界中&#xff0c;自动化已经成为我们日常生活和工作中的关键部分。它不仅提高了效率&#xff0c;还节省了大量的时间和精力。在这篇文章中&#xff0c;我们将探讨如何使用Python来实现一个特定的自动化任务 - PC屏幕截图自动发送到指定的邮箱。 这个任务可能看…

YoloV8改进策略:Block改进|DCNv4最新实践|高效涨点|完整论文翻译

摘要 涨点效果:在我自己的数据集上,mAP50 由0.986涨到了0.991,mAP50-95由0.737涨到0.753,涨点明显! DCNv4是可变形卷积的第四版,速度和v3相比有了大幅度的提升,但是环境搭建有一定的难度,对新手不太友好。如果在使用过程遇到编译的问题,请严格按照我写的环境配置。…

Linux 系统开始配置

文章目录 备份源为root 设置密码安装基本工具切换root 用户删除snap从 Ubuntu 移除 Snap 后使用 deb 文件安装软件商店和 Firefox在 Ubuntu 系统恢复到 Snap 软件包总结 删除 vim安装neovim在线安装neovim压缩安装neovim安装lazyvim安装剪切板 安装qt配置 Qt 环境不在sudoers文…

链表中的数字相加

不能简单认为将两条链表转变为整数后进行运算&#xff0c;然后将结果转变为链表。因为如果链表很长&#xff0c;这可能会导致整数溢出。 在正常的两个整数加法运算时&#xff0c;我们是从低位开始&#xff0c;然后依次相加更高位的数字&#xff0c;所以不难想到我们需要将链表反…

(每日持续更新)jdk api之NotSerializableException基础、应用、实战

博主18年的互联网软件开发经验&#xff0c;从一名程序员小白逐步成为了一名架构师&#xff0c;我想通过平台将经验分享给大家&#xff0c;因此博主每天会在各个大牛网站点赞量超高的博客等寻找该技术栈的资料结合自己的经验&#xff0c;晚上进行用心精简、整理、总结、定稿&…

【动态规划】【记忆化搜索】【回文】1312让字符串成为回文串的最少插入次数

作者推荐 【动态规划】【字符串】【表达式】2019. 解出数学表达式的学生分数 本文涉及知识点 动态规划汇总 记忆化搜索 回文 字符串 LeetCode1312. 让字符串成为回文串的最少插入次数 给你一个字符串 s &#xff0c;每一次操作你都可以在字符串的任意位置插入任意字符。 请…

基于Java SSM框架实现校园快领服务系统项目【项目源码+论文说明】

基于java的SSM框架实现校园快领服务系统演示 摘要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于校园快领服务系统当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了…

Python爬虫的基本原理

我们可以把互联网比作一张大网&#xff0c;而爬虫&#xff08;即网络爬虫&#xff09;便是在网上爬行的蜘蛛。把网的节点比作一个个网页&#xff0c;爬虫爬到这就相当于访问了该页面&#xff0c;获取了其信息。可以把节点间的连线比作网页与网页之间的链接关系&#xff0c;这样…

双非本科准备秋招(14.3)—— java线程

创建和运行线程 1、使用Thread Slf4j(topic "c.Test1")public class Test1 {public static void main(String[] args) {Thread t new Thread("t1") {Overridepublic void run() {log.debug("running");}};t.start();​log.debug("runnin…

Windows 共享文件 netlogon和sysvol的作用

在Windows域环境中&#xff0c;Netlogon和SYSVOL是两个非常重要的共享文件夹&#xff0c;它们由域控制器自动创建并管理&#xff0c;对于Active Directory&#xff08;AD&#xff09;域的正常运行至关重要&#xff1a; NETLOGON共享&#xff1a; 位置&#xff1a;\\<domain_…