Signal → trade
signal → first mint (fixed size)
Mirrors runner/src/plugins/03-exampleFirstMint
What this teaches
- Combine a signal source with a real Action[] return
- market.strikeAbove / strikeBelow hide grid math
- A fixed-size trade with no fancy sizing (yet)
New vs exampleSignal
- When signal is "buy" or "sell", the plugin actually submits a mint
import type { StrategyPlugin } from "@automark/runtime-core";
import { Market } from "@automark/sdk/market";
import { minutes } from "@automark/sdk/duration";
interface Signal {
action: "hold" | "buy" | "sell";
asset: string;
}
const FIXED_QUANTITY = 100_000_000n; // ignore sizing for now — see exampleSizing
export default function createExampleFirstMint(): StrategyPlugin {
const vaultId = process.env.VAULT_ID;
if (!vaultId) throw new Error("exampleFirstMint: VAULT_ID not set");
const signalUrl =
process.env.SIGNAL_URL ?? "https://your-backend.example/signal";
return {
name: "exampleFirstMint",
vaultId,
triggers: [{ kind: "cron", everySeconds: 60 }],
async decide(ctx) {
const signal = (await (await fetch(signalUrl)).json()) as Signal;
if (signal.action === "hold") {
return [{ kind: "noop", reason: "signal=hold" }];
}
const market = await Market.find({
asset: signal.asset,
expiryAfterMs: ctx.now + minutes(5),
client: ctx.suiClient,
});
const p = await market.price();
// buy → strike above forward, isUp; sell → strike below, !isUp
const isUp = signal.action === "buy";
const strike = isUp
? market.strikeAbove(p.forwardRaw, { pctBps: 300 })
: market.strikeBelow(p.forwardRaw, { pctBps: 300 });
return [
{
kind: "vault.mintBinary",
params: { marketId: market.id, strike, isUp, quantity: FIXED_QUANTITY },
},
];
},
};
}
// insights we can take from this example:
// The Market class lets you find and work with the markets you intend to trade on; you can filter, list, and define the operations you want to enter.
Environment variables
- VAULT_ID
- SIGNAL_URL