Implementation:Nautechsystems Nautilus trader PortfolioAnalyzer Calculate Statistics
| Field | Value |
|---|---|
| sources | https://github.com/nautechsystems/nautilus_trader , https://nautilustrader.io/docs/ |
| domains | performance analysis, portfolio management, risk assessment |
| last_updated | 2026-02-10 12:00 GMT |
Overview
Concrete tool for computing post-trade portfolio performance statistics provided by NautilusTrader.
Description
The PortfolioAnalyzer class tracks account balances, positions, realized PnLs, and returns. Its calculate_statistics method is the primary entry point: given an Account and a list of Position objects, it captures starting and current balances, iterates over positions to accumulate realized PnL per currency and realized returns per timestamp, and sorts the return series chronologically. The analyzer supports a plugin registry of PortfolioStatistic objects; query methods (get_performance_stats_pnls, get_performance_stats_returns, get_performance_stats_general) iterate the registered statistics and produce dictionaries of computed values. Both Python and Rust (PyO3) statistic implementations are supported, with automatic type conversion between pandas Series and lists/dicts.
Usage
The PortfolioAnalyzer is typically accessed through engine.kernel.portfolio.analyzer after a backtest run. You can also instantiate it independently for custom analysis workflows.
Code Reference
- Source location:
nautilus_trader/analysis/analyzer.py, lines 33--525 - Signature:
class PortfolioAnalyzer:
def __init__(self) -> None
def calculate_statistics(
self,
account: Account,
positions: list[Position],
) -> None
- Import:
from nautilus_trader.analysis.analyzer import PortfolioAnalyzer
I/O Contract
Inputs (calculate_statistics):
| Parameter | Type | Required | Description |
|---|---|---|---|
account |
Account |
Yes | The account object providing starting balances (via account.starting_balances()) and current total balances (via account.balances_total()).
|
positions |
list[Position] |
Yes | List of positions (open and/or closed) to analyze. Each position provides realized_pnl, realized_return, and ts_closed.
|
Outputs / Side Effects:
| Output | Type | Description |
|---|---|---|
| None (return) | None |
Method populates internal state for subsequent queries. |
_account_balances_starting |
dict[Currency, Money] |
Starting balances captured from the account. |
_account_balances |
dict[Currency, Money] |
Current total balances captured from the account. |
_realized_pnls |
dict[Currency, pd.Series] |
Per-currency realized PnL series indexed by position ID. |
_returns |
pd.Series |
Time-indexed series of realized returns, sorted chronologically. |
Key query methods after calculation:
| Method | Return Type | Description |
|---|---|---|
total_pnl(currency) |
float |
Total PnL (ending balance - starting balance + optional unrealized PnL). |
total_pnl_percentage(currency) |
float |
Total PnL as a percentage of starting balance. |
realized_pnls(currency) |
None | Per-position realized PnL series for the given currency. |
returns() |
pd.Series |
Raw returns time series. |
get_performance_stats_pnls(currency) |
dict[str, float] |
All registered PnL-based statistics plus total PnL and total PnL%. |
get_performance_stats_returns() |
dict[str, Any] |
All registered return-based statistics. |
get_performance_stats_general() |
dict[str, Any] |
All registered position-based statistics. |
Usage Examples
Accessing analysis after a backtest run:
from nautilus_trader.backtest.engine import BacktestEngine
engine = BacktestEngine()
# ... setup and run ...
engine.run()
# Access the analyzer through the portfolio
analyzer = engine.kernel.portfolio.analyzer
# Get PnL statistics for a specific currency
from nautilus_trader.model.currencies import USD
stats_pnl = analyzer.get_performance_stats_pnls(USD)
print(f"Total PnL: {stats_pnl['PnL (total)']}")
print(f"Total PnL%: {stats_pnl['PnL% (total)']}")
# Get return-based statistics
stats_returns = analyzer.get_performance_stats_returns()
for name, value in stats_returns.items():
print(f"{name}: {value}")
Standalone usage with custom statistics:
from nautilus_trader.analysis.analyzer import PortfolioAnalyzer
from nautilus_trader.analysis.statistics.sharpe_ratio import SharpeRatio
from nautilus_trader.analysis.statistics.win_rate import WinRate
analyzer = PortfolioAnalyzer()
analyzer.register_statistic(SharpeRatio())
analyzer.register_statistic(WinRate())
# account and positions obtained from cache or engine
analyzer.calculate_statistics(account, positions)
# Query total PnL
total = analyzer.total_pnl()
total_pct = analyzer.total_pnl_percentage()
print(f"Total PnL: {total}, Total PnL%: {total_pct}%")
# Query registered return-based statistics
returns_stats = analyzer.get_performance_stats_returns()
print(f"Sharpe Ratio: {returns_stats.get('Sharpe Ratio')}")
Getting formatted output for logging:
for line in analyzer.get_stats_pnls_formatted(USD):
print(line)
for line in analyzer.get_stats_returns_formatted():
print(line)
for line in analyzer.get_stats_general_formatted():
print(line)