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

news2024/12/28 20:28:58

上节回顾

上一节我们对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()来美化打印,于是做了以下尝试:

# 首先,你可能去 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

# __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早就给你安装过了。

###############################################################################
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()吧?

# 在看到 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的函数内容在内置评价输出时都不需要任何更改

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 -------------

2. 在添加analyzer的函数中,可以直接添加需要的评价,这个在实践过程中也不需要更改动

# 加入直接打分或统计的评价指标
### 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

3. 每一组内置评价的输出添加,用一个新的analyzer_output()函数来实践。

01_年化收益

  • 首先,我们打印sout01的内容,它是一个OrderedDict字典,根据回测的起止日期,得到这些年份下的年化收益率,比如当前数据是从2023年1月到2024年7月,就会得到2023的年化和2024的年化。
  • 我们可以用k,v in sout01.items()的方式,遍历得到这里面项数不确定的所有项
  • 把每一项的key(年份)加上字符串AR(annual Return)或者中文“年化”,添加到字典中
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个参数。

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个都不是复杂数据,放在一个实践中进行测试

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的转换成年化回报率的就可以了。

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_基本统计数据

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里面又来一遍等,我们把一些认为需要的参数先提取出来。

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中去

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

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

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出来。

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率

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的近期指标仍念念不忘,最后还是把它们给做出来。

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处理。

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

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

相关文章

建筑企业如何搭建数据仓库?做好这三步,大幅节约企业成本!

在当今这个数据驱动的时代,建筑企业正面临着前所未有的挑战和机遇。随着项目规模的扩大和市场环境的复杂化,如何有效管理和分析海量数据,已经成为提升企业竞争力的关键。数据仓库作为企业数据管理的核心,不仅能够整合分散的数据资…

攻防世界 reverse_re3

前言:做题笔记。 下载解压 查壳。 64ida 打开 查找字符并跟进 wasd一看,这题就是关于迷宫的题。跟进看看。 懵。。。不过还是得仔细看看。 观察发现。 进去看看。 然后最开始,因为数据太多把我卡住了。。。 回过头仔细看看。 猜测dword_202…

企业建站技术路线探索

前言 企业站是指企业或公司创建的官方网站,用于展示企业信息、产品和服务。它通常包括公司简介、产品或服务介绍、联系方式、新闻更新等内容。企业站的目的是提升品牌形象、提供客户服务和促进业务发展。在跨境贸易中,企业建站尤为关键,因为…

数据库集群技术

源码安装mysql mysql.com [https://downloads.mysql.com/archives/community/]: 官网下载 安装依赖性: [rootmysql-node2 ~]# dnf install cmake gcc-c openssl-devel \ ncurses-devel.x86_64 libtirpc-devel-1.3.3-8.el9_4.x86_64.rpm rpcgen.x86_64 下载并解压源…

Netty系列-1 NioEventLoopGroup和NioEventLoop介绍

背景 从本文开始开启一个新的专题Netty系列,用于收集Netty相关的文章,内容包含Netty的使用方式、运行原理等。 基于io.netty:netty-all:4.1.49.Final版本进行介绍 1.NioEventLoopGroup 介绍NioEventLoopGroup之前,有几个相关的组件需要提前…

idea导入maven项目(别人的项目)爆红

作为一个经常学习交流的人,或者工作需要,我们都或多或少会把别人写好的代码拷贝过来学习或编辑,大多数时候都是把整个项目拿过来;但是往往把代码拿到之后放在自己电脑用 idea 打开的时候就会出现 pom.xml 文件红线报错,然后倒入的…

大模型企业应用落地系列》基于大模型的对话式推荐系统》技术架构设计全攻略

注:此文章内容均节选自充电了么创始人,CEO兼CTO陈敬雷老师的新书《自然语言处理原理与实战》(人工智能科学与技术丛书)【陈敬雷编著】【清华大学出版社】 文章目录 大模型企业应用落地系列全貌基于大模型的对话式推荐系统》技术架…

如何使用ssm实现投稿系统+vue

TOC ssm231论文投稿系统vue 系统概述 1.1 研究背景 如今互联网高速发展,网络遍布全球,通过互联网发布的消息能快而方便的传播到世界每个角落,并且互联网上能传播的信息也很广,比如文字、图片、声音、视频等。从而,…

软件测试 | 概念(1)

目录 前言 需求的概念 开发模型 软件的生命周期 常见开发模型 瀑布模型 螺旋模型 增量模型,迭代模型 敏捷模型 Scrum模型 测试模型 V模型 W模型(双V模型) 前言 测试:验证软件的特性是否满足用户的需求。 用户的需求…

vue3前端界面布置到服务器,使用户能用网址访问到界面

1.下载Nginx: nginx: download 2.下载好的Nginx解压缩, 在解压缩的文件夹下找到conf > nginx.conf,修改nginx.conf中的server,配置服务器的ip地址和端口号 3.执行npm run build命令,vue生成的dist下的文件全部放置在…

基于微信小程序的行李寄存管理系统的设计与实现(论文+源码)_kaic

基于微信小程序的行李寄存管理系统的设计与实现(论文源码)_kaic 摘 要 人们外出旅行的时候,经常会需要到行李寄存的服务。行李寄存处在全国各地都很常见。现存的行李寄存方式很传统,适合小规模的行李寄存,当行李数量较多时,就…

【领域驱动设计 打通DDD最小闭环】三 模型的建立-领域建模

本篇BLOG为DDD流程的第二步,在模型的建立阶段,领域专家与技术人员通过领域建模来完成更为细致的模型建立讨论 领域建模的目的 领域建模主要有两个目的: 将知识可视化,准确、深刻地反映领域知识,并且在业务和技术人…

神经网络——非线性激活

1 非线性激活 1.1 几种常见的非线性激活: ReLU (Rectified Linear Unit)线性整流函数 Sigmoid 1.2代码实战: 1.2.1 ReLU import torch from torch import nn from torch.nn import ReLUinputtorch.tensor([[1,-0.5],[-1,3]])inputtorch.reshape(…

HT97226 160mW免输出耦合电容的立体声耳机放大器

特点: 输出无需隔直流电容 卓越的低音效果 无咔嗒/噼噗声,50uV (typical) Vos 低THDN:最低0.002% 低噪声,VN: 8.5uV 支持单端输入和全差分输入 2.5V至6V较宽的电源工作范围 输出功率:80mW(fIN1kHz,VDD3.6V,RL32Ω, THDN1%) 160mW(PVDD5V,fIN1kHz,RL32Ω…

Java中的抽象类 abstract

抽象方法: 将共性的行为(方法)抽取到父类之后。由于每一个子类执行的内容不一样,所以,在父类中不能确定具体的方法体。该方法就可以定义为抽象方法。 抽象类 如果一个类中存在抽象方法,那么该类就必须声…

【软件测试】软件测试-----概念篇

软件测试相关概念 一.需求的相关概念1.1 用户需求1.2 软件需求 二. 开发模型2.1 模型的基本概念.2.2 软件的生命周期2.2.1 理解软件生命周期每个阶段的具体任务 2.3 常见的开发模型.2.3.1 瀑布模型(适用场景:需求固定的小项目).2.3.2 螺旋模型(适用场景:…

ollama+llama3.1 405B 简介

ollamallama3.1 简介 Llama 3.1是一款来自Meta的最新型号,提供8B、70 B和405 B模型。 llama3.1:latestllama3.1:8bllama3.1:70bllama3.1:405bllama3.1:8b-instruct-fp16llama3.1:8b-instruct-q2_Kllama3.1:8b-instruct-q3_K_Sllama3.1:8b-instruct-q3_K_Mllama3.1…

python如何调用另一个文件中的函数

在同一个文件夹下 调用函数: A.py文件: def add(x,y):print(和为:%d%(xy)) B.py文件: import A A.add(1,2) 或 from A import add add(1,2) 在不同文件夹下 A.py文件的文件路径:E:\PythonProject\winycg B.py文件&a…

构建并升级openssh至OpenSSH_9.8p1

组件说明OpenSSH_9.8p1最新版本(2024年8月)OpenSSL 1.1.1pCentOS7中默认是OpenSSL 1.0.2k-fips 26 Jan 2017版本,OpenSSH_9.8p1不支持CentOS7主要是因为有大量CentOS老版本需要升级RPM,需要适配,故选择此版本。AnolisO…

输入一个正的奇数n(1≤n≤9),打印一个高度为n的、由“*”组成的沙漏图案。当n=5时,输出如下沙漏图案:

输入一个正的奇数n&#xff08;1≤n≤9&#xff09;&#xff0c;打印一个高度为n的、由“*”组成的沙漏图案。当n5时&#xff0c;输出如下沙漏图案&#xff1a; int main(){int i,j,n,m;scanf("%d",&n);m n / 2;for(im1;i<1;i--){ //m1是中间数for(jm1-i;j&g…