backtrader.analyzers.annualreturn 源代码

#!/usr/bin/env python
"""Annual Return Analyzer Module - Annual return calculation.

This module provides the AnnualReturn analyzer for calculating
year-by-year returns of a strategy.

Classes:
    AnnualReturn: Analyzer that calculates annual returns.

Example:
    >>> cerebro = bt.Cerebro()
    >>> cerebro.addanalyzer(bt.analyzers.AnnualReturn, _name='annret')
    >>> results = cerebro.run()
    >>> print(results[0].analyzers.annret.get_analysis())
"""

from collections import OrderedDict

from ..analyzer import Analyzer
from ..utils.date import num2date
from ..utils.py3 import range


# Calculate annual returns. The algorithm implementation is somewhat complex, so a pandas-based version MyAnnualReturn was written later with much simpler logic
[文档] class AnnualReturn(Analyzer): """ This analyzer calculates the AnnualReturns by looking at the beginning and end of the year Params: - (None) Member Attributes: - ``rets``: list of calculated annual returns - ``ret``: dictionary (key: year) of annual returns **get_analysis**: - Returns a dictionary of annual returns (key: year) """
[文档] def __init__(self): """Initialize the AnnualReturn analyzer. Initializes cache lists for storing dates and values during backtesting. """ super().__init__() # Cache data self._dt_cache = [] self._value_cache = []
[文档] def next(self): """Cache current date and account value on each bar. Stores the current datetime and portfolio value for later annual return calculation. """ # Cache current date and account value each time next is called dt_val = self.data.datetime[0] value_val = self.strategy.broker.getvalue() self._dt_cache.append(dt_val) self._value_cache.append(value_val)
[文档] def stop(self): """Calculate annual returns from cached data. Iterates through cached date-value pairs to calculate returns for each calendar year. Stores results in self.rets (list) and self.ret (dictionary keyed by year). """ # Must have stats.broker # Current year cur_year = -1 # Start value value_start = 0.0 # todo This value is not used, commented out # value_cur = 0.0 # Current value # End value value_end = 0.0 # Save return data # todo Directly setting in PyCharm will warn about setting attribute values outside __init__, use hasattr and setattr to set specific attribute values # self.rets = list() # # self.ret = OrderedDict() setattr(self, "rets", list()) setattr(self, "ret", OrderedDict()) # Calculate using cached data for i in range(len(self._dt_cache)): dt_val = self._dt_cache[i] value_cur = self._value_cache[i] # Convert date try: dt = num2date(dt_val) except Exception: continue # If the year at index i is greater than current year, if current year > 0, calculate return and save to self.ret, and start value equals end value # When years are not equal, it indicates current i is a new year if dt.year > cur_year: if cur_year >= 0: if value_start != 0: annual_ret = (value_end / value_start) - 1.0 else: annual_ret = 0.0 self.rets.append(annual_ret) self.ret[cur_year] = annual_ret # changing between real years, use last value as new start value_start = value_end else: # No value set whatsoever, use the currently loaded value value_start = value_cur cur_year = dt.year # No matter what, the last value is always the last loaded value value_end = value_cur # If current year hasn't ended and return hasn't been calculated, calculate at the end even if less than a full year if cur_year not in self.ret: # finish calculating pending data if value_start != 0: annual_ret = (value_end / value_start) - 1.0 else: annual_ret = 0.0 self.rets.append(annual_ret) self.ret[cur_year] = annual_ret
[文档] def get_analysis(self): """Return the annual return analysis results. Returns: OrderedDict: Dictionary mapping years to their annual returns. """ return self.ret
[文档] class MyAnnualReturn(Analyzer): """ This analyzer calculates the AnnualReturns by looking at the beginning and end of the year Params: - (None) Member Attributes: - ``rets``: list of calculated annual returns - ``ret``: dictionary (key: year) of annual returns **get_analysis**: - Returns a dictionary of annual returns (key: year) """
[文档] def stop(self): """Calculate annual returns using pandas. Uses pandas DataFrame operations to group data by year and calculate annual returns based on beginning and ending values for each year. Note: This method requires pandas to be installed. """ # Container for saving data - dictionary if not hasattr(self, "ret"): setattr(self, "ret", OrderedDict()) # Get data time and convert to date dt_list = self.data.datetime.get(0, size=len(self.data)) dt_list = [num2date(i) for i in dt_list] # Get account assets value_list = self.strategy.stats.broker.value.get(0, size=len(self.data)) # Convert to pandas format import pandas as pd df = pd.DataFrame([dt_list, value_list]).T df.columns = ["datetime", "value"] df["pre_value"] = df["value"].shift(1) # Calculate simple returns for each year df["year"] = [i.year for i in df["datetime"]] for year, data in df.groupby("year"): begin_value = list(data["pre_value"])[0] end_value = list(data["value"])[-1] annual_return = (end_value / begin_value) - 1 self.ret[year] = annual_return
[文档] def get_analysis(self): """Return the annual return analysis results. Returns: OrderedDict: Dictionary mapping years to their annual returns. """ return self.ret