量化策略——准备3 数据、Backtrader回测框架与quantstats评价指标

news/2023/6/9 19:36:51

我们一般使用AKShare这个库来获取股票数据或策略中用得到的数据:
AKShare github主页:https://github.com/akfamily/akshare

使用Backtrader框架作为回测的框架:
Backtrader github主页:https://github.com/mementum/backtrader

使用quantstats库作为回测结果评价的库:
quantstats github主页:https://github.com/ranaroussi/quantstats

这一部分准备好之后,后续我们将关注点主要放在【策略】上,对于数据、评价指标这些如无特殊处理,将不再赘述。整个量化的框架构造起来不太容易,如果以前有所了解,可以用自己习惯的方式;如果觉得困难较大,也可以先跳过,等后面能力够了之后,再上手构建。

ps:大家要慎重的使用网上的量化平台,因为偷策略这种事太正常了,大家还是最好自己本地搭一个测试的平台~

文章目录

  • 1. 数据准备
  • 2. Backtrader回测框架准备
  • 3. 评价指标

下面的数据准备与Backtrader回测准备,只是博主提供的一个参考,在开始正式介绍量化策略的时候是不会涉及到每个数据的采集,手把手的代码,这些都是不会提及的,只会提供一个backtrader的策略类,作为对策略的编程实现。

1. 数据准备

比如股票数据:

  1. 首先创建一个data文件夹,然后在文件夹里创建一个stock_data的文件夹

  2. 创建一个code文件夹用来存放程序文件

  3. 然后新建一个python文件,使用如下代码:

    import time
    import akshare as ak
    from tqdm import tqdm
    from loguru import loggerdef extract_data():start_date = "20150101"end_date = "20221101"stock_list = ak.stock_zh_a_spot_em()  # 东方财富网-沪深京 A 股-实时行情数据for stock_code in tqdm(stock_list['代码']):time.sleep(1)stock_df = ak.stock_zh_a_hist(symbol=stock_code, period="daily", start_date=start_date, end_date=end_date, adjust="hfq")  # 后复权stock_df.to_pickle("../data/stock_data/{}.pkl".format(stock_code))logger.debug("ADD DATA {}", stock_code)if __name__ == '__main__':extract_data()
    

这里博主把股票数据保存到data/stock_data/文件夹下,以股票代码.pkl的格式保存:

在这里插入图片描述

2. Backtrader回测框架准备

Backtrader足够简单,同时也非常接近实盘(国外可以一键切换实盘,国内没有接口)

Backtrader的使用请参考:Backtrader量化&回测1——基本的交易策略与挂单买卖

从策略到最终影响金额,都会经历四个步骤:

  1. 策略信号
  2. 委托
  3. 订单
  4. 金额与标的的置换

因此盯紧订单的变化就可以了解策略对金额变动的影响,为了将更多精力用于策略本身的编写上,我们写一个策略模版,然后以后的策略都可以通过继承这个模版,把与策略无关的变量、操作都写在模版里:

from loguru import logger
import backtrader as btclass TemplateStrategy(bt.Strategy):def __init__(self):# 记录用self.buy_bond_record = {}  # 记录购买的订单self.sell_bond_record = {} # 记录卖出的订单def next(self):"""最核心的触发策略"""raisedef notify_order(self, order):"""通知订单状态,当订单状态变化时触发"""today_time_string = self.datetime.datetime().strftime('%Y-%m-%d')if order.status in [order.Submitted, order.Accepted]:  # 接受订单交易,正常情况returnif order.status in [order.Completed]:if order.isbuy():self.buy_bond_record.setdefault(today_time_string, {})self.buy_bond_record[today_time_string].setdefault(order.data._name.replace(".", "_"), [])self.buy_bond_record[today_time_string][order.data._name.replace(".", "_")].append({"order_ref": order.ref,"bond_name": order.data._name,"size": order.size,"price": order.executed.price,"value": order.executed.value,"trade_date": self.datetime.datetime(0),})logger.debug('{} 订单{} 已购入 {} , 购入单价 {:.2f}, 数量 {}, 费用 {:.2f}, 手续费 {:.2f}'.format(self.datetime.date(), order.ref, order.data._name, order.executed.price, order.size,order.executed.value, order.executed.comm))elif order.issell():self.sell_bond_record.setdefault(today_time_string, {})self.sell_bond_record[today_time_string].setdefault(order.data._name.replace(".", "_"), [])self.sell_bond_record[today_time_string][order.data._name.replace(".", "_")].append({"order_ref": order.ref,"bond_name": order.data._name,"size": order.size,"price": order.executed.price,"value": - order.executed.price * order.size,"sell_type": order.info.sell_type,"trade_date": self.datetime.datetime(0),})logger.debug('{} 订单{} 已卖出 {}, 卖出金额 {:.2f}, 数量 {}, 费用 {:.2f}, 手续费 {:.2f}'.format(self.datetime.date(), order.ref, order.data._name, order.executed.price, order.size,-order.executed.price * order.size, order.executed.comm))elif order.status in [order.Margin, order.Rejected]:logger.warning('{} 订单{} 现金不足、金额不足拒绝交易', self.datetime.date(), order.ref)elif order.status in [order.Canceled]:logger.debug("{} 订单{} 已取消", self.datetime.date(), order.ref)elif order.status in [order.Expired]:logger.warning('{} 订单{} 超过有效期已取消, 订单开价 {}, 当天最高价{}, 最低价{}', self.datetime.date(), order.ref, order.price,order.data.high[0], order.data.low[0])

之后的策略均继承此TemplateStrategy策略类,并覆写def next(self)函数即可,这一部分会将所有的订单在日志中打印下来

在程序中,购买的订单可以使用如下代码:

self.buy(data=self.getdatabyname(stock_name), # 针对哪一个股票代码size=100, # 数量price=self.getdatabyname(stock_name).close[0], # 以当天的收盘价购买exectype=bt.Order.Limit, # 限价单valid=self.getdatabyname(stock_name).datetime.date(1),  # 有效期1天)

3. 评价指标

我们使用quantstats这个库来对回测结果进行评价,这个库里的计算方法简单粗暴,通过对已有的计算方法的封装,我们得到可以方便的进行评价的方法:

import pandas as pd
import quantstats as qsdef cal_daily_return(fund_values: pd.Series):"""根据资金变动,计算日资产的变化率:param fund_values: 每日的总资产"""fund_values = fund_values.sort_index()daily_re: pd.Series = (fund_values / fund_values.shift(1)) - 1daily_re.iloc[0] = 0return daily_redef cal_rolling_feature(daily_return_series: pd.Series, rf=0.02, record_dict: dict = None):"""计算各种指标:param daily_return_series: 日收益的变化率:param rf: 无风险收益,这里定为0.02:param record_dict: 指标的结果会追加到这个字典中"""if record_dict is None:record_dict = {}daily_return_series.index = pd.to_datetime(daily_return_series.index.values)feature_df = pd.DataFrame(index=daily_return_series.index)feature_df['累积收益率'] = qs.stats.compsum(daily_return_series).valuesfeature_df['回撤'] = qs.stats.to_drawdown_series(daily_return_series)record_dict.update({"累积收益率": feature_df['累积收益率'].iloc[-1]})feature_dict = {"复合年增长": qs.stats.cagr(daily_return_series, rf=rf),"夏普比率": qs.stats.sharpe(daily_return_series, rf=rf),"索蒂诺": qs.stats.sortino(daily_return_series, rf=rf),"omega": qs.stats.omega(pd.DataFrame(daily_return_series), rf=rf),"最大回撤": qs.stats.max_drawdown(daily_return_series),"最大回撤期(天)": int(qs.stats.drawdown_details(feature_df['回撤'])['days'].max()),"年波动率": qs.stats.volatility(daily_return_series),}record_dict.update(feature_dict)# 决定保留的小数for key, value in record_dict.items():if isinstance(value, float):record_dict[key] = value.round(3)return feature_df, record_dict

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

如若内容造成侵权/违法违规/事实不符,请联系郑州代理记账网进行投诉反馈,一经查实,立即删除!

相关文章

2022-11-01 网工进阶(三十四) IP组播协议(PIM)-模式概述、组播分发树的分类、PIM路由表项、PIM-DM工作原理(组播分发树的形成、配置举例)

PIM协议介绍 PIM(Protocol Independent Multicast)协议无关组播。协议无关指的是与单播路由协议无关,即PIM不需要维护专门的单播路由信息。目前常用版本是PIMv2,PIM报文直接封装在IP报文中,协议号为103,PI…

【初步了解网络原理】IP安全策略和相关网络概念

人若有志,万事可为!(放弃了,你就是笑话一段。) 目録1. IP安全策略简述2. 创建IP安全策略限制IP访问3. 创建防火墙入站规则限制IP访问4. IP地址、网关、端口、网段、子网掩码、MAC地址、DNS概念【每日一面】IPv4 与 IPv…

计算机网络——从直连网络到以太网

1. Ch1-1 计算机网络体系结构 搞清楚网络的基本概念 :要会计算几个重要的性能参数。 1.1. 什么是计算机网络? 计算机网络是互联的通用计算机的集合。 1.2. 构成 网络 {节点,链路} {V,L} 节点(Node)…

从Openvswitch代码看网络包的旅程

我们知道,Openvwitch可以创建虚拟交换机,而网络包可以通过虚拟交换机进行转发,并通过流表进行处理,具体的过程如何呢?一、内核模块Openvswitch.ko的加载OVS是内核态和用户态配合工作的,所以首先要加载内核态…

计算机网络与通信之网络互联及通信

这部分的内容主要围绕网络层来展开: 网络层概述网际协议IPv4地址解析协议ARP网际控制报文协议ICMP互联网路由协议 1. 网络层概述 网络的互联问题 大规模的计算机网络一般采用分层组网技术,即网络互联。原因: 避免大规模网络交换机寻址的复杂性&#xf…

Kubernetes网络分析之Flannel

Flannel是cereos开源的CNI网络插件,下图flannel官网提供的一个数据包经过封包、传输以及拆包的示意图,从这个图片里面里面可以看出两台机器的docker0分别处于不同的段:10.1.20.1/24 和 10.1.15.1/24 ,如果从Web App Frontend1 pod…

超长干货丨Kubernetes网络快速入门完全指南

Kubernetes网络一直是一个非常复杂的主题。本文将介绍Kubernetes实际如何创建网络以及如何为Kubernetes集群设置网络。 本文不包括如何设置Kubernetes集群。这篇文章中的所有例子都将使用Rancher 2.0集群(其他平台也同样适用)。即使你打算使用其他的公有…

最新 vue-cli 构建项目

vue-cli 构建项目 当前使用最新版本构建一个vue node项目 插件 vue-clivueelement-plusroutervuex 安装vue-cli npm install -g vue-cli安装完后 vue --version 查看版本 vue --version创建一个项目 vue create demo这里要选择版本,不同版本要相组合配置的插件…