Module backtrader.analyzers.timereturn
Expand source code
#!/usr/bin/env python
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
#
# Copyright (C) 2015-2023 Daniel Rodriguez
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from __future__ import (absolute_import, division, print_function,
unicode_literals)
from backtrader import TimeFrameAnalyzerBase
class TimeReturn(TimeFrameAnalyzerBase):
'''This analyzer calculates the Returns by looking at the beginning
and end of the timeframe
Params:
- ``timeframe`` (default: ``None``)
If ``None`` the ``timeframe`` of the 1st data in the system will be
used
Pass ``TimeFrame.NoTimeFrame`` to consider the entire dataset with no
time constraints
- ``compression`` (default: ``None``)
Only used for sub-day timeframes to for example work on an hourly
timeframe by specifying "TimeFrame.Minutes" and 60 as compression
If ``None`` then the compression of the 1st data of the system will be
used
- ``data`` (default: ``None``)
Reference asset to track instead of the portfolio value.
.. note:: this data must have been added to a ``cerebro`` instance with
``addata``, ``resampledata`` or ``replaydata``
- ``firstopen`` (default: ``True``)
When tracking the returns of a ``data`` the following is done when
crossing a timeframe boundary, for example ``Years``:
- Last ``close`` of previous year is used as the reference price to
see the return in the current year
The problem is the 1st calculation, because the data has** no
previous** closing price. As such and when this parameter is ``True``
the *opening* price will be used for the 1st calculation.
This requires the data feed to have an ``open`` price (for ``close``
the standard [0] notation will be used without reference to a field
price)
Else the initial close will be used.
- ``fund`` (default: ``None``)
If ``None`` the actual mode of the broker (fundmode - True/False) will
be autodetected to decide if the returns are based on the total net
asset value or on the fund value. See ``set_fundmode`` in the broker
documentation
Set it to ``True`` or ``False`` for a specific behavior
Methods:
- get_analysis
Returns a dictionary with returns as values and the datetime points for
each return as keys
'''
params = (
('data', None),
('firstopen', True),
('fund', None),
)
def start(self):
super(TimeReturn, self).start()
if self.p.fund is None:
self._fundmode = self.strategy.broker.fundmode
else:
self._fundmode = self.p.fund
self._value_start = 0.0
self._lastvalue = None
if self.p.data is None:
# keep the initial portfolio value if not tracing a data
if not self._fundmode:
self._lastvalue = self.strategy.broker.getvalue()
else:
self._lastvalue = self.strategy.broker.fundvalue
def notify_fund(self, cash, value, fundvalue, shares):
if not self._fundmode:
# Record current value
if self.p.data is None:
self._value = value # the portofolio value if tracking no data
else:
self._value = self.p.data[0] # the data value if tracking data
else:
if self.p.data is None:
self._value = fundvalue # the fund value if tracking no data
else:
self._value = self.p.data[0] # the data value if tracking data
def on_dt_over(self):
# next is called in a new timeframe period
# if self.p.data is None or len(self.p.data) > 1:
if self.p.data is None or self._lastvalue is not None:
self._value_start = self._lastvalue # update value_start to last
else:
# The 1st tick has no previous reference, use the opening price
if self.p.firstopen:
self._value_start = self.p.data.open[0]
else:
self._value_start = self.p.data[0]
def next(self):
# Calculate the return
super(TimeReturn, self).next()
self.rets[self.dtkey] = (self._value / self._value_start) - 1.0
self._lastvalue = self._value # keep last value
Classes
class TimeReturn (*args, **kwargs)
-
This analyzer calculates the Returns by looking at the beginning and end of the timeframe
Params
timeframe
(default:None
) IfNone
thetimeframe
of the 1st data in the system will be used
Pass
TimeFrame.NoTimeFrame
to consider the entire dataset with no time constraintscompression
(default:None
)
Only used for sub-day timeframes to for example work on an hourly timeframe by specifying "TimeFrame.Minutes" and 60 as compression
If
None
then the compression of the 1st data of the system will be useddata
(default:None
)
Reference asset to track instead of the portfolio value.
!!! note "Note: this data must have been added to a
cerebro
instance with"addata
,resampledata
orreplaydata
firstopen
(default:True
)
When tracking the returns of a
data
the following is done when crossing a timeframe boundary, for exampleYears
:- Last <code>close</code> of previous year is used as the reference price to see the return in the current year
The problem is the 1st calculation, because the data has no previous closing price. As such and when this parameter is
True
the opening price will be used for the 1st calculation.This requires the data feed to have an
open
price (forclose
the standard [0] notation will be used without reference to a field price)Else the initial close will be used.
fund
(default:None
)
If
None
the actual mode of the broker (fundmode - True/False) will be autodetected to decide if the returns are based on the total net asset value or on the fund value. Seeset_fundmode
in the broker documentationSet it to
True
orFalse
for a specific behaviorMethods
- get_analysis
Returns a dictionary with returns as values and the datetime points for each return as keys
Expand source code
class TimeReturn(TimeFrameAnalyzerBase): '''This analyzer calculates the Returns by looking at the beginning and end of the timeframe Params: - ``timeframe`` (default: ``None``) If ``None`` the ``timeframe`` of the 1st data in the system will be used Pass ``TimeFrame.NoTimeFrame`` to consider the entire dataset with no time constraints - ``compression`` (default: ``None``) Only used for sub-day timeframes to for example work on an hourly timeframe by specifying "TimeFrame.Minutes" and 60 as compression If ``None`` then the compression of the 1st data of the system will be used - ``data`` (default: ``None``) Reference asset to track instead of the portfolio value. .. note:: this data must have been added to a ``cerebro`` instance with ``addata``, ``resampledata`` or ``replaydata`` - ``firstopen`` (default: ``True``) When tracking the returns of a ``data`` the following is done when crossing a timeframe boundary, for example ``Years``: - Last ``close`` of previous year is used as the reference price to see the return in the current year The problem is the 1st calculation, because the data has** no previous** closing price. As such and when this parameter is ``True`` the *opening* price will be used for the 1st calculation. This requires the data feed to have an ``open`` price (for ``close`` the standard [0] notation will be used without reference to a field price) Else the initial close will be used. - ``fund`` (default: ``None``) If ``None`` the actual mode of the broker (fundmode - True/False) will be autodetected to decide if the returns are based on the total net asset value or on the fund value. See ``set_fundmode`` in the broker documentation Set it to ``True`` or ``False`` for a specific behavior Methods: - get_analysis Returns a dictionary with returns as values and the datetime points for each return as keys ''' params = ( ('data', None), ('firstopen', True), ('fund', None), ) def start(self): super(TimeReturn, self).start() if self.p.fund is None: self._fundmode = self.strategy.broker.fundmode else: self._fundmode = self.p.fund self._value_start = 0.0 self._lastvalue = None if self.p.data is None: # keep the initial portfolio value if not tracing a data if not self._fundmode: self._lastvalue = self.strategy.broker.getvalue() else: self._lastvalue = self.strategy.broker.fundvalue def notify_fund(self, cash, value, fundvalue, shares): if not self._fundmode: # Record current value if self.p.data is None: self._value = value # the portofolio value if tracking no data else: self._value = self.p.data[0] # the data value if tracking data else: if self.p.data is None: self._value = fundvalue # the fund value if tracking no data else: self._value = self.p.data[0] # the data value if tracking data def on_dt_over(self): # next is called in a new timeframe period # if self.p.data is None or len(self.p.data) > 1: if self.p.data is None or self._lastvalue is not None: self._value_start = self._lastvalue # update value_start to last else: # The 1st tick has no previous reference, use the opening price if self.p.firstopen: self._value_start = self.p.data.open[0] else: self._value_start = self.p.data[0] def next(self): # Calculate the return super(TimeReturn, self).next() self.rets[self.dtkey] = (self._value / self._value_start) - 1.0 self._lastvalue = self._value # keep last value
Ancestors
Class variables
var frompackages
var packages
var params
Methods
def on_dt_over(self)
-
Expand source code
def on_dt_over(self): # next is called in a new timeframe period # if self.p.data is None or len(self.p.data) > 1: if self.p.data is None or self._lastvalue is not None: self._value_start = self._lastvalue # update value_start to last else: # The 1st tick has no previous reference, use the opening price if self.p.firstopen: self._value_start = self.p.data.open[0] else: self._value_start = self.p.data[0]
Inherited members