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