Your First Strategy

Build, backtest, and read an EMA-crossover strategy in Strategy Studio in under five minutes.

Build a working EMA crossover from a blank canvas, run a backtest, and read the results. About five minutes. Open /studio on a computer (Studio is desktop-only).

The finished graph: when a fast EMA crosses above a slow EMA, go long; when it crosses back below, close. (If you'd rather start from a working graph, New opens the template gallery: see Examples & Templates.)

1. Set the timeframe

Drag a data.timeframe node onto the canvas. Leave it at 1h. There is exactly one of these per graph — it sets the global cadence everything else inherits.

2. Add the data source

Drag a data.candles node. Leave its coin on Current market (the default), or pick a market such as BTC. A backtest runs Current market on the fallback market in the Inspector's Markets section. You'll use its close output to feed the indicators.

3. Add two EMAs

Drag two ind.ema nodes:

  • Fast: period = 10
  • Slow: period = 30

Wire data.candles.close → each EMA's value input. (The value input is number|candle — you could wire candle instead and pick the source in the param, but close is simplest.)

4. Detect the cross

Drag a logic.cross node. Wire:

  • fast EMA out → cross.a
  • slow EMA out → cross.b

logic.cross emits events: up pulses the bar A crosses above B, down the bar it crosses below. These are signal<boolean> — exactly what an exec node's fire input wants. (Exec nodes default to fireMode: "edge", so even a sustained logic.compare boolean wired into fire would fire once on its rising edge rather than every bar — but an explicit event keeps the intent obvious. See signal vs level.)

5. Wire execution

Drag an exec.market node (set side = buy, sizeFallback = 0.01) and an exec.closePosition node.

  • cross.up → exec.market.fire
  • cross.down → exec.closePosition.fire

Both exec nodes keep fireOnBarClose: true, so they act once per bar close — no intra-bar surprises.

6. Add a position cap (optional but good habit)

Drag a risk.maxPositionSize node (coin = Current market, maxNotional = 5000) and a risk.preTradeValidate node. Wire:

  • exec.market.intent → preTradeValidate.intent
  • risk.maxPositionSize.ok → preTradeValidate.ok

Now the engine drops the entry at the submit barrier whenever the position would exceed $5,000 notional.

7. Run a backtest

Make sure the Issues tab is clean (no red errors). With nothing selected, check the Backtest range in the Inspector (30 days by default): the first 30 bars warm up the slowest indicator, the 30-period EMA. Pick Backtest in the toolbar's mode switch and hit Run. The engine replays historical 1h candles over the configured range in a worker.

8. Read the results

The bottom panel streams four things:

  • Timeline: the equity curve over the backtest. A rising curve is the headline.
  • Orders — every fill: time, side, size, fill price, fee. This is where you confirm the strategy actually traded when you expected.
  • Plots — any debug.plot series (none yet — add a debug.plot on ind.ema.out to overlay the EMAs).
  • Console — log lines, including order:rejected reasons (e.g. sub-min-notional) and risk-gate drops.

If you see no orders, the most common causes are: the range too short for the indicators to leave NaN or for a cross to occur, or sizeFallback producing a sub-$10 notional that gets rejected (check Console).

Next steps

Your First Strategy · Docs | Borsa