trader.strategies.base module¶
The strategy interface every trading rule implements.
Strategies emit only a direction. Position sizing, stops, and capital live outside them, so comparing two strategies compares their signals rather than their money management.
- class trader.strategies.base.Action(*values)[source]¶
Bases:
StrEnumLong-only action set.
- BUY = 'buy'¶
- SELL = 'sell'¶
- HOLD = 'hold'¶
- class trader.strategies.base.Signal(action, reason, indicators=<factory>)[source]¶
Bases:
objectA strategy’s decision, with enough context to explain it later.
reason and indicators are not decoration: they are what gets written to the decisions table, and what a later LLM reads when asked why a trade happened.
- Parameters:
action (Action)
reason (str)
indicators (dict[str, Decimal | float])
- reason: str¶
- indicators: dict[str, Decimal | float]¶
- class trader.strategies.base.Strategy(*args, **kwargs)[source]¶
Bases:
ProtocolA trading rule over a bar series.
- id: str¶
- backtestable: bool = True¶
Whether BacktestEvaluator may gate this strategy.
run_backtest calls evaluate() once per bar, which is free for a rule strategy and ruinous for one that makes a network call: a 400-bar window would be 400 model calls per symbol per cycle, and the historical news such a replay would need does not exist. Derived from the strategy rather than configured, so it cannot be misconfigured.
- warmup_bars()[source]¶
Bars needed before this strategy can produce a defined signal.
- Return type:
int
- evaluate(bars, position)[source]¶
Decide, given bars up to and including the decision bar.
The caller guarantees bars contains nothing after the decision bar, so a strategy cannot look ahead even by accident.
position is the holding itself rather than a flag, because a strategy deciding whether to exit needs to know what it is exiting: entry price, size and unrealized P/L. A bool made every sell decision blind to whether the position was up or down. Rule strategies that only care whether a position exists ask position is not None.
Beware False is not None — it is True. That is why the bool was removed rather than kept alongside this parameter: one representation of “is there a position”, so a caller cannot pass the wrong kind of truth.