Workflow
Chain operators, loops, conditionals, sleeps, waits, retries, and chain modifiers. The composition primitives that turn the CLI into a scripting language.
Chain operators
buy $100 ; cancel ; close
buy $100 & cancel
| Operator | Behavior |
|---|---|
; | Top-level chain separator. Fire-and-don't-await. |
& | Identical semantics to ;. |
sleep | The only step the runner blocks on (carries block: true). |
legacy @cmd prefix | Stripped silently (one-time warning per call). Skipped when @<word> is a known user var. |
Fire-and-don't-await means each step launches without waiting for the
previous step's exchange round-trip. This is intentional — most chains are
fire-then-react, not strictly sequential. When you actually need to wait
on a specific result, use wait or
capture syntax.
sleep
| Form | Effect |
|---|---|
sleep 5 | 5 seconds (default unit is seconds) |
sleep 250ms | 250 ms |
sleep 5s | 5 seconds (explicit) |
sleep 2m | 2 minutes |
Hard cap 10 minutes. Source: SleepCommand.ts, preprocess/sleep.ts.
wait
Condition-blocked sleep. Polls the local exchange store at 100 ms.
wait <condition> [timeout=<dur>] [for=<dur>]
Condition forms
| Form | Meaning |
|---|---|
wait 5s / wait 250ms | Bare duration — same as sleep |
wait time HHMM | Wait until next occurrence of HHMM UTC |
wait position [SYM] | Any position, focused or named |
wait long [SYM] / wait short [SYM] | Side-filtered |
wait anyposition | Any account, any symbol |
wait price > 50000 | Spot the trigger — uses ask on upside, bid on downside, falls back to last |
wait mark > 50000 / wait index < 50000 | Mark / index price |
wait pnl > 200 / pnl < -50 | uPnL on focused symbol |
wait fundingrate > 0.0001 | Funding rate |
wait premium > 5 | Premium |
wait fill / wait fill BTCUSDT | Wait for any fill on the symbol |
wait fill <id> / <id1>,<id2> | Wait for a specific order to fill |
wait @stop > 0 | LHS can be a user var (resolves via tracker/live/static) |
Comparators
> < >= <= == !=. The exact-compare forms are rarely useful
for prices but they exist for integer / zero checks.
Combinators
wait both (price > 50000) and (pnl > 0)
wait either (price < 49000) or (price > 51000)
Combinators nest. Source: preprocess/conditions.ts.
Modifiers
| Modifier | Default | Cap | Behavior |
|---|---|---|---|
timeout=<dur> | 30 s | 10 m | Give up after this long |
for=<dur> | — | — | Condition must hold continuously for this window (anti-fakeout) |
wait time 1200and it's currently 12:00:00 UTC — the parser interprets that as "next 12:00 UTC", i.e. ~24 hours from now. Usewait time 1201if you want the next-minute case.
loop
Fan out one step per matching position. Symbol is appended to the body.
loop <variant>: <body>
<variant> | Matches |
|---|---|
longs | All long positions |
shorts | All short positions |
openpos | All open positions (open accepted as alias) |
allpos | Every position (synonym for openpos) |
all | Same |
loop longs: buy 1$
loop shorts: close
loop allpos: cancel
Multi-statement bodies are not supported. If you need one, alias the body and loop the alias:
alias cleanup "cancel; close"
loop longs: cleanup
Source: preprocess/loop.ts.
repeat
repeat 5 (buy 1$; sleep 1m)
| Cap | Value |
|---|---|
| Max iterations | 1000 |
| Max nesting depth | 8 |
Source: preprocess/repeat.ts.
if / else / end
if (<condition>) (<then-body>) [else (<else-body>)]
<condition> uses the same vocabulary as wait.
Evaluated once at preprocess time against current store state, so
this is a one-shot conditional, not a runtime loop.
The body is fully reprocessed — nested if, repeat, loop, retry,
set ec all compose inside it.
if (long) (close)
if (pnl > 50) (close) else (sleep 5m)
if (position) (chase sell %all% 100% reduce) else (echo "nothing to close")
end
A bare end in the chain (or inside an if branch) terminates the
outer chain. Anything after is dropped.
buy $100 at $entry -1%
wait fill
if (pnl < -10) (close; end)
buy $100 at $entry +1%
If the if fires, end cancels the trailing buy.
Source: preprocess/ifElse.ts, preprocess.ts:204-213 (end),
:218-275 (if recursion).
retry
retry N <body>
Re-runs <body> up to N times after a failure. First attempt is "try 0",
so retry 3 means up to 4 total attempts.
| Bound | Behavior |
|---|---|
| N = 0 | Same as not using retry |
| N ∈ [0, 10] | Accepted |
| N > 10 | Falls through as a normal step — the dispatcher will reject it |
retry 3 chase buy $100 to 1%
retry 5 close
Source: preprocess/preprocess.ts:31-32, 146-157. Runner side at
apps/cli/src/repl.ts:1618+.
Chain modifiers
Mid-chain flags that apply to every subsequent step in the same chain. Idempotent — won't double-inject if the keyword is already on the step.
| Modifier | Effect |
|---|---|
set ec | Continue-on-error — subsequent failures print as warnings, chain runs on |
set ro / unset ro | Inject reduce into subsequent buy/sell/scale/swarm/chase/twap |
set po / unset po | Inject po into subsequent buy/sell heads. Market orders ignore it |
set ec
cancel
close
nuke # all three continue even if one fails
set ro
close # reduce-only is implicit now
cancel
buy $100 # still gets reduce injected
set po
buy $100 at 50000
sell $100 at 50100
unset po
buy $50 best # market-best, post-only no longer active
Source: preprocess/preprocess.ts:20-57, 133-142, 332-356.
po keyword on a single order
You can mark one order post-only without affecting the rest of the chain:
buy $100 at 50000 po
Same effect as set po; buy $100 at 50000; unset po.
Composition cheatsheet
A few canonical combinations:
# retry × repeat × sleep × set ec
repeat 5 (set ec; retry 2 buy 1$; sleep 1m)
# one-shot conditional
if (long) (close)
# fan-out reduce-only chase
loop longs: chase sell 50% reduce
# capture + wait fill + close
@o = buy $100 at 50000; wait fill @o; close
# soft-error cleanup
set ec; cancel; close; nuke
# chain reduce-only
set ro; close; cancel; buy $100
# mid-chain post-only toggle
set po; buy $100 at 50000; sell $100 at 50100; unset po; buy $50 best