perpadGitHub

Risk engine

A pure function. Given a proposal, account state and exchange limits, it returns allow or deny plus the clamped values. The model cannot talk its way past it.

Limits

FieldMeaning
marketsAllow-list of coins. Anything else is refused.
maxLeverageYour cap. Clamped further to the exchange cap read live from Hyperliquid meta (BTC 40x, ETH 25x at time of writing). A higher cap is clamped, never errors.
marginPerTradeUsdMargin per position, also clamped to withdrawable balance.
maxOpenPositionsConcurrent positions across all markets. One position per market.
maxDailyLossUsdOptional kill switch. Once realized PnL since 00:00 UTC (fees included) reaches the negative of this, no new positions open. Closing stays allowed.

Behavior

import { evaluate } from "perpad";
evaluate(
  { coin: "BTC", side: "long", leverage: 100, marginUsd: 900 },
  { openCoins: [], withdrawableUsd: 1000, dailyPnlUsd: 0, exchangeMaxLeverage: { BTC: 40 } },
  { markets: ["BTC"], maxLeverage: 50, marginPerTradeUsd: 100, maxOpenPositions: 1 },
);
// { allowed: true, adjusted: { leverage: 40, marginUsd: 100 }, reasons: [...clamped] }

Dry-run any proposal from an MCP client with the check_risk tool.