---
title: "Examples: The Five Perps Scenarios"
description: "The five canonical perps scripts behind the kScript (legacy) engine's acceptance battery, ported as Indicator files: liquidation on both sides, fee…"
order: 87
section: "strategies"
---

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

# Examples: The Five Perps Scenarios

The five canonical perps scripts behind the kScript (legacy) engine's acceptance battery, ported as Indicator files: liquidation on both sides, fee classification, funding erosion and bankruptcy accounting. They are reproduction scripts: each re-derives its pinned number on the fixture bars committed beside the engine's probe (prices near 100), which is what makes it verifiable; on a live chart they simply enter at that chart's prices. The [perps and leverage](perps-and-leverage.md) page has the formulas and the numbers each must land on. kScript's `barIndex == 0` gate becomes a row counter the file keeps itself.

## 1. Long liquidation

A 10x long entered at 100 with 0.5% maintenance margin must liquidate at exactly `(100 - 10) / 0.995 = 90.45226130653266`.

```typescript
// Scenario 1, long liquidation: a 10x long at 100 with 0.5% maintenance margin must liquidate at (100 - 10) / 0.995.
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: "fixed", qtyValue: 1, slippageBps: 0, makerFeePercent: 0, takerFeePercent: 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("L").send();
  out_close(close);
  emitRow();
}

export function reset(): void {
  bars = 0;
  close = NaN;
}
```

## 2. Short liquidation with taker fees

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`.

```typescript
// Scenario 2, short liquidation with taker fees: the short formula (100 + 10) / 1.005, both fills paying the taker rate.
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, qtyType: "fixed", qtyValue: 1, instrument: "perps", leverage: 10, maintenanceMarginPercent: 0.5, takerFeePercent: 0.05, funding: "off" });
input("close", ohlcv.close);
output("close", line, overlay, { description: "Close price" });

let close: f64 = NaN;

export function init(): void {}

export function state(): i32 {
  close = in_close();
  return 1;
}

export function finalize(): void {
  if (strategy.positionSize() == 0 && close < 100.5) strategy.short("S").send();
  out_close(close);
  emitRow();
}

export function reset(): void {
  close = NaN;
}
```

## 3. Maker and taker split

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.

```typescript
// Scenario 3, maker and taker split: a limit entry and its take-profit pay maker, a market entry and its stop pay taker.
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, qtyType: "fixed", qtyValue: 1, instrument: "perps", leverage: 5, makerFeePercent: 0.01, takerFeePercent: 0.05, funding: "off" });
input("close", ohlcv.close);
output("close", line, overlay, { description: "Close price" });

let close: f64 = NaN;
let held: f64 = 0;

export function init(): void {}

export function state(): i32 {
  close = in_close();
  held = strategy.positionSize();
  return 1;
}

export function finalize(): void {
  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);
  emitRow();
}

export function reset(): void {
  close = NaN;
  held = 0;
}
```

## 4. Funding erosion

Funding settles against the isolated margin: each settlement debits cash and committed margin together, 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. This release attaches no funding provider, so the port counts every unsettled open bar in `fundingUnavailableCount` and charges nothing; the numbers reproduce once a provider lands.

```typescript
// Scenario 4, funding erosion: settlements debit cash and committed margin together, and a depleting settlement liquidates at that bar's open.
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, qtyType: "fixed", qtyValue: 1, instrument: "perps", leverage: 10, maintenanceMarginPercent: 0.5, funding: "data" });
input("close", ohlcv.close);
output("close", line, overlay, { description: "Close price" });

let close: f64 = NaN;

export function init(): void {}

export function state(): i32 {
  close = in_close();
  return 1;
}

export function finalize(): void {
  if (strategy.positionSize() == 0 && close > 99.5) strategy.long("L").send();
  out_close(close);
  emitRow();
}

export function reset(): void {
  close = NaN;
}
```

## 5. Bankruptcy gap

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.

```typescript
// 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.
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";

strategy({ initialCapital: 10, instrument: "perps", leverage: 10, maintenanceMarginPercent: 0.5, qtyType: "fixed", qtyValue: 1, slippageBps: 0, makerFeePercent: 0, takerFeePercent: 0, funding: "off" });
input("close", ohlcv.close);
output("equity", line, lower, { description: "Strategy equity" });

let bars: i32 = 0;

export function init(): void {}

export function state(): i32 {
  bars += 1;
  return 1;
}

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

export function reset(): void {
  bars = 0;
}
```
