Source code for backtrader.analyzers.sqn

#!/usr/bin/env python
"""SQN Analyzer Module - System Quality Number calculation.

This module provides the SQN (System Quality Number) analyzer, defined
by Van K. Tharp to categorize trading systems.

Classes:
    SQN: Analyzer that calculates System Quality Number.

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

import math

from ..analyzer import Analyzer
from ..mathsupport import average, standarddev
from ..utils import AutoOrderedDict

__all__ = ["SQN"]

_PNL_EPSILON = 1e-10


# Get SQN indicator
[docs] class SQN(Analyzer): """SQN or SystemQualityNumber. Defined by Van K. Tharp to categorize trading systems. - 1.6 - 1.9 Below average - 2.0 - 2.4 Average - 2.5 - 2.9 Good - 3.0 - 5.0 Excellent - 5.1 - 6.9 Superb - 7.0 - Holy Grail? The formula: - SquareRoot(NumberTrades) * Average(TradesProfit) / StdDev(TradesProfit) The sqn value should be deemed reliable when the number of trades >= 30 Methods: - get_analysis Returns a dictionary with keys "sqn" and "trades" (number of considered trades) """ # System quality number alias = ("SystemQualityNumber",) # Create analysis
[docs] def create_analysis(self): """Replace default implementation to instantiate an AutoOrderedDict rather than an OrderedDict""" self.rets = AutoOrderedDict()
# Start, initialize pnl and count
[docs] def start(self): """Initialize the analyzer at the start of the backtest. Initializes lists to store trade P&L values for SQN calculation. """ super().start() self.pnl = [] self.count = 0
# Trade notification, if trade is closed, add profit/loss
[docs] def notify_trade(self, trade): """Collect P&L from closed trades. Args: trade: The trade object that was closed. """ if trade.status == trade.Closed: self.pnl.append(trade.pnlcomm) self.count += 1
# Stop, calculate SQN indicator, if trade count > 0, SQN equals average trade profit * sqrt(trade count) / standard deviation of trade profit
[docs] def stop(self): """Calculate the System Quality Number when backtest ends. SQN = sqrt(N) * average(P&L) / std(P&L) The result is stored in self.rets.sqn along with the number of trades in self.rets.trades. """ if self.count > 1: try: pnl_values = [0.0 if abs(value) <= _PNL_EPSILON else value for value in self.pnl] pnl_av = average(pnl_values) pnl_stddev = standarddev(pnl_values) except (TypeError, ValueError, ZeroDivisionError): sqn = None else: if not math.isfinite(pnl_av) or not math.isfinite(pnl_stddev) or pnl_stddev == 0.0: sqn = None else: sqn = math.sqrt(len(self.pnl)) * pnl_av / pnl_stddev if not math.isfinite(sqn): sqn = None else: sqn = 0 # Set SQN value and trades value self.rets.sqn = sqn self.rets.trades = self.count