Python Indicator Studio
Developer Reference

Build custom indicators, quantitative models, and mathematical overlays in pure Python. Scripts execute locally inside an in-browser sandbox with zero server latency and full formula privacy.

1. Execution Architecture

Client-Side Runtime

Code executes locally within a dedicated Web Worker sandbox. Active computation runs entirely on your local CPU for instant responsiveness.

Zero Server Transmission

Proprietary logic, custom indicators, and parameter configurations remain strictly within your browser session and are never transmitted to external servers.

Real-Time Canvas Sync

Computed series instantly synchronize with the charting canvas across all timeframes, updating seamlessly as live market candles progress.

2. The calculate(df, params) Function Protocol

Every custom indicator script must define a top-level function named calculate(df, params). The engine supplies candle history (df) and user inputs (params).

DataFrame (df) Properties

df.close

List of closing prices

df.open

List of opening prices

df.high

List of highest prices

df.low

List of lowest prices

df.volume

List of candle volume

len(df)

Total number of bars

3. Modules, Built-ins & Security Rules

Pre-Injected Libraries

ti (Technical Indicators Suite)

ti.sma(series, length)

ti.ema(series, length)

ti.rsi(series, length)

ti.bbands(series, length, stddev=2.0)

ti.macd(series, fast=12, slow=26, signal=9)

math (Standard Math Module)

math.sqrt, math.sin, math.cos, math.log, math.exp, math.pi

Safe Python Built-ins

abs, min, max, sum, len, range, enumerate, zip, map, filter, sorted, round, print, int, float, bool, str, list, dict, set, tuple

Sandbox Constraints

  • No External Imports: For client safety and sandboxing, raw import statements (import os, import requests) are blocked.
  • No Dynamic Execution: Functions like eval(), exec(), open(), and __import__ are disabled.
  • No Dunder Access: Accessing private Python dunder attributes (__class__, __subclasses__) is restricted.

4. Naming Indicators & Configuring Metadata

Your calculate() function returns a dictionary. Provide a "__meta__" sub-dictionary to define the display title, specify whether it overlays on the main price chart (is_overlay=True) or a lower panel (is_overlay=False), and configure line styles and widths.

__meta__ Specification
return {
    "__meta__": {
        "name": "Triple EMA Ribbon",            # Legend title
        "is_overlay": True,                     # True = Main chart; False = Lower sub-panel
        "colors": {
            "fast": "#00D6FF",                  # Series line color
            "mid":  "#FFFFFF",
            "slow": "rgba(255, 255, 255, 0.4)"
        },
        "widths": {
            "fast": 2.0,
            "mid":  1.5,
            "slow": 1.0
        },
        "styles": {
            "fast": "solid",                    # "solid", "dashed", or "dotted"
            "mid":  "dashed",
            "slow": "solid"
        }
    },
    "fast": fast_series,                        # Output lists matching len(df)
    "mid":  mid_series,
    "slow": slow_series
}

5. Production Code Examples

Copy and paste these pre-tested indicators directly into the SlateTick Python IDE.

Triple Exponential Moving Average (EMA) Ribbon

Overlay: True

Calculates 9, 21, and 55-period EMAs across closing prices to identify trend momentum and support/resistance zones.

ema_ribbon.py
def calculate(df, params):
    fast_len = int(params.get('fast_length', 9))
    mid_len  = int(params.get('mid_length', 21))
    slow_len = int(params.get('slow_length', 55))

    ema_fast = ti.ema(df.close, fast_len)
    ema_mid  = ti.ema(df.close, mid_len)
    ema_slow = ti.ema(df.close, slow_len)

    return {
        "__meta__": {
            "name": f"EMA Ribbon ({fast_len}/{mid_len}/{slow_len})",
            "is_overlay": True,
            "colors": {
                "fast": "#00D6FF",
                "mid":  "#FFFFFF",
                "slow": "rgba(255, 255, 255, 0.4)"
            },
            "widths": {
                "fast": 2.0,
                "mid":  1.5,
                "slow": 1.0
            }
        },
        "fast": ema_fast,
        "mid":  ema_mid,
        "slow": ema_slow
    }

Relative Strength Index (RSI) with Threshold Levels

Overlay: False

Computes standard 14-period RSI in a separate sub-panel with 70 (Overbought) and 30 (Oversold) threshold lines.

rsi_oscillator.py
def calculate(df, params):
    length = int(params.get('length', 14))
    overbought = float(params.get('overbought', 70.0))
    oversold   = float(params.get('oversold', 30.0))

    rsi_line = ti.rsi(df.close, length)
    n = len(df)

    return {
        "__meta__": {
            "name": f"RSI ({length})",
            "is_overlay": False,
            "colors": {
                "rsi": "#00D6FF",
                "ob":  "#FFFFFF",
                "os":  "#FFFFFF",
                "mid": "rgba(255, 255, 255, 0.2)"
            },
            "styles": {
                "rsi": "solid",
                "ob":  "dashed",
                "os":  "dashed",
                "mid": "dotted"
            },
            "widths": {
                "rsi": 2.0,
                "ob":  1.0,
                "os":  1.0
            }
        },
        "rsi": rsi_line,
        "ob":  [overbought] * n,
        "os":  [oversold] * n,
        "mid": [50.0] * n
    }

Bollinger Bands with StdDev Multiplier

Overlay: True

Calculates 20-period moving average with upper and lower statistical volatility bands.

bollinger_bands.py
def calculate(df, params):
    length = int(params.get('length', 20))
    stddev = float(params.get('stddev', 2.0))

    bands = ti.bbands(df.close, length, stddev=stddev)

    return {
        "__meta__": {
            "name": f"Bollinger Bands ({length}, {stddev})",
            "is_overlay": True,
            "colors": {
                "upper":  "#00D6FF",
                "middle": "#FFFFFF",
                "lower":  "#00D6FF"
            },
            "widths": {
                "upper":  1.2,
                "middle": 1.0,
                "lower":  1.2
            },
            "styles": {
                "upper":  "solid",
                "middle": "dashed",
                "lower":  "solid"
            }
        },
        "upper":  bands["upper"],
        "middle": bands["middle"],
        "lower":  bands["lower"]
    }

MACD (Moving Average Convergence Divergence)

Overlay: False

Calculates 12/26 MACD line, 9-period signal line, and difference histogram in a lower sub-panel.

macd.py
def calculate(df, params):
    fast_period   = int(params.get('fast', 12))
    slow_period   = int(params.get('slow', 26))
    signal_period = int(params.get('signal', 9))

    res = ti.macd(df.close, fast=fast_period, slow=slow_period, signal=signal_period)

    return {
        "__meta__": {
            "name": f"MACD ({fast_period},{slow_period},{signal_period})",
            "is_overlay": False,
            "colors": {
                "macd":   "#00D6FF",
                "signal": "#FFFFFF",
                "hist":   "rgba(0, 214, 255, 0.4)"
            },
            "widths": {
                "macd":   1.8,
                "signal": 1.2,
                "hist":   1.0
            }
        },
        "macd":   res["macd"],
        "signal": res["signal"],
        "hist":   res["hist"]
    }

Ready to Build Your Custom Indicators?

Open the SlateTick terminal, launch the Python IDE, and instantly overlay your quantitative logic on live global charts.