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: StrEnum

Long-only action set.

BUY = 'buy'
SELL = 'sell'
HOLD = 'hold'
class trader.strategies.base.Signal(action, reason, indicators=<factory>)[source]

Bases: object

A 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])

action: Action
reason: str
indicators: dict[str, Decimal | float]
class trader.strategies.base.Strategy(*args, **kwargs)[source]

Bases: Protocol

A 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.

Parameters:
Return type:

Signal