Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion backtrader/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
#
# Copyright (C) 2015-2023 Daniel Rodriguez
# Copyright (C) 2015-2024 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
Expand Down
2 changes: 1 addition & 1 deletion backtrader/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
#
# Copyright (C) 2015-2023 Daniel Rodriguez
# Copyright (C) 2015-2024 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
Expand Down
4 changes: 3 additions & 1 deletion backtrader/analyzers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
#
# Copyright (C) 2015-2023 Daniel Rodriguez
# Copyright (C) 2015-2024 Daniel Rodriguez
# Added by @baobach
#
# 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
Expand All @@ -28,6 +29,7 @@
from .drawdown import *
from .timereturn import *
from .sharpe import *
from .sortino import *
from .tradeanalyzer import *
from .sqn import *
from .leverage import *
Expand Down
2 changes: 1 addition & 1 deletion backtrader/analyzers/annualreturn.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
#
# Copyright (C) 2015-2023 Daniel Rodriguez
# Copyright (C) 2015-2024 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
Expand Down
2 changes: 1 addition & 1 deletion backtrader/analyzers/calmar.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
#
# Copyright (C) 2015-2023 Daniel Rodriguez
# Copyright (C) 2015-2024 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
Expand Down
2 changes: 1 addition & 1 deletion backtrader/analyzers/drawdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
#
# Copyright (C) 2015-2023 Daniel Rodriguez
# Copyright (C) 2015-2024 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
Expand Down
2 changes: 1 addition & 1 deletion backtrader/analyzers/leverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
#
# Copyright (C) 2015-2023 Daniel Rodriguez
# Copyright (C) 2015-2024 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
Expand Down
2 changes: 1 addition & 1 deletion backtrader/analyzers/logreturnsrolling.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
#
# Copyright (C) 2015-2023 Daniel Rodriguez
# Copyright (C) 2015-2024 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
Expand Down
2 changes: 1 addition & 1 deletion backtrader/analyzers/periodstats.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
#
# Copyright (C) 2015-2023 Daniel Rodriguez
# Copyright (C) 2015-2024 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
Expand Down
2 changes: 1 addition & 1 deletion backtrader/analyzers/positions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
#
# Copyright (C) 2015-2023 Daniel Rodriguez
# Copyright (C) 2015-2024 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
Expand Down
2 changes: 1 addition & 1 deletion backtrader/analyzers/pyfolio.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
#
# Copyright (C) 2015-2023 Daniel Rodriguez
# Copyright (C) 2015-2024 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
Expand Down
2 changes: 1 addition & 1 deletion backtrader/analyzers/returns.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
#
# Copyright (C) 2015-2023 Daniel Rodriguez
# Copyright (C) 2015-2024 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
Expand Down
9 changes: 7 additions & 2 deletions backtrader/analyzers/sharpe.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
#
# Copyright (C) 2015-2023 Daniel Rodriguez
# Copyright (C) 2015-2024 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
Expand Down Expand Up @@ -112,7 +112,7 @@ class SharpeRatio(Analyzer):
params = (
('timeframe', TimeFrame.Years),
('compression', 1),
('riskfreerate', 0.01),
('riskfreerate', 0.00),
('factor', None),
('convertrate', True),
('annualize', False),
Expand Down Expand Up @@ -142,6 +142,9 @@ def __init__(self):

def stop(self):
super(SharpeRatio, self).stop()
ret_free_avg = None
retdev = None

if self.p.legacyannual:
rate = self.p.riskfreerate
retavg = average([r - rate for r in self.anret.rets])
Expand Down Expand Up @@ -204,6 +207,8 @@ def stop(self):
self.ratio = ratio

self.rets['sharperatio'] = self.ratio
self.rets['ret_free_avg'] = ret_free_avg
self.rets['retdev'] = retdev


class SharpeRatio_A(SharpeRatio):
Expand Down
195 changes: 195 additions & 0 deletions backtrader/analyzers/sortino.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
#!/usr/bin/env python
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
#
# Added by: @baobach (2024)
#
# 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)

import math

from backtrader.utils.py3 import itervalues

from backtrader import Analyzer, TimeFrame
from backtrader.mathsupport import average, standarddev
from backtrader.analyzers import TimeReturn, AnnualReturn

class SortinoRatio(Analyzer):
'''This analyzer calculates the Sortino Ratio of a strategy using a risk free
asset which is simply an interest rate

See also:

- https://en.wikipedia.org/wiki/Sortino_ratio

Params:

- ``timeframe``: (default: ``TimeFrame.Years``)

- ``compression`` (default: ``1``)

Only used for sub-day timeframes to for example work on an hourly
timeframe by specifying "TimeFrame.Minutes" and 60 as compression

- ``riskfreerate`` (default: 0.01 -> 1%)

Expressed in annual terms (see ``convertrate`` below)

- ``convertrate`` (default: ``True``)

Convert the ``riskfreerate`` from annual to monthly, weekly or daily
rate. Sub-day conversions are not supported

- ``factor`` (default: ``None``)

If ``None``, the conversion factor for the riskfree rate from *annual*
to the chosen timeframe will be chosen from a predefined table

Days: 252, Weeks: 52, Months: 12, Years: 1

Else the specified value will be used

- ``annualize`` (default: ``False``)

If ``convertrate`` is ``True``, the *SortinoRatio* will be delivered in
the ``timeframe`` of choice.

In most occasions the SortinoRatio is delivered in annualized form.
Convert the ``riskfreerate`` from annual to monthly, weekly or daily
rate. Sub-day conversions are not supported

- ``stddev_sample`` (default: ``False``)

If this is set to ``True`` the *standard deviation* will be calculated
decreasing the denominator in the mean by ``1``. This is used when
calculating the *standard deviation* if it's considered that not all
samples are used for the calculation. This is known as the *Bessels'
correction*

- ``daysfactor`` (default: ``None``)

Old naming for ``factor``. If set to anything else than ``None`` and
the ``timeframe`` is ``TimeFrame.Days`` it will be assumed this is old
code and the value will be used

- ``legacyannual`` (default: ``False``)

Use the ``AnnualReturn`` return analyzer, which as the name implies
only works on years

- ``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 key "sortinoratio" holding the ratio

'''

params = (
('timeframe', TimeFrame.Years),
('compression', 1),
('riskfreerate', 0.00),
('factor', None),
('convertrate', True),
('annualize', False),
('stddev_sample', False),
('daysfactor', None),
('legacyannual', False),
('fund', None),
)

RATEFACTORS = {
TimeFrame.Days: 252,
TimeFrame.Weeks: 52,
TimeFrame.Months: 12,
TimeFrame.Years: 1,
}

def __init__(self):
self.timereturn = TimeReturn(
timeframe=self.p.timeframe,
compression=self.p.compression,
fund=self.p.fund)

def stop(self):
super(SortinoRatio, self).stop()
ret_free_avg = None
retdev = None

# Get the returns from the subanalyzer
returns = list(itervalues(self.timereturn.get_analysis()))

rate = self.p.riskfreerate

factor = None

if self.p.factor is not None:
factor = self.p.factor # user specified factor
elif self.p.timeframe in self.RATEFACTORS:
# Get the conversion factor from the default table
factor = self.RATEFACTORS[self.p.timeframe]

if factor is not None:
# A factor was found

if self.p.convertrate:
# Standard: downgrade annual returns to timeframe factor
rate = pow(1.0 + rate, 1.0 / factor) - 1.0
else:
# Else upgrade returns to yearly returns
returns = [pow(1.0 + x, factor) - 1.0 for x in returns]

lrets = len(returns) - self.p.stddev_sample
# Check if the ratio can be calculated
if lrets:
# Get the excess returns
ret_free = [r - rate for r in returns]
ret_free_avg = average(ret_free)
try:

# Calculate downside deviation
downside_returns = [x for x in ret_free if x < 0]
retdev = standarddev(downside_returns, avgx=ret_free_avg,
bessel=self.p.stddev_sample)

ratio = ret_free_avg / retdev

if factor is not None and \
self.p.convertrate and self.p.annualize:

ratio = math.sqrt(factor) * ratio
except (ValueError, TypeError, ZeroDivisionError):
ratio = None
else:
# no returns or stddev_sample was active and 1 return
ratio = None

self.ratio = ratio

self.rets['sortinoratio'] = self.ratio
self.rets['ret_free_avg'] = ret_free_avg
self.rets['retdev'] = retdev
2 changes: 1 addition & 1 deletion backtrader/analyzers/sqn.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
#
# Copyright (C) 2015-2023 Daniel Rodriguez
# Copyright (C) 2015-2024 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
Expand Down
2 changes: 1 addition & 1 deletion backtrader/analyzers/timereturn.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
#
# Copyright (C) 2015-2023 Daniel Rodriguez
# Copyright (C) 2015-2024 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
Expand Down
2 changes: 1 addition & 1 deletion backtrader/analyzers/tradeanalyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
#
# Copyright (C) 2015-2023 Daniel Rodriguez
# Copyright (C) 2015-2024 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
Expand Down
2 changes: 1 addition & 1 deletion backtrader/analyzers/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
#
# Copyright (C) 2015-2023 Daniel Rodriguez
# Copyright (C) 2015-2024 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
Expand Down
Loading