量化交易backtrader实践(四)_评价统计篇(2)_评价输出

上节回顾

上一节我们对backtrader内置的一堆评价指标进行了实践,从视觉上对这些评价指标的输出参数有了个大概的了解。接下来,我们在循环对多支股票或多个策略进行回测的时候,就可以将评价的一些值进行输出了,这样就能对于股票和策略进行汇总、排序,从而选择出评分高的股票并采用评分高的策略来进行操作。

补充评价结果打印

参考文档: Backtrader 文档学习- Analyzers_backtrader analyzer-CSDN博客

上一节我们没有对Analyzer的文档进行深入学习,直接拿到get_analysis()的返回对象就进行打印输出,并且我们已经整理了3种方法:

第一种方式 key,value处理

for key, value in sout.items():

print(f'{key}: {value}')

第二种方式 -- 使用pandas处理

series1 = pd.Series(sout))

print(series1)

第三种方式 -- 转为dict再处理

dict1 = dict(sout)

print(dict1)

在此基础上,我们又学习到了新的知识:

用pprint美化打印

偶然间,在某个AI回答中看到可以使用pprint()来美化打印,于是做了以下尝试:

python 复制代码
# 首先,你可能去 pip install pprint的时候不成功
# 但是没关系,你可以尝试直接来
import pprint
pp = pprint.PrettyPrinter(indent=2)

def add_analyzer_all(cerebro):
    cerebro.addanalyzer(bt.analyzers.PeriodStats, _name='_PeriodStats')  
    
def analyzer_output(result):
    print("---------------_PeriodStats -----------------")
    sout = result.analyzers._PeriodStats.get_analysis()
    pp.pprint(sout)

----------------------------
策略为 经典MACD , 期末总资金 111569.09  盈利为 11569.09 总共交易次数为 11 ,交易成功率为 36.4%
---------------_PeriodStats -----------------
OrderedDict([ ('average', 0.06155213863943909),
              ('stddev', 0.10583966175667991),
              ('positive', 1),
              ('negative', 1),
              ('nochange', 0),
              ('best', 0.167391800396119),
              ('worst', -0.04428752311724082)])

用内置的pprint打印

接着,我们从__init.py__中点击 .returns , 跳转到returns.py接着点击TimeFrameAnalyzerBase后跳转到analyzer.py

python 复制代码
# __init.py__
from .returns import *


# to returns.py
class Returns(TimeFrameAnalyzerBase):
    '''Total, Average, Compound and Annualized Returns calculated using a
    logarithmic approach
    ......'''


# to analyzer.py

在文档最上方,就可以看到 import pprint as pp的代码,所以,backtrader早就给你安装过了。

python 复制代码
###############################################################################
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import calendar
from collections import OrderedDict
import datetime
import pprint as pp    #### 看这里



......

    def pprint(self, *args, **kwargs):   #### 再看这里
        Prints the results returned by ``get_analysis`` using the pretty
        
        pp.pprint(self.get_analysis(), *args, **kwargs)

然后在这个文档中,我们看到了def pprint()的函数定义,它跟上面我们自已用pprint其实基本上是一样的。

用内置的print进行打印

在看到 def pprint()之前,应该也看到了def print()吧?

python 复制代码
# 在看到 def pprint(...)之前,应该看到了 def print(...)吧
'''
    def print(self, *args, **kwargs):
        Prints the results returned by ``get_analysis`` via a standard
        ``Writerfile`` object, which defaults to writing things to standard output
        
        writer = bt.WriterFile(*args, **kwargs)
        writer.start()
        pdct = dict()
        pdct[self.__class__.__name__] = self.get_analysis()
        writer.writedict(pdct)
        writer.stop()
'''
# 所以,其实backtrader早就给我们准备好了打印的函数,
# 而且这个print比pprint所谓的美化打印要更好一些。
def add_analyzer_all(cerebro):
    cerebro.addanalyzer(bt.analyzers.PeriodStats, _name='_PeriodStats')  
    
def analyzer_output(result):
    print("---------------_PeriodStats -----------------")
    result.analyzers._PeriodStats.print()


---------------------------
---------------_PeriodStats -----------------
===============================================================================
PeriodStats:
  - average: 0.06155213863943909
  - stddev: 0.10583966175667991
  - positive: 1
  - negative: 1
  - nochange: 0
  - best: 0.167391800396119
  - worst: -0.04428752311724082

由上,补充了backtrader已经内置的函数进行结果打印的,一个是pprint(),另一个是print()。在我们需要取某几个参数的时候,我们更倾向于用pprint()来观察参数在哪一层。

评价输出

00_实践计划

  1. run_main_analyzer1的函数内容在内置评价输出时都不需要任何更改
python 复制代码
def run_main_analyser1 (df_list,run_strategy,i, sdate1,sdate2, myplot=False, logoff=1):

    #...
    cerebro = bt.Cerebro()
    #...

    add_analyzer_all(cerebro)                # 加入---------analyzer -------------
    #...

    result = cerebro.run()     
    strat = result[0]
    analyzer_output(strat)                   # 输出--------analyzer -------------
  1. 在添加analyzer的函数中,可以直接添加需要的评价,这个在实践过程中也不需要更改动
python 复制代码
# 加入直接打分或统计的评价指标
### 01_AnnualReturn - 以自然年为分组的,比如 2023年 年化收益% , 2024年 年化收益%
### 02_drawdown  - 当前回撤(周期,值,百分比) + 最大回撤(周期,值,百分比)
### 04_Sharp Ratio - 夏普比率,直接给出回测周期的夏普率
### 05_TradeAnalyzer - 交易分析,多重字典,包括交易次数(完成的,open),毛利率,净利率,连胜,连败,这里的Long short应该是开多,开空
### 06_SQN or SystemQualityNumber - 交易系统的性能得分
### 11_Returns - 回报 有4个数值,分别是总收益,平均收益,年化收益,年化收益百分比, 这个跟01的自然年不同,应该是计算近1年的(验证下?)
### 12_VWR Variability-Weighted Return, 是一种改进的夏普比率计算方法,可变加权回报率 
### 15_PeriodStats 基本统计数据,包括回报率,标准差,正/负数量,最佳回报率和最差回报率

def add_analyzer_all(cerebro):

    cerebro.addanalyzer(bt.analyzers.AnnualReturn, _name='_AnnualReturn') # 年化收益率 01
    cerebro.addanalyzer(bt.analyzers.DrawDown, _name='_DrawDown') # 回撤 02
    # 03
    cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='_SharpeRatio') # 夏普比率 04
    cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='_TradeAnalyzer') # 交易分析 05
    cerebro.addanalyzer(bt.analyzers.SQN, _name='_SQN') # 交易系统性能得分 SQN 06
    # 07,08,09,10
    cerebro.addanalyzer(bt.analyzers.Returns, _name='_Returns', tann=252) # 计算252日度收益 11
    cerebro.addanalyzer(bt.analyzers.VWR, _name='_VWR')  # 可变加权回报率 VWR 12
    # 13,14
    cerebro.addanalyzer(bt.analyzers.PeriodStats, _name='_PeriodStats') # 基本数据统计 15
  1. 每一组内置评价的输出添加,用一个新的analyzer_output()函数来实践。

01_年化收益

  • 首先,我们打印sout01的内容,它是一个OrderedDict字典,根据回测的起止日期,得到这些年份下的年化收益率,比如当前数据是从2023年1月到2024年7月,就会得到2023的年化和2024的年化。
  • 我们可以用k,v in sout01.items()的方式,遍历得到这里面项数不确定的所有项
  • 把每一项的key(年份)加上字符串AR(annual Return)或者中文"年化",添加到字典中
python 复制代码
def analyzer_output(result):
    dic1 = {}
    sout01 = result.analyzers._AnnualReturn.get_analysis()  # 年化收益率
    print(sout01)
    for k,v in sout01.items():
        print(f'{k}:{v}')
#         dic1[f'AR{k}']= '%.2f%%'%(v*100)   # 制作为字符串显示,小数点位数控制
        dic1[f'AR{k}']= v*100
    
    print(dic1)

-----------------------------------
策略为 经典MACD , 期末总资金 111569.09  盈利为 11569.09 总共交易次数为 11 ,交易成功率为 36.4%
OrderedDict([(2023, 0.167391800396119), (2024, -0.04428752311724082)])
2023:0.167391800396119
2024:-0.04428752311724082
{'AR2023': 16.739180039611902, 'AR2024': -4.428752311724082}

02_回撤

回撤是两层字典,一层是当前回撤,包括周期数,回撤的资金,回撤的百分比,另一层是'max'即最大回撤的三项信息。

我们在这里只取回撤百分比和最大回撤百分比2个参数。

python 复制代码
def analyzer_output(result):
    dic1 = {}
    sout01 = result.analyzers._AnnualReturn.get_analysis()  # 年化收益率 01
    for k,v in sout01.items():
        dic1[f'{k}年化']= v*100
        
        
    sout02 = result.analyzers._DrawDown.get_analysis()      # 回撤 02
    print(sout02)
    dic1['回撤'] = sout02['drawdown']
    dic1['最大回撤'] = sout02['max']['drawdown']
    
    print(dic1)
---------------------
AutoOrderedDict([('len', 80), ('drawdown', 13.72833873324972), ('moneydown', 17753.89797314338), ('max', AutoOrderedDict([('len', 150), ('drawdown', 14.18411680845229), ('moneydown', 18343.32380264625)]))])

{'2023年化': 16.739180039611902, '2024年化': -4.428752311724082, '回撤': 13.72833873324972, '最大回撤': 14.18411680845229}

04,06,12_夏普,SQN和VWR

这里的3个都不是复杂数据,放在一个实践中进行测试

python 复制代码
def analyzer_output(result):
    dic1 = {}

    sout04 = result.analyzers._SharpeRatio.get_analysis()       # 夏普比率 04
    print(sout04)
    dic1['夏普率'] = sout04['sharperatio']
    
    sout06 = result.analyzers._SQN.get_analysis()     # 交易系统性能得分 SQN 06
    print(sout06)
    dic1['系统性能SQN'] = sout06['sqn']
    
    sout12 = result.analyzers._VWR.get_analysis()     # # 可变加权回报率 VWR 12
    print(sout12)
    dic1['VWR'] = sout12['vwr']
    print(dic1)

-----------------------------
OrderedDict([('sharperatio', 0.4870776964306147)])
AutoOrderedDict([('sqn', 0.7349808981765108), ('trades', 10)])
OrderedDict([('vwr', 4.415307366076662)])
{'夏普率': 0.4870776964306147, '系统性能SQN': 0.7349808981765108, 'VWR': 4.415307366076662}

11_回报-returns

上一节我们测试了252,52,12,1四组returns的数据,最后看下来,估计只需要252的转换成年化回报率的就可以了。

python 复制代码
def analyzer_output(result):
    dic1 = {}
    sout11 = result.analyzers._Returns.get_analysis()    # 计算252日度收益 11
#     print(sout11)
#     OrderedDict([('rtot', 0.10947386238015983), ('ravg', 0.00029992839008262964), 
#                  ('rnorm', 0.07851161276138774), ('rnorm100', 7.8511612761387735)])
    dic1['总回报率'] = sout11['rtot']
    dic1['平均回报率'] = sout11['ravg']
    dic1['年化回报率'] = sout11['rnorm100']
    
    print(dic1)

------------------------
{'总回报率': 0.10947386238015983, '平均回报率': 0.00029992839008262964, '年化回报率': 7.8511612761387735}

根据你提供的输出结果,这是一个有序字典(OrderedDict),其中包含了Returns分析器计算的几个关键指标。以下是每个指标的详细解读:

  • rtot: Total compound return 这个值是0.10947386238015983,表示从回测开始到结束期间,投资组合的总复合回报率。这个值是基于对数方法计算的,反映了投资组合在这段时间内的整体增长情况。
  • ravg: Average return for the entire period (timeframe specific) 这个值是0.00029992839008262964,表示整个回测期间(特定于时间框架)的平均回报率。这个值是通过将总复合回报率除以时间周期数(即回测期间的天数、周数等)得到的。
  • rnorm: Annualized/Normalized return 这个值是0.07851161276138774,表示年化/标准化回报率。这个值是通过将平均回报率乘以一个调整因子(tann参数)得到的,这个因子是根据回测期间的时间框架计算的,以确保回报率的年化。
  • rnorm100: Annualized/Normalized return expressed in 100% 这个值是7.8511612761387735,是将rnorm值乘以100得到的百分比形式的年化/标准化回报率。这个值更容易理解和比较,因为它以百分比的形式表示了年化/标准化回报率。

据上面解释,总回报率是对数方法计算,对我们来讲并不直观,因此这个评价里面,我们直接取第4个数据rnorm100即年化回报率。

15_基本统计数据

python 复制代码
def analyzer_output(result):
    dic1 = {}
    sout15 = result.analyzers._PeriodStats.get_analysis()  # # 基本数据统计 15
#     print(sout15)
    
#     OrderedDict([('average', 0.06155213863943909), ('stddev', 0.10583966175667991), ('positive', 1), 
#      ('negative', 1), ('nochange', 0), ('best', 0.167391800396119), ('worst', -0.04428752311724082)])

    dic1['平均'] = sout15['average']
    dic1['标准差'] = sout15['stddev']
    dic1['最佳收益'] = sout15['best']
    dic1['最大亏损'] = sout15['worst']
    
    print(dic1)

---------------------------------------
{'平均': 0.06155213863943909, '标准差': 0.10583966175667991, '最佳收益': 0.167391800396119, '最大亏损': -0.04428752311724082}

05_交易分析 TradeAnalyzer

TradeAnalyzer中有大量的交易统计的数据,用内置print()打印出来如下:

复制代码
TradeAnalyzer:
  -----------------------------------------------------------------------------
  - total:
    - total: 11
    - open: 1
    - closed: 10
  -----------------------------------------------------------------------------
  - streak:
    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    - won:
      - current: 0
      - longest: 1
    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    - lost:
      - current: 2
      - longest: 2
  -----------------------------------------------------------------------------
  - pnl:
    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    - gross:
      - total: 15641.388419738887
      - average: 1564.1388419738887
    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    - net:
      - total: 15232.749667811115
      - average: 1523.2749667811115
  -----------------------------------------------------------------------------
  - won:
    - total: 4
    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    - pnl:
      - total: 32567.260408578655
      - average: 8141.815102144664
      - max: 16320.18708936759
  -----------------------------------------------------------------------------
  - lost:
    - total: 6
    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    - pnl:
      - total: -17334.51074076754
      - average: -2889.0851234612564
      - max: -7116.267063844403
  -----------------------------------------------------------------------------
  - long:
    - total: 10
    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    - pnl:
      - total: 15232.749667811115
      - average: 1523.2749667811115
      *************************************************************************
      - won:
        - total: 32567.260408578655
        - average: 8141.815102144664
        - max: 16320.18708936759
      *************************************************************************
      - lost:
        - total: -17334.51074076754
        - average: -2889.0851234612564
        - max: -7116.267063844403
    - won: 4
    - lost: 6
  -----------------------------------------------------------------------------
  - short:
    - total: 0
    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    - pnl:
      - total: 0.0
      - average: 0.0
      *************************************************************************
      - won:
        - total: 0.0
        - average: 0.0
        - max: 0.0
      *************************************************************************
      - lost:
        - total: 0.0
        - average: 0.0
        - max: 0.0
    - won: 0
    - lost: 0
  -----------------------------------------------------------------------------
  - len:
    - total: 132
    - average: 13.2
    - max: 29
    - min: 3
    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    - won:
      - total: 93
      - average: 23.25
      - max: 29
      - min: 15
    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    - lost:
      - total: 39
      - average: 6.5
      - max: 11
      - min: 3
    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    - long:
      - total: 132
      - average: 13.2
      - max: 29
      - min: 3
      *************************************************************************
      - won:
        - total: 93
        - average: 23.25
        - max: 29
        - min: 15
      *************************************************************************
      - lost:
        - total: 39
        - average: 6.5
        - max: 11
        - min: 3
    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    - short:
      - total: 0
      - average: 0.0
      - max: 0
      - min: 9223372036854775807
      *************************************************************************
      - won:
        - total: 0
        - average: 0.0
        - max: 0
        - min: 9223372036854775807
      *************************************************************************
      - lost:
        - total: 0
        - average: 0.0
        - max: 0
        - min: 9223372036854775807

这里其实很多参数是重复的,比如won,在long里面又来一遍等,我们把一些认为需要的参数先提取出来。

python 复制代码
def analyzer_output(result):
    dic1 = {}
    
    sout05 = result.analyzers._TradeAnalyzer.get_analysis()   # 交易分析 05
    dic1['关闭交易'] = sout05['total']['closed']              # open的订单还没关闭,下面的数据与最终总资金不同
    dic1['连胜次数'] = sout05['streak']['won']['current']
    dic1['最大连胜'] = sout05['streak']['won']['longest']
    dic1['连负次数'] = sout05['streak']['lost']['current']
    dic1['最大连负'] = sout05['streak']['lost']['longest']
    dic1['毛利润'] = sout05['pnl']['gross']['total']
    dic1['净利润'] = sout05['pnl']['net']['total']
    dic1['总胜次数'] = sout05['won']['total']
    dic1['总盈利'] = sout05['won']['pnl']['total']
    dic1['最大盈利'] = sout05['won']['pnl']['max']
    dic1['总亏次数'] = sout05['lost']['total']
    dic1['总亏损'] = sout05['lost']['pnl']['total']
    dic1['最大亏损'] = sout05['lost']['pnl']['max']
    
    print(dic1)

-------------------------------------
{'关闭交易': 10, '连胜次数': 0, '最大连胜': 1, '连负次数': 2, '最大连负': 2, 
'毛利润': 15641.388419738887, '净利润': 15232.749667811115, '总胜次数': 4, 
'总盈利': 32567.260408578655, '最大盈利': 16320.18708936759, '总亏次数': 6, 
'总亏损': -17334.51074076754, '最大亏损': -7116.267063844403}

其他指标计算输出

某股票软件指标说明

我们来看某个股票软件交易系统评测的评价指标说明

除去刚才backtrader内置直接能获取的,我们还有一些指标需要添加

  • 期末权益,盈亏比,盈亏时间比,盈亏次数比
  • 阿尔法收益率,贝塔收益率
  • 胜率,区间回撤,持仓周期数/持仓周期比率
  • MAR比较,年化波动率,标准离差

这些指标中,有些是直接用内置评价指标简单四则运算就能得到(比如盈亏比),有的需要把数据记录取出来进行sum()计算得到(比如区间涨幅),还有的计算公式比较复杂(比如阿尔法,年化波动率等)

所以,先把需要通过数据记录进行计算的添加到analyzer中去

python 复制代码
def add_analyzer_all(cerebro):

    cerebro.addanalyzer(bt.analyzers.AnnualReturn, _name='_AnnualReturn') # 年化收益率 01
    cerebro.addanalyzer(bt.analyzers.DrawDown, _name='_DrawDown') # 回撤 02
    # 03
    cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='_SharpeRatio') # 夏普比率 04
    cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='_TradeAnalyzer') # 交易分析 05
    cerebro.addanalyzer(bt.analyzers.SQN, _name='_SQN') # 交易系统性能得分 SQN 06
    # 07,08,09,10
    cerebro.addanalyzer(bt.analyzers.Returns, _name='_Returns', tann=252) # 计算252日度收益 11
    cerebro.addanalyzer(bt.analyzers.VWR, _name='_VWR')  # 可变加权回报率 VWR 12
    # 13,14
    cerebro.addanalyzer(bt.analyzers.PeriodStats, _name='_PeriodStats') # 基本数据统计 15
    
    
    # 需要通过数据记录进行计算    
    cerebro.addanalyzer(bt.analyzers.TimeReturn, _name='pnl') # 返回收益率时序数据    # 03
    cerebro.addanalyzer(bt.analyzers.PositionsValue, _name='_PositionsValue')  # position 08
    cerebro.addanalyzer(bt.analyzers.Transactions, _name='_Transactions')  # Transactions 09

添加期末权益,胜率,盈亏比,盈亏时间比

期末权益相当于就是最后的总资产,可以从broker.getvalue()来得到,在analyzer返回的result中也能获取到。

盈亏比即 总盈利/总亏损, 而胜率 = 总胜次数/ 总闭环交易次数 *100

盈亏时间比是盈利的周期数/ 总周期数

python 复制代码
def analyzer_output(result):
    list1 = []
    dic1 = {}
    
    sout05 = result.analyzers._TradeAnalyzer.get_analysis()   # 交易分析 05
    dic1['关闭交易'] = sout05['total']['closed']              
    dic1['总胜次数'] = sout05['won']['total']
    dic1['总盈利'] = sout05['won']['pnl']['total']
    dic1['总亏损'] = sout05['lost']['pnl']['total']

    # 胜率与盈亏比
    dic1['胜率'] = dic1['总胜次数']/dic1['关闭交易'] * 100
    dic1['盈亏比'] = abs(dic1['总盈利']/dic1['总亏损'] )
    
    # 期末权益 
    dic1['期末权益'] = result.broker.getvalue()
    
    # 盈亏时间比 盈利周期数/亏损周期数
    dic1['盈亏时间比'] = sout05['len']['won']['total'] / sout05['len']['lost']['total']
    print(dic1)

-----------------------------------
{'关闭交易': 10, '总胜次数': 4, 
'总盈利': 32567.260408578655, '总亏损': -17334.51074076754, 
'胜率': 40.0, '盈亏比': 1.8787527894852278, 
'期末权益': 111569.09090491984, '盈亏时间比': 2.3846153846153846}

添加区间涨幅,持仓周期数

区间涨幅不应该使用pnl的数据,因为pnl是策略应用的情况下产生的收益,而区间涨幅与策略无关,只是这支股票的走势情况,所以使用起止日期的收盘价之差即可。

而持仓周期数,可以利用08_positions持仓来进行统计,把不为0的数值count出来。

python 复制代码
from datetime import datetime

def analyzer_output(result):
    list1 = []
    dic1 = {}

    # 区间涨幅
    len1 = len(result.data.close)  # 总共周期数
    date1 = result.data.datetime[-len1+1]   # 起始日期
    print(bt.num2date(date1)                # 转成日期格式打印
    # 2023-01-11 00:00:00

    close_end = result.data.close[0]        # [0]代表最后一个数据
    close_start = result.data.close[-len1+1]  # 取第一个数据的收盘价

    qjzf = (close_end - close_start)/ close_start
    qjzf_pct = qjzf * 100

    dic1['区间涨幅'] = qjzf_pct



    # 持仓周期数  - 可以直接用positions来统计
    sout08 = result.analyzers._PositionsValue.get_analysis()
#     result.analyzers._PositionsValue.pprint()
#     OrderedDict([(datetime.date(2023, 1, 11), [0.0]),  # 日期做index, 值是List,但仅有一项
#              (datetime.date(2023, 1, 12), [0.0]),

    s3 = dict(sout08)   # 一层字典,转为dict类型,便于处理取list中的第一项
    for x in s3:
        s3[x] = s3[x][0]    # 将值更改为list中的第一项[0]
    s2 = pd.Series(s3)      # 处理后的dict可直接转为Series类型
    cnt_zero = s2.eq(0).sum()   # 值为0的项   = 211
    cnt_all = s2.count()      # 非NaN项,即所有有效项  365
    cnt_position = cnt_all - cnt_zero
    
    dic1['持仓周期数'] = cnt_position
    dic1['持仓占比'] = cnt_position/cnt_all * 100

    print(dic1)
------------------------
{'区间涨幅': -3.2258064516129057, '持仓周期数': 154, '持仓占比': 42.19178082191781}

添加MAR率

python 复制代码
def analyzer_output(result):
    list1 = []
    dic1 = {}
    
    # MAR比率  - 年化收益率/最大回撤比
    sout11 = result.analyzers._Returns.get_analysis()    # 计算252日度收益 11
    dic1['年化回报率'] = sout11['rnorm100']
    sout02 = result.analyzers._DrawDown.get_analysis()      # 回撤 02
    dic1['最大回撤'] = sout02['max']['drawdown']
    
    dic1['MAR率'] = dic1['年化回报率']/dic1['最大回撤']  
    

    print(dic1)

-------------------------------
{'年化回报率': 7.8511612761387735, '最大回撤': 14.18411680845229, 
'MAR率': 0.55351781024958}

添加近期涨幅指标

对于基金PK的近期指标仍念念不忘,最后还是把它们给做出来。

python 复制代码
def analyzer_output(result):
    dic1 = {}
    sout03 = result.analyzers.pnl.get_analysis()  
#     result.analyzers.pnl.pprint()
    a2 = pd.Series(sout03)
#     print(series_pnl)

    recent_1m = a2[-21:].sum() *100             # 近1月
    recent_3m = a2[-64:].sum() *100
    recent_6m = a2[-126:].sum() *100
    recent_1y = a2[-252:].sum() *100
    
    dic1['近1月'] = recent_1m
    dic1['近3月'] = recent_3m 
    dic1['近6月'] = recent_6m 
    dic1['近1年'] = recent_1y 

    print(dic1)

-----------------------------
{'近1月': -3.0154148669233605, 
'近3月': -12.396872470662734, 
'近6月': -3.4178941682405317, 
'近1年': 13.508146407432731}

下一步实践计划

目前,我们还有几个指标没有做进来,包括阿尔法收益,贝塔收益,波动率等,这些都涉及到比较复杂的计算,我们可以自己做,但容易出错,我想大家也不希望辛苦做出来的由于一些细小的疏漏,或对经济学或数学的不太理解,出来的是一个错误的数值吧。

在上一节也提到了,backtrader有所谓的内置的pyfolio评价指标,而实际上还有一个pyfolio库,而且不是那么容易应用,不过似乎这些阿尔法收益等评价在pyfolio里面都有,后续我们再这个方向上再实践一下~

最后,我们还需要把上面这些评价指标用一个结构记录下来,并在多股票和多策略循环后在一张表里进行输出。暂时不打算再做对于这张表的处理,这些后处理的数据统计分析工作可以在excel表格中完成,或者用Pandas处理。

相关推荐
感谢地心引力2 分钟前
【Python】基于 PyQt6 和 Conda 的 PyInstaller 打包工具
数据库·python·conda·pyqt·pyinstaller
xiaohanbao092 小时前
Transformer架构与NLP词表示演进
python·深度学习·神经网络
love530love2 小时前
【笔记】 Podman Desktop 中部署 Stable Diffusion WebUI (GPU 支持)
人工智能·windows·笔记·python·容器·stable diffusion·podman
程序员晚枫2 小时前
Python 3.14正式发布!这5大新特性太炸裂了
python
先做个垃圾出来………3 小时前
SortedList
python
这里有鱼汤3 小时前
从DeepSeek到Kronos,3个原因告诉你:Kronos如何颠覆传统量化预测
后端·python·aigc
晓宜3 小时前
Java25 新特性介绍
java·python·算法
深栈3 小时前
机器学习:决策树
人工智能·python·决策树·机器学习·sklearn
MediaTea3 小时前
Python:匿名函数 lambda
开发语言·python
hui函数4 小时前
Python全栈(基础篇)——Day07:后端内容(函数的参数+递归函数+实战演示+每日一题)
后端·python