# Features

Single-day backtest, multi-day backtest, parameter sweep, scatter widgets, preventive tag filtering.

The analyzer tab is separate from the legacy backtester. State lives in `analyzer.store.ts`; the chart instance ID is `analyzer-chart`.

Strategy workflow:
- Pick a data file, date or date range, and a strategy.
- Run for a single-combo backtest, or Optimize for a parameter sweep.
- Sweep configuration: cartesian (every combination) or fixed-N (random sample of size N).
- Results render in a metrics grid plus 2D and 3D scatter widgets for choosing combos.
- Selecting a combo previews its full backtest on the chart.

Indicators attached in the analyzer render on the analyzer chart only and do not feed strategy logic; they exist to visualize context alongside trades.

Per-instance Strategy TF override pill in the chart top bar: switch a strategy from M1 to M5 (etc.) without leaving the chart.

### Notes

- Mode-aware view TF: after a run resolves, the chart TF snaps to the executed TF unless the user manually touched the view TF this session.
- Multi-day strategy runs aggregate stats across days while preserving per-day drill-down.

<a id="preventive-tag-filtering"></a>
## Preventive tag filtering

Tag dropdown re-runs through the Rust engine with a trading_mask. No Python re-execution.

When you select a tag in the analyzer, signal bundles preserved from the last optimization are sent back into Rust with a `trading_mask` built from the tag's pre-computed boolean array. The engine refuses to open trades where the mask is false. Result: post-hoc tag filtering matches NinjaTrader-style preventive zone filtering exactly, instead of the older 4.6x trade-count divergence from cascading "one position at a time" violations.

### Notes

- Signal bundles are preserved after optimization, cleaned up on next optimization or cancel.
- Tag helpers in Python: [`between()`](https://quantchartsllc.com/docs/python/py-signals.md#between) is inclusive (>=, <=), [`above()`](https://quantchartsllc.com/docs/python/py-signals.md#above) and [`below()`](https://quantchartsllc.com/docs/python/py-signals.md#below) are strict (>, <).
- Same pipeline applies to indicator-emitted tags via `with_tag()` (Rust) or returning a tag dict (Python).

<a id="backtester-vectorized"></a>
## Backtester (vectorized)

Rust engine with SL/TP brackets, long + short, conflict resolution, MAE / MFE / SNR.

Backtest pipeline:
1. Renderer calls `window.electron.backtest.runVectorized(strategyCode, params)`.
2. Main process loads tick or OHLC data into a binary buffer.
3. Python or Rust executor runs the strategy and returns signal arrays.
4. Rust engine processes signals into trades with SL/TP brackets.
5. Returns `{ trades, signals, stats, equityCurve }`.

Fill conventions (microstructure):
- Long entry uses **ask** price; long exit uses **bid** price.
- Short entry uses **bid** price; short exit uses **ask** price.

SL/TP modes:
- entry-only: SL/TP locked at entry tick; remainder of the in-position SL/TP arrays is ignored.
- per-tick (Rust default): bracket re-evaluated each tick, supporting trailing patterns.

### Notes

- Conflicting same-bar signals: exit is resolved before entry.
- `avg_volume_per_trade` is computed post-hoc in TS by walking each trade window, not in Rust.

<a id="mcp-server"></a>
## MCP server

Local MCP server exposes workspace files and backtest tools to external AI clients (Claude Desktop, Cursor, etc.).

Connect any MCP-aware client to read and edit your workspace strategies, indicators, and notebooks, validate them, and kick off backtests from outside the app.

Available tools (auth-guarded, subscription-aware):
- file lookup, listing, read / write / edit
- per-language validation (Python AST + decorator check, Rust cargo check)
- backtest execution returning summary stats

The server ships with a knowledge pack that teaches the connected model the QC API surface (Python OHLC + Rust TBBO), data-mode rules, and the analyzer workflow.

<a id="ai-panel"></a>
## AI panel

Four-quadrant mode picker: (strategy | indicator) x (TBBO Rust | OHLC Python). Mode-specific system prompts and starter prompts.

The Claude panel in the right sidebar has two pills in the empty state: a STRATEGY/INDICATOR/NOTEBOOK kind picker and a TBBO/OHLC language picker. The combination drives:
- Which system prompt route is used (`buildStrategyTbboRustPrompt`, `buildStrategyOhlcPythonPrompt`, `buildIndicatorTbboRustPrompt`, `buildIndicatorOhlcPythonPrompt`).
- Which starter prompts are offered.
- Which baseline templates are referenced in the system prompt's "Reference Templates" block.

The panel knows about the active file in the editor and can edit it in place. Notebook mode is unchanged from prior versions; it does not split by language.

### Notes

- Old "What is data_mode BOTH vs TICK?" prompt was replaced with "What's the difference between TBBO and OHLC files?".
- The system prompt no longer mentions `DataMode.BOTH`. Python is OHLC-only; Rust is TBBO.

<a id="volume-pane"></a>
## Volume pane

Toggle "Show Volume Pane" in chart settings. Histogram subpane (bottom 20%) showing per-bar volume.

Renders via LWC `HistogramSeries` with `priceScaleId: 'volume'`. Bar coloring is green / red by close-vs-open. Hidden automatically in tick mode (no volume concept at tick granularity). For TBBO bars that lack a [`volume`](https://quantchartsllc.com/docs/python/py-data.md#volume) field the pane falls back to `tickCount`.

<a id="notebook-integration"></a>
## Notebook integration

Jupyter notebooks share the same Python engine as the Analyzer. Run backtests inline or load saved Analyzer runs by name.

Two paths to a `BacktestResults`:

1. Inline run from a notebook:
   `import quant_charts as qc`
   `r = qc.run("strategies/foo.py", data="ES.parquet", date_range=("2026-04-15","2026-04-15"), params={"fast":9})`
   `qc.optimize(...)` and `qc.wfa(...)` follow the same pattern for sweeps and walk-forward analysis.

2. Load a saved Analyzer run:
   In the Analyzer top bar, click "Send to Notebook ↗" after a run; a notebook auto-opens with `r = qc.load_run("<name>")` pre-filled. From any notebook: `qc.runs()` lists saved names, `qc.delete_run(name)` cleans up.

Saved runs persist on disk and are never overwritten by future runs. The full BacktestResults API (Monte Carlo, MAE/MFE, parameter sensitivity, raw [`r.trades`](https://quantchartsllc.com/docs/python/py-backtest.md#r-trades) DataFrame) works on both.
