---
title: Build Your First Indicator
description: Build a real EMA crossover signal step by step, one idea at a time, from a single line to a finished indicator with markers.
---


<div class="flex gap-3 mb-6">
  <span class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-green-50 text-green-600 text-sm font-medium">
    Beginner
  </span>
  <span class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-gray-100 text-gray-600 text-sm font-medium">
    8 min read
  </span>
</div>

On the [last page](/kscript/getting-started/primer-first-steps) you drew a single line. Now we build something traders actually watch: a **moving-average crossover**. When a fast average rises above a slow one, momentum is turning up. We will get there in four small steps, each adding exactly one idea. Type along, hit Apply after each step, and watch the chart change.

## Step 1: Price and one moving average

Let's start with the close plus one moving average laid over it. A moving average smooths out the noise so the trend is easier to see.

```javascript title="scripts/probes/primer/with_average.ks"
//@version=2
define(title="Price and Average", position="onchart", axis=false)

timeseries d = ohlcv(symbol=currentSymbol, exchange=currentExchange)
timeseries avg = sma(source=d.close, period=20)
plotLine(value=d.close, colors=["#94a3b8"], width=1, label=["Close"], desc=["closing price"])
plotLine(value=avg, colors=["#2563eb"], width=2, label=["SMA 20"], desc=["20-period simple moving average"])
```


Same three parts as before, with one new line in the middle. `sma(source=d.close, period=20)` computes a **simple moving average**: at each bar it averages the last 20 closing prices. We store it in `avg` and draw it as a second, thicker blue line. The close is now a faint gray line underneath so the smooth average stands out.

On the chart you'll see the jagged close and, riding through the middle of it, a calm blue line. That blue line is the trend.

## Step 2: A second, faster average

A moving average is just an average that slides forward one bar at a time. The `period` controls how much it smooths: a large period (like 20) reacts slowly and shows the broad trend, while a small period (like 9) hugs price closely and reacts fast.

That difference in speed is the whole idea behind a crossover. If we plot a **fast** average and a **slow** average together, the fast one leads and the slow one lags. The moment the fast crosses above the slow is the moment short-term momentum has pulled ahead of the longer trend.

We will also switch from `sma` to `ema`, the **exponential** moving average. An EMA weights recent bars more heavily, so it turns a little sooner. Here are both, fast and slow:

```javascript title="Step 2: a fast and a slow EMA"
//@version=2
define(title="EMA Crossover", position="onchart", axis=false)

timeseries d = ohlcv(symbol=currentSymbol, exchange=currentExchange)

timeseries fast = ema(source=d.close, period=9)
timeseries slow = ema(source=d.close, period=21)

plotLine(value=fast, colors=["#2563eb"], width=2, label=["Fast EMA"], desc=["9-period EMA of close"])
plotLine(value=slow, colors=["#f97316"], width=2, label=["Slow EMA"], desc=["21-period EMA of close"])


Now there are two lines: a blue fast EMA (period 9) and an orange slow EMA (period 21). Watch how the blue line whips around more, while the orange one glides. Where the blue line pokes up through the orange one, momentum is flipping upward. That crossing point is what we want to catch.
```
## Step 3: Detecting the cross

Now we want to know the exact moment the fast line crosses above the slow line, not eyeball it. kScript has a built-in for precisely this: `crossover(fast, slow)`. It returns true on the single bar where `fast` rises from below `slow` to above it, and false everywhere else.

```javascript title="Step 3: detect the crossover"
var crossedUp = crossover(fast, slow)
```
Read it out loud: "crossed up is true when fast crosses over slow." On most bars `crossedUp` is false. On the one bar where the blue line breaks above the orange line, it flips to true. That single true is our signal. Next we draw something there.

## Step 4: Marking the cross on the chart

A boolean is invisible. To see the signal we plot a marker, but only on the bars where the cross happened. We wrap a plot in an `if` so it fires only when `crossedUp` is true, and we use `plotShape` to drop a little triangle.

```javascript title="Step 4: mark the cross"
var crossedUp = crossover(fast, slow)
if (crossedUp) {
  plotShape(value=d.low[0], shape="triangleup", width=10, colors=["#16a34a"], fill=true, location="belowBar", label=["Cross up"], desc=["fast EMA crossed above slow EMA"])
}


A few things to unpack here:
```
- **`plotShape`** draws a marker instead of a line. `shape="triangleup"` makes it an upward green triangle, a natural "momentum turning up" cue.
- **`location="belowBar"`** places the triangle just under the candle, out of the way of the price action, so the signal is easy to read.
- **`value=d.low[0]`** anchors the marker to the bar's low. The `[0]` means "this bar" (kScript lets you look back with `[1]`, `[2]`, and so on, but here we just want the current bar).
- Because it lives inside `if (crossedUp)`, the triangle only appears on crossover bars. Everywhere else, nothing is drawn.

## The finished indicator

Put all four steps together and you have a complete, useful indicator: two EMAs drawn on the price chart, with a green triangle marking every bar where the fast EMA crosses above the slow one.

```javascript title="scripts/probes/primer/first_indicator.ks"
//@version=2
define(title="EMA Crossover", position="onchart", axis=false)

// 1. Load the chart's candles.
timeseries d = ohlcv(symbol=currentSymbol, exchange=currentExchange)

// 2. A fast and a slow moving average of the close.
timeseries fast = ema(source=d.close, period=9)
timeseries slow = ema(source=d.close, period=21)

// 3. Plot both lines.
plotLine(value=fast, colors=["#2563eb"], width=2, label=["Fast EMA"], desc=["9-period EMA of close"])
plotLine(value=slow, colors=["#f97316"], width=2, label=["Slow EMA"], desc=["21-period EMA of close"])

// 4. Mark the bar where fast crosses above slow.
var crossedUp = crossover(fast, slow)
if (crossedUp) {
  plotShape(value=d.low[0], shape="triangleup", width=10, colors=["#16a34a"], fill=true, location="belowBar", label=["Cross up"], desc=["fast EMA crossed above slow EMA"])
}
```


Apply it, and you'll see the two EMAs weaving across each other, with a green triangle popping up under the candle at each upward cross. That is a genuine momentum signal, built from the same three parts you learned on page one plus two new ideas: a function that detects a condition (`crossover`) and a plot that fires only when that condition is true (`plotShape` inside an `if`).

{% hint style="success" %}
You just built a real indicator. Try changing the periods (say 12 and 26), or swap `triangleup` for `circle`, and re-apply. Breaking things and fixing them is the fastest way to learn.
{% endhint %}

## Next

You now know enough to read most kScripts and write simple ones. The next page points you to where to go from here: complete real-world recipes, how the engine runs your code, and the function references you'll reach for most.

[Next Steps →](/kscript/getting-started/primer-next-steps)
