본문으로 건너뛰기

구조

ADR-0015: Decorators on strategy parameters: the source says which parameters a host's optimizer may move

이 페이지는 영어입니다. 하나의 언어로 유지된 사양은 하나의 내용만 말합니다.

Status: accepted (2026-10-23)

Context#

A strategy's parameters are what a sweep or an optimizer varies, but nothing in the source said which ones, or how far. The terminal's Backtests window fixed them in main() (trade.run(ema_cross(9, 21, qty(0.01)))) and showed them as text; the optimizer (trading-terminal/crates/optimizer) took its search space from a separate JSON spec. The person who writes the strategy is the one who knows that fast is worth moving between 3 and 30 and that size is not. The language had only // @param comments, which exist for indicator Config fields and marked constants, are read from raw text by the terminal, and are invisible to the compiler.

Decision#

  • A decorator is part of the syntax, @param(key: value, …) before a parameter of a strategy (language 0.2; @ is a new token). Its values are literals, or the name of another parameter for above / below. @param is the only decorator; any other name is E-DECO-UNKNOWN, and one anywhere but a strategy parameter is E-DECO-PLACE. It is not a general annotation mechanism: adding a decorator takes a new ADR.
  • Keys: min and max (required), step, log, label, group, hint, above, below. The compiler checks them (E-DECO-VALUE, E-DECO-REF) and refuses a program that says lang ">=0.1.0, <0.2.0" (E-VER-FEATURE).
  • The compiler carries it and does nothing else. StrategyParam gains an optional tune (ParamTune) written only when present, so an artifact without decorators is byte for byte what it was; IR 0.1.4 → 0.1.5 because an older reader would drop the field. A decorator never changes what a strategy does.
  • The value stays in main(). The argument the program passes is where the terminal's form starts; the decorator says how far it may move. A strategy can therefore be run as written, edited by hand, or searched.
  • The terminal reads the graph, not the text. The host keeps the StrategyValue (graph and arguments) of a strategy handed to the Backtests window, so it can bind other arguments and translate again without running main() a second time. Rebinding the arguments and translating again, rather than patching the translated definition, matters because some parameters are baked in when a strategy is translated.

Alternatives#

  • Keep // @param comments for strategy parameters. Cheaper, but a comment is not checked by the compiler, cannot be tied to a parameter by anything but position, and is lost by every tool that reads the artifact.
  • A search-space file beside the strategy. What the optimizer's CLI does today; it drifts from the source and has to be kept in step by hand.
  • Ranges as a type (Int<3..30>). Would make the range part of the type system and force every use of the parameter to carry it; the range is advice to a tool, not a property of the value.
  • Attributes on any item (#[...]). More than is needed, and every new attribute is a compatibility surface.

Consequences#

  • The grammar gains one token and one production; the formatter and the highlighter treat @param(...) like any other tokens.
  • The terminal can build a form with a slider, a step and a group per parameter without running the script, and can hand the same ranges to the optimizer.
  • A decorated strategy compiled by an older compiler is refused at lang, not misread.