sf buy / sf sell. Intents are persisted, replayable, race-free, and reviewable before execution.
Two ways to run it
| Mode | Where it runs | Credentials | Best for |
|---|---|---|---|
| Local | Your machine, foreground or background | Local Kalshi / Polymarket private keys (file paths) | Operators, dev, single-machine setups. |
| Cloud (BYOK) | SimpleFunctions cloud | Encrypted exchange credentials, connected by the user and never returned by the API | Always-on automation without keeping a laptop awake. |
Quick start (local)
What runtime watches
Each tick (every 30 seconds) the runtime:- Reads your active intents from
GET /api/intents?status=.... - For each intent, checks the trigger — hard price/time triggers locally, soft NL conditions if
--smartis enabled. - Marks newly-firing intents
armed→triggered→executing. - Places the order on the configured venue using your local exchange keys.
- Tracks fills against the intent (
filledQuantity), retries partial fills on the next tick. - Writes structured status / errors to
~/.sf/runtime.log.
~/.sf/:
| File | Purpose |
|---|---|
~/.sf/runtime.pid | PID of the foreground / daemonized runtime. |
~/.sf/runtime.log | Append-only log of tick activity, fills, and errors. |
~/.sf/runtime-executed.json | De-dupes execution attempts across crashes. |
CLI
sf runtime start
| Flag | Default | Notes |
|---|---|---|
--daemon | off | Detach into a background process. PID written to ~/.sf/runtime.pid. |
--smart | off | Evaluate softCondition strings on intents (“only if VIX < 20”) with the configured LLM. Costs LLM tokens. |
sf setup --enable-trading.
sf runtime stop
~/.sf/runtime.pid. Falls back to scanning for orphan runtime processes.
sf runtime status
Intents
An intent is a persisted instruction to do something on a venue when a trigger fires. Create them with the CLI or the HTTP API.Create an intent (CLI)
| Flag | Default | Notes |
|---|---|---|
--price <cents> | none (market) | Max price per contract in cents (1–99). |
--side <yes|no> | yes | Direction. |
--trigger <trigger> | immediate | See trigger forms below. |
--soft "<condition>" | none | NL condition the runtime evaluates each tick when --smart is on. |
--expire <duration> | 1d | When the intent auto-cancels: 1h, 6h, 1d, 3d, 1w. |
--venue <kalshi|polymarket> | kalshi | Venue. |
--rationale "<text>" | none | Free-form why-string saved on the intent. |
--auto | off | Skip the interactive confirmation. |
--style <immediate|twap> | immediate | Execution style. twap slices the order over time. |
--json | off | Return JSON envelope. |
| Form | Meaning |
|---|---|
immediate | Fire on the next tick (or right now if not arming). |
below:<cents> | Fire when price ≤ that many cents. |
above:<cents> | Fire when price ≥ that many cents. |
time:<ISO> | Fire at the given ISO-8601 timestamp. |
--soft) are evaluated by the configured LLM on every tick when --smart is enabled. Examples:
List + cancel
Lifecycle
| Status | Meaning |
|---|---|
created | Persisted but not yet activated (e.g. waiting for activateAt). |
armed | Watching; trigger has not fired. |
triggered | Trigger fired this tick; runtime is about to submit. |
executing | Order submitted, awaiting fill. |
filled | Fully filled (or partially, if the venue closed early). |
canceled | User canceled. |
expired | expireAt passed without firing. |
failed | Venue rejected or runtime errored. The error is captured on the intent row. |
Intents API
POST /api/intents
Required body fields| Field | Type | Notes |
|---|---|---|
action | "buy" | "sell" | Order direction. |
venue | "kalshi" | "polymarket" | Venue. |
marketId | string | Kalshi ticker or Polymarket conditionId. |
marketTitle | string | Display title — saved on the intent for UX. |
direction | "yes" | "no" | Contract side. |
targetQuantity | integer ≥ 1 | Number of contracts to fill. |
| Field | Type | Notes |
|---|---|---|
maxPrice | integer 1–99 | Max price per contract in cents. |
executionStyle | "immediate" | "twap" | TWAP slices the order. |
triggerType | string | "immediate", "price_below", "price_above", "time". |
triggerPrice | number | Cents threshold for price triggers. |
triggerAt | ISO timestamp | For triggerType: "time". |
triggerParams | object | Trigger-specific extras. |
softCondition | string | NL condition evaluated each tick when --smart is on. |
expireAt | ISO timestamp | When the intent auto-cancels. |
autoExecute | boolean | If true, skip confirmation. |
source | string | Free-form attribution ("cli", "agent", …). |
sourceId | string | Linked thesis id, evaluation id, etc. |
rationale | string | Why-string saved on the intent. |
| Status | Body | Cause |
|---|---|---|
400 | Missing required fields: ... | Missing required field. |
400 | action must be "buy" or "sell" | Bad enum. |
400 | venue must be "kalshi" or "polymarket" | Bad enum. |
400 | direction must be "yes" or "no" | Bad enum. |
400 | targetQuantity must be a positive integer | Bad number. |
400 | maxPrice must be 1-99 cents | Out of range. |
401 | unauthorized | No / invalid auth. |
GET /api/intents
| Query | Notes |
|---|---|
status | Filter (active, armed, triggered, executing, filled, canceled, expired, failed). |
venue | Filter by venue. |
source | Filter by source field. |
limit | Cap on rows. |
PATCH / DELETE
PATCH updates status, softCondition, expireAt, or triggerParams. DELETE cancels.
Smart mode
--smart enables three behaviors:
- Soft-condition evaluation. Each tick, if any active intent has a
softCondition, the runtime calls the LLM with current market context and the condition. Only fires the order if the LLM returns a clear positive. - Edge re-check before firing. Just before submitting the order, the runtime calls
inspect_tickeron the market and aborts if the suggestion has flipped toavoid. - Adaptive delay. Trades that look like they’d cross the spread are deferred until the spread is reasonable — implementation default is 8 cents.
sf agent --budget-usd ... and / or per-thesis monthlyBudgetUsd on heartbeat.
Risk gates
The runtime calls the same risk-gate engine as the autopilot tick. Before any order is placed it checks:- per-trade max notional
- per-market exposure cap
- daily loss circuit breaker
- max open positions
- minimum balance
- per-tick max orders
risk_gate_fail reason to the intent and stops there — the intent stays armed for the next tick. See Risk gates.
Cloud runtime (BYOK)
The cloud runner gives you “always on” without keeping a laptop awake. Treat this as an advanced operator surface: start locally, verify dry-run behavior, then connect cloud credentials only when you are ready for unattended automation.Enable
sf setup --cloud connects encrypted exchange credentials for the cloud runner. The API never returns plaintext credentials; rotate or revoke them from the CLI when access should change.
The cloud runner uses the same intent + tick + risk-gate code paths as the local runtime. The only difference is the host.
One-shot remote exec
sf --remote <command>. Sends a single CLI invocation to the cloud runner, returns a runId, and lets you stream or poll output. Auth: Authorization: Bearer sf_live_....
This is not a long-running daemon — for that, use sf runtime start --remote.
Events
Runtime emits webhook events when configured:| Event | When |
|---|---|
intent.armed | Trigger watcher attached. |
intent.triggered | Trigger fired. |
intent.executing | Order submitted. |
intent.filled | Order fully filled. |
intent.canceled | User canceled. |
intent.expired | expireAt passed without firing. |
intent.failed | Venue rejected or runtime errored. |
Operational tips
- Run
sf doctorbefore going live to catch missing keys, time-skew, or a stale CLI. - Use
sf intent list --all --jsonto review what the runtime is watching before you start it in--smartmode (smart-mode runs LLM calls). - Soft conditions are tokens; over-broad conditions on many intents accumulate cost. Prefer hard triggers when the rule is mechanical.
- Cloud runner respects
executionMode—dry-runevaluates everything but skips placing orders. Flip toliveonly when you’re satisfied with dry-run output.
Related
sf agent is for reasoning + tool use. sf telegram is for human-in-the-loop. The runtime is the worker that closes the loop.
See also
Trade intents
The intent-object model in depth.
Risk gates
Pre-trade safety rails.
Portfolio autopilot
Cloud-run portfolio loop with BYOK credential connection.
Webhooks
Signed delivery for runtime events.