Skip to content

Action glossary

The engine validates every action name against a registry before it starts. That registry is the list of verbs a .strategy file can call. The registry is not much fun to read. This page is the version written for people.

There is one warning before the tables: the registry still contains older names that the current runtime does not handle. They pass validation, then do nothing. The Reserved names section lists them. Do not use those in a live file.

How to write an action

Actions use dots between words. The engine turns dots into underscores, so order.place is the action order_place internally.

Arguments are always named:

order.place(market: "spot", side: "buy", qty: $params.qty,
            price: $local.buy.px, tif: "ALO", dedup_key: "buy:initial")

Positional arguments are rejected. Values can be literals (1.0, true, "text") or expressions ($platform.spot.bid * 0.99).

The tif argument controls how a limit order behaves at the venue:

value what the venue does
ALO Post-only. The order rests as a maker. If it would cross, the venue rejects it.
GTC Normal limit. It rests if it can, and takes if the market is already through the price.
IOC Take what is available now, cancel the rest.

price is optional. If you leave it out of order.place, the engine sends a market order. For order.replace, pass a price; the action is meant for resting orders. market defaults to "spot"; use "perp" for a perpetual. symbol is optional and overrides the asset from the config entry.

The defaults for tif are worth remembering: order.place defaults to IOC, and order.replace defaults to ALO. Pass the value explicitly if the order type matters to you.

Effectful order actions need a dedup_key. The key is the order's identity. Reusing a key means "this is the same order slot," not "send another one."

The small set

Most custom overlays need only these.

These order actions are the overlay path. The primary grid has its own buy and sell ledger and its own fill routing; an order.* fill is tagged as an overlay order, so the grid will not track it. Use them in an overlay file, not in a primary strategy.

action arguments what it does
market.facts none Refresh the market and perpetual facts for this cycle: bid, ask, spot bid and ask, mark, funding, position, margin, minimum notional. Call it before reading $platform.* market facts.
order.place side, qty, dedup_key; optional market, symbol, price, tif, reduce_only, post_only Send one order. Use reduce_only: true when closing a position.
order.replace same as order.place Keep one resting order for the key. It places the order if it is not there, amends it if the price or size changed, and cancels it when qty is 0. This is the action to use when a strategy maintains a resting order.
order.cancel order_id, dedup_key; optional market Cancel one order by venue id. You need the id, so most overlays use order.replace(qty: 0) instead.
quote.reserve amount Reserve that much quote for the overlay. The primary grid's available quote drops by the same amount.
quote.release none Release the overlay's quote reservation (sets it to zero).
reserve.base qty Move base into the reserved ledger so the grid will not sell it. A negative quantity releases; the ledger clamps at zero.
set.gate name, value Write a remembered variable. You will normally write name = expression in the step body; the compiler turns that into set_gate. Calling it directly is only useful if you are generating steps.

post_only is there for the rare case where you need to pass the flag separately from tif. In normal use, tif: "ALO" is the clearer way to ask for a maker order.

Perpetuals

A perp position is opened and closed with the same two actions. A sell opens or adds to a short; a buy closes it. Put reduce_only: true on the buy so the venue refuses any size past flat. The position is signed, so the closing quantity is 0.0 - $platform.perp.qty.

order.replace is the one to use for a resting exit: it amends the same order when the price or size moves, and cancels it at qty: 0. order.place sends one order and is better for a fill-or-cancel action. market.facts must run before you read $platform.perp.*.

See A short position, start to finish for the full sequence.

Grid scaffolding

These are the phases the shipped jacobs_ladder.strategy composes. They assume the grid's context: the asset, the strategy state, the open-order ledger, and the cycle facts. They are not a general API for a new strategy, but they are the easiest way to see how the grid is put together. The grid actions take no arguments; they publish their results as $platform.* facts, which is why the strategy reads flags between phases.

If you are writing a small overlay, you do not need any of them. Use market.facts and the order actions above.

Preamble and cleanup

action what it does
cycle.prepare Run the grid preamble for this cycle.
init.venue.state Initialise venue-specific state such as fees and lot size.
prepare.recovery Restore the TIF and order recovery latch after a restart.
resolve.book Read the current book into the cycle context.
refresh.maker.fee Refresh the maker fee.
expire.tif.recovery Clear an expired TIF-recovery latch.
mark.stale.cycle Record that this cycle used a stale balance.
cycle.cleanup Run the grid cleanup phase.
expire.amend.cooldowns Clear expired amend cooldowns.
evict.ghost.orders Drop orders the venue no longer has.
scan.open.orders Sync the grid's open orders.

Facts and gates

action what it does
early.facts Publish the early cycle facts (price missing, balance freshness).
cycle.facts Publish the rest of the cycle facts (buy and sell posture).
cycle.guard Publish whether the cycle may continue.
buy.gate Publish whether the buy leg is active.

Buy leg

action what it does
cancel.excess.buys Cancel extra buys so only one is open.
buy.place.plan Compute the fresh-buy plan and publish its facts.
buy.place.send Send the planned buy.
buy.place.send.insufficient Handle an underfunded buy when the balance is stale.
buy.place.latch.capital.low Latch the capital-low state.
buy.place.warn.quote Log the missing-quote warning.
buy.place Run the full buy-placement phase.
buy.amend Run the buy-amend phase.
buy.amend.has.sell Publish whether a resting or pending sell is tracked.
buy.amend.with.sell Amend the buy when a sell is present.
buy.amend.no.sell Amend the buy when no sell is present.

Sell leg

action what it does
plan.sell.order Compute the next sell rung and reconcile the persisted ladder.
sell.place Run the gated sell-placement phase.
sell.place.should Publish whether a fresh sell is placeable this cycle.
sell.place.body Place the owed or restored sell.
sell.finalize Run the sell finalize phase.
sell.finalize.facts Publish the excess-sweep gate.
sell.finalize.latch Retry-latch bookkeeping.
sell.excess.sweep.phase Run the excess-inventory sweep.
sell.finalize.end Clear the resume marker.

Events

action what it does
apply.order.event Route the current order event into the grid handler. jacobs_ladder.strategy pairs this with stop so the cycle ends after the event.

Reserved names

These names are registered, so dio strategy validate will accept them. The current runtime handler does not implement them, so they do nothing. Some are older names from the reference engine; some were declared for a refactor that has not landed. Do not build on them.

Pricing and read helpers:

compute.grid.price    compute.buy.ref.price   owed.sell.price
available.base        rung.price              compute.sell.price
compute.amend.price   read.book               read.capacity
read.open.orders      buy.facts

Direct order verbs:

place.buy   place.sell   amend.buy   cancel.all   cancel.order

State and accounting:

accumulate   track.buy   track.sell   set.time   set.cooldown
update.reserved.base   notify.oracle

Gates and reconciliation:

gate.balance   gate.capital.halted   is.ghost
reconcile.position   reconcile.persisted.sell.levels

If you find yourself reaching for one of these, use the wired equivalent instead: order.place, order.replace, or order.cancel. If there is no equivalent, ask an engineer to wire the action rather than relying on a name that validates and then silently does nothing.

Where the names come from

The declaration (name, argument schema, class) lives in src/engine/strategies/actions/strategy_actions_builtin.ml. The runtime handler lives in src/engine/strategies/actions/strategy_actions_cycle.ml. The validator checks the declaration; the runtime calls the handler. Adding an action means changing both files and adding a row here.