Your First Quant Charts Script

New to Quant Charts? Start here. What it is, then a first working indicator.

What This System Can Do

TBBO + OHLC backtester with Python (OHLC bars) and Rust (TBBO ticks) strategy/indicator APIs, an analyzer with parameter sweeps, and an in-app AI panel for editing files.

Quant Charts is a desktop trading research environment. It loads parquet files (OHLC bars or TBBO tick-by-tick), renders them through TradingView Lightweight Charts, and runs strategies/indicators against them. Strategies in Python execute on aggregated OHLC bars; strategies in Rust execute on raw TBBO ticks for tick-precise microstructure logic. Backtests run through a vectorized Rust engine that returns trades, equity curves, and stats. The analyzer sweeps parameter grids in parallel, persists results, and renders 2D and 3D scatter widgets for combo selection.

Notes

  • Workspace lives at your workspace/ with strategies/, indicators/, notebooks/, whiteboards/, exports/.
  • Templates are seeded into empty folders on first launch and refreshed via baseline migration.
  • Authentication and licensing are LemonSqueezy-backed; subscriptions sync from LemonSqueezy into Supabase.

Feature checklist

High-level capability list. Each row links to deeper sections in the Features group.

Charting:

  • TradingView Lightweight Charts v5 with custom canvas primitives (TradeBracketPrimitive, IndicatorOverlayPrimitive)
  • TBBO tick-mode rendering plus OHLC bar mode (any timeframe via WASM aggregator)
  • Volume pane (histogram subpane below price), bar coloring, plotshape markers, bgcolor / box regions

Backtesting:

  • Vectorized Rust engine with SL/TP brackets (entry-only or per-tick re-evaluation)
  • Long + short signals, conflict resolution, fill conventions (long entry = ask, exit = bid; short reversed)
  • Per-trade MAE / MFE / SNR tracking

Analyzer:

  • Strategy mode: single-day backtest, multi-day backtest, parameter sweep (cartesian or fixed-N), 2D + 3D scatter widgets, top-N tables
  • Indicator mode: per-indicator horizon analysis (forward returns, hit rates, MAE/MFE distributions)
  • Preventive tag filtering through trading_mask (Rust-only re-run, no Python re-execution)
  • Per-instance Strategy TF override pill in chart top bar

MCP server:

  • Local MCP server exposes workspace files to Claude Desktop / other clients
  • Tools: list, read, write, edit, validate, run_backtest, find_workspace_file (fuzzy lookup)
  • Subscription-guarded, IPC-isolated

In-app AI panel:

  • Four mode quadrants: (strategy | indicator) x (TBBO Rust | OHLC Python)
  • Mode-specific system prompts and starter prompts
  • File-aware editing of the active strategy or indicator

Notebooks + scripts:

  • Jupyter integration for inline backtests (qc.run, qc.optimize, qc.wfa) and saved-run analysis (qc.load_run)
  • @script .py files with script_helpers (export_csv, export_parquet, export_json)

Keyboard:

Where to start

Pick a path: chart a file, write a strategy, or sweep a grid.

New users:

  1. Drag a parquet file into the chart, or open one from File > Open Data.
  2. Open the Templates section in this tab to find a baseline strategy / indicator that matches your goal.
  3. In the Files panel, double-click a .py or .rs template to open it. The Monaco editor and AI panel both pick up the file.
  4. Click Run in the strategy/indicator panel to attach it to the chart, or open the Analyzer tab to sweep parameters.

For TBBO research:

  1. Load TBBO_Converted.parquet.
  2. Open vp.rs for a tick-fidelity Volume Profile, or orderflow.rs for imbalance + CVD-direction tags.

01. Your first Python indicator

A guided 6-step walkthrough that ends with a working dual_sma.py indicator: two moving averages plus a fill that turns green when fast is above slow and red when it crosses below.

You will use @indicator, declare two input.int parameters and two input.color parameters, compute averages with ta.sma, and call plot() and fill(). By the end you will know how to: declare a Python indicator, expose user-tunable parameters, read the OHLC dataframe, plot named series, and fill between them. Read each step in order; the final step has the complete file you can paste into indicators/.

Step 1. Imports

Pull only what you need from quant_charts. Each imported name is documented in the Python tab.

Example

import numpy as np
from quant_charts import indicator, input, ta, plot, fill

Notes

  • ta is the vectorized technical-analysis namespace (ta.sma, ta.ema, ta.rsi, ta.atr, ...).
  • Importing plot and fill here lets you call them as plain functions inside calculate().

Step 2. The decorator

Mark the class as an indicator. The decorator registers it with the engine and exposes its parameters in the chart settings panel.

Example

@indicator(
    name="Dual SMA",
    description="Fast/slow SMA with cross-direction fill.",
    overlay=True,
    data_mode="ohlc",
    required_columns=["close"],
)
class DualSMA:
    pass

Notes

  • overlay=True draws on the price pane. Set to False for a separate subpane (e.g. RSI, MACD).
  • data_mode="ohlc" runs the indicator on aggregated bars. Use "both" if it should also work in tick-by-tick TBBO view.
  • required_columns makes the loader ship only the columns you read across the worker boundary. Keeps sweep memory tight.

Step 3. Parameters

Declare class-level input.* fields. They become editable inputs in the indicator panel and the analyzer parameter form.

Example

class DualSMA:
    fast_period = input.int(20, "Fast Period", min=2, max=500)
    slow_period = input.int(50, "Slow Period", min=2, max=500)
    fast_color = input.color("#7aa2f7", "Fast Color")
    slow_color = input.color("#e0af68", "Slow Color")
    bull_fill = input.color("#22c55e40", "Bull Fill")
    bear_fill = input.color("#ef444440", "Bear Fill")

Notes

  • input.int(default, label, min=, max=, step=) exposes an integer field. input.float is the same shape for floats.
  • input.color(default_hex, label) accepts 6-char #rrggbb or 8-char #rrggbbaa. The trailing 40 here is hex alpha (40/255 ~= 25%).
  • Read parameters as self.fast_period, self.fast_color, etc., inside calculate().

Step 4. The calculate body

Compute both averages and decide the fill color per bar.

Example

    def calculate(self, df):
        close = np.asarray(df["close"], dtype=np.float64)
        fast = ta.sma(close, self.fast_period)
        slow = ta.sma(close, self.slow_period)
        # element-wise: pick bull color where fast > slow, bear color otherwise
        return fast, slow

Notes

  • Convert pandas columns to numpy with np.asarray(..., dtype=np.float64). The TA namespace expects numpy arrays.
  • ta.sma(close, period) returns an array the same length as close, with NaN for the warmup bars before period samples are available.
  • The return value of calculate() is ignored when you use plot()/fill() directly. Returning is optional for indicators (signal dicts are for strategies, not indicators).

Step 5. Plot the lines and fill between

Two plot() calls register named series on the chart. fill() references those names and shades the area between them.

Example

    def calculate(self, df):
        close = np.asarray(df["close"], dtype=np.float64)
        fast = ta.sma(close, self.fast_period)
        slow = ta.sma(close, self.slow_period)

        plot(fast, "Fast SMA", color=self.fast_color, linewidth=2)
        plot(slow, "Slow SMA", color=self.slow_color, linewidth=2)

        # fill takes the two plot names; color is hex with optional alpha
        fill("Fast SMA", "Slow SMA", color=self.bull_fill)

Notes

  • The name you pass to plot() is what fill() references. Names must be unique within an indicator.
  • Single-color fill is the simplest path. To get a regime-switching fill (green when bull, red when bear), call fill() twice with two pairs of bookkeeping plots, or use bgcolor() on a per-bar basis. The single-color path is enough for this tutorial.

Step 6. The full file

Save this as dual_sma.py under your workspace/indicators/ and Quant Charts auto-loads it.

Example

import numpy as np
from quant_charts import indicator, input, ta, plot, fill


@indicator(
    name="Dual SMA",
    description="Fast/slow SMA with cross-direction fill.",
    overlay=True,
    data_mode="ohlc",
    required_columns=["close"],
)
class DualSMA:
    fast_period = input.int(20, "Fast Period", min=2, max=500)
    slow_period = input.int(50, "Slow Period", min=2, max=500)
    fast_color = input.color("#7aa2f7", "Fast Color")
    slow_color = input.color("#e0af68", "Slow Color")
    fill_color = input.color("#7aa2f733", "Fill")

    def calculate(self, df):
        close = np.asarray(df["close"], dtype=np.float64)
        fast = ta.sma(close, self.fast_period)
        slow = ta.sma(close, self.slow_period)

        plot(fast, "Fast SMA", color=self.fast_color, linewidth=2)
        plot(slow, "Slow SMA", color=self.slow_color, linewidth=2)
        fill("Fast SMA", "Slow SMA", color=self.fill_color)

Step 7. Run it

Open the indicator in Quant Charts and attach it to a chart.

  1. Open the Indicator panel from the chart top bar, click "Add", select "Dual SMA".
  2. Tune Fast Period / Slow Period from the panel; the chart re-runs on every change.

Notes

  • Validation runs on save. If it fails, errors print to the in-app terminal panel.
  • Adjusting an input.color from the panel respects the 8-char hex with alpha format.
Machine-readable: full API at /llms-full.txt · examples on GitHub.