量化回测:backtrader回测封装代码

 

main

# -*- coding:utf-8 -*-


#正常显示画图时出现的中文和负号
from pylab import mpl
mpl.rcParams['font.sans-serif']=['SimHei']
import backtrader as bt
import numpy as np
from testpkg import backtest
#策略
class GridStrategy(bt.Strategy):
    params = (
        ("printlog", True),
        ("top", 4.2),
        ("buttom", 3.5),
    )

    def __init__(self):
        self.mid = (self.p.top + self.p.buttom) / 2.0
        # 百分比区间计算
        # 这里多1/2,是因为arange函数是左闭右开区间。
        perc_level = [x for x in np.arange(1 + 0.02 * 5, 1 - 0.02 * 5 - 0.02 / 2, -0.02)]
        # 价格区间
        # print(self.mid)
        self.price_levels = [self.mid * x for x in perc_level]
        # 记录上一次穿越的网格
        self.last_price_index = None
        # 总手续费
        self.comm = 0.0


    def next(self):
        # print(self.last_price_index)
        # 开仓
        if self.last_price_index == None:
            # print("b", len(self.price_levels))
            for i in range(len(self.price_levels)):
                price = self.data.close[0]
                # print("c", i, price, self.price_levels[i][0])
                if self.data.close[0] > self.price_levels[i]:
                    self.last_price_index = i
                    self.order_target_percent(target=i / (len(self.price_levels) - 1))
                    print("a")
                    return
        # 调仓
        else:
            signal = False
            while True:
                upper = None
                lower = None
                if self.last_price_index > 0:
                    upper = self.price_levels[self.last_price_index - 1]
                if self.last_price_index < len(self.price_levels) - 1:
                    lower = self.price_levels[self.last_price_index + 1]
                # 还不是最轻仓,继续涨,再卖一档
                if upper != None and self.data.close > upper:
                    self.last_price_index = self.last_price_index - 1
                    signal = True
                    continue
                # 还不是最重仓,继续跌,再买一档
                if lower != None and self.data.close < lower:
                    self.last_price_index = self.last_price_index + 1
                    signal = True
                    continue
                break
            if signal:
                self.long_short = None
                self.order_target_percent(target=self.last_price_index / (len(self.price_levels) - 1))


    # 输出交易记录
    def log(self, txt, dt=None, doprint=False):
        if self.params.printlog or doprint:
            dt = dt or self.datas[0].datetime.date(0)
            print('%s, %s' % (dt.isoformat(), txt))


    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(
                    '执行买入, 价格: %.2f, 成本: %.2f, 手续费 %.2f' %
                    (order.executed.price,
                     order.executed.value,
                     order.executed.comm))
                self.buyprice = order.executed.price


            self.comm += order.executed.comm
        elif order.status in [order.Canceled, order.Margin, order.Rejected]:
            self.log("交易失败")
            self.order = None
        else:
                self.log(
                    '执行卖出, 价格: %.2f, 成本: %.2f, 手续费 %.2f' %
                    (order.executed.price,
                    order.executed.value,
                    order.executed.comm))
                self.comm += order.executed.comm

    # 输出手续费
    def stop(self):
        self.log("手续费:%.2f 成本比例:%.5f" % (self.comm, self.comm / self.broker.getvalue()))




if __name__ == "__main__":
    start = "2018-01-01"
    end = "2020-07-05"
    name = ["300etf"]
    code = ["510300"]
    backtest = backtest.BackTest(GridStrategy, start, end, code, name, 100000)
    result = backtest.run()
    # backtest.output()
    print(result)

backtest

# coding:utf-8
# 量化交易回测类


import backtrader as bt
import backtrader.analyzers as btay
import tushare as ts
import os
import pandas as pd
import datetime
import matplotlib.pyplot as plt
import empyrical as ey
import math
import tushare as ts
import numpy as np
from scipy import stats
from backtrader.utils.py3 import map
from backtrader import Analyzer, TimeFrame
from backtrader.mathsupport import average, standarddev
from backtrader.analyzers import AnnualReturn
import operator
from pathlib import Path


# 回测类
class BackTest:
    def __init__(self, strategy, start, end, code, name, cash=0.01, commission=0.0003, benchmarkCode="510300",
                 bDraw=True):
        self.__cerebro = None
        self.__strategy = strategy
        self.__start = start
        self.__end = end
        self.__code = code
        self.__name = name
        self.__result = None
        self.__commission = commission
        self.__initcash = cash
        self.__backtestResult = pd.Series()
        self.__returns = pd.Series()
        self.__benchmarkCode = benchmarkCode
        self.__benchReturns = pd.Series()
        self.__benchFeed = None
        self.__bDraw = bDraw
        self.__start_date = None
        self.__end_date = None
        self._init()

    # 执行回测
    def run(self):
        self.__backtestResult["期初账户总值"] = self.getValue()
        self.__results = self.__cerebro.run()
        self.__backtestResult["期末账户总值"] = self.getValue()
        self._Result()
        if self.__bDraw == True:
            self._drawResult()
        self.__returns = self._timeReturns(self.__results)
        self.__benchReturns = self._getBenchmarkReturns(self.__results)
        self._riskAnaly(self.__returns, self.__benchReturns, self.__backtestResult)
        return self.getResult()

    # 获取账户总价值
    def getValue(self):
        return self.__cerebro.broker.getvalue()

    # 获取回测指标
    def getResult(self):
        return self.__backtestResult

    # 获取策略及基准策略收益率的序列
    def getReturns(self):
        return self.__returns, self.__benchReturns

    # 执行参数优化的回测
    def optRun(self, *args, **kwargs):
        self._optStrategy(*args, **kwargs)
        results = self.__cerebro.run()
        if len(kwargs) == 1:
            testResults = self._optResult(results, **kwargs)
        elif len(kwargs) > 1:
            testResults = self._optResultMore(results, **kwargs)
        self._init()
        return testResults

    # 输出回测结果
    def output(self):
        print("夏普比例:", self.__results[0].analyzers.sharpe.get_analysis()["sharperatio"])
        print("年化收益率:", self.__results[0].analyzers.AR.get_analysis())
        print("最大回撤:%.2f,最大回撤周期%d" % (self.__results[0].analyzers.DD.get_analysis().max.drawdown,
                                      self.__results[0].analyzers.DD.get_analysis().max.len))
        print("总收益率:%.2f" % (self.__results[0].analyzers.RE.get_analysis()["rtot"]))
        # self.__results[0].analyzers.TA.pprint()

    # 进行参数优化
    def _optStrategy(self, *args, **kwargs):
        self.__cerebro = bt.Cerebro(maxcpus=1)
        self.__cerebro.optstrategy(self.__strategy, *args, **kwargs)
        self._createDataFeeds()
        self._settingCerebro()

    # 真正进行初始化的地方
    def _init(self):
        self.__cerebro = bt.Cerebro()
        self.__cerebro.addstrategy(self.__strategy)
        self._createDataFeeds()
        self._settingCerebro()

    # 设置cerebro
    def _settingCerebro(self):
        # 添加回撤观察器
        self.__cerebro.addobserver(bt.observers.DrawDown)
        # 添加基准观察器
        self.__cerebro.addobserver(bt.observers.Benchmark, data=self.__benchFeed, timeframe=bt.TimeFrame.NoTimeFrame)
        # 设置手续费
        self.__cerebro.broker.setcommission(commission=self.__commission)
        # 设置初始资金
        self.__cerebro.broker.setcash(self.__initcash)
        # 添加分析对象
        self.__cerebro.addanalyzer(btay.SharpeRatio, _name="sharpe", riskfreerate=0.02, stddev_sample=True,
                                   annualize=True)
        self.__cerebro.addanalyzer(btay.AnnualReturn, _name="AR")
        self.__cerebro.addanalyzer(btay.DrawDown, _name="DD")
        self.__cerebro.addanalyzer(btay.Returns, _name="RE")
        self.__cerebro.addanalyzer(btay.TradeAnalyzer, _name="TA")
        self.__cerebro.addanalyzer(btay.TimeReturn, _name="TR")
        self.__cerebro.addanalyzer(btay.TimeReturn, _name="TR_Bench", data=self.__benchFeed)
        self.__cerebro.addanalyzer(btay.SQN, _name="SQN")

    # 建立数据源
    def _createDataFeeds(self):
        # 建立回测数据源
        for i in range(len(self.__code)):
            dataFeed = self._createDataFeedsProcess(self.__code[i], self.__name[i])
            self.__cerebro.adddata(dataFeed, name=self.__name[i])
        self.__benchFeed = self._createDataFeedsProcess(self.__benchmarkCode, "benchMark")
        self.__cerebro.adddata(self.__benchFeed, name="benchMark")

    # 建立数据源的具体过程
    def _createDataFeedsProcess(self, code, name):
        df_data = self._getData(code)
        start_date = list(map(int, self.__start.split("-")))
        end_date = list(map(int, self.__end.split("-")))
        self.__start_date = datetime.datetime(start_date[0], start_date[1], start_date[2])
        self.__end_date = datetime.datetime(end_date[0], end_date[1], end_date[2])
        dataFeed = bt.feeds.PandasData(dataname=df_data, name=name,
                                       fromdate=datetime.datetime(start_date[0], start_date[1], start_date[2]),
                                       todate=datetime.datetime(end_date[0], end_date[1], end_date[2]))
        return dataFeed

    # 计算胜率信息
    def _winInfo(self, trade_info, result):
        total_trade_num = trade_info["total"]["total"]
        if total_trade_num > 1:
            win_num = trade_info["won"]["total"]
            lost_num = trade_info["lost"]["total"]
            result["交易次数"] = total_trade_num
            result["胜率"] = win_num / total_trade_num
            result["败率"] = lost_num / total_trade_num

    # 根据SQN值对策略做出评估
    # 按照backtrader文档写的
    def _judgeBySQN(self, sqn):
        result = None
        if sqn >= 1.6 and sqn <= 1.9:
            result = "低于平均"
        elif sqn > 1.9 and sqn <= 2.4:
            result = "平均水平"
        elif sqn > 2.4 and sqn <= 2.9:
            result = "良好"
        elif sqn > 2.9 and sqn <= 5.0:
            result = "优秀"
        elif sqn > 5.0 and sqn <= 6.9:
            result = "卓越"
        elif sqn > 6.9:
            result = "大神?"
        else:
            result = "很差"
        self.__backtestResult["策略评价(根据SQN)"] = result
        return result

    # 计算并保存回测结果指标
    def _Result(self):
        self.__backtestResult["账户总额"] = self.getValue()
        self.__backtestResult["总收益率"] = self.__results[0].analyzers.RE.get_analysis()["rtot"]
        self.__backtestResult["年化收益率"] = self.__results[0].analyzers.RE.get_analysis()["rnorm"]
        # self.__backtestResult["交易成本"] = self.__cerebro.strats[0].getCommission()
        self.__backtestResult["夏普比率"] = self.__results[0].analyzers.sharpe.get_analysis()["sharperatio"]
        self.__backtestResult["最大回撤"] = self.__results[0].analyzers.DD.get_analysis().max.drawdown
        self.__backtestResult["最大回撤期间"] = self.__results[0].analyzers.DD.get_analysis().max.len
        self.__backtestResult["SQN"] = self.__results[0].analyzers.SQN.get_analysis()["sqn"]
        self._judgeBySQN(self.__backtestResult["SQN"])

        # 计算胜率信息
        trade_info = self.__results[0].analyzers.TA.get_analysis()
        self._winInfo(trade_info, self.__backtestResult)

    # 取得优化参数时的指标结果
    def _getOptAnalysis(self, result):
        temp = dict()
        temp["总收益率"] = result[0].analyzers.RE.get_analysis()["rtot"]
        temp["年化收益率"] = result[0].analyzers.RE.get_analysis()["rnorm"]
        temp["夏普比率"] = result[0].analyzers.sharpe.get_analysis()["sharperatio"]
        temp["最大回撤"] = result[0].analyzers.DD.get_analysis().max.drawdown
        temp["最大回撤期间"] = result[0].analyzers.DD.get_analysis().max.len
        sqn = result[0].analyzers.SQN.get_analysis()["sqn"]
        temp["SQN"] = sqn
        temp["策略评价(根据SQN)"] = self._judgeBySQN(sqn)
        trade_info = self.__results[0].analyzers.TA.get_analysis()
        self._winInfo(trade_info, temp)
        return temp

    # 在优化多个参数时计算并保存回测结果
    def _optResultMore(self, results, **kwargs):
        testResults = pd.DataFrame()
        i = 0
        for key in kwargs:
            for value in kwargs[key]:
                temp = self._getOptAnalysis(results[i])
                temp["参数名"] = key
                temp["参数值"] = value
                returns = self._timeReturns(results[i])
                benchReturns = self._getBenchmarkReturns(results[i])
                self._riskAnaly(returns, benchReturns, temp)
                testResults = testResults.append(temp, ignore_index=True)
            # testResults.set_index(["参数值"], inplace = True)
        return testResults

    # 在优化参数时计算并保存回测结果
    def _optResult(self, results, **kwargs):
        testResults = pd.DataFrame()
        params = []
        for k, v in kwargs.items():
            for t in v:
                params.append(t)
        i = 0
        for result in results:
            temp = self._getOptAnalysis(result)
            temp["参数名"] = k
            temp["参数值"] = params[i]
            i += 1
            returns = self._timeReturns(result)
            benchReturns = self._getBenchmarkReturns(result)
            self._riskAnaly(returns, benchReturns, temp)
            testResults = testResults.append(temp, ignore_index=True)
        # testResults.set_index(["参数值"], inplace = True)
        return testResults

    # 计算收益率序列
    def _timeReturns(self, result):
        return pd.Series(result[0].analyzers.TR.get_analysis())

    # 运行基准策略,获取基准收益值
    def _getBenchmarkReturns(self, result):
        return pd.Series(result[0].analyzers.TR_Bench.get_analysis())

    # 分析策略的风险指标
    def _riskAnaly(self, returns, benchReturns, results):
        risk = riskAnalyzer(returns, benchReturns)
        result = risk.run()
        results["阿尔法"] = result["阿尔法"]
        results["贝塔"] = result["贝塔"]
        results["信息比例"] = result["信息比例"]
        results["策略波动率"] = result["策略波动率"]
        results["欧米伽"] = result["欧米伽"]
        # self.__backtestResult["夏普值"] = result["夏普值"]
        results["sortino"] = result["sortino"]
        results["calmar"] = result["calmar"]

    # 回测结果绘图
    def _drawResult(self):
        self.__cerebro.plot(numfigs=2)
        figname = type(self).__name__ + ".png"
        plt.savefig(figname)

    # 获取数据
    def _getData(self, code):
        filename = code + ".csv"
        path = "./data/"
        # 如果数据目录不存在,创建目录
        if not os.path.exists(path):
            os.makedirs(path)
        # 已有数据文件,直接读取数据
        if os.path.exists(path + filename):
            df = pd.read_csv(path + filename)
        else:  # 没有数据文件,用tushare下载
            df = ts.get_k_data(code, autype="qfq", start=self.__start, end=self.__end)
            df.to_csv(path + filename)
        df.index = pd.to_datetime(df.date)
        df['openinterest'] = 0
        df = df[['open', 'high', 'low', 'close', 'volume', 'openinterest']]
        return df


# 用empyrical库计算风险指标
class riskAnalyzer:
    def __init__(self, returns, benchReturns, riskFreeRate=0.02):
        self.__returns = returns
        self.__benchReturns = benchReturns
        self.__risk_free = riskFreeRate
        self.__alpha = 0.0
        self.__beta = 0.0
        self.__info = 0.0
        self.__vola = 0.0
        self.__omega = 0.0
        self.__sharpe = 0.0
        self.__sortino = 0.0
        self.__calmar = 0.0

    def run(self):
        # 计算各指标
        self._alpha_beta()
        self._info()
        self._vola()
        self._omega()
        self._sharpe()
        self._sortino()
        self._calmar()
        result = pd.Series(dtype="float64")
        result["阿尔法"] = self.__alpha
        result["贝塔"] = self.__beta
        result["信息比例"] = self.__info
        result["策略波动率"] = self.__vola
        result["欧米伽"] = self.__omega
        result["夏普值"] = self.__sharpe
        result["sortino"] = self.__sortino
        result["calmar"] = self.__calmar
        return result

    def _alpha_beta(self):
        self.__alpha, self.__beta = ey.alpha_beta(returns=self.__returns, factor_returns=self.__benchReturns,
                                                  risk_free=self.__risk_free, annualization=1)

    def _info(self):
        self.__info = ey.excess_sharpe(returns=self.__returns, factor_returns=self.__benchReturns)

    def _vola(self):
        self.__vola = ey.annual_volatility(self.__returns, period='daily')

    def _omega(self):
        self.__omega = ey.omega_ratio(returns=self.__returns, risk_free=self.__risk_free)

    def _sharpe(self):
        self.__sharpe = ey.sharpe_ratio(returns=self.__returns, annualization=1)

    def _sortino(self):
        self.__sortino = ey.sortino_ratio(returns=self.__returns)

    def _calmar(self):
        self.__calmar = ey.calmar_ratio(returns=self.__returns)


# 测试函数
def test():
    # 构造测试数据
    returns = pd.Series(
        index=pd.date_range("2017-03-10", "2017-03-19"),
        data=(-0.012143, 0.045350, 0.030957, 0.004902, 0.002341, -0.02103, 0.00148, 0.004820, -0.00023, 0.01201))
    print(returns)
    benchmark_returns = pd.Series(
        index=pd.date_range("2017-03-10", "2017-03-19"),
        data=(-0.031940, 0.025350, -0.020957, -0.000902, 0.007341, -0.01103, 0.00248, 0.008820, -0.00123, 0.01091))
    print(benchmark_returns)
    # 计算累积收益率
    creturns = ey.cum_returns(returns)
    print("累积收益率\n", creturns)
    risk = riskAnalyzer(returns, benchmark_returns, riskFreeRate=0.01)
    results = risk.run()
    print(results)
    # 直接调用empyrical试试
    alpha = ey.alpha(returns=returns, factor_returns=benchmark_returns, risk_free=0.01)
    calmar = ey.calmar_ratio(returns)
    print(alpha, calmar)
    # 自己计算阿尔法值
    annual_return = ey.annual_return(returns)
    annual_bench = ey.annual_return(benchmark_returns)
    print(annual_return, annual_bench)
    alpha2 = (annual_return - 0.01) - results["贝塔"] * (annual_bench - 0.01)
    print(alpha2)

    # 自己计算阿尔法贝塔
    def get_return(code, startdate, endate):
        df = ts.get_k_data(code, ktype="D", autype="qfq", start=startdate, end=endate)
        p1 = np.array(df.close[1:])
        p0 = np.array(df.close[:-1])
        logret = np.log(p1 / p0)
        rate = pd.DataFrame()
        rate[code] = logret
        rate.index = df["date"][1:]
        return rate

    def alpha_beta(code, startdate, endate):
        mkt_ret = get_return("sh", startdate, endate)
        stock_ret = get_return(code, startdate, endate)
        df = pd.merge(mkt_ret, stock_ret, left_index=True, right_index=True)
        x = df.iloc[:, 0]
        y = df.iloc[:, 1]
        beta, alpha, r_value, p_value, std_err = stats.linregress(x, y)
        return (alpha, beta)

    def stocks_alpha_beta(stocks, startdate, endate):
        df = pd.DataFrame()
        alpha = []
        beta = []
        for code in stocks.values():
            a, b = alpha_beta(code, startdate, endate)
            alpha.append(float("%.4f" % a))
            beta.append(float("%.4f" % b))
        df["alpha"] = alpha
        df["beta"] = beta
        df.index = stocks.keys()
        return df

    startdate = "2017-01-01"
    endate = "2018-11-09"
    stocks = {'中国平安': '601318', '格力电器': '000651', '招商银行': '600036', '恒生电子': '600570', '中信证券': '600030',
              '贵州茅台': '600519'}
    results = stocks_alpha_beta(stocks, startdate, endate)
    print("自己计算结果")
    print(results)

    # 用empyrical计算
    def stocks_alpha_beta2(stocks, startdate, endate):
        df = pd.DataFrame()
        alpha = []
        beta = []
        for code in stocks.values():
            a, b = empyrical_alpha_beta(code, startdate, endate)
            alpha.append(float("%.4f" % a))
            beta.append(float("%.4f" % b))
        df["alpha"] = alpha
        df["beta"] = beta
        df.index = stocks.keys()
        return df

    def empyrical_alpha_beta(code, startdate, endate):
        mkt_ret = get_return("sh", startdate, endate)
        stock_ret = get_return(code, startdate, endate)
        alpha, beta = ey.alpha_beta(returns=stock_ret, factor_returns=mkt_ret, annualization=1)
        return (alpha, beta)

    results2 = stocks_alpha_beta2(stocks, startdate, endate)
    print("empyrical计算结果")
    print(results2)
    print(results2["alpha"] / results["alpha"])


# 测试夏普值的计算
def testSharpe():
    # 读取数据
    stock_data = pd.read_csv("stock_data.csv", parse_dates=["Date"], index_col=["Date"]).dropna()
    benchmark_data = pd.read_csv("benchmark_data.csv", parse_dates=["Date"], index_col=["Date"]).dropna()
    # 了解数据
    print("Stocks\n")
    print(stock_data.info())
    print(stock_data.head())
    print("\nBenchmarks\n")
    print(benchmark_data.info())
    print(benchmark_data.head())
    # 输出统计量
    print(stock_data.describe())
    print(benchmark_data.describe())
    # 计算每日回报率
    stock_returns = stock_data.pct_change()
    print(stock_returns.describe())
    sp_returns = benchmark_data.pct_change()
    print(sp_returns.describe())
    # 每日超额回报
    excess_returns = pd.DataFrame()
    risk_free = 0.04 / 252.0
    excess_returns["Amazon"] = stock_returns["Amazon"] - risk_free
    excess_returns["Facebook"] = stock_returns["Facebook"] - risk_free
    print(excess_returns.describe())
    # 超额回报的均值
    avg_excess_return = excess_returns.mean()
    print(avg_excess_return)
    # 超额回报的标准差
    std_excess_return = excess_returns.std()
    print(std_excess_return)
    # 计算夏普比率
    # 日夏普比率
    daily_sharpe_ratio = avg_excess_return.div(std_excess_return)
    # 年化夏普比率
    annual_factor = np.sqrt(252)
    annual_sharpe_ratio = daily_sharpe_ratio.mul(annual_factor)
    print("年化夏普比率\n", annual_sharpe_ratio)

    # 用empyrical算
    sharpe = pd.DataFrame()
    a = ey.sharpe_ratio(stock_returns["Amazon"], risk_free=risk_free)  # , annualization = 252)
    b = ey.sharpe_ratio(stock_returns["Facebook"], risk_free=risk_free)
    print("empyrical计算结果")
    print(a, b)
    print(a / annual_sharpe_ratio["Amazon"], b / annual_sharpe_ratio["Facebook"])


if __name__ == "__main__":
    # testpkg()
    testSharpe()

 

posted @ 2020-10-11 14:18  -零  阅读(2300)  评论(2编辑  收藏  举报