Types
Signal, Direction, TargetPosition, OrderAction, Decision
All core types are frozen dataclasses with validation at construction.
Direction
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+1for Increase,-1for Decrease,0for Flatten
Urgency
class Urgency(str, Enum):
Immediate = "immediate" # marketable, cross the spread
Patient = "patient" # limit orders, VWAP slicing
Passive = "passive" # post-only
Signal
@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)
confidencemust be in[0, 1]expected_vol_bpsmust be>= 0horizonmust be strictly positivecarver_forecast, if set, must be in[-20, 20]
Properties
effective_ttl: timedelta: returnsttlif set, elsehorizonsharpe: float: returnsexpected_edge_bps / expected_vol_bps(0 if vol is 0)
Constructor helpers
All attached to Signal via signal_helpers.py:
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
@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 oftarget_notional_usd
Decision
Risk check outcome:
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.
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
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.
@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
@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 pricespread: float | None.ask - bidspread_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.