Next steps

You built your first Indicator. Here is where to go next, framed by what you will want to do. You have a working crossover and you understand the three parts…

You built your first Indicator. Here is where to go next, framed by what you will want to do. You have a working crossover and you understand the three parts every Indicator shares: declarations, state, and the four functions. From here, pick the path that matches what you want to do next.

Where to go from here

  • Want to build something real? The Cookbook has complete, copy-ready recipes for genuinely useful Indicators, each shown next to the kScript (legacy) recipe it came from: a volume spike detector, an anchored VWAP, a no-repaint regime filter, a zone tracker, key levels, and cumulative volume delta. Each one is a finished file you can read end to end and adapt.

  • Want to understand how your code actually runs? Execution model explains how the host walks your file bar by bar, oldest bar to newest, why the first bars of a line are empty, and what "no lookahead" means when you write the code. Once this clicks, keeping a previous value in a variable instead of indexing close[1] will feel obvious.

  • Want to take it off the chart? Quick start is the same file on your machine: scaffold, build, install, and read every output as a metric with om metric series, then arm a watch condition on it. That is the path to alerts, screens, series, and backtests.

  • Ready for the part kScript could not do? Multi-source and aggregation shows how a package reads other markets and other venues through pinned inputs and combines them with ordinary arithmetic, and Data sources lists every feed and celled class, footprints and order books included.

  • Looking for the right function? The TA library is the catalog of stateful classes, one per kScript (legacy) TA builtin (Sma, Ema, Rsi, Macd, Atr, Supertrend, Vwap, and the rest). The Plotting reference covers every way to draw: lines, bars, histograms, candles, marks, boxes, segments, labels, and tables.

  • Something not working? Common errors lists the messages you are most likely to hit, in the order the build produces them, what causes each, and how to fix it. If your file compiled but drew nothing, start there (it is almost always warm-up).

Try this first

The best next step is to open the editor and change something. Take your crossover and add the mark for crosses the other way: a second Cross answer, a second gate, a second shape output at the bar's high. This is the finished exercise; compare it with your own before reading it.

import { input, line, none, ohlcv, output, overlay, param, shape } from "./sdk/declare";
import { in_close, in_high, in_low } from "./gen/inputs";
import { emitRow, out_cross_down, out_cross_up, out_fast, out_is_cross_down, out_is_cross_up, out_slow } from "./gen/outputs";
import { p_fast, p_slow } from "./gen/params";
import { Cross, Ema } from "./sdk/ta";

param("fast", 9, { min: 1, max: 200, description: "Fast EMA length" });
param("slow", 21, { min: 2, max: 400, description: "Slow EMA length" });
input("close", ohlcv.close);
input("high", ohlcv.high);
input("low", ohlcv.low);
output("fast", line, overlay, { color: "#2563eb", width: 2, description: "Fast EMA" });
output("slow", line, overlay, { color: "#f97316", width: 2, description: "Slow EMA" });
output("cross_up", shape, overlay, { color: "#16a34a", shape_where: "is_cross_up", description: "Fast crossed above slow" });
output("cross_down", shape, overlay, { color: "#dc2626", shape_where: "is_cross_down", description: "Fast crossed below slow" });
output("is_cross_up", none);
output("is_cross_down", none);

let fast = new Ema(9);
let slow = new Ema(21);
let cross = new Cross();
let fastValue: f64 = NaN;
let slowValue: f64 = NaN;
let high: f64 = NaN;
let low: f64 = NaN;
let crossed: i32 = 0;

export function init(): void {
  fast = new Ema(i32(p_fast()));
  slow = new Ema(i32(p_slow()));
  cross = new Cross();
}

export function state(): i32 {
  const close = in_close();
  high = in_high();
  low = in_low();
  fastValue = fast.update(close);
  slowValue = slow.update(close);
  crossed = cross.update(fastValue, slowValue); // +1 up, -1 down, 0 otherwise
  return isNaN(slowValue) ? 0 : 1;
}

export function finalize(): void {
  out_fast(fastValue);
  out_slow(slowValue);
  out_cross_up(low); // the up mark sits under the candle
  out_cross_down(high); // the down mark sits above it
  out_is_cross_up(crossed == 1 ? 1.0 : 0.0);
  out_is_cross_down(crossed == -1 ? 1.0 : 0.0);
  emitRow();
}

export function reset(): void {
  fast.reset();
  slow.reset();
  cross.reset();
  fastValue = NaN;
  slowValue = NaN;
  high = NaN;
  low = NaN;
  crossed = 0;
}

Then put the fast average in its own pane, or swap the marks for a position output that stays 1 between an up cross and a down cross. You learn Indicators by writing them, and the compiler tells you the line when a change does not fit.