구조
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 astrategy(language 0.2;@is a new token). Its values are literals, or the name of another parameter forabove/below.@paramis the only decorator; any other name isE-DECO-UNKNOWN, and one anywhere but a strategy parameter isE-DECO-PLACE. It is not a general annotation mechanism: adding a decorator takes a new ADR. - Keys:
minandmax(required),step,log,label,group,hint,above,below. The compiler checks them (E-DECO-VALUE,E-DECO-REF) and refuses a program that sayslang ">=0.1.0, <0.2.0"(E-VER-FEATURE). - The compiler carries it and does nothing else.
StrategyParamgains an optionaltune(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 runningmain()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
// @paramcomments 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.