Skip to content
Runner · ExamplesLadderrung 04

Sizing inside the vault's risk caps

size against vault risk caps

Mirrors runner/src/plugins/04-exampleSizing

What this teaches

  • Never trade a hardcoded quantity in production — the vault primitive enforces caps and aborts oversized trades
  • maxSinglePosition + exposureHeadroom give you the binding ceiling
  • Halving for safety margin is a typical pattern
  • Plugin defensively short-circuits when there is no headroom

New vs exampleFirstMint

  • Trade size is computed from vault state, not hardcoded
  • Plugin checks isFrozen before doing anything
import { noop, type StrategyPlugin } from "@automark/runtime-core";
import { Market } from "@automark/sdk/market";
import { bigintMin } from "@automark/sdk/math";
import { minutes } from "@automark/sdk/duration";

interface Signal {
  marketId: string;
  action: "hold" | "buy" | "sell";
  asset: string;
  strike: bigint;
  quantity: bigint;
}

export default function createExampleSizing(): StrategyPlugin {

  const vaultId = process.env.VAULT_ID;

  if (!vaultId) throw new Error("exampleSizing: VAULT_ID not set");

  const signalUrl =
    process.env.SIGNAL_URL ?? "https://your-backend.example/signal";

  return {
    name: "exampleSizing",
    vaultId,
    triggers: [{ kind: "cron", everySeconds: 60 }],

    async decide(ctx) {

      const { isFrozen, maxSinglePosition, exposureHeadroom } = ctx.vault;

      if (isFrozen) return [noop("vault frozen")];

      const signal = (await (await fetch(signalUrl)).json()) as Signal;

      if (signal.action === "hold") return [noop("signal=hold")];

      // Pick the smaller of the two binding caps, then halve for margin.
      // `bigintMin` from @automark/sdk/math — same as `a < b ? a : b` but
      // explicit, and you'll repeat this pattern in every sizing path.
      const ceiling = bigintMin(maxSinglePosition, exposureHeadroom);

      let quantity = ceiling / 2n;

      if (quantity === 0n) return [noop("no headroom")];

      const isUp = signal.action === "buy";

      quantity = bigintMin(signal.quantity, ceiling);


      return [
        {
          kind: "vault.mintBinary",
          params: { marketId: signal.marketId, strike: signal.strike, isUp, quantity },
        },
      ];
    },
  };
}


// insights we can take from this example:

// The `ceiling` const is a strategy that makes sense because it checks whether ... (FILL IN HERE)

// The signal came in strongly oriented; the strategy possibly uses the Market class available in @automark/sdk directly in its backend
// The runner uses nothing different from @automark/sdk — it just abstracted it in a fast and robust way, but everything that exists in the runner can also be
// used in your backend: vault values, markets, on-chain pools, etc.

Environment variables

  • VAULT_ID
  • SIGNAL_URL