Examples
Complete strategy files to start from: the five perps scenarios behind the engine's acceptance battery and a Hyperliquid perp walkthrough, each with a line on what it shows. The three spot examples live on the pages that teach them, listed first.
To run one, replace the starter in a New indicator draft with the file and press Backtest; the overview has the steps. The strategy trades the chart's own market, and the Strategy Tester docks under the chart with the trades and the stats (Reading the Strategy Tester).
Spot examples
| File | What it shows | Page |
|---|---|---|
| Moving average cross | The smallest useful strategy: one entry rule, one exit rule. percentOfEquity sizing keeps the position proportional as equity compounds, and commission plus slippageBps make every fill pay realistic costs. | Strategies overview |
| RSI reversion with a protective stop | Buys oversold dips and arms a stop under every entry, so a dip that keeps dipping is cut instead of riding to the bottom. strategy. scopes the stop to the named entry, and the stop follows the close at placement time. Built up line by line on its page. | Build your first strategy |
| Trend entries with bracket exits | A crossover entry bracketed by a stop and a take-profit limit on one exit id, a one-cancels-all pair: whichever the market touches first closes the trade and cancels the other. fillModel: "pathHeuristic" decides a bar that touches both (fill simulation). | Writing strategies |
The five perps scenarios
The five canonical perps scenarios behind the engine's acceptance battery: liquidation on both sides, fee classification, funding erosion and bankruptcy accounting. They are reproduction scripts: each lands on the number the engine's battery pins for bars priced near 100, which is what makes it verifiable. On a chart they run against that chart's own prices, where a scenario that waits for a price near 100 may never trade, so read them for the mechanics rather than to match the numbers. Perps has the formulas. A first-bar gate is a row counter the file keeps itself.
1. Long liquidation
What it shows: a 10x long entered at 100 with 0.5% maintenance margin must liquidate at exactly (100 - 10) / 0.995 = 90.45226130653266.
// Scenario 1, long liquidation: a 10x long at 100 with 0.5% maintenance margin must liquidate at (100 - 10) / 0.995.
strategy({ initialCapital: 10000, instrument: "perps", leverage: 10, maintenanceMarginPercent: 0.5, qtyType: "fixed", qtyValue: 1, slippageBps: 0, makerFeePercent: 0, takerFeePercent: 0, funding: "off" });
output("close", line, overlay, { description: "Close price" });
let bars: i32 = 0;
function onBar(): void {
bars += 1;
if (bars == 1) strategy.long("L").send();
out_close(bar.close());
}2. Short liquidation with taker fees
What it shows: the short side of the same formula, (100 + 10) / 1.005 = 109.45273631840797, with a market entry so both the entry fill and the liquidation fill pay takerFeePercent.
// Scenario 2, short liquidation with taker fees: the short formula (100 + 10) / 1.005, both fills paying the taker rate.
strategy({ initialCapital: 10000, qtyType: "fixed", qtyValue: 1, instrument: "perps", leverage: 10, maintenanceMarginPercent: 0.5, takerFeePercent: 0.05, funding: "off" });
output("close", line, overlay, { description: "Close price" });
function onBar(): void {
const close = bar.close();
if (strategy.positionSize() == 0 && close < 100.5) strategy.short("S").send();
out_close(close);
}3. Maker and taker split
What it shows: two trades, four fills, four fee classifications. A limit entry and its take-profit limit pay maker; a market entry and its protective stop pay taker. Also a guard-design lesson: the file runs at the bar's close, after fills, so entry zones stay disjoint from exit prices or the flat-position guard re-arms on the very bar an exit filled.
// Scenario 3, maker and taker split: a limit entry and its take-profit pay maker, a market entry and its stop pay taker.
strategy({ initialCapital: 10000, qtyType: "fixed", qtyValue: 1, instrument: "perps", leverage: 5, makerFeePercent: 0.01, takerFeePercent: 0.05, funding: "off" });
output("close", line, overlay, { description: "Close price" });
function onBar(): void {
const close = bar.close();
const held = strategy.positionSize();
if (held == 0 && close > 100.5 && close < 103.0) strategy.long("LimitIn").limit(95.0).send();
if (held == 0 && close > 110.5) strategy.long("MarketIn").send();
if (held > 0) {
strategy.exit("TP").from("LimitIn").limit(105.0).send();
strategy.exit("SL").from("MarketIn").stop(92.0).send();
}
out_close(close);
}4. Funding erosion
What it shows: funding settlements debit cash and committed margin together, so the liquidation price tightens as margin erodes, and a settlement that depletes the margin liquidates the position at that bar's open with zero price PnL. The chart attaches no funding data to a strategy run, so on the chart this file charges nothing and counts every unsettled open bar in fundingUnavailableCount, and the Strategy Tester's Run details say "Funding data did not cover N bars" (Funding).
// Scenario 4, funding erosion: settlements debit cash and committed margin together, and a depleting settlement liquidates at that bar's open.
strategy({ initialCapital: 10000, qtyType: "fixed", qtyValue: 1, instrument: "perps", leverage: 10, maintenanceMarginPercent: 0.5, funding: "data" });
output("close", line, overlay, { description: "Close price" });
function onBar(): void {
const close = bar.close();
if (strategy.positionSize() == 0 && close > 99.5) strategy.long("L").send();
out_close(close);
}5. Bankruptcy gap
What it shows: price gaps straight through the liquidation level. The fill is the bar price because it is worse, the loss beyond committed margin is recorded as bankruptcyDeficit, and equity floors at exactly zero.
// Scenario 5, bankruptcy gap: a gap through the liquidation level fills at the worse bar price, the loss past the margin is the bankruptcy deficit, and equity floors at zero.
strategy({ initialCapital: 10, instrument: "perps", leverage: 10, maintenanceMarginPercent: 0.5, qtyType: "fixed", qtyValue: 1, slippageBps: 0, makerFeePercent: 0, takerFeePercent: 0, funding: "off" });
output("equity", line, lower, { description: "Strategy equity" });
let bars: i32 = 0;
function onBar(): void {
bars += 1;
if (bars == 1) strategy.long("L").send();
out_equity(strategy.equity());
}Backtest a Hyperliquid perp strategy
What it shows: a trend strategy under perps accounting on a venue that is leveraged, funded and liquidatable, with isolated margin at 10x, maker and taker fees, recorded funding and a protective stop off the average entry. A backtest that ignores those three venue facts will happily approve a strategy the venue would have destroyed.
// A funding-aware trend strategy at 10x: isolated margin, maker and taker rates, recorded funding, and a stop 3% under the average entry.
strategy({ initialCapital: 10000, instrument: "perps", leverage: 10, qtyType: "percentOfEquity", qtyValue: 10, makerFeePercent: 0.015, takerFeePercent: 0.045, funding: "data", slippageBps: 2 });
output("fast", line, overlay, { description: "21-period EMA of close" });
output("slow", line, overlay, { description: "55-period EMA of close" });
const fastEma = new Ema(21);
const slowEma = new Ema(55);
const cross = new Cross();
function onBar(): void {
const fast = fastEma.update(bar.close());
const slow = slowEma.update(bar.close());
if (isNaN(fast) || isNaN(slow)) return;
const crossed = cross.update(fast, slow);
if (crossed == 1) strategy.long("Trend").send();
if (crossed == -1) strategy.closeAll();
if (strategy.positionSize() > 0) strategy.exit("Protect").from("Trend").stop(strategy.positionAvgPrice() * 0.97).send();
out_fast(fast);
out_slow(slow);
}Put the chart on a Hyperliquid perpetual (BTC, for example) before you press Backtest: the file reads the chart's own close through bar.close() and pins no market, so the chart you run it on is the market it tests. Reading the declaration, which does most of the perps work:
instrument: "perps"withleverage: 10: sizing commits margin, not notional.qtyValue: 10means each entry commits 10% of equity as isolated margin; the notional is that margin times leverage. The broker tracks the liquidation price from your leverage and maintenance margin and closes you there if a bar proves or assumes the level traded.makerFeePercent: 0.015, takerFeePercent: 0.045: fees route by fill type, so market entries, stops andcloseAllpay taker while limit-bound fills pay maker. Set your own tier's numbers; they travel inside the indicator when you publish it.funding: "data": the broker settles recorded funding against the open position when funding data is attached. The chart attaches none to a strategy run, so the run counts every unsettled open bar infundingUnavailableCountand charges nothing; the count is the disclosure, andfunding: "off"declares a strategy that should not depend on funding.strategy.positionAvgPrice() * 0.97: the stop tracks the average entry, re-armed every held bar.
Reading the result
Three lines to check before believing the equity curve:
- Funding. "Funding data did not cover N bars" in Run details (
fundingUnavailableCount) is how many held bars went unsettled, and the Overview's Funding chart says no funding was applied. A long that looks fine gross can bleed through settlements while it holds, and this run has not charged them. - Liquidations. Trades that read Liquidated under Exit via (
liquidationCountcounts them), and the Overview's line saying when the first one happened. At 10x, a 3% protective stop and the liquidation level are uncomfortably close neighbors. If liquidations show up, the venue closed you before your stop did. - Model-settled fills. The Performance tab's count (
ambiguousFillCount) and the ± fill badge in the Trades tab: fills the declared fill model settled where one bar touched the stop and a level; Run details read "Fill precision: bar resolution" once one has.
Tune it like it is real
- Leverage down first. At 5x the liquidation level sits twice as far; watch the Liquidated exits disappear before you tune anything else.
- Widen the stop or drop the interval. Brackets arm on the bar after entry, so a tight stop on a coarse chart is exposed for one full bar; fill simulation covers the trade-off.
- Run the spot twin. Copy the file into a second New indicator draft, drop the perps settings, and press Backtest on both: with two strategies on the chart, the Strategy Tester's Compare shows what leverage and fees cost you. That difference is the part most backtests never model.