Declare a perps strategy, and understand leveraged margin sizing, liquidation, maker and taker fees, funding and the perps stats. The formulas are the kScript (legacy) engine's own, reproduced by the same broker; the five canonical scenarios that pin them ship as ports on the examples page.
What perps mode changes
Set instrument: "perps" when the backtest should use isolated leveraged perpetual accounting instead of spot. Perps mode keeps the same order API and one-net-position model, but it changes how default sizing is interpreted, how entries are admitted, how fees are charged, and how open positions can be liquidated or debited by funding.
// Perps margin sizing: percentOfEquity sizes the margin commitment, and notional is that margin times leverage.
import { input, line, ohlcv, output, overlay } from "./sdk/declare";
import { in_close } from "./gen/inputs";
import { emitRow, out_close } from "./gen/outputs";
import { strategy } from "./gen/strategy";
strategy({ initialCapital: 10000, instrument: "perps", leverage: 10, maintenanceMarginPercent: 0.5, qtyType: "percentOfEquity", qtyValue: 10, makerFeePercent: 0.02, takerFeePercent: 0.05, slippageBps: 0, funding: "off" });
input("close", ohlcv.close);
output("close", line, overlay, { description: "Close price" });
let bars: i32 = 0;
let close: f64 = NaN;
export function init(): void {}
export function state(): i32 {
close = in_close();
bars += 1;
return 1;
}
export function finalize(): void {
if (bars == 1) strategy.long("MarginLong").limit(100.0).send();
out_close(close);
emitRow();
}
export function reset(): void {
bars = 0;
close = NaN;
}On bars priced near 100, that entry fills at 100 with qtyType: "percentOfEquity", qtyValue: 10 and leverage: 10: the open trade has qty 100, committedMargin 1000 and fees 2, with makerFeesPaid 2 and takerFeesPaid 0. The 10% sizing setting committed 1000 of margin, not 1000 of notional; the notional was margin times leverage. The bars counter is the port of kScript's barIndex == 0 gate: the file keeps its own row count.
Margin sizing and admission
For perps, qtyType: "percentOfEquity" and qtyType: "cash" size the margin commitment. The broker then computes notional = margin * leverage and qty = notional / fillPrice. qtyType: "fixed" and an explicit .qty(...) are direct quantities instead, margin-checked as abs(qty) * fillPrice / leverage.
Entries are admitted against realized-basis equity: committedMargin + entryFee <= availableMargin, where the available margin excludes unrealized PnL from the open position. A winning open position does not become collateral for adding exposure. Reversals stay atomic: the broker simulates the closing leg, then checks whether the new opening leg fits; if it does not, the whole reversal is rejected and the old position remains open.
Liquidation price
long 1 unit at 100, leverage 10, maintenance 0.5%
entry 100 -+------------------------------------
| committed margin = 100/10 = 10
| the price may fall ~9.55 before
| margin (less maintenance) is gone
P_liq ----+---- 90.4522... <- liquidation
|
+-- lower leverage pushes this line further awayThe broker maintains an implicit system liquidation stop for the final open net position. It is not a user order, cannot be canceled, and closes with exitReason: "liquidation" when the bar proves or assumes the level traded. For a long the isolated liquidation level is P_liq = (Q * E - M) / (Q * (1 - m)); for a short P_liq = (E + M / Q) / (1 + m), where Q is the absolute quantity, E the average entry, M the committed isolated margin and m the maintenance margin as a fraction. A single 10x long entered at 100 with 0.5% maintenance liquidates at 90.45226130653266; the short mirror at 109.45273631840797.
Entry-bar liquidation is possible, because the market entry fills at the bar's open and the phase-end check sees the same bar's range. When fills on the same bar change the final position, the bar cannot prove whether the adverse extreme came before or after the change; the broker assumes liquidation if the final level is reached, clamps the fill into the bar's traded range, counts it in ambiguousFillCount and flags the trade. When price gaps through the level, the fill is the bar price, because that is where the market traded, and the loss beyond committed margin is stats.bankruptcyDeficit with equity floored at zero.
Maker and taker fees
Perps ignores commissionPercent; use makerFeePercent and takerFeePercent. Limit-bound fills pay maker: limit entries, take-profit limit legs, and the flattening leg of a reversal a limit entry triggered. Fills that cross the market pay taker: market entries, stop entries, protective stops, trailing stops, signal exits, closeAll, liquidation, and reversal flattening legs a market or stop entry triggered. Declaring the other instrument's fees is not an error; the build warns and the broker ignores them.
Funding
Funding is a drip against isolated margin, and the liquidation line moves closer with every settlement paid. With funding: "data" the broker consumes settlement events from recorded funding rates when a provider is attached; the rate is decimal (1 bp is 0.0001), positive rates mean longs pay and shorts receive, and stats.fundingPaid is signed with positive meaning the strategy paid. A settlement that erodes committed margin to zero or below liquidates the prior position at that bar's open, and later same-bar fills cannot rescue it. This release attaches no provider on any host: funding: "data" counts every unsettled open bar in fundingUnavailableCount and charges nothing; funding: "off" is an exact no-op. Partial coverage is counted, never charged.
Perps stats
The output always carries the perps stats, zero on spot: makerFeesPaid and takerFeesPaid by fill class; fundingPaid signed; fundingEventsApplied; fundingUnavailableCount (bars with an open position and no settlement data); liquidationCount; liquidationHalted; bankruptcyDeficit (how far equity would have gone below zero before the floor). The stats reference defines each.
Not bugs
commissionPercent does nothing under perps and the warning is expected. Unsettled funding bars are counted in fundingUnavailableCount, not charged. Liquidation uses the bar's traded prices, not a mark price the broker does not have. Spot economics are unchanged unless you opt into instrument: "perps".