Introducing Indicators

Indicators are designed to be approachable whether you are coming from kScript (legacy), from Pine, or from a TypeScript codebase. Create technical indicators…

Indicators are designed to be approachable whether you are coming from kScript (legacy), from Pine, or from a TypeScript codebase. Create technical indicators, analyze market trends, and study order flow in one file that compiles in your browser, publishes as a package, and runs on your own machine unchanged. The syntax is TypeScript with fixed-width numbers, the kit is small, and you will have a working Indicator on a live chart in minutes.

What can you build?

Indicators read the same market data kScript reads, and they add the things a script held by the platform could never do: a package you install, a metric id you can alert on, and a module that runs wherever you run it.

BuildHow
Custom technical indicatorsMoving averages, RSI, MACD, Bollinger bands, and anything you can express as arithmetic over a per-bar state, with your own parameters and declared looks.
Multi-source contextA second input pinned to another market (symbol + exchange), a coarser interval read as of its close, funding, open interest, liquidations, options volatility, token supply.
Order-flow studiesCelled inputs serve a whole block per bar: a footprint's per-price rows (volume_profile) or a book snapshot's levels (book), so buy versus sell by price is answerable inside one bar.
Event oddsPolymarket probabilities as an input (odds), pinned to a market or bound per use, combined with price into conviction and divergence scores.
Alerts, screens, series, backtestsEvery output of an installed Indicator is a metric id: a watch condition can cross it, a screen can rank a universe on it, om metric series can read its history, a backtest can replay it.
Installable packagesPublish once under your scope; anyone adds it to a chart from the Indicators tab or installs it with one command, and a published version never changes underneath them.

Example Indicators

A complete Indicator in fourteen non-blank lines: a 20-bar average of the close, drawn on the price pane.

import { input, line, ohlcv, output, overlay, param } from "./sdk/declare";
import { in_close } from "./gen/inputs";
import { emitRow, out_sma } from "./gen/outputs";
import { p_period } from "./gen/params";
import { Sma } from "./sdk/ta";

param("period", 20, { min: 1, max: 200 });
input("close", ohlcv.close);
output("sma", line, overlay, { color: "#2563eb", width: 2, description: "20-period simple moving average" });

let sma = new Sma(20);
let value: f64 = NaN;

export function init(): void { sma = new Sma(i32(p_period())); }
export function state(): i32 { value = sma.update(in_close()); return isNaN(value) ? 0 : 1; }
export function finalize(): void { out_sma(value); emitRow(); }
export function reset(): void { sma.reset(); value = NaN; }
LineWhat it does
The import linesBring in the declaring words, the generated readers and writers, and the Sma class
param(...)A setting the chart user can change, with its default and range
input(...)Get the close price from the current chart, one value per bar
output(...)Draw a blue line on the price pane
init()Size the average from the setting, once
state()Fold this bar's close in; abstain while the window is filling
finalize()Write the value and commit the row
reset()Put the state back when the chart replays the forming bar

A full-featured RSI with three settings, its own pane, level lines, and a line that changes color when it enters the overbought or oversold zone:

import { input, line, lower, none, ohlcv, output, param } from "./sdk/declare";
import { in_close } from "./gen/inputs";
import { emitRow, out_overbought, out_oversold, out_rsi, out_zone } from "./gen/outputs";
import { p_overbought, p_oversold, p_period } from "./gen/params";
import { Rsi } from "./sdk/ta";

// Customizable settings: the RSI length and the two levels.
param("period", 14, { min: 2, max: 200, description: "RSI length" });
param("overbought", 70, { min: 50, max: 100, description: "Overbought level" });
param("oversold", 30, { min: 0, max: 50, description: "Oversold level" });
// The chart's own close.
input("close", ohlcv.close);
// The RSI in its own pane, tinted per bar by the zone output: 0 neutral, 1 overbought, 2 oversold.
output("rsi", line, lower, { width: 2, color_by: "zone", colors: ["#f59e0b", "#dc2626", "#16a34a"], description: "Relative Strength Index" });
output("overbought", line, lower, { color: "#64748b", width: 1, description: "Overbought level" });
output("oversold", line, lower, { color: "#64748b", width: 1, description: "Oversold level" });
output("zone", none, lower, { description: "0 neutral, 1 overbought, 2 oversold" });

let rsi = new Rsi(14);
let overbought: f64 = 70.0;
let oversold: f64 = 30.0;
let value: f64 = NaN;

export function init(): void {
  rsi = new Rsi(i32(p_period()));
  overbought = p_overbought();
  oversold = p_oversold();
}

export function state(): i32 {
  value = rsi.update(in_close());
  return isNaN(value) ? 0 : 1;
}

export function finalize(): void {
  out_rsi(value);
  out_overbought(overbought);
  out_oversold(oversold);
  out_zone(value >= overbought ? 1.0 : value <= oversold ? 2.0 : 0.0);
  emitRow();
}

export function reset(): void {
  rsi.reset();
  value = NaN;
}

The level lines are outputs written to the same value on every bar, and the zone is a data-only output the rsi line indexes its palette with: a decision is a number, and the sheet turns numbers into looks.

Why Indicators?

The file is TypeScript, so if you know JavaScript or Pine you can read it on day one, and the compiler is AssemblyScript, so every number has a width and every mismatch is named with a line and column before anything runs. The module is a few kilobytes of compiled code, allocated once and run once per bar and once per tick, in your browser on the chart and on your own machine as a metric. Every claim behind that, with the place in the product that proves it, is Why Indicators.

Start from a template

Fifteen templates ship with the kit, and the chart's New indicator opens the first one: sma-codefirst (the moving average above, with a comment on every statement), sma-box (the same average with a box shading a band around it), sma (the metadata-first form with a hand-written sheet), vp-buy-share-codefirst (a celled volume-profile input with a per-bar text renderer), polymarket-odds, conviction-score, event-asset-divergence, and escalation-risk (event odds combined with price and funding), hud-terminal (a terminal-style readout drawn with anchored, left-aligned handles), market-dashboard (a six-row decision table beside the chart, fed by one frame), multi-timeframe-terminal (inputs pinned to 4h feeding a side panel and docked levels), viewport-hud (a regime readout pinned to the pane's corner with an RSI meter), large-prints (the trade tape filtered by size, boxed on the live bar and listed in a feed), plots-and-panes (every plot naming its pane, with two histogram windows below the chart), and series-address (a published series read by its address, metadata-first, with a card). On your machine om wrun templates lists them and om wrun create scaffolds one (quick-start.md).

AI-assisted development

Every page of this documentation is bundled into one file, llms-full.txt, served beside the docs. Attach it to your assistant and it knows the exact declaration grammar, the four functions, and the TA classes, so generated Indicators compile instead of guessing at an API. Always verify generated code by running it: the compiler and the sheet validator refuse anything that does not fit, and faq/common-errors.md maps every message to its fix.

Ready to start building?

  • First steps: open the editor, run the template, read it line by line.
  • Build your first Indicator: an EMA crossover with a mark on every cross, one idea at a time.
  • Quick start: the same file from a terminal, built, installed, and read as a metric.
  • Quick reference: the whole vocabulary on one page.
  • From kScript: port a script construct by construct, with the construct map beside it for frames, panels, handles, anchors and series inputs.