Decorators

Top-level annotations that turn a Python file into an indicator, strategy, script, or day-start hoist.

@indicator

@indicator(name, overlay?, description?, data_mode?, timeframe?, visible_on?, warmup_days?, continuous?)

Mark a Python class as a chart indicator.

The @indicator decorator transforms a Python class into a chart indicator that receives price data and draws visual output. The class must implement a calculate(self, df) method that receives the chart DataFrame.

Call plot(), hline(), or fill() inside calculate() to draw on the chart. Access price data via the global series: close, open, high, low, volume.

When you declare warmup_days the DataFrame is self-describing: a string session column (the ET trading-day key) and a tz-aware ET DatetimeIndex. Cross-day reads need NO Quant Charts API, just pandas: previous-session close df[df.session == df.session.unique()[-2]].close.iloc[-1], a 3-session VP POC, or a 3000-bar EMA over the warm buffer. Whether per-day incremental panning is safe (causal) vs a full-span recompute (span-aggregate like Volume Profile) is auto-detected; there is no incremental argument.

Python indicators run on OHLC bars. For TBBO tick-level indicators, use a Rust .rs file with the #[indicator] macro instead.

Parameters

  • name (str, default required): Display name shown in the indicator panel
  • overlay (bool, default False): If True, draws on price chart. If False, separate pane
  • description (str, default None): Optional tooltip description
  • data_mode (str, default 'ohlc'): Always 'ohlc' for Python indicators. Use Rust for tick.
  • timeframe (Timeframe, default None): The COMPUTE timeframe the indicator runs at. "tick" (default) follows the chart; "1m"/"5m"/etc. computes on its own bars at that timeframe, independent of the chart.
  • visible_on (str | list, default 'auto'): Which chart timeframes the indicator DRAWS on (visibility, not compute). "auto" (default) = directional: visible on its compute timeframe and every FINER chart timeframe, hidden on coarser ones (a 1m indicator shows on 1m/30s/tick, hidden on 5m). "all" = every timeframe. ["1m","5m"] = only those. For a held level (VWAP-as-level, POC, prior-day levels) prefer plot_type=PlotType.STEPLINE so a coarse series on a finer chart reads as steps, not interpolated ramps.
  • warmup_days (int, default 0): Prior trading days of read-only history prepended before each day so the indicator is warm at the first bar (e.g. a 20-day volume profile, or a 200-bar SMA that needs a few prior sessions to fill). Truncated at a contract rollover. 0 = legacy per-day.
  • continuous (bool, default False): If True, the indicator computes over the WHOLE loaded window in one pass and its line bridges session gaps, instead of resetting each session. Use for cumulative-over-the-window indicators (session/anchored VWAP, range-anchored profiles). Note its values extend/shift as you pan in earlier history. Default False = per-session reset, stable as more days load.

Returns

None. Use plot() inside calculate() to draw output.

Example

from quant_charts import indicator, input, plot, close

@indicator("SMA", overlay=True)
class SMA:
    period = input.int(20, "Period", min=1, max=500)
    color = input.color("#2962FF", "Color")

    def calculate(self, df):
        result = close.rolling(self.period).mean()
        plot(result, f"SMA({self.period})", color=self.color)

Notes

  • calculate() is called every time the chart data updates.
  • Use input.* class variables to create user-configurable parameters.
  • Multiple plot() calls create multiple series on the same indicator.

@strategy

@strategy(name, overlay?, timeframe?, visible_on?, description?, data_mode?, uses_sltp?, emit_sltp?, required_columns?, warmup_days?, continuous?)

Mark a Python class as a backtestable trading strategy.

The @strategy decorator creates a trading strategy that returns entry/exit signals for backtesting. The class must implement calculate(self, df) which returns a dict of boolean signal Series.

The backtester uses these signals to simulate trades and compute equity curves, win rates, drawdowns, and other performance metrics.

Python strategies run on OHLC bars only. For tick-level TBBO strategies use a Rust .rs file.

Parameters

  • name (str, default required): Display name shown in the strategy panel
  • overlay (bool, default True): If True, draws on the price chart
  • timeframe (Timeframe, default None): Default execution timeframe
  • visible_on (str | list, default 'auto'): Which chart timeframes the strategy's plotted lines DRAW on. 'auto' (default) = directional (the execution timeframe and every finer chart timeframe, hidden on coarser); 'all' = every timeframe; ['1m','5m'] = only those. Trades and markers always reposition regardless.
  • description (str, default None): Optional tooltip description
  • data_mode (str, default 'ohlc'): Always 'ohlc' for Python strategies
  • uses_sltp (bool, default False): If True, the strategy returns sl_long/tp_long/sl_short/tp_short arrays
  • emit_sltp (str, default 'entry_only'): 'entry_only' (set once at entry) or 'per_tick' (re-evaluated each bar)
  • required_columns (list[str], default auto): Columns the strategy reads. SHM only ships these to workers.
  • warmup_days (int, default 0): Prior trading days of read-only history prepended before each day so indicators are warm at the first bar (e.g. a 20-day volume profile, or a 200-bar SMA that needs a few prior sessions to fill). Counted in trading days, truncated at a contract rollover. 0 = legacy per-day, identical results.
  • continuous (bool, default False): If True, positions carry across calendar days instead of force-closing at EOD (still force-flat at a detected contract rollover / maintenance gap). Needed for daily, hourly, and swing strategies.

Returns

A dict with keys: "entry_long", "exit_long", "entry_short", "exit_short". With uses_sltp=True, also "sl_long", "tp_long", "sl_short", "tp_short".

Example

from quant_charts import strategy, input, indicators, cross_above, cross_below, Timeframe

@strategy("MA Cross", overlay=True, timeframe=Timeframe.M1)
class MACross:
    fast = input.int(10, "Fast", min=1, max=100)
    slow = input.int(20, "Slow", min=2, max=200)

    def calculate(self, df):
        fast_ma = indicators.sma(period=self.fast, color="#26A69A")
        slow_ma = indicators.sma(period=self.slow, color="#EF5350")

        return {
            'entry_long': cross_above(fast_ma, slow_ma),
            'exit_long': cross_below(fast_ma, slow_ma),
            'entry_short': cross_below(fast_ma, slow_ma),
            'exit_short': cross_above(fast_ma, slow_ma),
        }

Notes

  • Use indicators.sma() / indicators.ema() to add visual overlays alongside your strategy.
  • Signal Series must be boolean. Use cross_above() / cross_below() for crossovers.
  • Conflicting signals on the same bar: exit is resolved before entry.
  • Hoist param-independent work into a @day_start method for fast optimization sweeps.

@day_start

@day_start(method)

Mark a method to run once per day per worker, before the parameter sweep.

Hoists parameter-independent computation out of the per-combo calculate() body. The decorated method runs once per trading day per worker process; results stored on self are visible inside calculate() for every parameter combination.

Use this for any heavy work that does not depend on swept params: full-series ta calls with literal periods, day-level summary stats, regime detection, cached indicator outputs.

Parameters

  • method (callable, default required): Method on the strategy/indicator class with signature (self, df) -> None

Returns

None. Store hoisted arrays as self.<name> for use inside calculate().

Example

from quant_charts import strategy, day_start, input, ta, close, cross_above, cross_below

@strategy("Hoisted EMA Cross")
class HoistedCross:
    fast = input.int(10, "Fast", min=2, max=50)
    slow = input.int(30, "Slow", min=5, max=200)

    @day_start
    def prep(self, df):
        # hoisted: param-independent, runs once per day not once per combo
        self.atr = ta.atr(df['high'], df['low'], df['close'], 14)

    def calculate(self, df):
        fast_ma = close.rolling(self.fast).mean()
        slow_ma = close.rolling(self.slow).mean()
        return {
            'entry_long': cross_above(fast_ma, slow_ma),
            'exit_long':  cross_below(fast_ma, slow_ma),
            'entry_short': cross_below(fast_ma, slow_ma),
            'exit_short':  cross_above(fast_ma, slow_ma),
        }

Notes

  • Critical for fast optimization sweeps: a 100-combo sweep with hoisted ATR runs the ATR once instead of 100 times.
  • Anything assigned to self.* inside @day_start is visible in calculate().
  • Calls inside @day_start should use literal periods, not swept parameters.

@script

@script(name, description?, version?)

Mark a Python class as a utility script for data analysis. Used in .py files only (not notebooks).

The @script decorator creates a standalone script that runs once when executed. Unlike indicators and strategies, scripts do not draw on the chart. Instead they process data, export files, or produce analysis output.

Note: Scripts are standalone .py files executed from the file explorer. For notebook-based analysis, use regular Python cells instead.

Scripts have access to script_helpers for exporting data, reading chart state, and accessing indicator values.

Parameters

  • name (str, default required): Display name for the script
  • description (str, default None): Optional description
  • version (str, default None): Optional version string

Returns

Optional dict, displayed in the output panel.

Example

from quant_charts import script, input
from quant_charts.script_helpers import get_chart_data, export_csv

@script("Data Export")
class DataExporter:
    filename = input.string("export.csv", "Filename")

    def execute(self, df):
        df.to_csv(self.filename)
        return {"status": "success", "rows": len(df)}

Output

>>> Output
{'status': 'success', 'rows': 14523}

Notes

  • Scripts implement execute(self, df) instead of calculate(self, df).
  • Import from quant_charts.script_helpers for data access and export.