本文へ移動

仕組み

ADR-0011: T0 Lang 0.2 — strict mode, named books, market signals, and the handler model

これらのページは英語です。仕様は一つの言語で保つことで、一つのことだけを語ります。

Status: decisions 1–4 accepted and implemented (2026-10-18); decision 5 (handlers) accepted as a design, not implemented.

Context#

Strategies had grown past what 0.1 could say: partial closes and pyramiding, several positions on one symbol, state a rule remembers, what to do when an order is turned away, and signals from the footprint, the funding, the open interest, the liquidations and the book. The user asked for a language able to write very complex order-flow strategies, ready for production, without breaking any 0.1 program and without letting a strategy see the future.

Decision#

  1. Versions side by side. 0.2.0 sits next to 0.1.2. A program is compiled as the newest supported version its lang range allows; a 0.1 program is unchanged and a 0.2 construct in it is E-VER-FEATURE. The artifact records the effective version. Hosts accept <0.3.0.
  2. Errors are mandatory only in strict mode. mode "strict"; turns W-UNUSED-RESULT into E-UNUSED-RESULT (also for Option); let _ = expr; discards on purpose; and a strategy that sends orders must say what happens when one is turned away (halt_on_error;, or a rule reading refused(rule), rejected(rule) or last_error(): E-STRAT-UNHANDLED-REJECTION). Without the header nothing becomes an error: 0.1 habits keep working.
  3. Named books. position name; declares an independent position; rule … for name says which one a rule belongs to. The runtime tags every order and lot with its book (side maps in the runtime state, so no struct literal across the workspace changed), allows one order per book per step, and acts on every position of a book for Reduce, Close and Protect. Intent.Add adds an entry to an open book. Strategies that declare no book have one, main, and behave as before: the identity hash of an old strategy does not change (new fields are skipped when empty).
  4. Market signals are features, with a rule for unknown. Footprint figures (the chart's own definitions, tested against aggregation), facts (funding, open interest, liquidations, book), levels of the UTC day and large prints of the tape are signals of the strategy graph. Each is unknown — never zero, never carried — when it was not watched in full, when a hole reaches into it, or when the environment does not carry it. The capability catalogue says where each is served; a backtest refuses, in words and before it runs, a dataset that lacks a stream, and a deployment to live refuses what the live feed does not deliver yet.
  5. Handlers (as built; the design below was simplified). Imperative strategies — on bar(5min), on fill, on order, on timer — keep their state by value and call out only through a ctx that gives what the engine already knows at that instant (closed bars, the footprint, positions per book, the account, declared features) and nothing after it. The compiler lowers each handler to a function (state, ctx) -> Step; the runtime gains an Instance API (load the artifact once, call functions with a budget per call), and TTT implements strategy_core::Strategy on top of it so a backtest, a replay and a demo run the same code. Open questions the implementer must settle: Value is Rc-based, so an instance cannot cross threads — build it on the backtest thread; budget per call versus per run; the cost of on trade, which warns (W-HOT-HANDLER).

Consequences#

  • Existing programs and strategies are untouched; the new surface is additive in the host contract (features 0.1.8, API 0.1.13).
  • Everything in 1–4 is covered by conformance fixtures, unit tests and end-to-end backtests that run twice and compare journals.
  • What is not validated: the GUI with these strategies, derivatives and book on the live feed, and anything against a real venue (strategies stay demo, replay and backtest). See LAUNCH_TODO.md §0 and t0-lang/STATUS.md.

Addendum (2026-10-18): what was built differently, and what was decided since#

  • Handlers are functions the strategy names, not an on … block language: handler bar(5) = on_bar(ctx, px, avg); with state = init();. The market reaches a handler only through the strategy's signals (so there is nothing to clip to the instant); what the strategy holds reaches it through ctx, a handlers.Context (positions of every book, working orders, equity). No on trade: the cost question is moot. Measured: a handler on a one-minute bar over 30 days costs about 8 % more than the same strategy as rules.
  • Modules (import "lib/x.tz" as x;) are one text to the compiler (the program, then its modules, deps first): no IR change, diagnostics remapped to the import line, the language services follow a name into the module that declares it. Orders gained Stop/StopLimit (entries and exits), exits that rest at a price (Limit, Stop, StopLimit on Close and Reduce), OrderOption.PostOnly/ImmediateOrCancel/Expire, and a stop entry carries its own bracket.
  • The IR moved by a patch, 0.1.3 (compatibility policy): an older runtime refuses an artifact that uses Map.
  • Data: funding rate, open interest (and its change) reach a live strategy from the terminal's poll, as the same events a dataset holds; the resting book reaches it from the capture's heat columns, as book frames stamped with their arrival. Neither is offered to a replay or a backtest where it would not repeat or does not exist. The catalogue said a replay served the book while nothing produced the event; it was corrected.
  • Not built: a venue parameter and gamma/spot/other exchanges in strategies (the datasets have no such streams); liquidations and settled funding live (the backend gives aggregated buckets); the book in a replay (the replay would have to hold its clock for the column).