---
title: "Perps: Margin, Liquidation, and Funding"
description: "instrument: \"perps\" switches the broker from spot accounting to isolated-margin perpetual futures: leverage, maker and taker fees, funding settlement and…"
order: 84
section: "strategies"
---

<!-- source: docs/indicators/strategies/perps.md; generated by packages/cli/scripts/gen-indicator-docs.ts, do not edit -->

# Perps: Margin, Liquidation, and Funding

`instrument: "perps"` switches the broker from spot accounting to isolated-margin perpetual futures: leverage, maker and taker fees, funding settlement and liquidation, each with its own counters so a result is never quietly shaped by machinery you cannot see. The model is the kScript (legacy) engine's; this page names what each mechanic does and what this release does not attach.

## What perps mode is

Four mechanics turn on:

- **Margin.** Every entry commits isolated margin of `notional / leverage`; entries that cannot be margined are rejected and counted.
- **Maker and taker fees.** Fills are charged `makerFeePercent` or `takerFeePercent` instead of `commissionPercent`; see [slippage and costs](slippage-and-costs.md).
- **Funding.** Recorded funding events settle against the open position (`funding: "data"`, the default) when a funding provider is attached, or funding is off entirely (`funding: "off"`).
- **Liquidation.** A position that exhausts its margin is force-closed at the broker's implicit liquidation stop, and `onLiquidation` decides whether the run keeps trading or halts.

A spot run carries none of this: the perps stats report zero (`makerFeesPaid`, `takerFeesPaid`, `fundingPaid`, `fundingEventsApplied`, `fundingUnavailableCount`, `liquidationCount` all `0`, `liquidationHalted: false`) and the perps-only series are omitted. The defaults, echoed by a bare declaration: `instrument: "spot"`, `leverage: 1`, `maintenanceMarginPercent: 0.5`, `makerFeePercent: 0`, `takerFeePercent: 0`, `funding: "data"`, `onLiquidation: "continue"`.

## Margin

Each perps entry commits isolated margin equal to the fill's notional divided by `leverage`; `committedMarginSeries` on the output carries the committed total at each bar's close, `0` when flat. Any number above `0` is legal leverage, fractions included; `maintenanceMarginPercent: 0` is legal (the lower bound is inclusive).

**Leverage scales margin, not PnL.** The same signals with the same fixed quantity produce the same `netProfit` as spot, whatever the leverage, until fees or a liquidation differ. Leverage decides how much equity a position ties up and where liquidation sits, not what a trade earns.

An entry whose required margin exceeds the available margin is rejected and counted in `stats.rejectedOrders`. The required amount is fee-inclusive: `notional / leverage` plus the entry's fee.

## Liquidation

The broker maintains an implicit liquidation stop on every open perps position, from the entry fill onward. For a long the level is `entry * (1 - 1 / leverage) / (1 - maintenanceMarginPercent / 100)`; [perps and leverage](perps-and-leverage.md) has both formulas and the five scenarios that pin them. Liquidated trades carry `exitReason: "liquidation"` and the liquidation order is system-owned: it is not in your pending queue and cannot be canceled.

| `onLiquidation` | Behavior |
| --- | --- |
| `"continue"` (default) | Trading continues; every liquidation counts in `liquidationCount`. |
| `"halt"` | The first liquidation stops the strategy: `liquidationHalted: true`, and every subsequent entry is rejected and counted. |

```typescript
// Liquidation with on_liquidation "continue": 25x leverage puts the broker's liquidation stop inside an ordinary swing, and trading goes on after each one.
import { input, line, lower, ohlcv, output } from "./sdk/declare";
import { in_close } from "./gen/inputs";
import { emitRow, out_equity } from "./gen/outputs";
import { strategy } from "./gen/strategy";
import { Cross, Sma } from "./sdk/ta";

strategy({ initialCapital: 10000, instrument: "perps", leverage: 25, maintenanceMarginPercent: 0.5, funding: "off", onLiquidation: "continue", qtyType: "fixed", qtyValue: 1, pyramiding: 1 });
input("close", ohlcv.close);
output("equity", line, lower, { description: "Strategy equity" });

const fastMa = new Sma(4);
const slowMa = new Sma(9);
const cross = new Cross();
let crossed: i32 = 0;

export function init(): void {}

export function state(): i32 {
  const close = in_close();
  const fast = fastMa.update(close);
  const slow = slowMa.update(close);
  if (isNaN(fast) || isNaN(slow)) return 0;
  crossed = cross.update(fast, slow);
  return 1;
}

export function finalize(): void {
  if (crossed == 1) strategy.long("Long").send();
  out_equity(strategy.equity());
  emitRow();
}

export function reset(): void {
  fastMa.reset();
  slowMa.reset();
  cross.reset();
  crossed = 0;
}
```

At 25x the liquidation level sits about 3.5% under the entry, inside an ordinary swing on most pairs: expect trades closed by `liquidation`, `liquidationCount` counting them, and trading continuing after each one. `stats.bankruptcyDeficit` reports any shortfall a liquidation fill left beyond the position's committed margin; equity floors at zero.

## Funding

`funding: "data"` settles recorded funding events against the open position, and `funding: "off"` disables funding entirely; only those two literals are accepted. Settlements apply only while a position is open, debit cash and committed margin together (so the liquidation price tightens as margin erodes), and a settlement that depletes margin liquidates the position at that bar's open with zero price PnL. `fundingPaid` is signed from the strategy's side (positive when the strategy paid), `fundingEventsApplied` counts the settlements applied, and `fundingPaidSeries` is the cumulative series.

This release attaches no funding provider on any host. `funding: "data"` therefore settles nothing and counts every bar an open position could not be settled in `fundingUnavailableCount`; the backtest card discloses that count on every run it applies to. No rate is ever invented. `funding: "off"` is the exact no-op it always was, and a strategy that should not depend on funding declares it.

## Perps output series

Perps runs add two bar-aligned series to the output, present on every perps run and omitted on spot: `committedMarginSeries` (margin committed to the open position at each bar close) and `fundingPaidSeries` (cumulative funding paid, ending at `stats.fundingPaid`).

## Validation

The settings are validated at build time with the engine's rules: `instrument` is `"spot"` or `"perps"`; `leverage` more than 0; `maintenanceMarginPercent` at least 0 and under 100; the fee rates at least 0; `funding` is `"data"` or `"off"`; `onLiquidation` is `"continue"` or `"halt"`. A setting linked to a param is checked at `init()` with the same rules, and an out-of-range value falls back to the default and is reported.
