New to Quant Charts? Start here. What it is, then a first working indicator.
Your First Quant Charts Script
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/withstrategies/,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:
- Drag a parquet file into the chart, or open one from File > Open Data.
- Open the Templates section in this tab to find a baseline strategy / indicator that matches your goal.
- In the Files panel, double-click a
.pyor.rstemplate to open it. The Monaco editor and AI panel both pick up the file. - Click Run in the strategy/indicator panel to attach it to the chart, or open the Analyzer tab to sweep parameters.
For TBBO research:
- Load
TBBO_Converted.parquet. - Open
vp.rsfor a tick-fidelity Volume Profile, ororderflow.rsfor 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
tais the vectorized technical-analysis namespace (ta.sma,ta.ema,ta.rsi,ta.atr, ...).- Importing
plotandfillhere lets you call them as plain functions insidecalculate().
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=Truedraws on the price pane. Set toFalsefor 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_columnsmakes 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.floatis the same shape for floats.input.color(default_hex, label)accepts 6-char#rrggbbor 8-char#rrggbbaa. The trailing40here is hex alpha (40/255 ~= 25%).- Read parameters as
self.fast_period,self.fast_color, etc., insidecalculate().
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 asclose, with NaN for the warmup bars beforeperiodsamples are available.- The return value of
calculate()is ignored when you useplot()/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
nameyou pass toplot()is whatfill()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 usebgcolor()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.
- Open the Indicator panel from the chart top bar, click "Add", select "Dual SMA".
- Tune
Fast Period/Slow Periodfrom 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.colorfrom the panel respects the 8-char hex with alpha format.