Types

Signal, Direction, TargetPosition, OrderAction, Decision

All core types are frozen dataclasses with validation at construction.

Direction

python
class Direction(str, Enum):
    Increase = "increase"
    Decrease = "decrease"
    Flatten  = "flatten"

Asset-class-neutral direction. The executor translates to venue-native side:

  • Equity: Increase → buy long; Decrease → sell / short
  • Options: Increase → long the contract; Decrease → short
  • Prediction: Increase → buy Yes; Decrease → sell Yes / buy No

Property:

  • sign: int: returns +1 for Increase, -1 for Decrease, 0 for Flatten

Urgency

python
class Urgency(str, Enum):
    Immediate = "immediate"   # marketable, cross the spread
    Patient   = "patient"     # limit orders, VWAP slicing
    Passive   = "passive"     # post-only

Signal

python
@dataclass(frozen=True)
class Signal:
    # Required
    market_id: str
    direction: Direction
    confidence: float                  # [0, 1]
    expected_edge_bps: float           # bps per $1 over horizon
    expected_vol_bps: float            # std dev in bps
    horizon: timedelta

    # Optional. attribution / lifecycle
    strategy_id: str = ""
    reason: str = ""
    features: dict[str, float] = field(default_factory=dict)
    generated_at: datetime | None = None
    ttl: timedelta | None = None       # default = horizon

    # Optional. sizing hints
    preferred_notional_usd: float | None = None
    max_notional_usd: float | None = None

    # Optional. execution hint
    urgency: Urgency = Urgency.Patient

    # Optional. Carver scaled forecast, in [-20, 20]
    carver_forecast: float | None = None

    # Optional. free-form tags
    tags: tuple[str, ...] = ()

Validation (raises ValueError)

  • confidence must be in [0, 1]
  • expected_vol_bps must be >= 0
  • horizon must be strictly positive
  • carver_forecast, if set, must be in [-20, 20]

Properties

  • effective_ttl: timedelta: returns ttl if set, else horizon
  • sharpe: float: returns expected_edge_bps / expected_vol_bps (0 if vol is 0)

Constructor helpers

All attached to Signal via signal_helpers.py:

python
Signal.increase(market, edge_bps=30, horizon="1d")
Signal.decrease(market, edge_bps=20)
Signal.flatten(market, reason="risk exit")

Signal.from_score(market, score=-2.3, edge_per_stdev=15)
Signal.from_probability(market, p_true=0.65, current_price=0.52)
Signal.from_classifier(market, model_output=(0.28, 0.72))
Signal.from_forecast(market, forecast_return=0.015, forecast_vol=0.08)
Signal.from_carver_forecast(market, forecast=15, instrument_vol=0.20)

All accept string horizons: "3d", "1h", "15m", "2w".

TargetPosition

python
@dataclass(frozen=True)
class TargetPosition:
    market_id: str
    target_notional_usd: float              # +ve long, -ve short, 0 flat
    urgency: Urgency = Urgency.Patient
    reason: str = ""
    contributing_signal_ids: tuple[str, ...] = ()

Property:

  • direction: Direction: inferred from the sign of target_notional_usd

Decision

Risk check outcome:

python
class DecisionKind(str, Enum):
    Pass   = "pass"
    Reject = "reject"
    Resize = "resize"

@dataclass(frozen=True)
class Decision:
    kind: DecisionKind
    reason: str = ""
    resized_quantity: float | None = None

    @classmethod
    def pass_(cls) -> Decision
    @classmethod
    def reject(cls, reason: str) -> Decision
    @classmethod
    def resize(cls, new_quantity: float, reason: str = "") -> Decision

    @property
    def passed: bool   # True for Pass or Resize

OrderAction

What the executor emits for the risk layer to inspect before submission.

python
class OrderActionKind(str, Enum):
    Place  = "place"
    Amend  = "amend"
    Cancel = "cancel"

@dataclass(frozen=True)
class OrderAction:
    kind: OrderActionKind
    market_id: str = ""
    side: str = ""                      # "buy" | "sell"
    quantity: float = 0.0
    price: float | None = None          # None = market order
    order_type: str = "limit"           # "limit" | "market" | ...
    time_in_force: str = "day"
    urgency: Urgency = Urgency.Patient
    order_id: str | None = None
    reduce_only: bool = False
    venue_hint: str | None = None
    metadata: dict[str, Any] = field(default_factory=dict)

Class methods

python
OrderAction.place(market_id, side, quantity, price=None, order_type="limit", ...)
OrderAction.cancel(order_id, market_id="")
OrderAction.amend(order_id, new_quantity=None, new_price=None)

Context

Injected into Strategy.evaluate() when the strategy opts into the third argument.

python
@dataclass
class Context:
    now: datetime
    feeds: dict[str, FeedData]
    portfolio: PortfolioState | None
    params: dict[str, Any]

Method:

  • exchange(venue_name: str): escape hatch to reach the imperative API of a specific venue from inside a strategy. Use sparingly.

FeedData

python
@dataclass(frozen=True)
class FeedData:
    market_id: str
    price: float
    bid: float | None = None
    ask: float | None = None
    volume_24h: float | None = None
    last_trade_size: float | None = None
    last_trade_is_buy: bool | None = None
    timestamp: datetime | None = None

Properties:

  • mid: float: midpoint when both sides exist, else last price
  • spread: float | None. ask - bid
  • spread_bps: float | None: spread as bps of mid

Method:

  • is_stale(now, max_age) -> bool

PortfolioState

Large read-only dataclass exposing equity, cash, P&L, drawdown, positions, Greeks, per-strategy attribution, risk state. See state docs for the full field list.

Source of truth

All of the above live in horizon/types.py and horizon/context.py. Import them directly or from the top-level package re-exports.

Next