# How to Debug Strategy Logic and Inspect Internal State in Nautilus Trader

> Debug Nautilus Trader strategy logic by enabling DEBUG logging and inspecting self.cache and self.portfolio attributes to trace real-time state without code modification.

- Repository: [Nautech Systems/nautilus_trader](https://github.com/nautechsystems/nautilus_trader)
- Tags: how-to-guide
- Published: 2026-02-16

---

**Enable DEBUG logging via `strategy._log.setLevel("DEBUG")` and inspect `self.cache` and `self.portfolio` attributes to trace real-time strategy state without modifying library code.**

Nautilus Trader is a high-performance algorithmic trading platform that isolates strategy execution within a `Strategy` instance. When you need to debug strategy logic and inspect internal state, the framework provides built-in logging, cache access, and portfolio inspection capabilities that allow deep visibility without altering the core library.

## Enable Detailed Logging for Strategy Debugging

Every `Strategy` instance creates its own logger accessible via `self._log`. This logger captures all framework interactions, including order submissions, position changes, and market data events.

### Configure Logging via StrategyConfig

Set logging flags when defining your strategy configuration in [`nautilus_trader/trading/config.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/nautilus_trader/trading/config.py):

```python
from nautilus_trader.trading.config import StrategyConfig

class MyStrategyConfig(StrategyConfig, frozen=True):
    log_events: bool = True      # Log all events received by the strategy

    log_commands: bool = True    # Log all commands sent by the strategy

```

These flags ensure that the strategy's internal message processing is recorded in the logs.

### Runtime Log Level Adjustment

For existing strategy instances, change the log level dynamically to capture DEBUG output:

```python
strategy._log.setLevel("DEBUG")

```

This is useful when you need verbose output only during specific market conditions or debugging sessions.

### File Output for Post-Mortem Analysis

Redirect logs to a file for offline analysis without modifying the core library:

```python
import logging

strategy._log.addHandler(logging.FileHandler("strategy_debug.log"))

```

All subsequent log entries write to `strategy_debug.log`, preserving a record of strategy logic execution.

## Inspect Core Components at Runtime

The `Strategy` class in `nautilus_trader/trading/strategy.pyx` maintains references to core components after registration with the `Trader`. These attributes provide read-only access to the platform's internal state.

### Access the Cache for Market Data

The `self.cache` attribute (type `Cache`) stores market data, order-book snapshots, and instrument definitions:

```python
def on_bar(self, bar: Bar) -> None:
    # Query the latest quote for the instrument

    quote = self.cache.quote_tick(bar.instrument_id)
    self._log.debug(f"Latest quote: {quote}")
    
    # Access historical bars if cached

    bars = self.cache.bars(bar.instrument_id)
    self._log.debug(f"Cached bars count: {len(bars)}")

```

### Query Portfolio State and Positions

The `self.portfolio` attribute (type `PortfolioFacade`) exposes open positions, cash balances, and P&L calculations:

```python
def on_trade(self, trade: TradeTick) -> None:
    # List all open positions

    positions = self.portfolio.positions()
    self._log.debug(f"Open positions: {list(positions)}")
    
    # Check exposure for specific instrument

    exposure = self.portfolio.exposure(trade.instrument_id)
    self._log.debug(f"Current exposure: {exposure}")
    
    # Access unrealized P&L

    pnl = self.portfolio.unrealized_pnl(trade.instrument_id)
    self._log.debug(f"Unrealized P&L: {pnl}")

```

Both components reflect the live state maintained by the `Trader` and update synchronously with market events.

## Use Python Inspection Utilities

Nautilus Trader includes utilities in [`nautilus_trader/core/inspect.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/nautilus_trader/core/inspect.py) to differentiate platform objects from user-defined types.

### Verify Method Signatures

When debugging command submissions, verify expected parameters using Python's standard `inspect` module:

```python
import inspect

# Check submit_order signature before calling

sig = inspect.signature(self.submit_order)
self._log.debug(f"submit_order signature: {sig}")

```

This prevents errors from incorrect argument passing when debugging complex order logic.

### Identify Nautilus Object Types

Use `is_nautilus_class` to filter collections containing mixed object types:

```python
from nautilus_trader.core.inspect import is_nautilus_class

def debug_cache_contents(self):
    for obj in self.cache.objects():
        if is_nautilus_class(type(obj)):
            self._log.debug(f"Nautilus object: {obj}")
        else:
            self._log.debug(f"User object: {obj}")

```

This distinction helps when serializing objects or diagnosing type-related bugs.

## Interactive Debugging Techniques

### Breakpoint Debugging with pdb

Since the strategy runs in the same Python process as the `Trader`, you can insert standard breakpoints:

```python
def on_trade(self, trade: TradeTick) -> None:
    import pdb; pdb.set_trace()
    
    # When execution pauses, inspect:

    # (pdb) self.cache.quote_tick(trade.instrument_id)

    # (pdb) self.portfolio.positions()

    # (pdb) self._log.handlers

```

At the breakpoint, you have direct access to `self`, `self.cache`, `self.portfolio`, and all strategy attributes.

### External Trader Inspection

When running interactive sessions (Jupyter or REPL) with a `Trader` reference, query loaded strategies externally:

```python

# trader is an instance of nautilus_trader.trading.trader.Trader

for strat in trader.strategies():
    print(f"Strategy ID: {strat.id}")
    print(f"State: {strat.state}")
    print(f"Position count: {len(strat.portfolio.positions())}")

```

The `Trader` also exposes `actor_ids()`, `strategy_ids()`, and component clocks for system-wide diagnostics.

## Running Tests with Diagnostic Logging

The repository contains unit tests that exercise the strategy API in [`tests/unit_tests/trading/test_strategy_pyo3.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/tests/unit_tests/trading/test_strategy_pyo3.py). Run these with debug logging enabled to observe internal behavior:

```bash
export NAUTILUS_LOG_LEVEL=DEBUG
pytest -vv tests/unit_tests/trading/test_strategy_pyo3.py -k test_strategy

```

This outputs the same structured logs you will see in production, allowing you to verify strategy behavior before deployment.

## Summary

- **Enable DEBUG logging** via `strategy._log.setLevel("DEBUG")` or `StrategyConfig` flags to capture all events and commands.
- **Inspect internal state** through `self.cache` (market data) and `self.portfolio` (positions, P&L) attributes available in every strategy.
- **Use Python inspection tools** like `inspect.signature()` and `is_nautilus_class()` to verify API calls and filter object types.
- **Attach breakpoints** using `pdb.set_trace()` for interactive debugging with full access to strategy components.
- **Query the Trader** externally via `trader.strategies()` to monitor loaded strategies in Jupyter or REPL sessions.

## Frequently Asked Questions

### How do I enable DEBUG logging in Nautilus Trader?

Set the log level on the strategy instance after creation: `strategy._log.setLevel("DEBUG")`. Alternatively, configure `log_events=True` and `log_commands=True` in your `StrategyConfig` subclass. For file output, add a `logging.FileHandler` to `strategy._log`.

### Can I modify strategy state during a pdb debugging session?

Yes. When execution pauses at a `pdb.set_trace()` breakpoint inside a strategy method, you have full read-write access to `self` and all its attributes including `self.cache`, `self.portfolio`, and private variables. You can call methods and modify state interactively.

### What is the difference between self.cache and self.portfolio?

`self.cache` (type `Cache`) provides access to market data such as quote ticks, trade ticks, bars, and order-book snapshots. `self.portfolio` (type `PortfolioFacade`) exposes trading account state including open positions, cash balances, exposure, and unrealized P&L. Both are read-only from the strategy perspective.

### How do I inspect all loaded strategies from the Trader instance?

Access the `strategies()` method on a `Trader` instance: `for strat in trader.strategies(): print(strat.id, strat.state)`. The `Trader` class in [`nautilus_trader/trading/trader.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/nautilus_trader/trading/trader.py) also provides `strategy_ids()` and `actor_ids()` methods for component enumeration.