Skip to content

Guides

Build a moving-average crossover

Your first algorithm, from an empty editor to a backtest, in about twenty minutes.

A crossover buys when a fast moving average rises above a slow one, and sells when it falls below. It is simple, easy to check on a chart, and a good base to build on.

Do this: Open Algorithms, type a name such as “SMA crossover”, and select New.

You should see: The editor, on an unsaved version 1.

2. Declare the settings you will want to change

Section titled “2. Declare the settings you will want to change”

Do this: Replace the editor’s contents with:

const externals = [
{ name: "fast", defaultValue: 5 },
{ name: "slow", defaultValue: 20 },
{ name: "stopPips", defaultValue: 20 },
{ name: "targetPips", defaultValue: 40 },
];
if (fast >= slow) {
error("fast must be smaller than slow");
}
let wasAbove = sma(fast) > sma(slow);

Each external becomes a number you can use by name, and you can change it for each backtest or session without editing code. error() stops the run with your message if the settings make no sense.

Code at the top level runs once, when the first price arrives, so wasAbove starts as whether the fast average is above the slow one then, and a variable declared there keeps its value between bars. Algorithm concepts explains more.

Do this: Add below it:

function onIntervalClose() {
const isAbove = sma(fast) > sma(slow);
if (isAbove !== wasAbove && OPEN_POSITIONS_COUNT === 0) {
openPosition(isAbove ? "BUY" : "SELL", {
quantity: 10000,
stopLossPips: stopPips,
takeProfitPips: targetPips,
});
}
wasAbove = isAbove;
plot("Fast SMA", sma(fast));
plot("Slow SMA", sma(slow));
}

onIntervalClose runs once at the end of each bar, so a cross is judged on closed bars rather than on every flicker of the price. Only one position is open at a time, and every position has a stop and a target.

Do this: Press Ctrl+S and add a short commit message. Open the Backtest tab and select New Backtest. Pick EUR/USD at 1 hour on Minute data, over the last six months, and select OK.

You should see: The run complete in a minute or two, with your two averages drawn over the price and a marker for each position.

Open a few positions on the chart. Each should open on the bar after the averages cross, and close at its stop or target. If not, see Debug an algorithm.

Why onIntervalClose and not onTick? On every tick the averages can cross and uncross many times inside one bar. Waiting for the close trades once per real cross.

Should I use these settings? No. They are a starting point. Read Optimize without overfitting before you tune them.