algo() in place of indicator(), reads its settings and whatever data it needs, and says in algo.onArm which orders to place. When you press Arm, the script runs that function once, and the orders it makes are placed: in this tab on Paper or Live, or on 0xArchive’s server.
Runs as orders
An algo runs as orders when everyorder.* call sits inside the function you give algo.onArm. Arming then places fixed orders: a chase with its prices, a stop at its price, a condition with its test. These are the same order types the server runs, so an algo that runs as orders can run on the server with your tab closed, edited or not.
An order.* call anywhere else, in chart.onBar or a book’s onUpdate, makes the algo decide later, on live events. The picker tags it Script. A Script algo arms in this tab, on Paper or Live. The server trades a strategy’s triggers and plans, nothing else: a Script algo that is not a strategy is not offered Server, and a strategy’s own order calls are not sent there. Live logic below says how it runs.
The picker and the settings show which it is before you arm anything. The compiler decides from the source: keep order calls lexically inside onArm’s function. A helper function declared at the top level and called from onArm counts as outside, so declare the helper inside onArm if it places orders.
The order types
Each call is checked against the order type’s own ranges when the algo plans. A value out of range is refused with the same words the server uses, before anything is sent.
trigger.mark, trigger.book (the mid), trigger.trade, trigger.funding and trigger.oiUsd are the prices the server watches itself. Their tests are 'above', 'below', 'crossesAbove' and 'crossesBelow'. A trigger outside this list, such as a book lean, needs code to watch it, which is live logic.
Sizes
A size is one of{ usd }, { coins }, { pctOfPosition }, { pctOfEquity }, or { riskUsd, stop }. The last is the size that loses riskUsd if the price reaches stop, counted from the order’s price. An exit sized in dollars or as a share of equity is turned into coins at the mark, rounded down to the market’s size step.
Reading the account
account() gives the account as it stands when the algo plans: position (its side, size in coins, entry price and notional), equity and mark. Read it inside algo.onArm; elsewhere it reads flat. A stop protects the position the account holds at that moment, in whichever direction it faces:
atr.value is warm by the time you arm it.
Roles
role: 'protect' promises the algo only reduces your position: every order it makes must be an exit or reduce-only, or arming is refused. 'entry' may add to the position, and 'both' does either. On Live, an entry obeys the account’s kill switch; a protective algo keeps working under it.
Settings for trading
input.price, input.size, input.side, input.percent and input.duration draw the fields a trader expects: a price, an amount with its unit, buy or sell, a percent, and a time written as '30s', '5m' or '4h' (read in seconds). Group the ones most people leave alone with group: 'More', and the settings fold them.
algo.describe(() => '...') gives the sentence the settings lead with. Without it, the settings word the orders themselves.
What the host adds
Every algo’s settings carry rows the script can’t change: the market, where it runs (this tab or the server), the account (Paper or Live; the server is always Live), and when it ends. Below them, the settings list in words the orders arming will place, from the account as it is now. Arm plans once more at the press, so the orders sent are the account’s at that moment. On the server, Preview asks the server to check them first. Several orders from oneonArm are placed one after another. If the server refuses one, those before it stay armed, and the settings say how many.
Live logic
A Script algo keeps running after you arm it. Each order call it makes on a live event, a closed bar or a book update or a trade after the arm, is a request, and the tab checks it before anything is placed:- Stop first. An entry needs a working stop from the same algo on that market: an
order.fastStop, anorder.timeStop, or anorder.whenthat closes or reduces. Place it inalgo.onArm. - Entries. The settings’ Entries row says how many times it may open or add to a position on one arm. One by default.
- Entry caps. At most 10 entries a minute and 200 a day on the account, across every algo.
- The kill switch stops entries on Live while server trading is signed in.
trades.maxPositionUsd, when the script declares it, caps the position an entry may take it to.
order.market, order.limit, order.reduce or order.close places at once.
In history, or before you arm it, an order call sends nothing. Its ref says which: 'sent' on a live event, 'none' otherwise. Count what you sent from the ref, so the history run doesn’t use it up:
account() reads the account as the tab last saw it while the algo is armed. acct.onOrder hears each answer and change to the algo’s orders, refusals included, and acct.onFill each fill; both run on live events only. order.cancel('entry') cancels the working orders made with that tag.
Disarm stops the script and cancels its working entries; its stops keep protecting the position. It also ends at its end time, when its script fails, or once its entries are used and its orders have ended. A reload ends it.
Quoting
order.quote({ bidPx, askPx, size }) quotes both sides: a post-only bid and ask rest in the account, each of size ({ usd } or { coins }). Call it from a live handler as often as you like. Each call moves the quotes to its prices, and null pulls that side. A quote whose price moved less than half a basis point stays where it is, keeping its place in the queue; a moved one goes to the back of its new level. The tab applies at most four calls a second, the newest.
trades: { maxPositionUsd }: the position, valued at the mark, never passes it. The side that would take it past is pulled, and the other side keeps quoting, which works the position back. Quotes are not entries: the entry caps and the stop-first rule above do not apply to them, the cap does. Disarm pulls its quotes and leaves the position; Flatten pulls them and closes the position at market. Quoting runs on Paper. The built-in Quote the spread leans its quotes against what it holds.
A fill pulls at once whatever the cap no longer allows; nothing is placed again until the script’s next call says where. Quoting combines with exits: the built-in Fade the extremes rests its bid and ask at the edges of the 1-minute Bollinger band, and once a side fills it places order.fastStop and order.timeStop for that position, quotes only the take profit ({ coins } of the whole position, at the band’s middle), and cancels its exits with order.cancel when the position is flat again.
Strategies
A strategy is an algo that stays on:algo(name, { lifetime: 'standing' }). Its script finds the moments to trade (algo.trigger), and you steer it while it runs. Each trigger trades only when all of these hold:
- you have armed it;
- the bias allows that side;
- its price is inside one of the strategy’s zones, if it has any;
- the governor has room for the day;
- no cooldown is running.
control.number, choice, toggle, price and button, at most 16, declared at the top level like inputs. A change is an event the script reads from its next handler: it never re-runs the script, and the journal records it. A control.price is a line on the chart you drag. In history a control reads its history value, and a button never fires.
The plan turns a trigger into a trade, in your terms. It sets the entry (cross, or chase with a worst price) and the size (riskUsd, notionalUsd or equityPct). It must set a stop: bps, a price, or beyondZone. Targets in R and a time stop are optional. When a trigger trades, its stop goes in first, protecting the side the entry will open, and then the entry. Once the entry fills, the stop rests on the exchange as a reduce-only stop market for what filled, and each target as a reduce-only take-profit market, so they protect the position when the tab closes. The tab watches the stop only until the exchange lists it, or for the whole trade if the exchange refuses it. The time stop runs in the tab. Every order goes through the admission and caps in Live logic. A strategy places entries only through its plan: an entry its handlers place directly is refused.
Start on Paper or Start on Live in its settings starts it Watching, on that account: bias Off and no shots, so nothing trades until you arm it. Live places real orders with your trading key, each through the same checks. A strategy never trades a market where you hold a position it didn’t open. Its row in Running is where you drive it, and its state is one word at the row’s start: Watching, Armed with its shots and bias (“Armed 2 · Long”), In trade, Paused or Stopped.
- Bias: Long, Short, Both or Off. Off always wins, even when the script sets the bias with
algo.bias.set. - Shots and Arm: pick how many trades it may open (1, 2, 3, 5, or Auto for no limit) and press Arm. Each trade it opens takes one; at none it watches again. While it’s armed, Shots says what is left and the button is Disarm. On Live, Arm asks a second time, naming the account.
- Armed for and Each trade, in its details: how many minutes an arm lasts, and Confirm or At once. Confirm comes first on Live. With Confirm, each accepted trigger waits on a card showing the trade it will place. Trade places it. Skip, or 30 seconds passing, places nothing and keeps the shot.
- The menu (⋯) holds what ends something, each asking again on the row. Flatten & disarm turns entries off, cancels its entry and its resting stop and targets, and closes the position its trade opened. Stop ends the strategy: its resting stop and targets stay on the exchange and keep protecting the position. Its row in History says what still rests.
- The state and the next trade, and the governor’s day: “Day P&L −$40 of −$150 · 1 of 4 trades”. The day counts every trade on the account: on Live, today’s closed P&L less fees from the account’s fills, plus what is open. While it can’t be read, it takes no entries. At the loss limit it halts until the next UTC day. Reset lifts the halt and turns the day’s loss limit off until tomorrow.
- Its controls, live.
panel.checklistandpanel.log, if the script writes them: what it waits for, and its own notes.- The journal: the last 30 events.
On history
A strategy replays its history with fills, and its settings show the result under On history: trades, win rate, net, drawdown and costs, from the strategy() account’s fees, book slippage and funding. The replay assumes what you would own live: armed for every trigger and its declared bias, unlessalgo({ backtest: { capital, assume: { bias, armed } } }) says otherwise. The governor counts the simulated day. When it halts the strategy, the replay arms it again at the next UTC day, when the halt lifts, as you would. With armed: 'script', only the script’s own algo.arm arms it.
It trades at the bar tier:
- the entry fills at the next bar’s open;
- the stop and the first target rest as one exit;
- the time stop counts from the fill: its exit goes in at the first bar close past it and fills at the next bar’s open, so a one-bar time stop goes out at the open after the bar it filled in.
book() handler, against the archive’s book every 5 seconds (Data), at the book tier:
- the entry meets the next update, never the one that fired it, and walks the opposite side up to the plan’s worst price, each level giving at most 96% of its size; what is left does not fill, and a lost book before it cancels the entry;
- a resting entry fills at its price only when a later update’s best or a later bar trades through it, never at a touch;
- the stop is seen on the first update at or past it and fills walking that book, never better than the stop; a time stop closes on the first update past it;
- a target fills at its price only when the book or a bar trades through it; when the stop and a target are reached in one update, the stop wins.
assumptions.fills is 'book 5s', and replay.bookTier counts the triggers, fills, stops, targets and time stops, with the largest share of the book any fill took. Trade and liquidation triggers are still not replayed, and are counted in replay.notReplayed.