# Open-position dashboard and restart-state recovery

Use this reference when a paper/live trading bot already has journals and paper exchange state, but needs safer visibility into current exposure and robust restarts.

## Why this matters

Closed-trade PnL is not enough. Before any live-preview or live trading discussion, the operator must see current exposure:

- open position count per strategy;
- current unrealized PnL;
- notional and margin committed;
- leverage;
- entry/current price;
- whether a restarted bot can still manage exits correctly.

A bot that forgets trailing-stop, break-even, ATR-stop, or entry-time state after restart can hold a position blindly. Treat this as a live-readiness blocker.

## Dashboard pattern

Read-only dashboards should inspect runtime state only. Add columns to the strategy overview:

```text
Open pos
Open PnL
```

Add a dedicated `Open Positions` section with one row per paper/live position:

```text
strategy_id
coin
side
contracts
entry_price
current_price
notional_usd
margin_usd
leverage
unrealized_pnl_usd
```

For paper trading, read positions from each strategy runtime directory, e.g.:

```text
~/.local/state/<Bot>/experiments/<strategy_id>/paper_state.json
```

Fetch current mids from the exchange info endpoint if available; if live price fetch fails, fall back to entry price and show zero/unmoved unrealized PnL rather than crashing the dashboard. Keep dashboard rendering side-effect-free: no restarts, no order placement, no config mutation.

## Restart-state pattern

Persist enough metadata in the paper/live position state for exit logic to survive process restarts:

```text
entryTs
highPrice
atrSlPx
beActive
trailingActive
```

On new entries, write those fields alongside the paper position. During each exit-loop pass, update the persisted metadata after break-even/trailing/high-watermark changes.

On startup, reconstruct in-memory `PositionState` from persisted metadata. If metadata is missing because the position predates the new state format, use conservative defaults:

```text
entry_ts = now - dead_fish_time_limit
high_px = entry_price
atr_sl_px = entry_price - atr_sl_multiplier * atr
be_active = false
trailing_active = false
```

Log a throttled warning for reconstructed legacy positions. The conservative time default makes an old position eligible for time-stop evaluation instead of being accidentally held forever.

## Tests to add

- Dashboard snapshot reads `paper_state.json` and reports `open_position_count`.
- Dashboard computes unrealized PnL, notional, margin, and leverage from current mids.
- Dashboard HTML contains an `Open Positions` section and remains read-only.
- Restart recovery uses persisted `entryTs`, `highPrice`, `atrSlPx`, `beActive`, and `trailingActive` when present.
- Restart recovery without metadata uses conservative defaults based on dead-fish time and ATR stop.
- Paper exchange preserves the state metadata through `fetch_positions()` so the bot can recover it.

## Pitfalls

- Do not treat closed-trade journals as full exposure truth; open positions can dominate real risk.
- Do not reset `entry_ts` to `now` on restart for legacy positions; that delays time-stop exits and can create zombie holds.
- Do not silently ignore missing restart metadata; log it with throttling.
- Do not add dashboard controls that mutate state. The dashboard is an observability boundary, not an operator console.
