量化交易backtrader实践(四)_评价统计篇(1)_内置评价

背景

通过对基础的学习和不断深入的实践,当我们已经能够制作出快速获取数据,以及制作出多个股票 乘上多种策略进行回测的部分的时候,我们就会明显发现数据有点多了,比如10支股票都用了3种策略就得到30段数据,一页显示不下,眼睛看不过来,也不知道怎么对比策略哪个好。所以这个时候我们就需要添加评价指标。

我们可以先看一下可能大家比较熟悉的对于基金进行PK时所展现的一些内容:

首先是业绩表现,即不同阶段的涨跌幅,能在一张图上画不同基金的线以看到它们之间的差别

  • 近一周
  • 近一月
  • 近半年
  • 近1年
  • 今年来 等

然后有风险数据对比,包括

  • 波动率
  • 夏普比率
  • 最大回撤
  • 收益回撤比等

以上,就可以生成基本的对比和评价,我们也可以通过这个对于评价指标有一个简单的印象。后续我们再对评价指标的原理和定义进行细致的学习,我先把一些参考的资源列在这里

股票软件评测系统

大部分的商用的股票及交易软件都会有类似交易评测系统的功能。在这个评测系统中,会有几个页面,包括

  • 评测公式的选择(以MACD交易系统为例),
  • 建仓规则(时间段和资金),
  • 交易方式(手续费,多头/空头价,滑点),
  • 平仓规则(止损,止盈,目标周期平仓),
  • 评测品种(选择哪些股票)

在进行评测后,会出具报告,一些评价指标就会列举出来,比如 盈利数,胜率,净利润,收益率,最大回撤等,还有一些细节的数据例如平均盈利,盈亏比,连涨次数......

其实,大多数时候,大多数人只关心胜率和收益率,连回撤都懒得看,盈亏比也并不关心。对于单支或几支股票在单策略上的回测的确可以这样,注意力可能直接放在K线图上,这里买,那里应该卖,策略是否可以优化等。但如果股票数量很多,策略也很多,某些股票走势反常,有些股票就不适应某些策略等,那就需要得到更简洁、有效的评价指标,通过对这些评价进行分析处理(例如排序,加权计算等)来筛选出合适的股票以及合适的应用策略甚至合适的时机。

Backtrader内置评价

有哪些内置评价

我们已经看到了基金PK有模板式的评价指标,股票软件评价也有模板式的评价指标,那么我们在backtrader中也是有内置的评价指标的。

我们可以通过在spyder或其他IDE中按Ctrl+左键点击进入相关.py文件

# CTRL + 点击bt.analyzers的analyzers
cerebro.addanalyzer(bt.analyzers.TimeReturn, _name='pnl')



# 跳到 \site-packages\backtrader\analyzers\__init__.py

###############################################################################
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

# The modules below should/must define __all__ with the objects wishes
# or prepend an "_" (underscore) to private classes/variables

from .annualreturn import *
from .drawdown import *
from .timereturn import *
from .sharpe import *
from .tradeanalyzer import *
from .sqn import *
from .leverage import *
from .positions import *
from .transactions import *
from .pyfolio import *
from .returns import *
from .vwr import *

from .logreturnsrolling import *

from .calmar import *
from .periodstats import *

我们现在有AI助手了,直接把这些代码扔给AI,它就返回一个清楚的解释

您提供的代码片段看起来像是一个Python模块的导入语句,其中包含了多个与金融分析和交易策略相关的库。这些库涉及多种金融指标和分析工具,如年化收益率、最大回撤、时间收益率、夏普比率、交易分析、系统质量数(SQN)、杠杆率、持仓分析、交易记录分析、Pyfolio(一个金融分析库)、收益率分析、变异权重回报(VWR)和卡马尔比率等。

这些库和模块通常用于量化交易策略的开发和评估。例如:

  • annualreturn: 用于计算年化收益率。
  • drawdown: 用于计算最大回撤。
  • timereturn: 用于计算时间收益率。
  • sharpe: 用于计算夏普比率。
  • tradeanalyzer: 用于分析交易策略的表现。
  • sqn: 用于计算系统质量数。
  • leverage: 用于计算杠杆率。
  • positions: 用于分析持仓情况。
  • transactions: 用于分析交易记录。
  • pyfolio: 用于金融分析和投资组合优化。
  • returns: 用于分析收益率。
  • vwr: 用于计算变异权重回报。
  • logreturnsrolling: 用于计算对数收益率的滚动统计。
  • calmar: 用于计算卡马尔比率。
  • periodstats: 用于计算特定时间周期的统计数据。

这些工具和库可以帮助投资者评估策略的表现,识别潜在的风险,并进行有效的策略优化。

了解了这个评价指标大概的信息后,我们要一步一步实践,看看这些评价指标具体是什么,哪些需要用,哪些看不懂,哪些不能用。

内置评价实践

00_评价程序

首先,Analyzer在哪里添加;然后,结果在哪里输出。

# 创建和配置Cerebro引擎
cerebro = Cerebro()

# 添加策略和分析器
cerebro.addstrategy(MyStrategy)
cerebro.addanalyzer(xxxxx)

# 运行回测
results = cerebro.run()

# 获取分析结果
strat = results[0]
analysis = strat.analyzers.xxxx.get_analysis()

由于我们有15个评价指标要实践,而我又不希望每次都把上面的部分重写一遍,所以做了一个函数来简化我们的工作量,这里的df_list是自选股列表,run_strategy是策略,i是自选股列表中的第i支股票,sdate1,sdate2分别是起始日期和结束日期,然后禁止了绘图,且不输出交易的log,防止它们占用屏幕空间,这样把焦点都只放到评价指标的输出上来。

def run_main_analyser1 (df_list,run_strategy,i, sdate1,sdate2, myplot=False, logoff=1): 
    iSel = i
    code_ = df_list.iloc[iSel,0]

    df1 = get_bt_feed_data(code_)
    sdate = sdate1
    edate = sdate2

    cerebro = bt.Cerebro()
    cerebro = bt.Cerebro(stdstats=False)    
    cerebro.addobserver(bt.observers.Broker)
    cerebro.addobserver(my_Trades)
    cerebro.addobserver(my_BuySell)
    cerebro.addobserver(bt.observers.DrawDown)
    add_analyzer_all(cerebro)                # 加入---------analyzer -------------

    cerebro.addstrategy(run_strategy, log_off= logoff)         

    data = bt.feeds.PandasData(dataname=df1, fromdate=sdate, todate=edate) 
    cerebro.adddata(data, name=code_)   

    cerebro.broker.setcash(100000.0)    
    mycomm = StockCommission(commission=0.0002)  
    cerebro.broker.addcommissioninfo(mycomm)     
    cerebro.addsizer(bt.sizers.PercentSizer,percents=40) 
    cerebro.broker.set_coc(True)        

    result = cerebro.run()     
    strat = result[0]
    analyzer_output(strat)                   # 输出--------analyzer -------------
    
    
    if logoff !=1:
        print('最终资产价值: %.2f' % cerebro.broker.getvalue()) # 打印最终结果

    if myplot:
        cerebro.plot(style='candle')

这样我们这个函数就不需要每次进行更改,只要更新与Analyzer相关的两个函数就可以了。

01_年化收益率-annualreturn

年化收益率就是字面上的意思,以自然年(例如2023,2024)计算其年化收益率。

def add_analyzer_all(cerebro):
    cerebro.addanalyzer(bt.analyzers.AnnualReturn, _name='_AnnualReturn') # 年化收益率


def analyzer_output(result):
    print("--------------- AnnualReturn -----------------")
    sout = result.analyzers._AnnualReturn.get_analysis()
    print(sout)

接着我们在notebook中再运行一下,这句每次都要运行一遍,后面就偷懒省略了:

run_main_analyser1(df,St_MACD_class,3,d2,d1,myplot=False, logoff=1)

 如果myplot=True,就会在notebook中绘制图形

得到的结果如下:

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

这里得到的数据是OrderedDict()类型,我们可以使用几种不同的方式来处理它。

# 第一种方式  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)

-------------------------

2023: 0.167391800396119       # 第一种
2024: -0.04428752311724082

2023    0.167392              # 第二种
2024   -0.044288
dtype: float64

{2023: 0.167391800396119, 2024: -0.04428752311724082}  # 第三种

02_回撤-Drawdown

def add_analyzer_all(cerebro):
    cerebro.addanalyzer(bt.analyzers.DrawDown, _name='_DrawDown') # 回撤

def analyzer_output(result):
    print("--------------- DrawDown -----------------")
    sout = result.analyzers._DrawDown.get_analysis()
    print(sout)

运行后得到结果为:

策略为 经典MACD , 期末总资金 111569.09  盈利为 11569.09 总共交易次数为 11 ,交易成功率为 36.4%
--------------- DrawDown -----------------
AutoOrderedDict([('len', 80), ('drawdown', 13.72833873324972), 
('moneydown', 17753.89797314338), 
('max', AutoOrderedDict([('len', 150), ('drawdown', 14.18411680845229), ('moneydown', 18343.32380264625)]))])

 很明显,这里的输出结果是个多重字典,我们直接用dict(sout)是得不到最终要的结果的,这里可以用pd.DataFrame(sout),但出来的表现形式并不是我们想要的。

于是,用了一个笨方法来进行输出,通过函数递归调用把每一层字典都遍历,先转成一个标准的字典类型,然后再按层级缩进进行打印输出:

from collections import OrderedDict

def convert_to_regular_dict(odict):
    if isinstance(odict, OrderedDict):
        return {k: convert_to_regular_dict(v) for k, v in odict.items()}
    else:
        return odict


def print_dict(d, indent=0):
    """
    打印字典,按层级缩进。    """
    for key, value in d.items():
        print('  ' * indent + str(key) + ':', end=' ')
        if isinstance(value, dict):
            print()
            print_dict(value, indent + 1)
        else:
            print(value)

这样能得到结果:

len: 80
drawdown: 13.72833873324972
moneydown: 17753.89797314338
max: 
  len: 150
  drawdown: 14.18411680845229
  moneydown: 18343.32380264625

 所以Drawdown得到的结果是2组,1是当前的回撤周期+回撤百分比+回撤金额;2是最大回撤的周期+回撤百分比+回撤金额,这些数据是我们最常用的评价指标之一。

03_timerun(时间序列收益 - 历史收益)

可以理解为基金信息中的历史收益,每天的收益率。这里偷懒直接用pandas.Series()处理得到Series类型的数据。

def add_analyzer_all(cerebro):
    cerebro.addanalyzer(bt.analyzers.TimeReturn, _name='_pnl') # 返回收益率时序数据
    
def analyzer_output(result):
    print("--------------- pnl -----------------")
    sout = result.analyzers._pnl.get_analysis()
#     print(sout)
    s = pd.Series(result.analyzers._pnl.get_analysis()) 
    print(s.tail(15))
策略为 经典MACD , 期末总资金 111569.09  盈利为 11569.09 总共交易次数为 11 ,交易成功率为 36.4%
--------------- pnl -----------------
2024-06-26    0.008373
2024-06-27   -0.007266
2024-06-28   -0.001046
2024-07-01    0.005233
2024-07-02    0.009371
2024-07-03    0.013410
2024-07-04   -0.022394
2024-07-05    0.002082
2024-07-08   -0.012469
2024-07-09    0.003156
2024-07-10   -0.012586
2024-07-11    0.019120
2024-07-12   -0.003127
2024-07-15   -0.008365
2024-07-16   -0.002109
dtype: float64

04_夏普比率-SharpRatio

夏普比率是描述股票或组合在单位风险下的所能获得超额收益的程度。它将一只标的或组合的风险归一化,便于更好的比较组合之间的有效性。数值越高代表考虑风险的情况下股票或组合表现越好。这里我们先不探讨它是怎么得到的,我们先实践内置评价看它的输出是什么。

从下面的结果看,这里输出是只有1项的字典,key是"sharperatio",value为数值 0.487

def add_analyzer_all(cerebro):
    cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='_SharpeRatio') # 夏普比率
    
def analyzer_output(result):
    print("--------------- Sharp Ratio -----------------")
    sout = result.analyzers._SharpeRatio.get_analysis()
    print(sout)
    for key, value in sout.items():
        print(f'{key}: {value}')
策略为 经典MACD , 期末总资金 111569.09  盈利为 11569.09 总共交易次数为 11 ,交易成功率为 36.4%
--------------- Sharp Ratio -----------------
OrderedDict([('sharperatio', 0.4870776964306147)])
sharperatio: 0.4870776964306147

05_tradeanalyzer 交易分析 

def add_analyzer_all(cerebro):
    cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='_TradeAnalyzer')
    
def analyzer_output(result):
    print("---------------_TradeAnalyzer -----------------")
    sout = result.analyzers._TradeAnalyzer.get_analysis()
#     print(sout)
    sdict = convert_to_regular_dict(sout)
    print_dict(sdict)

 在这里先说明一下交易分析的结构

total - 交易总次数/ open / closed次数

streak - 连胜/最高连胜次数 , 连败/最高连败次数

pnl - 毛利率(总,平均);净利率(总,平均)

won - 盈利(次数,总盈利,平均,最大盈利)

lost - 亏损(次数,总亏损,平均,最大亏损)

long - 买多 (次数,pnl, won, lost)   -- A股只能做多,所以long有数据

short- 买空 (次数,pnl, won, lost)  -- short没有数据,都是0

len - 各种情况的周期数(略)

---------------------------------------------------------------------

策略为 经典MACD , 期末总资金 111569.09  盈利为 11569.09 总共交易次数为 11 ,交易成功率为 36.4%
---------------_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

06_SQN(System Quality Number)

SQN(System Quality Number)是一个由 Van Tharp 提出的评价指标,用于衡量交易系统的性能。<br>SQN 数值是基于交易结果的期望值、标准差和交易次数计算出来的。

  • SQN 值大于 1.6 表示交易系统可能具有正期望值。
  • SQN 值在 1.6 到 2.0 之间表示交易系统很好。
  • SQN 值在 2.0 到 2.5 之间表示交易系统非常好。
  • SQN 值大于 2.5 表示交易系统非常出色。

SQN的结果是有2个项的字典,分别是 'sqn' : 0.735 和 ’trades':10 

def add_analyzer_all(cerebro):
    cerebro.addanalyzer(bt.analyzers.SQN, _name='_SQN') 
    
def analyzer_output(result):
    print("---------------SQN -----------------")
    sout = result.analyzers._SQN.get_analysis()
    print(sout)
    print(f'SQN: {sout["sqn"]}, Trades: {sout["trades"]}')

------------------------------
策略为 经典MACD , 期末总资金 111569.09  盈利为 11569.09 总共交易次数为 11 ,交易成功率为 36.4%
---------------SQN -----------------
AutoOrderedDict([('sqn', 0.7349808981765108), ('trades', 10)])
SQN: 0.7349808981765108, Trades: 10

 

 07_杠杆-leverage

毛杠杆率(Gross Leverage)的分析器,
输出的结果就是对应每个交易日,买了股票的那部分占净资产的比重,也可以简单理解为仓位;
这里我们用的是当前资金的40%左右(买100整数倍的股票会略有偏差),所以出来的数据基本上都是0.4上下,并且如果股票涨了,值会增加,跌了值会减小。

def add_analyzer_all(cerebro):
    cerebro.addanalyzer(bt.analyzers.GrossLeverage, _name='_leverage') 
    
def analyzer_output(result):
    print("---------------_leverage -----------------")
    sout = result.analyzers._leverage.get_analysis()
#     print(sout)
#     for dt, leverage in sout.items():
#         print(f'Datetime: {dt}, Gross Leverage: {leverage}')
    dfout = pd.Series(sout)
    print(dfout.iloc[40:60])
策略为 经典MACD , 期末总资金 111569.09  盈利为 11569.09 总共交易次数为 11 ,交易成功率为 36.4%
---------------_leverage -----------------
2023-03-15    0.000000
2023-03-16    0.000000
2023-03-17    0.000000
2023-03-20    0.000000
2023-03-21    0.000000
2023-03-22    0.000000
2023-03-23    0.401787
2023-03-24    0.396490
2023-03-27    0.397676
2023-03-28    0.397676
2023-03-29    0.395300
2023-03-30    0.403532
2023-03-31    0.407565
2023-04-03    0.401787
2023-04-04    0.395896
2023-04-06    0.388062
2023-04-07    0.000000
2023-04-10    0.000000
2023-04-11    0.000000
2023-04-12    0.000000
dtype: float64

08_ 持仓-positions

  • positionsValue 就是持仓的值
  • 一旦买入就有持仓,涨了则这个值会增大,跌了则这个值会减小
  • 这样看positions与上面的leverage其次是非常类似的,它们都是每日的数据记录,不是直接的打分评价
def add_analyzer_all(cerebro):
    cerebro.addanalyzer(bt.analyzers.PositionsValue, _name='_PositionsValue') 
    
def analyzer_output(result):
    print("---------------PositionsValue -----------------")
    sout = result.analyzers._PositionsValue.get_analysis()
#     print(sout)
    for dt, v in sout.items():
        print(f'Datetime: {dt}, Value: {v}')
策略为 经典MACD , 期末总资金 111569.09  盈利为 11569.09 总共交易次数为 11 ,交易成功率为 36.4%
---------------PositionsValue -----------------
Datetime: 2023-01-11, Value: [0.0]
Datetime: 2023-01-12, Value: [0.0]

Datetime: 2023-03-21, Value: [0.0]
Datetime: 2023-03-22, Value: [0.0]
Datetime: 2023-03-23, Value: [40293.39853300734]
Datetime: 2023-03-24, Value: [39413.20293398534]
Datetime: 2023-03-27, Value: [39608.801955990224]
Datetime: 2023-03-28, Value: [39608.801955990224]
Datetime: 2023-03-29, Value: [39217.60391198044]
Datetime: 2023-03-30, Value: [40586.797066014675]
Datetime: 2023-03-31, Value: [41271.393643031784]
Datetime: 2023-04-03, Value: [40293.39853300734]
Datetime: 2023-04-04, Value: [39315.40342298288]
Datetime: 2023-04-06, Value: [38044.009779951106]
Datetime: 2023-04-07, Value: [0.0]
Datetime: 2023-04-10, Value: [0.0]
Datetime: 2023-04-11, Value: [0.0]

 09_交易记录-transaction

transaction在这里就表示交易的意思,它记录了每笔交易的数据。包括日期,金额,价格,sid是导入backtrader的股票的ID,symbol是股票代号,以及值。

我们的数据回测这里输出总共21条,从前面的tradeanalyzer可知 close的交易数为10,则有10对买和卖共20笔,open的1笔即还有一笔开仓但没卖,加起来总共21笔。

def add_analyzer_all(cerebro):
    cerebro.addanalyzer(bt.analyzers.Transactions, _name='_Transactions') 
    
def analyzer_output(result):
    print("---------------Transactions -----------------")
    sout = result.analyzers._Transactions.get_analysis()
#     print(sout)
    print(len(sout))
    for date, transactions in sout.items():
        print(f'Date: {date}')
        for x in transactions:
            print(x)
            print(f'  Amount: {x[0]}, Price: {x[1]}, Sid: {x[2]}, Symbol: {x[3]}, Value: {x[4]}')

 

策略为 经典MACD , 期末总资金 111569.09  盈利为 11569.09 总共交易次数为 11 ,交易成功率为 36.4%
---------------Transactions -----------------
21
Date: 2023-03-23 00:00:00
[9779.9511002445, 4.09, 0, '601086', -40000.0]
  Amount: 9779.9511002445, Price: 4.09, Sid: 0, Symbol: 601086, Value: -40000.0
Date: 2023-04-07 00:00:00
[-9779.9511002445, 3.89, 0, '601086', 38044.009779951106]
  Amount: -9779.9511002445, Price: 3.89, Sid: 0, Symbol: 601086, Value: 38044.009779951106
Date: 2023-05-04 00:00:00
[10454.333757131215, 3.75, 0, '601086', -39203.751589242056]
  Amount: 10454.333757131215, Price: 3.75, Sid: 0, Symbol: 601086, Value: -39203.751589242056

......

 

 10_金融投资组合分析库-pyfolio

我在学习和实践的过程中,看到很多关于pyfolio的文章,列了一些如下:

从上面的文章,了解到一些信息

  • 本来,backtrader可以集成PyFolio这个第三方库,方便地以可视化方法输出这些指标。
  • 但是,由于PyFolio后来变更了接口,两者就没有集成了。
  • 好在,还有一个第三方库quantstats,可以非常方便地与backtrader集成。

不过这些提到的pyfolio应该是另外一个库,而并非我们这里的backtrader内置评价指标,我们后续再去研究pyfolio库和quantstats库的使用,当前先把内置指标输出看看它做了哪些工作。

打开\backtrader\analyzers\pyfolio.py文件,如下

class PyFolio(bt.Analyzer):
    '''This analyzer uses 4 children analyzers to collect data and transforms it
    in to a data set compatible with ``pyfolio``

    Children Analyzer

      - ``TimeReturn``

        Used to calculate the returns of the global portfolio value

      - ``PositionsValue``

        Used to calculate the value of the positions per data. It sets the
        ``headers`` and ``cash`` parameters to ``True``

      - ``Transactions``

        Used to record each transaction on a data (size, price, value). Sets
        the ``headers`` parameter to ``True``

      - ``GrossLeverage``

        Keeps track of the gross leverage (how much the strategy is invested)

由此可知,这里的pyfolio使用四个子分析器来收集数据,并将其转换为与 pyfolio 兼容的数据集。

PyFolio 包含以下四个子分析器:

  • TimeReturn:用于计算全球投资组合价值的回报。               -- 03
  • PositionsValue:用于计算每个数据的持仓价值。                 -- 08
  • Transactions:用于记录每个数据的交易(数量、价格、价值)。 -- 09
  • GrossLeverage:用于跟踪毛杠杆率(策略的投资水平)。 -- 07
def add_analyzer_all(cerebro):
    cerebro.addanalyzer(bt.analyzers.PyFolio, _name='_PyFolio')  
    
def analyzer_output(result):
    print("---------------_PyFolio -----------------")
    sout = result.analyzers._PyFolio.get_analysis()
#     print(sout)

    for k,v in sout.items():
        print(f'key: {k}, Value: ...')

------------------------------------------
策略为 经典MACD , 期末总资金 111569.09  盈利为 11569.09 总共交易次数为 11 ,交易成功率为 36.4%
---------------_PyFolio -----------------
key: returns, Value: ...
key: positions, Value: ...
key: transactions, Value: ...
key: gross_lev, Value: ...

放到函数中运行,的确是得到四组多重字典,这四组在前面都分别实践过了,就不重复了。

11_回报-returns

该分析器名为 Returns,用于计算总复合回报率、平均回报率、复合回报率和年化回报率。
返回的字典包含以下键:

  •  rtot:总复合回报率
  •  ravg:整个期间(时间框架特定)的平均回报率
  •  rnorm:年化/标准化回报率
  •  rnorm100:年化/标准化回报率,以100%表示

这里我们同样打开文件 \backtrader\analyzers\returns.py ,里面有参数的说明

- ``tann`` (default: ``None``)

        Number of periods to use for the annualization (normalization) of the

        namely:

          - ``days: 252``
          - ``weeks: 52``
          - ``months: 12``
          - ``years: 1``

于是我们把这四个参数都试了一遍,并参考某文章中对年化收益率的计算公式

ri = (1+Ri)^\frac{250}{period} - 1

def add_analyzer_all(cerebro):
    cerebro.addanalyzer(bt.analyzers.Returns, _name='_Returns_day', tann=252) # 计算252日度收益
    cerebro.addanalyzer(bt.analyzers.Returns, _name='_Returns_week', tann=52) # 计算52周度收益
    cerebro.addanalyzer(bt.analyzers.Returns, _name='_Returns_month', tann=12) # 计算12月度收益
    cerebro.addanalyzer(bt.analyzers.Returns, _name='_Returns_year', tann=1) # 计算1年度收益
    
def analyzer_output(result):
    print("---------------_PyFolio -----------------")
    sout1 = result.analyzers._Returns_day.get_analysis()
    sout2 = result.analyzers._Returns_week.get_analysis()
    sout3 = result.analyzers._Returns_month.get_analysis()
    sout4 = result.analyzers._Returns_year.get_analysis()
    print(sout1)
    print(sout2)
    print(sout3)
    print(sout4)
策略为 经典MACD , 期末总资金 111569.09  盈利为 11569.09 总共交易次数为 11 ,交易成功率为 36.4%
---------------_PyFolio -----------------
OrderedDict([('rtot', 0.10947386238015983), ('ravg', 0.00029992839008262964), ('rnorm', 0.07851161276138774), ('rnorm100', 7.8511612761387735)])
OrderedDict([('rtot', 0.10947386238015983), ('ravg', 0.00029992839008262964), ('rnorm', 0.015718532957296315), ('rnorm100', 1.5718532957296316)])
OrderedDict([('rtot', 0.10947386238015983), ('ravg', 0.00029992839008262964), ('rnorm', 0.003605625365242038), ('rnorm100', 0.3605625365242038)])
OrderedDict([('rtot', 0.10947386238015983), ('ravg', 0.00029992839008262964), ('rnorm', 0.0002999733730993339), ('rnorm100', 0.02999733730993339)])

 

12_变异加权回报率-VWR

VWR (Variability-Weighted Return)是一种改进的夏普比率计算方法,它考虑了回报的变异性来加权回报,从而提供了一个更好的风险调整后收益的衡量标准。

同上,我们暂不考虑它的原理和计算,先把内置评价看输出结果。

def add_analyzer_all(cerebro):
    cerebro.addanalyzer(bt.analyzers.VWR, _name='_VWR') 
    
def analyzer_output(result):
    print("---------------_VWR -----------------")
    sout = result.analyzers._VWR.get_analysis()
    print(sout)

--------------------------------
策略为 经典MACD , 期末总资金 111569.09  盈利为 11569.09 总共交易次数为 11 ,交易成功率为 36.4%
---------------_VWR -----------------
OrderedDict([('vwr', 4.415307366076662)])

结果也是只有一项的字典结构,即 VWR的值。

13_滚动回报率-logreturnsrolling

# 分析器名为 LogReturnsRolling,用于计算给定时间框架和压缩的滚动回报。
def add_analyzer_all(cerebro):
    cerebro.addanalyzer(bt.analyzers.LogReturnsRolling, _name='_LogReturnsRolling') 
    
def analyzer_output(result):
    print("---------------_LogReturnsRolling -----------------")
    sout = result.analyzers._LogReturnsRolling.get_analysis()
#     print(sout)
    for key, value in sout.items():
        print(f'{key}: {value}')
策略为 经典MACD , 期末总资金 111569.09  盈利为 11569.09 总共交易次数为 11 ,交易成功率为 36.4%
---------------_LogReturnsRolling -----------------
2023-01-11 00:00:00: 0.0
2023-01-12 00:00:00: 0.0

2023-03-21 00:00:00: 0.0
2023-03-22 00:00:00: 0.0
2023-03-23 00:00:00: 0.002849920446183877
2023-03-24 00:00:00: -0.008815650741360098
2023-03-27 00:00:00: 0.0019657606321923085
2023-03-28 00:00:00: 0.0
2023-03-29 00:00:00: -0.003935393091582192
2023-03-30 00:00:00: 0.013706647466756726
2023-03-31 00:00:00: 0.006783509431897122
2023-04-03 00:00:00: -0.00970487369790382
2023-04-04 00:00:00: -0.009799982039630341
2023-04-06 00:00:00: -0.012885266481896753
2023-04-07 00:00:00: -0.00027168001429825724
2023-04-10 00:00:00: 0.0

 

14_卡马尔比率-Calmar

Calmar 分析器用于计算Calmar比率,这是一个衡量投资组合风险调整后收益的指标
Calmar = 最大回撤期间前的高峰回报率 / 最大回撤期间的回撤百分比
不知道为什么出来的值都是NaN,这个评价指标就暂时不用了

def add_analyzer_all(cerebro):
    cerebro.addanalyzer(bt.analyzers.Calmar, _name='_Calmar') 
    
def analyzer_output(result):
    print("---------------_Calmar -----------------")
    sout = result.analyzers._Calmar.get_analysis()
    print(sout)
    for key, value in sout.items():
        print(f'{key}: {value}')
策略为 经典MACD , 期末总资金 111569.09  盈利为 11569.09 总共交易次数为 11 ,交易成功率为 36.4%
---------------_Calmar -----------------
OrderedDict([(datetime.datetime(2023, 1, 31, 0, 0), nan), (datetime.datetime(2023, 2, 28, 0, 0), nan), (datetime.datetime(2023, 3, 31, 0, 0), nan), (datetime.datetime(2023, 4, 30, 0, 0), nan), (datetime.datetime(2023, 5, 31, 0, 0), nan), (datetime.datetime(2023, 6, 30, 0, 0), nan), (datetime.datetime(2023, 7, 31, 0, 0), nan), (datetime.datetime(2023, 8, 31, 0, 0), nan), (datetime.datetime(2023, 9, 30, 0, 0), nan), (datetime.datetime(2023, 10, 31, 0, 0), nan), (datetime.datetime(2023, 11, 30, 0, 0), nan), (datetime.datetime(2023, 12, 31, 0, 0), nan), (datetime.datetime(2024, 1, 31, 0, 0), nan), (datetime.datetime(2024, 2, 29, 0, 0), nan), (datetime.datetime(2024, 3, 31, 0, 0), nan), (datetime.datetime(2024, 4, 30, 0, 0), nan), (datetime.datetime(2024, 5, 31, 0, 0), nan), (datetime.datetime(2024, 6, 30, 0, 0), nan), (datetime.datetime(2024, 7, 31, 0, 0), nan)])
2023-01-31 00:00:00: nan
2023-02-28 00:00:00: nan
2023-03-31 00:00:00: nan
2023-04-30 00:00:00: nan
2023-05-31 00:00:00: nan
2023-06-30 00:00:00: nan
2023-07-31 00:00:00: nan
2023-08-31 00:00:00: nan ...

15_周期统计-periodstats

这是内置的最后一个指标了,我们直接读.py里的说明

get_analysis 方法返回一个字典,其中包含以下键:
average:平均回报率。
stddev:回报率的标准差。
positive:正回报率的数量。
negative:负回报率的数量。
nochange:无变化回报率的数量。
best:最佳回报率。
worst:最差回报率。
def add_analyzer_all(cerebro):
    cerebro.addanalyzer(bt.analyzers.PeriodStats, _name='_PeriodStats')  
    
def analyzer_output(result):
    print("---------------_PeriodStats -----------------")
    sout = result.analyzers._PeriodStats.get_analysis()
#     print(sout)
    for key, value in sout.items():
        print(f'{key}: {value}')
策略为 经典MACD , 期末总资金 111569.09  盈利为 11569.09 总共交易次数为 11 ,交易成功率为 36.4%
---------------_PeriodStats -----------------
average: 0.06155213863943909
stddev: 0.10583966175667991
positive: 1
negative: 1
nochange: 0
best: 0.167391800396119
worst: -0.04428752311724082

内置评价实践小结 

总体看下来,可以把这些内置评价的类分为2个大类:
第1个是直接打分统计结果的类

  •  01_AnnualReturn - 以自然年为分组的,比如 2023年 年化收益% , 2024年 年化收益%
  •  02_drawdown  - 当前回撤(周期,值,百分比) + 最大回撤(周期,值,百分比)
  •  04_Sharp Ratio - 夏普比率,直接给出回测周期的夏普率
  •  05_TradeAnalyzer - 交易分析,多重字典,包括交易次数(完成的,open),毛利率,净利率,连胜,连败,这里的Long short应该是开多,开空
  •  06_SQN or SystemQualityNumber - 交易系统的性能得分
  •  11_Returns - 回报 有4个数值,分别是总收益,平均收益,年化收益,年化收益百分比, 这个跟01的自然年不同,应该是计算近1年的(验证下?)
  •  12_VMR Variability-Weighted Return, 是一种改进的夏普比率计算方法,可变加权回报率 
  •  15_PeriodStats 基本统计数据,包括回报率,标准差,正/负数量,最佳回报率和最差回报率

第2个其实是数据存贮

  •  03_TimeReturn 相当于每天的涨跌幅记录
  •  07_GrossLeverage 类似每天的仓位%
  •  08_PositionsValue 每天持仓数据
  •  09_Transactions 每笔交易的数据
  •  10_pyfolio ,这个是前面4个数据存贮的合集(03,07,08,09)
  •  13_LogReturnsRolling 滚动回报率,好像还是对数形式的

简单的与股票软件的评价系统对比起来看一下,大部分的都有
对于某些可以自己根据数据存贮那几个类取数据再进行相关的运算,甚至还可以自己写继承自bt.Analyzer的类...

  • 24
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值