title: Analyzer API description: Complete Analyzer class API reference


Analyzer API

The Analyzer class is the base class for all performance analysis tools in Backtrader. Analyzers calculate and report strategy metrics including returns, drawdowns, Sharpe ratio, trade statistics, and more.

Class Definition

class backtrader.Analyzer:
    """Base class for all analyzers."""

```bash

## Parameters

### `params`

Tuple of parameter definitions for the analyzer.

```python
class MyAnalyzer(bt.Analyzer):
    params = (
        ('period', 20),
        ('threshold', 1.5),
    )

```bash
Access parameters via `self.p.parameter_name` or `self.params.parameter_name`.

## Core Methods

### `__init__(self)`

Called once during analyzer instantiation. Use to initialize data structures.

```python
def __init__(self):
    super().__init__()  # Always call super first
    self.trades = []
    self.total_pnl = 0.0

```bash

### `start(self)`

Called at the start of the backtest, after initialization is complete.

```python
def start(self):
    self.initial_cash = self.broker.getcash()
    self.start_value = self.broker.getvalue()

```bash

### `prenext(self)`

Called for each bar before minimum period is reached. By default calls `next()`.

### `nextstart(self)`

Called once when minimum period is first reached. By default calls `next()`.

### `next(self)`

Called for each bar after minimum period is reached. Contains per-bar analysis logic.

```python
def next(self):
    current_value = self.broker.getvalue()
    self.values.append(current_value)

```bash

### `stop(self)`

Called at the end of the backtest. Perform final calculations.

```python
def stop(self):
    total_return = (self.broker.getvalue() / self.start_value) - 1
    self.rets['total_return'] = total_return

```bash

## Notification Methods

### `notify_trade(self, trade)`

Called when a trade status changes.

```python
def notify_trade(self, trade):
    if trade.isclosed:
        self.trades.append({
            'pnl': trade.pnlcomm,
            'commission': trade.commission,
        })

```bash

- *Trade Attributes**:

| Attribute | Description |

|-----------|-------------|

| `trade.status` | Current status (Open, Closed) |

| `trade.pnl` | Gross profit/loss |

| `trade.pnlcomm` | Net profit/loss (after commission) |

| `trade.commission` | Commission paid |

| `trade.isclosed` | Whether trade is closed |

| `trade.long` | Whether it was a long position |

| `trade.barlen` | Number of bars in trade |

### `notify_order(self, order)`

Called when an order status changes.

```python
def notify_order(self, order):
    if order.status == order.Completed:
        self.orders.append({
            'type': 'buy' if order.isbuy() else 'sell',
            'price': order.executed.price,
            'size': order.executed.size,
        })

```bash

- *Order Status Values**:

| Status | Description |

|--------|-------------|

| `Order.Created` | Order created |

| `Order.Submitted` | Submitted to broker |

| `Order.Accepted` | Accepted by broker |

| `Order.Partial` | Partially filled |

| `Order.Completed` | Fully filled |

| `Order.Canceled` | Canceled |

| `Order.Margin` | Margin insufficient |

| `Order.Rejected` | Rejected |

### `notify_cashvalue(self, cash, value)`

Called when cash or portfolio value changes.

```python
def notify_cashvalue(self, cash, value):
    self.history.append({
        'cash': cash,
        'value': value,
    })

```bash

### `notify_fund(self, cash, value, fundvalue, shares)`

Called when fund-related data changes (when using fund mode).

```python
def notify_fund(self, cash, value, fundvalue, shares):
    self.fund_history.append({
        'fundvalue': fundvalue,
        'shares': shares,
    })

```bash

## Result Methods

### `create_analysis(self)`

Create the analysis results container. Override to customize structure.

```python
def create_analysis(self):
    self.rets = OrderedDict()
    self.rets['total_trades'] = 0
    self.rets['winning_trades'] = 0

```bash

### `get_analysis(self)`

Return the analysis results. Override to return custom format.

```python
def get_analysis(self):
    return self.rets

```bash

### `print(self)`

Print analysis results via standard print.

```python
analyzer.print()  # Equivalent to print(analyzer.get_analysis())

```bash

### `pprint(self)`

Pretty print analysis results.

```python
analyzer.pprint()  # Formatted output

```bash

## Built-in Analyzers

### SharpeRatio

Calculates the Sharpe ratio using a risk-free rate.

```python
cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe',
                    riskfreerate=0.01, timeframe=bt.TimeFrame.Days)

```bash

| Parameter | Default | Description |

|-----------|---------|-------------|

| `riskfreerate` | 0.01 | Annual risk-free rate (1%) |

| `timeframe` | TimeFrame.Years | Timeframe for calculation |

| `factor` | None | Annualization factor |

| `convertrate` | True | Convert annual rate to timeframe |

| `annualize` | False | Return annualized Sharpe ratio |

| `stddev_sample` | False | Use Bessel's correction |

| `fund` | None | Use fund mode |

- *Output**:

```python
{'sharperatio': 1.23}

```bash

### SharpeRatio_Annual

Annualized Sharpe ratio (same as SharpeRatio with `annualize=True`).

```python
cerebro.addanalyzer(bt.analyzers.SharpeRatio_A, _name='sharpe')

```bash

### DrawDown

Calculates drawdown statistics.

```python
cerebro.addanalyzer(bt.analyzers.DrawDown, _name='drawdown')

```bash

| Parameter | Default | Description |

|-----------|---------|-------------|

| `fund` | None | Use fund mode |

- *Output**:

```python
{
    'drawdown': 5.23,        # Current drawdown in %
    'moneydown': 5230.50,    # Current drawdown in currency
    'len': 10,               # Current drawdown length
    'max': {
        'drawdown': 15.67,   # Maximum drawdown in %
        'moneydown': 15670.00,
        'len': 45,           # Maximum drawdown length
    }
}

```bash

### TimeDrawDown

Time-frame based drawdown analyzer.

```python
cerebro.addanalyzer(bt.analyzers.TimeDrawDown, _name='dd',
                    timeframe=bt.TimeFrame.Months)

```bash

- *Output**:

```python
{
    'maxdrawdown': 12.34,
    'maxdrawdownperiod': 30,
}

```bash

### Returns

Total, average, compound and annualized returns.

```python
cerebro.addanalyzer(bt.analyzers.Returns, _name='returns',
                    timeframe=bt.TimeFrame.Years, tann=252)

```bash

| Parameter | Default | Description |

|-----------|---------|-------------|

| `timeframe` | None | Timeframe for returns |

| `tann` | None | Annualization periods |

| `fund` | None | Use fund mode |

- *Output**:

```python
{
    'rtot': 0.234,      # Total log return
    'ravg': 0.001,      # Average return
    'rnorm': 0.287,     # Annualized return
    'rnorm100': 28.7,   # Annualized return in %

}

```bash

### AnnualReturn

Year-by-year returns.

```python
cerebro.addanalyzer(bt.analyzers.AnnualReturn, _name='annret')

```bash

- *Output**:

```python
{
    2020: 0.15,
    2021: 0.22,
    2022: -0.08,
}

```bash

### TradeAnalyzer

Detailed trade statistics.

```python
cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='ta')

```bash

- *Output**:

```python
{
    'total': {
        'total': 100,
        'open': 2,
        'closed': 98,
    },
    'won': {
        'total': 55,
        'pnl': {'total': 5500.0, 'average': 100.0, 'max': 500.0},
    },
    'lost': {
        'total': 45,
        'pnl': {'total': -2250.0, 'average': -50.0, 'max': -200.0},
    },
    'long': {
        'total': 60,
        'pnl': {'total': 3000.0, 'average': 50.0},
    },
    'short': {
        'total': 40,
        'pnl': {'total': 250.0, 'average': 6.25},
    },
    'streak': {
        'won': {'current': 3, 'longest': 8},
        'lost': {'current': 0, 'longest': 5},
    },
    'pnl': {
        'gross': {'total': 3250.0, 'average': 33.16},
        'net': {'total': 3250.0, 'average': 33.16},
    },
    'len': {
        'total': 500,
        'average': 5.1,
        'max': 20,
        'min': 1,
    }
}

```bash

### SQN

System Quality Number (Van K. Tharp).

```python
cerebro.addanalyzer(bt.analyzers.SQN, _name='sqn')

```bash

- *SQN Scale**:
- 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?

- *Output**:

```python
{
    'sqn': 2.34,
    'trades': 50,
}

```bash

### Calmar

Calmar ratio (annual return / maximum drawdown).

```python
cerebro.addanalyzer(bt.analyzers.Calmar, _name='calmar',
                    timeframe=bt.TimeFrame.Months, period=36)

```bash

| Parameter | Default | Description |

|-----------|---------|-------------|

| `timeframe` | TimeFrame.Months | Timeframe for calculation |

| `period` | 36 | Lookback period |

| `fund` | None | Use fund mode |

- *Output**:

```python
{
    datetime(2021, 12, 31): 1.23,
    datetime(2022, 12, 31): 0.98,
}

```bash

### Transactions

Transaction log for all executed orders.

```python
cerebro.addanalyzer(bt.analyzers.Transactions, _name='txn')

```bash

- *Output**:

```python
{
    datetime(2021, 1, 1, 9, 30): [
        [100, 150.0, 0, 'AAPL', -15000.0],  # [amount, price, sid, symbol, value]
    ],
}

```bash

### TimeReturn

Time-weighted returns by period.

```python
cerebro.addanalyzer(bt.analyzers.TimeReturn, _name='timeret',
                    timeframe=bt.TimeFrame.Months)

```bash

- *Output**:

```python
{
    datetime(2021, 1, 31): 0.0234,
    datetime(2021, 2, 28): 0.0156,
}

```bash

### Positions

Position analysis.

```python
cerebro.addanalyzer(bt.analyzers.Positions, _name='pos')

```bash

### TotalValue

Total value tracking over time.

```python
cerebro.addanalyzer(bt.analyzers.TotalValue, _name='tv')

```bash

### PyFolio

Integration with pyfolio library for advanced analytics.

```python
cerebro.addanalyzer(bt.analyzers.PyFolio, _name='pyfolio')

```bash

### Other Analyzers

- `LogReturnsRolling`: Rolling logarithmic returns
- `PeriodStats`: Statistics by time period
- `Leverage`: Leverage tracking
- `VWR`: Variance-Weighted Return

## TimeFrameAnalyzerBase

Base class for time-frame aware analyzers.

```python
class MyTimeFrameAnalyzer(bt.TimeFrameAnalyzerBase):
    params = (
        ('timeframe', bt.TimeFrame.Days),
        ('compression', 1),
    )

    def on_dt_over(self):

# Called when timeframe period changes
        pass

```bash

## Integration with Cerebro

```python
import backtrader as bt

# Create strategy

class MyStrategy(bt.Strategy):
    pass

# Create cerebro

cerebro = bt.Cerebro()

# Add strategy

cerebro.addstrategy(MyStrategy)

# Add analyzers

cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe')
cerebro.addanalyzer(bt.analyzers.DrawDown, _name='drawdown')
cerebro.addanalyzer(bt.analyzers.Returns, _name='returns')
cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='ta')

# Run

results = cerebro.run()
strat = results[0]

# Get analysis results

print('Sharpe Ratio:', strat.analyzers.sharpe.get_analysis())
print('Max Drawdown:', strat.analyzers.drawdown.get_analysis()['max']['drawdown'])
print('Total Return:', strat.analyzers.returns.get_analysis()['rnorm100'])
print('Total Trades:', strat.analyzers.ta.get_analysis()['total']['closed'])

```bash

## Custom Analyzer Example

### Simple Trade Counter

```python
class TradeCounter(bt.Analyzer):
    """Count total trades and win rate."""

    def create_analysis(self):
        self.rets = OrderedDict()
        self.rets['total'] = 0
        self.rets['wins'] = 0
        self.rets['losses'] = 0

    def notify_trade(self, trade):
        if not trade.isclosed:
            return

        self.rets['total'] += 1
        if trade.pnlcomm >= 0:
            self.rets['wins'] += 1
        else:
            self.rets['losses'] += 1

    def stop(self):
        if self.rets['total'] > 0:
            self.rets['win_rate'] = self.rets['wins'] / self.rets['total']

```bash

### Win/Loss Ratio Analyzer

```python
class WinLossRatio(bt.Analyzer):
    """Calculate win/loss ratio and average win/loss amounts."""

    def start(self):
        self.wins = []
        self.losses = []

    def notify_trade(self, trade):
        if not trade.isclosed:
            return

        if trade.pnlcomm >= 0:
            self.wins.append(trade.pnlcomm)
        else:
            self.losses.append(abs(trade.pnlcomm))

    def stop(self):
        self.rets = OrderedDict()

        if self.wins:
            self.rets['avg_win'] = sum(self.wins) / len(self.wins)
            self.rets['max_win'] = max(self.wins)
        else:
            self.rets['avg_win'] = 0
            self.rets['max_win'] = 0

        if self.losses:
            self.rets['avg_loss'] = sum(self.losses) / len(self.losses)
            self.rets['max_loss'] = max(self.losses)
        else:
            self.rets['avg_loss'] = 0
            self.rets['max_loss'] = 0

        if self.losses and self.rets['avg_loss'] > 0:
            self.rets['win_loss_ratio'] = self.rets['avg_win'] / self.rets['avg_loss']
        else:
            self.rets['win_loss_ratio'] = float('inf') if self.wins else 0

```bash

### Monthly Returns Analyzer

```python
class MonthlyReturns(bt.TimeFrameAnalyzerBase):
    """Calculate monthly returns."""

    params = (('timeframe', bt.TimeFrame.Months),)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.month_start_value = None
        self.month_returns = OrderedDict()

    def start(self):
        self.month_start_value = self.strategy.broker.getvalue()

    def on_dt_over(self):
        month_end_value = self.strategy.broker.getvalue()
        ret = (month_end_value / self.month_start_value) - 1
        self.month_returns[self.dtkey] = ret
        self.month_start_value = month_end_value

    def get_analysis(self):
        return self.month_returns

```bash

### Hold Time Analyzer

```python
class HoldTimeAnalyzer(bt.Analyzer):
    """Analyze average hold time for trades."""

    def create_analysis(self):
        self.rets = OrderedDict()
        self.hold_times = []

    def notify_trade(self, trade):
        if not trade.isclosed:
            return

        self.hold_times.append(trade.barlen)

    def stop(self):
        if self.hold_times:
            self.rets['avg_hold_bars'] = sum(self.hold_times) / len(self.hold_times)
            self.rets['min_hold_bars'] = min(self.hold_times)
            self.rets['max_hold_bars'] = max(self.hold_times)
            self.rets['total_trades'] = len(self.hold_times)
        else:
            self.rets['avg_hold_bars'] = 0
            self.rets['min_hold_bars'] = 0
            self.rets['max_hold_bars'] = 0
            self.rets['total_trades'] = 0

```bash

## Fund Mode

Many analyzers support fund mode for fund-like accounting:

```python

# Enable fund mode on broker

cerebro.broker.set_fundmode(True, fundstart=10000.0)

# Analyzer will use fund value instead of portfolio value

cerebro.addanalyzer(bt.analyzers.DrawDown, _name='dd', fund=True)

```bash

## Analyzer Lifecycle

```mermaid
stateDiagram-v2
    [*] --> __init__: Analyzer Created
    __init__ --> create_analysis: Initialize results
    create_analysis --> start: Backtest Starts
    start --> prenext: Initial bars
    prenext --> prenext: minperiod not reached
    prenext --> nextstart: minperiod reached
    nextstart --> next: First valid bar
    next --> next: Processing bars
    next --> notify_trade: Trade closes
    next --> notify_order: Order executes
    next --> notify_cashvalue: Value changes
    next --> stop: Backtest ends
    stop --> get_analysis: Return results
    get_analysis --> [*]: Complete

```bash

## CSV Output

Analyzers can save results to CSV (if `csv` attribute is `True`):

```python
cerebro.run()

# Results automatically saved to CSV if configured

```bash

## Best Practices

1. **Call `super().__init__()` first**- Always call parent constructor

2.**Use `create_analysis()`**- Initialize result structure here
3.**Check `trade.isclosed`**- Only count closed trades in `notify_trade`
4.**Use OrderedDict**- Preserves insertion order for results
5.**Handle edge cases**- Check for division by zero, empty collections
6.**Return dict-like objects**- Use `get_analysis()` to return results
7.**Access via strategy** - Results accessed via `strategy.analyzers.{_name}`

## Next Steps

- [Indicators API](indicator.md) - Technical indicators
- [Strategy API](strategy.md) - Strategy development
- [Data Feeds API](data-feeds.md) - Data sources