---
name: ftw-arcana
description: Write and run Arcana strategies and time-series programs, locally with your own sources or embedded in ftw.trade. Host declarations, CLI files and producer pipes, checkpoints, types and na, series, triggers, orders, tapes, phases, libraries, std modules, exact integers, trained models, invariants and tests. Use whenever you write or read Arcana.
---

# Arcana

Arcana is a standalone language, compiler and evaluator for strategies and
time-series programs. A program reads observations, computes values and
requests actions when its conditions turn true. The evaluator is deterministic
and enforces work and memory bounds. The host supplies sources, library
imports and action consumers; ftw.trade is one host, with its own data and
execution services.

Every compile error has a stable code (`FS0412`), names its line and column,
and suggests a fix. Draft, check, read the errors, revise.

The full language reference is references/language.md (generated from
`arcana/docs/reference.md`). Its examples use a generic host with
`bars.close("BTC-USD", "1h")` and `paper.buy({ symbol, quote })`; on
ftw.trade, read the market as below.

## Run independently with your own sources

Read references/local.md (also [Run Arcana locally](https://arcn.ftw.trade/docs/local))
for a complete standalone CLI project, custom numeric and record sources,
JSONL files or producer pipes and explicit checkpoint recovery. The installed
CLI compiles against `--host host.json`; nothing injects `market`, `interval`,
capabilities or FTW source names. `check` validates the program; `run` emits
actual outputs and unsent action intents. A producer implements collection;
the CLI provides no provider subscription, signing or order submission.

Choose the host before writing a program. Outside ftw.trade, use the explicit
source and capability names the caller declares, and compile against that
host. The npm package and native Rust library support the same application
boundary. File checkpoints validate the consumed prefix and configuration on
explicit resume; stdout and checkpoints are not a transactional outbox. Do
not silently restart after a refusal or use FTW module discovery to infer an
external application's capabilities.

## A strategy on ftw.trade

```arcana
// params: fastLen=12, slowLen=26, size=100
const close = bars.close(market, interval);
const fast = ema(close, fastLen);
const slow = ema(close, slowLen);
metric("fast", fast);
metric("slow", slow);

when (crossover(fast, slow) && !inPosition()) { paper.buy({ quote: size }); }
when (inPosition() && crossunder(fast, slow)) { paper.sell({}); }
```

- Above the first line ftw.trade adds `const market = "<series>";`,
  `const interval = "<interval>";` and one `const` per parameter, set to
  its argument (`fastLen`, `slowLen`, `size` here). It also gives `started`
  (the unix second the run started) to a program that reads it.
- With no `uses` line a strategy gets `uses { bars: "^1", paper: "^1" }`
  (plus `sources` and `pool` when it reads them). Write your own first line
  to trade a perp: `uses { bars: "^1", funding: "^1", perp: "^1" }`.
- Intervals: <!-- generated:intervals -->
`5s`, `15s`, `1m`, `5m`, `15m`, `1h`, `4h`, `1d` (5s and 15s on a pool, built from its swaps, or on a perp, built from its recorded order book).
<!-- /generated:intervals -->

What a strategy may read and do on ftw.trade, every order and setting,
worked examples and pitfalls: references/strategy-guide.md in
`ftw-strategies` (the same text `get_strategy_guide` returns).

## Modules: what a strategy can import

A strategy's `uses` line names what it reads and does: market data
(`bars`, `pool`, `feed`, `funding`, `book`, `sources`), orders and books
(`paper`, `perp`, `lp`, `wallet`, `funds`, `husher`, `poll`), protocols
(`aave`, `morpho`, `pendle`, `derive`, …) and the `earn` router, each as
`name: "^1"`. After it, `import "std/erc20";` brings in a std contract
module and `import "mylib";` one of your libraries.

- `list_modules { kind?, chain?, search? }` lists every module with the
  line that imports it; `get_module { id }` gives one whole: its reads,
  actions, markets, the series to backtest it on, how far back its history
  goes, and an example that compiles (a ready `source` for save_strategy).
- references/modules.md is the same list as Markdown; people browse it on
  Developers → Modules, where "New strategy from this" saves an example.
- A name the uses line does not declare is refused with the line to add,
  and a mistyped one with the module it may mean.

For a protocol, copy `get_module { id }`'s `start` and read its `skill`.
`get_module_help { id, member? }` lists every declared native read/action and
imported helper with signatures, argument types, units, scope and docstrings.
The editor uses the same native descriptions for hover and signature help.
Help is available before writing a strategy; there is no runtime
`protocol.help()` method. Unknown methods are refused.

The binding and the helper library have different jobs:

| Layer | What makes it available | Responsibility |
|---|---|---|
| Native protocol methods, such as `aave.borrow(...)` | The host registers a capability/source declaration; the program enables it with `uses { aave: "^1" }` | Read/account/action signatures and host dispatch. On the Rust protocol pattern, the crate's `Protocol::surface()` owns this declaration and its descriptions. |
| Source helpers, such as `healthBorrowRoom(...)` | The host supplies the source; `import "protocols/aave";` loads it | Pure Arcana calculations, called by their bare names. Comments document those functions; imports do not add native money actions. |
| Protocol execution | An actual driver consuming that binding | Obtain the requested observations, apply the protocol's checks, build approved calls and reconcile outcomes. A declaration alone supplies none of these effects. |

The Rust/WASM interface moves serialized observations, requests and results
between the host and the binding; arbitrary public Rust functions are not
automatically callable from Arcana. A native action's declared arguments
are the public contract, even when its underlying Rust type has more fields.
Use the module catalogue to author calls and the protocol skill to stage them.
Several actions are not one atomic transaction, and a paper transition does
not confirm a live fill. See `ftw-protocols` for the testing and settlement
workflow. Other hosts must register and implement their own advertised native
operations; ftw.trade's `get_module` does not describe an external host.

## Dependent actions and recovery

Read references/action-recovery.md before composing dependent money actions.
Discover each primitive with `get_module_help { id, member? }` and the protocol
skill. Put the next action inside the prior action's `.onFill`; adjacent
statements, including `await` statements, do not establish a success
dependency. Give every forward and recovery leg its own `.onRevert` and
`.onAbort` policy. Several transactions are not atomic, and a failed unwind
leaves confirmed inventory to reconcile.

`outcome.args.<field>` reads the current action's original evaluated argument,
captured from its decision rather than recomputed at settlement. A capability
manifest selects these fields with `evaluated`; contract parameters and
built-in evaluated fields are also captured. The compiled action's
`typed_args` gives the exact names; `program.outcomeArgsSites` lists consuming
sites. Static unevaluated arguments are unavailable. Pass a parent
output into a child's declared argument before using it in that child's
recovery; outer `fill`, `result` and `progress` are not implicit captures.
The external-host `requestRun` API retains these values and invocation IDs
across restart. A raw reporting host supplies and validates its own original
captures. A timeout, unknown send or stuck order remains unresolved: retain
its identity and resources until authoritative reconciliation.

The guide includes a runnable isolated three-leg scenario and lifecycle tests.
ftw.trade's multi-book paper runner reports outcomes for native onchain
protocol actions and market spot actions (`paper.buy`, `buyPct`, `sell`,
`sellPct`). Outcome handlers on its other book/service actions are refused
at strategy load. In same-bar protocol continuations, account/source reads
retain the bar's recorded observation; use `fill` and `outcome.args` for
the action's actual output and captured input.
The CLI does not execute it, and this syntax does not install live settlement
handlers in ftw.trade's protocol followers. `.t.arcn` tests declared inputs
and configured paper outcomes; use the embedding API for delayed external
outcome and request-recovery tests. Users can compose primitive actions in a
strategy or write their own host tools. Flow helper libraries remain pure
and cannot hide effectful action chains.

## Build your own libraries and tools

Read references/extending.md when building reusable helpers, embedding the
language in another application, or integrating an external model. It gives a
tested library wrapper and a runnable model-to-strategy example.

On ftw.trade, use `save_library { name, source, description? }` for pure
functions and contract interfaces. Save dependencies first; a library may
write `import "features";` or import a standard helper. The complete closure
has at most 15 unique libraries. Shared dependencies load once; cycles,
missing imports and conflicting function names are refused. Functions share
one unqualified namespace, so prefix public names. A library declares no
`uses`, metrics, triggers or effects; pass observations into helper arguments
so the platform can discover the strategy's data subscriptions.

Use `test_strategy { source, tests }` or native `test_strategy { source, testFlow }`
with a wrapper that imports and calls
the library. Saving an unused helper does not typecheck every possible call.
Then check and backtest a consuming strategy before running it.

## Native strategy and library tests

Prefer `.t.arcn` for new test files. Shared Flow declarations/imports live
outside named `test "behavior" { ... }` blocks; declarations inside a case
are isolated from other cases. `setup { ... }` shares fixture inputs and
settings, and a case can override them. Assertions use ordinary Flow
expressions, so imported math, sizing and risk helpers are tested directly.

```text
fn size(liquidity) { return min(liquidity * 0.001, 250); }
setup { bars = 3; }
test "size respects the ceiling" {
  assert always(size(1000000) == 250);
}
```

`source bars.close("check", "1h") = [100, 101, 102];` supplies an exact
read's observations. A fixture can also use `{ constant: 100 }`, ramps,
steps, sine or a seeded walk. `runs = 30;` reruns generated paths with
reproducible seeds. For randomized property tests, use
`fuzz = { trials: 100, seed: 42 };` with `{ random: { min: 1, max: 1000 } }`,
`{ integer: { min: 0, max: 250 } }` or
`{ choose: { values: [0, 1, 100, na] } }` source fixtures. `assert always(...)`
checks an invariant on every decided bar of every trial. Trials start fresh;
failures retain the seed, one-based trial and assertion location. Keep the
fixtures and add `trial: 7` to the same `fuzz` record to rerun the original
seventh trial directly. `fuzz` and `runs` cannot be combined. Random numeric
generators deliberately sample endpoints, so use them to explore boundaries
rather than model a return distribution. Test missing observations and warmup explicitly.
`assert at(2, close == 102);`, `assert always(...)`, `ever`, `never`, and
`last` check series; a bare assertion checks the last bar. An assertion
with no decided bars is refused. `expect fires(aerodrome.swap, { at: 3,
count: 1, args: { amountIn: 100 } });` checks the evaluated decision.
Use `warns`, `refuses`, `metric`, `fills`, `reverts`, `paused` for their
respective compiler, broker and invariant expectations.

For `test_strategy { source, testFlow }` or `{ id, testFlow }`, the native
document supplies fixtures, settings and assertions for the selected
strategy. Keep strategy declarations in `source`; replacement code in
`testFlow` is refused. `filter` selects case names by a literal substring
and refuses zero matches. Give one format: `testFlow` or legacy `tests`.
The report includes failing test-document locations. Native test calls do
not save tests on the strategy; `save_strategy.tests` still takes case objects.
For an ftw source-bound pure-helper case without a price fixture, set
`paper = false;` to disable the host's default paper broker. Self-contained
pure `.t.arcn` cases need no broker unless enabled. Spot cases can use exact
keyed bar inputs or OHLC shorthand; the case's declared representation prices
the broker, and another market's observations cannot price its fills.

Locally, `ftw-trade local test path/to/tests` discovers `.t.arcn` files;
`--source strategy.arcn` binds a fixture-only suite to an exact strategy.
Without `--source`, a fixture-only `strategy.t.arcn` automatically tests its
sibling `strategy.arcn` when present. A file containing ordinary Flow code
remains self-contained; absent companions allow pure assertions, while
unresolved names and companion read errors are refused.
The standalone `arcana test path/to/tests --host host.json` uses your
own host. Both support `--filter` and `--json`. In JavaScript,
`parseTestFlow` parses, `checkTestFlow` checks without replay, and
`testFlow` executes against the same compiler and replay engine as legacy
suites. See references/testing.md for the complete
syntax and limits. Declared inputs and emitted actions establish test
behavior; protocol fills require the binding's simulation and settlement gates.
`get_module { id }` returns your library source and dependencies;
`get_module_help { id, member? }` shows its declared function signatures and
adjacent comments. Follow dependency module IDs to explore their helpers.

Saving a strategy pins all dependency sources, including reached standard
libraries. Tests by saved strategy ID and backtests use those pinned sources;
draft tests use current libraries. `list_libraries` shows direct and transitive
consumers and whether each has the current closure. Save affected strategies
again to adopt changes. Unresolvable current dependencies return explicit
problems; a saved strategy retains its earlier code.

For project tools with IO, use the source and TypeScript action workflows in
`ftw-sources` and `ftw-scripts`; a `.arcn` import cannot install an arbitrary
JavaScript, Python or Rust callback. In an external application,
`@arcana/core` lets its host declare typed sources, capability manifests
and integrations. The application implements and authorizes the consumers;
declaring a manifest alone does not create an execution path.

For external ML, record the model/feature identity, input as-of time and
prediction availability time. Feed predictions only when they became
available, guard unknown or stale scores, and replay recorded outputs. Model
loading, training and remote inference belong in the host. Retain the
compiled program and incremental run for repeated evaluation; select cadence
and measure inference, transport, evaluation and execution separately.
ftw.trade's configured strategy bars still determine its decision cadence.

Read references/performance.md (also
[Performance and latency](https://arcn.ftw.trade/docs/performance)) when
choosing a latency-sensitive host or evaluating speed claims. Native Rust
embeds the same core; npm uses public WASM with JSON transport; ftw.trade's
hosted strategies still decide on closed bars. QuickJS TypeScript extensions
are a separate path. Retain compiled runs, bound state/queues/outputs, and
measure p99 across data delivery, evaluation, ML refits, durable intent, risk,
signer RPC and venue acknowledgement/settlement. Built-in `ml.*` fitting runs
synchronously even when a steady prediction is cheap. Whole-second evaluator
timestamps are not precision timers; preserve actual observation/prediction
availability separately from source time. A faster backend grants no new
authority and never removes execution safety checks.

## The language in one page

- **Values**: numbers, conditions (`true`, `false` or `na`), text,
  records `{ a: 1 }`, lists `[1, 2]`, and a series of each. Every `const`
  is a series, one value per bar; `x[1]` is the previous bar's value.
- **`na`** means "not known yet" (an indicator warming up, a division by
  zero). It spreads through arithmetic and comparisons, `!na` is `na`, and
  a trigger never fires on `na`. Test with `isNa(x)`, replace with
  `nz(x, d)`.
- **Expressions**: `+ - * / %`, comparisons (they do not chain), `&& || !`,
  `c ? a : b`, `if (c) a else b`. Durations `seconds(n)`, `minutes(n)`,
  `hours(n)` are seconds.
- **Functions**: `fn mid(a: Series<number>, b: Series<number>):
  Series<number> { return (a + b) / 2; }`, lambdas `(x) => x * 2`, and
  `fold(series, seed, (st, x) => next)`, the one way to carry state.
  Guard every fold step: `isNa(x) ? st : …`.
- **Prelude indicators**: `sma ema wma rma hma dema tema vwma vwap rsi
  stoch cci mfi obv roc momentum macd macdSignal macdHist bbUpper bbLower
  stddev zscore wilderAtr trueRange adx diPlus diMinus supertrend sar
  highest lowest rolling_sum cum linreg corr barssince valuewhen pivothigh
  pivotlow` and more (references/language.md, "The prelude").
- **Trained models**: `ml.linear ml.ridge ml.lasso ml.elasticNet
  ml.logistic ml.svm ml.svr ml.knn ml.boost ml.boostClassifier`, each `(y, [x1, x2, …], window: n,
  horizon: h, retrain: r, retrainOn: cond, …settings)`. The model learns `y`
  `horizon` bars after the predictors, refits every `retrain` rows (0: once)
  or when `retrainOn` holds, and answers a prediction per bar, the same in a
  backtest and live (references/language.md, "Trained models").
- **Market profile**: `tpo.poc tpo.vah tpo.val tpo.vpoc tpo.vpocShift
  tpo.poorHigh tpo.poorLow tpo.singleAbove tpo.singleBelow tpo.nakedPocAbove
  tpo.nakedPocBelow` and more, each `(high, low, close, volume, t, window: n,
  …)`: the last whole day's (or week's, or the forming session's) TPO
  levels, the same in a backtest and live (references/language.md, "Market
  profile").
- **Metrics**: `metric("name", value)` publishes a value per bar. Publish
  what the strategy decides on; it is how you see why it did or did not
  trade.

## Triggers

- `when (cond) { … }` fires on the bar `cond` turns true, once per crossing.
  Entries belong here.
- `while (cond) { … }` fires at every bar `cond` holds. Only for actions
  meant to repeat, with a guard that turns false (warning FS0902 catches a
  `while` that buys with no guard).
- Combinators: `crossover crossunder rising falling changed once sustained
  cooldown hysteresis every("1d") daily during`.
- An `if` inside a body is read at the bar the trigger fires.

## Orders on ftw.trade

- Spot (a pool or a feed): `paper.buy({ quote })`, `paper.buyPct({ pct })`,
  `paper.sell({})` (the whole position), `paper.sellPct({ pct })`, and
  resting `paper.buyLimit/buyStop({ quote, at })`,
  `paper.sellLimit/sellStop({ at })`.
- Perps: `perp.long({ usd })`, `perp.short({ usd })`, `perp.close({})`.
- Perp maker orders: `perp.bid({ usd, at })`, `perp.ask({ usd, at })` rest post-only at `at` (any expression) until a later bar trades through, filling at `at` with the maker fee; `perp.cancel({})` pulls them. One per line, replaced by its next order. A bid must sit under the bar's close (an ask over it) or it reverts, and the book snapshot a bar reads can be older than its close, so clamp it: `at: min(book.bid(market, interval), close * 0.9999)`. On a live project's wallets a run over its one perp rests them on the venue; a run over more markets rests them on a paper project's wallets only (starter "Maker quotes").
- Taking only what the book holds near the middle: `maxImpactBps` on `perp.long`, `perp.short`, `perp.target` and `perp.hedge` (D1645) cuts the order to what rested that near in the recorded book (live: an IOC limit that far from the live middle).
- Over more than one market: `lp.range`, `lp.close`, `perp.hedge`,
  `perp.target`, `funds.toPerp`, `funds.toWallet`.
- Protocols, directly (D1342), each named by its id with the same surface
  as every protocol of its kind. Lending markets (`aave`, `spark`,
  `compound`, `kamino`): `<id>.supply`, `<id>.withdraw`, `<id>.borrow`,
  `<id>.repay` on the market read with `<id>.supplyApy("chain:asset",
  interval)`; books `<id>.supplied()`, `<id>.borrowed()`,
  `<id>.healthFactor()` (Spark and Compound supply only). Vaults (`morpho`,
  `fluid`, `euler`, `yearn`, `sky`, `ethena`): `<id>.deposit`,
  `<id>.withdraw` in the vault read with `<id>.apy("chain:vault",
  interval)`; book `<id>.deposited()`. Euler, Yearn and Morpho also take a
  vault by its address, `base:0x…`, probed for the brand on save (D1346). One lending
  market and one vault per strategy; `get_strategy_guide` lists every market.
  Husher: `husher.toPerp`, `husher.toWallet`, landing `delayMinutes` later;
  `husher.inFlight()`. A move that cannot be done reverts with why, never
  stops the run. On a live project's wallets each protocol's live markets
  follow the books on chain (Kamino from each wallet's Solana address, Sky
  through its USDC PSM, Ethena through Curve and sUSDe's cooldown); Husher
  stays on paper.
- Fixed rates (D1344): Pendle PTs, a market named by its address
  (`pendle.impliedApy("ethereum:0x…", interval)`, `pendle.ptPrice(…)`, or a
  text parameter), `pendle.buy`, `pendle.sell`, book `pendle.held()`. Live
  through Pendle's router where the market's SY takes USDC (D1347). Pairs
  with a lender: buy the PT while its implied APY beats
  `aave.supplyApy(…)` by a margin, back to Aave when the gap closes. ftw.trade's
  `protocols` stream sends every market Pendle's factories make
  (`where: { protocol: "pendle", kind: "CreateNewMarket" }`), and `prices`
  each new value of a market's feeds (`where: { feed: "pendle-ethereum-0x…-implied-apy" }`).
- Options (D1356): Derive's options on ETH, BTC, SOL, HYPE and eight more
  underlyings, on paper and on a live project's wallets (D1423).
  `derive.index("eth", interval)`, `derive.iv7d(…)` and `derive.iv30d(…)`
  (at-the-money volatility, percent) read the underlying;
  `derive.buy({ type: "call" | "put", days, strike?, usd | units })`,
  `derive.sell({ … })` (cash-secured: a put locks its strike) and
  `derive.close({})`; books `derive.value()`, `derive.delta()`,
  `derive.positions()`. Options settle at expiry against the index. See
  the ftw-protocols skill's `references/derive.md` for strategies.
- Intents (D1343): `earn` routes an asset instead of naming a protocol.
  `earn.apy("usdc", interval)` is the best rate across the registry's live
  USDC markets (`usdc:all` adds paper-only ones, `usdc:aave,morpho` keeps
  those, `usd` is any dollar); `earn.deposit`, `earn.withdraw`,
  `earn.rebalance({ minGainPct?, costUsd? })`; books `earn.deposited()`,
  `earn.heldApy()`. Use a protocol's capability to choose the protocol, earn
  to choose the asset; not both in one strategy.
- A size is a number or a parameter **as written**; a computed size is
  refused when the strategy is saved unless the program publishes it as a
  metric (`const usd = 0 - derive.delta(); metric("hedge_usd", usd);` then
  `perp.target({ usd })`), so each step hands back the value it traded.
  Size by the book with `paper.buyPct`.
- The book: `inPosition()`, `avgEntry()`, `realizedPnl()`,
  `unrealizedPnl()`, `timeInPosition()` (seconds). On a perp they read
  `na`.

## Tapes: sources and swaps

A tape is a stream of typed events, read with `map`, `window(t, "15m")`,
`resample(t, "1h")` and the reducers `count sum mean first last distinct`.
Each event lands in the bar that contains it.
A program that reads sources says so in its `uses` line, as `uses { bars: "^1", sources: "^1", paper: "^1" }`, and a source named `f3-btc-spot` is read as `sources.f3_btc_spot()` (`-` written `_`).

```text
const calls = sources.kol_calls();            // a typed source (ftw-sources)
const s = pool.swaps();                        // every swap of the run's pool
const bought = sum(window(map(s, (e) => match (e.side) { buy => e.quote, sell => 0 }), "15m"));
metric("bought_15m", bought);
when (count(window(calls, "15m")) >= 3 && !inPosition()) { paper.buy({ quote: 100 }); }
```

A number field is a number, a boolean 1 or 0, every other kind text (JSON
as compact text); an optional field left out reads 0 or `""`. Text can be
compared (`e.token == "0x…"`) but never sizes or names an order.

## Enums, match and phases

```arcana
enum Regime { hot, quiet, normal }
const close = bars.close(market, interval);
const vol = stddev(change(ln(close)), 24);
const regime = vol > 0.02 ? Regime.hot : (vol < 0.005 ? Regime.quiet : Regime.normal);
metric("size", match (regime) { hot => 0, quiet => 1, _ => 0.5 });
```

`match` must cover every variant or end in `_`. A sum-type source's `case`
and a swap's `side` are enums. `phases Trade { phase Flat { … } phase Long
{ … } }` is a state machine whose `when … => Phase` moves between phases
at most once a bar.

## Libraries

`save_library { name, source }` keeps `fn` definitions (and contract
interfaces) under a name; `import "name";` after the `uses` line gives a
strategy its functions. A strategy keeps the library version it was saved
with until it is saved again (`list_libraries` says who is behind).

## Contract calls and the std modules

A program can call any contract function:

```arcana
uses { bars: "^1", usdc: ERC20("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48") }
import "std/erc20";
const allowed = usdc.allowance("0x00000000000000000000000000000000000000aa", "0x91ae00000000000000000000000000000000abcd");
metric("allowed", allowed);
metric("close", bars.close(market, interval));
when (allowed < tokenUnits(100, 6)) {
  usdc.approve({ spender: "0x91ae00000000000000000000000000000000abcd", amount: tokenUnits(1000, 6) });
}
```

- `uses { v: abi("Name", "0x…", ["sig", …], { Struct: "(…)" }) }` imports
  the listed functions of one contract; `interface X = abi([...])` declares
  them without an address and `uses { t: X("0x…") }` binds it.
- Arguments are passed by name. A tuple or struct is a record with exactly
  its fields, an array a list; `as` renames an overload; `from`,
  `retries`, `backoff` and `value` are call options. A `view returns (T)`
  is read like a stream with literal arguments.
- The std modules the compiler offers:

<!-- generated:arcana-std -->
**`import "std/erc20";`**: the ERC-20 token standard (EIP-20), as an interface a program binds to a token's address: `import "std/erc20"; uses { usdc: ERC20("0x…") }`. Parameters that the standard calls `from` are named `owner` here, because `from:` is a call option; the selector is the standard's own.

- `ERC20`: `transfer(address to, uint256 amount)`, `approve(address spender, uint256 amount)`, `transferFrom(address owner, address to, uint256 amount)`, `balanceOf(address who) view returns (uint256)`, `allowance(address owner, address spender) view returns (uint256)`, `totalSupply() view returns (uint256)`, `decimals() view returns (uint8)`
- `fn tokenUnits(amount: number, decimals: number): number`

**`import "std/erc4626";`**: the tokenized vault standard (EIP-4626), as an interface a program binds to a vault's address: `import "std/erc4626"; uses { vault: ERC4626("0x…") }`. A vault is also an ERC-20 for its shares; import "std/erc20" and bind the same address as an ERC20 to move shares.

- `ERC4626`: `deposit(uint256 assets, address receiver)`, `mint(uint256 shares, address receiver)`, `withdraw(uint256 assets, address receiver, address owner)`, `redeem(uint256 shares, address receiver, address owner)`, `asset() view returns (address)`, `totalAssets() view returns (uint256)`, `balanceOf(address who) view returns (uint256)`, `convertToAssets(uint256 shares) view returns (uint256)`, `convertToShares(uint256 assets) view returns (uint256)`, `maxWithdraw(address owner) view returns (uint256)`, `maxRedeem(address owner) view returns (uint256)`, `previewDeposit(uint256 assets) view returns (uint256)`, `previewRedeem(uint256 shares) view returns (uint256)`

**`import "std/math";`**: Exact monetary and signed-counter arithmetic. Results are decimal text, not floats; use u256Cmp/i64Cmp for ordering and explicit display conversions.

- `fn uint256(value)`
- `fn long(value)`
- `fn parseUnits(amount, decimals)`
- `fn formatUnits(amount, decimals)`
- `fn mulDiv(a, b, denominator)`
- `fn mulDivUp(a, b, denominator)`
- `fn basisPoints(amount, bps)`
- `fn basisPointsUp(amount, bps)`

**`import "std/vwap";`**: the reads a VWAP trader makes around the VWAP itself: where price sits in σ, the week's and the month's anchors, whether the day is balanced or trending, and a failed push into a band. The VWAPs are the prelude's: periodVwap (a session's, reset each period), anchoredVwap (from any bar) and their σ, periodVwapDev and anchoredVwapDev.

- `fn weekStarts(t: Series<number>): Series<bool>`
- `fn monthStarts(t: Series<number>): Series<bool>`
- `fn vwapZ(price: Series<number>, vw: Series<number>, dev: Series<number>): Series<number>`
- `fn vwapDrift(vw: Series<number>, dev: Series<number>, anchor: Series<bool>): Series<number>`
- `fn ridingAbove(close: Series<number>, vw: Series<number>, dev: Series<number>, len: number): Series<number>`
- `fn ridingBelow(close: Series<number>, vw: Series<number>, dev: Series<number>, len: number): Series<number>`
- `fn failedPushUp(high: Series<number>, open: Series<number>, close: Series<number>, upper: Series<number>, within: number): Series<bool>`
- `fn failedPushDown(low: Series<number>, open: Series<number>, close: Series<number>, lower: Series<number>, within: number): Series<bool>`

Each module has a `.test.yaml` suite beside it in `arcana/std/`, run in CI.
<!-- /generated:arcana-std -->

**On ftw.trade today**, a contract call compiles, and a backtest or a test
records each call with its arguments as evaluated at its bar (a test can
expect `{ fires: usdc.approve, args: { amount: 1000000000 } }`). A running
strategy does **not** send contract calls from project wallets yet: it
carries out paper, perp, lp and funds orders only. Use contract calls to
design and test; tell the person they will not execute on ftw.trade until
that is built.

## Exact integers (u256)

A number is a 64-bit float, exact only to 2^53. The `u256` functions carry
an unsigned 256-bit integer as a word of decimal digits:
`u256(x)`, `u256Add`, `u256Sub`, `u256Mul`, `u256Div`, `u256Mod`,
`u256Min`, `u256Max`, `u256Cmp(a, b)` (-1, 0, 1), `u256Units(amount,
decimals)` (exact base units, so 1.1 is eleven tenths) and
`u256Number(x, decimals)` (back to a number, for charts and thresholds,
never for an amount you send). Overflow, underflow and division by zero are
`na`, and an action with an `na` argument is refused before it is signed. A
contract-event source's `exact` field holds integers as such words.

## Invariants (risk limits)

```arcana
const close = bars.close(market, interval);
when (crossover(close, ema(close, 20)) && !inPosition()) { paper.buy({ quote: 100 }); }
when (inPosition() && crossunder(close, ema(close, 20))) { paper.sell({}); }
invariants {
  drawdown() <= pct(5);
  dailyLoss() <= 50;
  trades.perDay() <= 4;
  on_breach: pause;
}
```

While a bound is breached (or unreadable) every entry is refused and the
refusal recorded; a sell is never refused. `flatten_and_pause` also sells
everything when a drawdown or daily-loss bound breaks. Backtests, tests and
live runs enforce the same cage; a backtest lists refusals under `paused`,
and a test expects one with `{ paused: "paper.buy", at?, by?: "drawdown" }`.

## Tests

`test_strategy { id | source, tests }` feeds a strategy bars you write and
checks what it does; saved tests (`save_strategy { tests }`) run on every
save, and `run_strategy` refuses while one fails.

```json
{ "name": "buys the step up once, never sells", "bars": 80,
  "ohlc": { "steps": [[0, 100], [30, 130]] },
  "expect": [
    { "fires": "paper.buy", "at": 30, "args": { "quote": 100 } },
    { "fills": "paper.buy", "at": 30 },
    { "fires": "paper.sell", "count": 0 } ] }
```

- Series are lists (`null` is na) or generators: `constant`, `ramp`,
  `sine`, `steps`, `walk` (seeded), each with `na: [bars]`.
- Expectations: `check` (a condition over the program's consts, at a bar or
  `ever | always | never | last`), `fires`, `fills`, `reverts`, `paused`,
  `metric … equals | na`, `warns`.
- `runs: N` repeats a case over N seeded random walks: the way to state a
  rule that must hold in any market ("never more than a fifth of equity
  in").
- A failed `fires … at` says what each part of the condition read at that
  bar.

## Diagnostics and warnings

Errors stop a save. Warnings compile but usually mean a bug: FS0901 a
condition never true (`close > highest(close, 20)`; compare with
`highest(close, 20)[1]`), FS0902 a `while` that buys every bar, FS0903 an
unused const, FS0904 a divide by literal 0, FS0905 `bps(n)` used as a number
(it is n, not n / 10000). A backtest also lists under
`quiet` each trigger that never fired and why.

## Coming from Pine Script

`get_pine_translation { pine }` translates a Pine strategy into Arcana
and compiles it, so what comes back either compiles or says why.
