Backtest Results

Load and analyze prior backtest runs from notebook scripts.

runner.run

runner.run(strategy, data, date_range, params, *, timeframe=None, starting_equity=100000, position_mode="sequential", slippage=0)

Run a strategy across a date range with one parameter set. Works for .py and .rs.

Runs the same engine the analyzer uses, returns a full BacktestResults with all analytics. Call from any Python context: editor Run, notebook cell. Works on Rust TBBO strategies too: you analyze the trades in Python while the strategy logic stays in Rust.

Runs through the optimization worker thread (separate Python subprocess), so it doesn't block the chart, the editor pool, or each other when called from inside an editor Run.

Parameters

  • strategy (str): Path to a .py or .rs file (relative to workspace strategies/ folder, or absolute)
  • data (str): Path to a .parquet file (relative to workspace data/ folder, or absolute)
  • date_range ((str, str)): Inclusive ISO date tuple, e.g. ("2025-01-01", "2025-01-31")
  • params (dict): Strategy parameter values. Names must match the strategy file declarations.

Returns

BacktestResults

Example

from quant_charts import runner
r = runner.run(
    strategy='strategies/ma_cross.py',
    data='ES.parquet',
    date_range=('2025-01-01', '2025-01-31'),
    params={'fast': 10, 'slow': 30},
)
print(r.summary())
r.plot_equity()
import matplotlib.pyplot as plt; plt.show()

Notes

  • For .rs strategies the analyzer engine is invoked exactly as the chart-attached path; trade results are identical.
  • Sharing _runLock with the analyzer: if the Analyzer is mid-optimize, runner.run() blocks until it finishes (a "runner: waiting for analyzer" log line is printed while waiting).
  • Worked TBBO/Rust example: notebooks/built-in/multi_day.ipynb.

runner.optimize

runner.optimize(strategy, data, date_range, grid, *, metric="sharpe", timeframe=None)

Run a parameter grid sweep across a date range. Returns SweepResults.

The analyzer's full grid sweep, callable from Python. Each grid axis must be uniformly spaced (use list(range(...)) or numpy.linspace). Works for .py and .rs strategies; Rust strategies are routed through the same worker pool the Analyzer uses.

Parameters

  • strategy (str): Path to a .py or .rs file
  • data (str): Path to a .parquet data file
  • date_range ((str, str)): Inclusive ISO date tuple
  • grid (dict[str, list]): Param-name to uniformly-spaced value list
  • metric (str, default "sharpe"): "sharpe", "profit_factor", "total_pnl", "win_rate", "avg_snr", "median_snr", "recovery_factor", "payoff_ratio"

Returns

SweepResults

Example

from quant_charts import runner
sweep = runner.optimize(
    strategy='strategies/momentum.rs',
    data='ES_TBBO.parquet',
    date_range=('2025-01-01', '2025-01-31'),
    grid={
        'period': list(range(10, 31, 2)),
        'threshold': [0.3, 0.5, 0.7],
    },
    metric='sharpe',
)
print(sweep.results_df.head())
sweep.scatter_3d('period', 'threshold', 'sharpeRatio')
sweep.best().plot_equity()

Notes

  • Grid axes MUST be uniformly spaced. The engine sweeps min/max/step; arbitrary value lists are rejected.
  • sweep.best() lazily fetches trades for the top-scoring combo from the in-memory trade store.

qc.run / qc.optimize / qc.wfa

qc.run / qc.optimize / qc.wfa(strategy, data, date_range, params|grid, ...)

Run a fresh backtest, sweep, or WFA inline from a notebook. Returns a BacktestResults (run) or SweepResults (optimize / wfa).

Inline backtest entry points. No UI involvement. Same engine the Analyzer uses; results are bit-for-bit identical.

For sweeps, grid axes must be uniformly spaced (use range(...) or numpy.linspace). For WFA, pass in_sample_days and out_of_sample_days plus a grid.

Returns

BacktestResults | SweepResults

Example

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, 'slow': 21})
r.summary()

opt = qc.optimize('strategies/foo.py',
                  data='ES.parquet',
                  date_range=('2026-04-15', '2026-04-15'),
                  grid={'fast': range(5, 21), 'slow': range(15, 50)})
opt.top(10)

qc.runs / load_run / save_run / delete_run

qc.runs / load_run / save_run / delete_run() | (name)

Saved-runs registry. List, load, save, or delete named exports.

Saved runs persist with an index.json registry. Names are alphanumeric plus _-.. Saved runs are explicit and never overwritten by future analyzer runs; call delete_run(name) to clean up.

Returns

pd.DataFrame | BacktestResults | dict | None

Example

import quant_charts as qc

qc.runs()                       # DataFrame: name, strategy, dateRange, kind, nTrades, savedAt
r = qc.load_run('ema_cross_a')
qc.save_run('my_combo')         # persist whatever\'s in memory
qc.delete_run('my_combo')

qc.cancel

qc.cancel()

Cancel the in-flight runner call. Safe from a signal handler or another thread.

Sends a cancel signal to the runner bridge. The currently blocked qc.run / qc.optimize / qc.wfa call raises RunnerCancelled on its next poll. No-op when no call is in flight.

Use case: Jupyter cell that started a long sweep; press the stop button (which triggers cancel) without waiting for the worker to finish naturally.

Returns

None

Example

import quant_charts as qc
import threading

def stop_after(seconds):
    import time
    time.sleep(seconds)
    qc.cancel()

threading.Thread(target=stop_after, args=(60,), daemon=True).start()
# Will be cancelled after 60s if still running:
opt = qc.optimize('strategies/foo.py', data='ES.parquet',
                  date_range=('2026-04-01', '2026-04-30'),
                  grid={'fast': range(5, 30), 'slow': range(20, 60)})

load_csv

load_csv(name_or_path?)

Load a CSV trade export into a BacktestResults for analysis.

Loads trades from a CSV file exported via the Analyzer's Export button. The CSV is parsed back into a full BacktestResults with equity curve, so all analysis methods (summary, plot_equity, monte_carlo, filtering) work the same as for an inline qc.run().

With no argument, loads the most recent CSV in the exports/ folder.

Parameters

  • name_or_path (str, default None): CSV filename (e.g. "my_backtest.csv"), full path, or None for most recent

Returns

BacktestResults

Example

from quant_charts.results import load_csv
r = load_csv()                     # most recent export
r = load_csv("MA_Cross_2024.csv")  # specific file
r.summary()
r.longs().plot_equity()
r.monte_carlo(1000).plot()

Notes

  • CSV files are saved to the exports/ folder when you click Export in the Analyzer top bar.
  • Tags are preserved. r.by_tag("morning") works on CSV-loaded results.
  • Use list_exports() to see available CSV files.

list_exports

list_exports()

List available CSV trade exports with dates and sizes.

Prints a formatted table of all CSV files in the exports/ folder, sorted by modification time (newest first). Returns a list of filenames.

Returns

list[str]

Example

from quant_charts.results import list_exports
files = list_exports()

Output

>>> list_exports()
Filename                                 Modified             Size
----------------------------------------------------------------------
MA_Cross_2024-01-01_to_2024-06-30.csv   2024-07-15 14:32       12.3K
RSI_Scalper_2024-03-01_to_2024-03-31.csv 2024-07-14 09:18        4.1K

r.summary

r.summary()

Print comprehensive stats table.

Displays total trades, win rate, PnL, profit factor, Sharpe ratio, max drawdown, MAE/MFE, streaks, average duration, and more.

Returns

None (prints table)

Example

r.summary()

r.trades

All trades as a pandas DataFrame.

Columns: id, side, entryTime, exitTime, entryPrice, exitPrice, pnl, pnlPercent, mae, mfe, snr, duration, exitReason, tags, stopLoss, takeProfit. Also has entryTime_et / exitTime_et (Eastern Time).

Returns

pandas DataFrame

Example

# Standard pandas operations on trades
r.trades.groupby('side')['pnl'].describe()
r.trades[r.trades['duration'] < 60000]
r.trades.query('exitReason == "sl_hit"')['pnl'].hist()

r.winners / r.losers

r.winners / r.losers()

Filter by PnL sign. Returns a new BacktestResults.

All filter methods return a new BacktestResults. The original is never modified. The filtered result has its own rebuilt equity curve.

Returns

BacktestResults

Example

r.winners().summary()
r.losers().plot_equity()

r.longs / r.shorts

r.longs / r.shorts()

Filter by trade side.

Returns

BacktestResults

Example

r.longs().winners().summary()  # winning longs

r.by_tag

r.by_tag(tag_name)

Filter to trades that have a specific tag.

Parameters

  • tag_name (str, default required): Tag name to filter by

Returns

BacktestResults

Example

r.by_tag("morning").summary()

r.where

r.where(**kwargs)

Django-style field lookups for flexible filtering.

Operators: field=value (exact), field__gt (>), field__lt (<), field__gte (>=), field__lte (<=), field__ne (!=), field__in (list), field__contains (string), field__hour (ET hour), field__dayofweek (0=Mon).

Three-part accessor for timestamps: entryTime__hour__gte=9

Returns

BacktestResults

Example

# Morning longs that won
r.longs().where(
    entryTime__hour__gte=9,
    entryTime__hour__lte=11,
    pnl__gt=0
).summary()

r.plot_equity

r.plot_equity(by_trade?)

Plot the equity curve.

Parameters

  • by_trade (bool, default True): True = x-axis is trade number, False = time

Returns

matplotlib Figure

Example

r.plot_equity()
r.winners().plot_equity()

r.monte_carlo

r.monte_carlo(n?, method?)

Monte Carlo simulation for risk assessment.

Shuffles or bootstraps trade PnL to estimate range of outcomes. Returns a MonteCarloResult with .plot(), .summary(), .percentile(), .ruin_probability(), and .confidence_interval() methods.

Parameters

  • n (int, default 1000): Number of simulations
  • method (str, default "bootstrap"): "bootstrap" or "shuffle"

Returns

MonteCarloResult

Example

mc = r.monte_carlo(1000)
mc.plot(ci=0.95)
mc.summary()
mc.ruin_probability(5000)

r.simulate_sl_tp

r.simulate_sl_tp(sl, tp)

Simulate different SL/TP using MAE/MFE data (instant, no re-run).

Parameters

  • sl (float, default required): Stop loss in dollars
  • tp (float, default required): Take profit in dollars

Returns

BacktestResults

Example

sim = r.simulate_sl_tp(sl=30, tp=15)
print(f"Original: ${r.trades.pnl.sum():.2f}")
print(f"Simulated: ${sim.trades.pnl.sum():.2f}")

r.monthly_returns

r.monthly_returns()

Year x Month pivot table of PnL.

Returns

pandas DataFrame

Example

r.monthly_returns()