Skip to content

API reference

The full public surface of strix. Signatures, parameters, and behavior — pulled directly from the source where docstrings exist, supplemented by hand where they don't.

The reference is generated by mkdocstrings when the docs site is built. Below each section is the directive that drives it; if you're reading the raw Markdown, those render as full signature blocks on the built site.

Top-level functions

strix.init

Start a NEW session.

By default, if transport has an open prior session it is closed and the new session is seeded by carrying forward its open orders and positions.

Pass initial_state=InitialState(...) to override carry-forward with an explicit snapshot. The override is atomic: both positions and open orders are taken from initial_state. InitialState() with no arguments starts the session completely flat.

risk configures pre-trade risk checks for this session. Defaults to no limits.

config configures session-wide behavior policies (how to react to invalid executions, missing market data). Defaults to :class:SessionConfig with all-default values. Each init call receives a fresh config — the prior session's config is NOT carried forward (re-supply it if you want the same behavior).

market_data is the :class:MarketDataAdapter used for pre-trade checks that need a live price (notional cap on market orders, price thresholds). Required if any such check is configured in risk AND you use market orders; otherwise optional. Not persisted — supply on every init / resume call.

When transport is omitted, a fresh InMemoryTransport is created for this session — no cross-call persistence. Hold a reference to an InMemoryTransport instance across calls if you want carry-forward without disk persistence.

Accepts transport, initial_state, risk, config, and market_data — all keyword-only. See SessionConfig for the config argument's shape; the rest are covered in the dedicated guides linked elsewhere on this page.

strix.resume

strix.resume(
    *,
    transport: Transport,
    market_data: MarketDataAdapter | None = None,
) -> Session

resume accepts transport and market_data. initial_state and risk are not valid here — they apply only to init. Resumed state (including the risk policy in effect when the session was started) is reconstructed from the event log. market_data is a live source, not persisted, so it must be re-supplied on every resume call where Phase 2 risk checks need a quote.

Continue the existing open session in transport.

market_data is the :class:MarketDataAdapter used for pre-trade checks that need a live price. Not persisted — supply it on every resume call where you need such checks.

Raises NoActiveSessionError if there is no open session.

strix.order

Context-managed order submission.

Records the order in PENDING_NEW, runs pre-trade risk checks, yields the Order for broker submission, then transitions to NEW on clean exit or REJECTED (with reject_reason) on exception.

When order_id is omitted, the SDK generates a UUIDv7 — collision is astronomically improbable. When the caller supplies an explicit order_id that's already registered in the active session's order book, raises :class:StrixDuplicateOrderIdError (also a :class:ValueError for back-compat). The order book retains both open and terminal orders for the lifetime of a session, so an id is not reusable once registered, even after the order ends. Across sessions, carry-forward brings open orders' ids into the new session; terminal orders do not carry forward, so their ids become reusable.

strix.cancel

@contextmanager
strix.cancel(*, order_id: str) -> Iterator[None]

Context-managed cancel submission.

Transitions the order to PENDING_CANCEL on entry, yields for the caller to submit the cancel to their broker, then transitions to CANCELLED on clean exit. On exception, the order is reverted to its prior status and a CancelAttemptFailed event is emitted for audit.

The order must be in a cancellable status (PENDING_NEW, NEW, or PARTIALLY_FILLED). Cancelling a terminal or already-pending-cancel order raises :class:StrixOrderNotCancellableError (also a :class:ValueError for back-compat). An unknown order_id raises :class:StrixUnknownOrderError (also a :class:KeyError for back-compat). Catch the umbrella :class:StrixCancelError to handle both with one branch.

If an execution arrives during the block (a fill-wins race per FIX semantics), the cancel becomes a no-op on exit — broker reality wins.

Validation errors (raised before any state transition):

Condition Exception
order_id not in the order book StrixUnknownOrderError (subclass of both StrixCancelError and KeyError)
Order is in a non-cancellable status (FILLED, REJECTED, CANCELLED, PENDING_CANCEL) StrixOrderNotCancellableError (subclass of both StrixCancelError and ValueError)
No active session (init/resume not called) RuntimeError

Catch StrixCancelError to handle both validation failures together (typical when racing with concurrent fills), or the specific subclass when the distinction matters. Existing handlers using bare except KeyError: / except ValueError: keep working unchanged — both subclasses inherit from their stdlib counterpart for back-compat.

Cancellable prior statuses are PENDING_NEW, NEW, and PARTIALLY_FILLED. On clean exit, status transitions to CANCELLED. On body exception, status reverts to the prior value and a CancelAttemptFailed event is appended.

strix.set_risk

strix.set_risk(risk: RiskSettings, /) -> None

Replace the active session's pre-trade risk policy.

The whole policy is replaced atomically — every field of risk becomes the new truth. Pass RiskSettings() with no arguments to clear all limits. Partial updates (e.g. changing only max_qty_per_order) are intentionally not supported: build a new RiskSettings carrying the full desired policy.

The new policy applies to the next order to enter strix.order(...). Orders already past the risk check (status NEW or PARTIALLY_FILLED) are not re-evaluated; risk is pre-trade, not continuous.

Emits a RiskSettingsChanged event so the policy is persisted and survives strix.resume.

Raises RuntimeError if no session is active.

Replaces the active session's risk policy atomically. The new policy applies to the next order entering strix.order(...); orders already past the risk check (status NEW or PARTIALLY_FILLED) are not re-evaluated. Emits a RiskSettingsChanged event so the policy survives strix.resume.

Condition Exception
No active session (init/resume not called) RuntimeError
risk is not a RiskSettings instance TypeError

strix.ingest_execution

Apply an execution to the current session's orders and positions.

Returns the matching order's resulting :class:OrderStatus so callers can distinguish partial fills from completions without re-querying :func:open_orders. Specifically:

  • OrderStatus.PARTIALLY_FILLED — the order has more qty outstanding.
  • OrderStatus.FILLED — the order is now fully filled.
  • The order's prior status (e.g. NEW) — when the execution was an anomaly (mismatched symbol/side/qty, or applied to a terminal order): the position book reflects the broker's reality but the order book is deliberately untouched, so its status didn't change.
  • None — when the execution was a duplicate (already-seen execution_id) or referenced an unknown order_id.

Anomaly handling (status returned but StrixInvalidExecutionError raised under the default on_invalid_execution="raise" policy) is described in :class:StrixInvalidExecutionError.

Invalid-execution categories. The execution's position effect is applied to the position book regardless (broker authoritative) and an ExecutionAnomalyDetected event is appended. SessionConfig.on_invalid_execution decides whether ingest_execution raises:

Condition Category Default policy ("raise")
order_id not in the order book missing-order StrixInvalidExecutionError
execution.symbol ≠ order symbol symbol-mismatch StrixInvalidExecutionError
execution.side ≠ order side side-mismatch StrixInvalidExecutionError
Order is FILLED, REJECTED, or CANCELLED terminal-order StrixInvalidExecutionError
filled_qty + execution.qty > order.qty (overfill) overfill StrixInvalidExecutionError

Other errors:

Condition Exception
execution is not an Execution instance TypeError
No active session (init/resume not called) RuntimeError

order_id and Execution.order_id must satisfy the order_id contract: non-empty, ≤128 characters, no ASCII control characters. Violations raise ValueError from the Order / Execution constructor.

Anomaly categories typically indicate caller bugs (malformed broker integration, mapping mistake) — but the broker has already moved the position, so Strix records the effect rather than dropping the fill. See order lifecycle § Validation for the conceptual breakdown.

strix.mark_to_market

strix.mark_to_market(prices: Mapping[str, Numeric], /) -> None

Update the active session's mark cache.

Pass a mapping of {symbol: mark_price}. Each entry replaces the cached mark for that symbol; symbols not in the payload are left unchanged. Marks feed unrealized-P&L computation for stop-loss evaluation (Phase 2 risk).

Empty mappings are a no-op. Numerics are normalized to Decimal.

Marks are ephemeral runtime state — no per-tick mark event is written to the log. Instead, applying marks may emit a throttled PnLSnapshot (see :class:SessionConfig.pnl_snapshot_every_seconds). On :func:strix.resume, the mark cache starts empty and repopulates as the caller resumes feeding marks via this function — the configured :class:MarketDataAdapter must be re-supplied for any pre-trade check that needs a live price.

Use :func:strix.active_symbols to discover which symbols Strix needs marks for.

Raises RuntimeError if no session is active. Raises TypeError if prices is not a mapping.

Updates the session's mark cache. Feeds unrealized-P&L computation for stop-loss evaluation. Empty mapping is a no-op. Numerics are normalized to Decimal. Marks are ephemeral runtime state — no per-tick event is written; on strix.resume the cache starts empty and repopulates as the caller resumes feeding marks. The cache update may emit a throttled PnLSnapshot (see SessionConfig.pnl_snapshot_every_seconds).

When RiskSettings.stale_mark_threshold is set, logs a WARNING for each active symbol not in the payload. Use strix.active_symbols() to discover which symbols need marks.

Condition Exception
No active session RuntimeError
prices is not a mapping TypeError

strix.active_symbols

strix.active_symbols() -> set[str]

Symbols Strix needs prices/marks for right now.

A symbol is "active" if either:

  • It has a non-zero net position (long or short), or
  • It has at least one order in an open status (PENDING_NEW, NEW, PARTIALLY_FILLED, PENDING_CANCEL).

Symbols with only terminal orders (FILLED, REJECTED, CANCELLED) and a flat position drop out of the set. Symbols that have never been touched are not included.

Use this to filter strix.mark_to_market(...) payloads to the minimum set of symbols required for continuous-risk evaluation to be accurate.

Raises RuntimeError if no session is active.

Returns the set of symbols Strix needs marks for: non-zero net position OR at least one order in an open status (PENDING_NEW/NEW/PARTIALLY_FILLED/PENDING_CANCEL). Derived from in-memory state; no watchlist API. Use to filter mark_to_market payloads.

Condition Exception
No active session RuntimeError

strix.resume_trading

strix.resume_trading(*, reason: str = "") -> None

Clear every active stop-loss halt and resume trading immediately.

Manual override for when the operator needs to act before auto-recovery fires (e.g. recovery threshold was never set, or marks aren't improving fast enough). Global in scope — clears session-stop and every per-symbol cost-based halt in one shot.

reason is recorded in the audit event for later investigation.

No-op when nothing is halted. Otherwise emits a StopLossManuallyOverridden event so the override is auditable on replay.

The stop-loss configuration is unchanged. If the underlying P&L condition still violates the threshold, the next execution or mark_to_market call will re-trigger the same halt. Place the action you need promptly.

Raises RuntimeError if no session is active.

Manual override for stop-loss halts. Global scope — clears every active halt (session and per-position) in one shot. Emits StopLossManuallyOverridden carrying the snapshot of cleared halts + the optional reason for audit. No-op when nothing is halted.

Does not re-evaluate continuous risk after clearing. If the underlying P&L condition still violates the threshold, the next execution or mark_to_market call will re-trigger. The override gives you a window to act; it doesn't disable the stop.

Condition Exception
No active session RuntimeError

strix.reconcile

strix.reconcile(
    broker: BrokerReadAdapter,
    /,
    *,
    since: str | None = None,
    on_mismatch: Literal["raise", "warn", "trust"] = "raise",
    check_positions: bool = True,
    check_open_orders: bool = True,
) -> ReconcileResult

Reconcile the active session's state against a broker via :class:BrokerReadAdapter.

Pulls missed executions and (optionally) sanity-checks the resumed positions and open orders against the broker's current view. Replaces the manual gap-fill loop from docs/guides/crash-recovery.md.

Parameters

broker A user-owned object implementing :class:BrokerReadAdapter. Strix ships no production broker integrations; the user wires their own broker SDK in here. since Adapter-defined cursor passed verbatim to :meth:BrokerReadAdapter.fetch_executions. None (default) means "use the marker recorded by the previous reconcile call in this session" (or None if no prior call). Explicit values override the recorded marker for this call only. on_mismatch How to handle position / open-order divergence:

- ``"raise"`` (default): raise :class:`BrokerReconciliationError`
  carrying the full mismatch lists.
- ``"warn"``: log per-mismatch warnings, return the result, do
  not raise.
- ``"trust"``: emit ``PositionAdjusted`` events to adopt the
  broker's view of positions, and auto-cancel Strix-open orders
  that the broker no longer reports as open. Other order
  mismatch cases (status divergence, broker-only orders) are
  NOT auto-corrected; if any remain, the call still raises
  :class:`BrokerReconciliationError` at the end. To suppress
  the raise even on unresolved trust-mode mismatches, use
  ``on_mismatch="warn"`` instead.

check_positions When False, skip the position diff entirely. check_open_orders When False, skip the open-order diff entirely.

Returns

:class:ReconcileResult Counts of executions ingested / deduped, plus the full mismatch lists (each entry tagged with resolution indicating what reconcile did about it) and the marker recorded for this call.

Raises

RuntimeError No active session — call :func:init or :func:resume first. :class:BrokerFetchError The adapter raised inside a fetch_* call. __cause__ holds the original exception. :class:BrokerReconciliationError Mismatches were detected under on_mismatch="raise" (any mismatch) or on_mismatch="trust" (an unresolved order mismatch).

Cross-session caveat

Execution dedupe is intra-session. If you reconcile in session A, close A, start session B and reconcile again, the broker may return executions that A already ingested; B's dedupe set is empty so they would be applied again, double-counting positions. Mitigation: pass since= accurately enough that the broker doesn't return already- ingested executions across the session boundary.

Reconciles the active session against a BrokerReadAdapter: pulls missed executions (deduped by execution_id), then sanity-checks resumed positions and open orders against the broker's current view. Returns a ReconcileResult carrying counts and mismatch lists; raises BrokerReconciliationError on detected divergence by default. See the crash recovery guide for the cookbook.

since defaults to the marker recorded by the previous successful reconcile call in this session (or None if no prior call). An explicit value overrides the recorded marker for this call only.

Condition Exception
No active session (init/resume not called) RuntimeError
The adapter's fetch_* method raised BrokerFetchError (__cause__ holds the original)
Mismatches detected under on_mismatch="raise", or unresolved order mismatches under on_mismatch="trust" BrokerReconciliationError

strix.positions

Snapshot of all positions known to the current session.

Includes zero-quantity entries for symbols that were previously held and are now flat — callers filter if they want non-zero only.

strix.open_orders

Snapshot of orders in open statuses: PENDING_NEW, NEW, PARTIALLY_FILLED, PENDING_CANCEL.

PENDING_CANCEL counts as open because the broker still has the order — a fill can still arrive before the cancel acks (FIX 4.2 scenario D4). Terminal orders (FILLED, REJECTED, CANCELLED) are excluded from this view but remain in the event log for audit — use :func:get_order to look them up by id.

strix.get_order

Look up the current state of an order by id.

Returns the latest :class:Order snapshot for order_id, or None if no such order exists in the active session's order book.

Symmetric with :func:positions and :func:open_orders. Unlike :func:open_orders, which excludes terminal statuses, this accessor returns orders in any status — FILLED, REJECTED, CANCELLED included.

The typical use is reading state after a strix.order(...) block has exited: the snapshot yielded inside the block is frozen at PENDING_NEW, and the post-block transition lives in the order book rather than mutating the yielded object.

.. code-block:: python

with strix.order(symbol="AAPL", side=Side.BUY, qty=100) as o:
    broker.submit(o.order_id, o.symbol, o.qty)
current = strix.get_order(o.order_id)
assert current is not None and current.status is OrderStatus.NEW

Raises RuntimeError if no session is active.

Returns the current :class:Order snapshot for the given order_id, or None when no such order exists in the active session. Unlike :func:open_orders (open statuses only), :func:get_order reaches orders in any status — including terminal FILLED / REJECTED / CANCELLED. Use it to read state after a strix.order(...) block has exited, since the snapshot yielded inside the block stays frozen at PENDING_NEW.

Condition Exception
No active session (init/resume not called) RuntimeError

strix.replay

strix.replay(
    *,
    data_dir: str,
    session_id: str | None = None,
    follow: bool = False,
    poll_interval: float = 0.5,
) -> Iterator[SessionEvent]

Read-only consumer-side event reader. Yields parsed SessionEvent instances from a LocalTransport data_dir without acquiring the lock, so a sidecar monitor can run concurrently with a writer process (algo, backtest, etc.). The documented JSONL audit format is the contract; consumers do not need to parse JSON themselves.

  • session_id=None (default) reads the session named in <data_dir>/active_session and raises NoActiveSessionError if none is open.
  • session_id="<uuid>" reads a specific session — including closed ones — for offline analysis.
  • follow=True keeps the file open and polls for new lines (tail-style), yielding new events as they're appended. poll_interval controls poll cadence.

Use isinstance(evt, PnLSnapshot) (or any other event class — all exported on strix) to filter. Typical pattern:

import strix
from strix import PnLSnapshot, StopLossTriggered

for evt in strix.replay(data_dir="./strix_data", follow=True):
    if isinstance(evt, PnLSnapshot):
        ...
    elif isinstance(evt, StopLossTriggered):
        ...
Condition Exception
data_dir missing or session has no events.jsonl FileNotFoundError
session_id=None and no active session recorded NoActiveSessionError
.strix-storage marker carries an unsupported format_version StorageVersionError
A line is malformed JSON or carries an unknown event type StorageCorruptError

strix.active_session_id

strix.active_session_id(*, data_dir: str) -> str | None

Read-only accessor for the session id recorded in <data_dir>/active_session. Returns None if no session is open or the data_dir doesn't exist. Does not acquire the storage lock. Useful for tooling that needs to correlate event-log reads with the active session without opening a LocalTransport handle.

strix.list_sessions

strix.list_sessions(*, data_dir: str) -> list[str]

Read-only listing of every session id in a LocalTransport data_dir, oldest first. Both open and closed sessions are included. Returns an empty list when the data_dir doesn't exist or contains no sessions yet. Does not acquire the storage lock — safe to call alongside a writer process.

Order is chronological — session ids are UUIDv7, so lexicographic sort matches creation order. Pair with strix.replay(data_dir=..., session_id=...) to walk each session's events for audit, replay, or analytics:

import strix
from strix import CancelAttemptFailed

for sid in strix.list_sessions(data_dir="./strix_data"):
    failures = [
        e for e in strix.replay(data_dir="./strix_data", session_id=sid)
        if isinstance(e, CancelAttemptFailed)
    ]
    if failures:
        print(sid, len(failures), "cancel attempts failed")

Domain types

Side

Bases: str, Enum

Order/execution direction. BUY increases position, SELL decreases it.

OrderStatus

Bases: str, Enum

Order lifecycle states.

  • PENDING_NEW: created and risk-checked, broker not yet acked.
  • NEW: broker accepted (clean exit from the strix.order block).
  • REJECTED: pre-trade risk check failed, or the user code raised inside the order block.
  • PARTIALLY_FILLED: at least one execution applied, more qty outstanding.
  • FILLED: cumulative fills equal order qty. Terminal.
  • PENDING_CANCEL: cancel requested via strix.cancel, broker not yet acked. Still counts as open at the broker.
  • CANCELLED: broker confirmed cancel (clean exit from the strix.cancel block). Terminal.

Aligned with FIX 4.2 OrdStatus semantics.

Order

@dataclass(frozen=True)
class Order:
    order_id: str                      # required; UUIDv7 by default when omitted from strix.order(...)
    symbol: str                        # required; non-empty
    side: Side                         # required; BUY or SELL
    qty: Decimal                       # required; positive; Numeric accepted
    limit_price: Decimal | None = None # None = market-style from Strix's perspective
    timestamp: datetime | None = None  # caller-supplied; informational
    filled_qty: Decimal = Decimal(0)   # cumulative fills; non-negative
    avg_fill_price: Decimal | None = None
    status: OrderStatus = OrderStatus.PENDING_NEW
    reject_reason: str | None = None   # populated when status == REJECTED

Constructed by strix.order(...) in normal use. You only build Order directly when implementing BrokerReadAdapter.fetch_open_orders (broker-side view of working orders — populate status with NEW or PARTIALLY_FILLED; other statuses are Strix-internal).

An order tracked by Strix.

Created by :func:strix.order (the only supported path for new orders during a live session). Immutable — status transitions and fill updates rebuild the instance under the hood. Yielded inside the strix.order context for the caller to submit to their broker.

Attributes:

Name Type Description
order_id str

Strix-assigned unique id (UUIDv7 by default; user-supplied via the order_id parameter to :func:strix.order).

symbol str

Trading symbol (non-empty).

side Side

BUY or SELL.

qty Decimal

Order quantity (positive).

limit_price Decimal | None

Optional limit price; None indicates a market-style order from Strix's perspective. (Strix does not route orders — this field is informational for the dashboard and the user's broker call.)

timestamp datetime | None

Optional caller-supplied timestamp.

filled_qty Decimal

Cumulative filled quantity. Starts at 0, grows with each applied execution.

avg_fill_price Decimal | None

Volume-weighted average price across applied executions, or None if no priced executions have been applied.

status OrderStatus

Current :class:OrderStatus.

reject_reason str | None

Populated when status == REJECTED. For risk rejections this is the StrixRiskError.reason string; for user-exception rejections it is f"{type(exc).__name__}: {exc}".

Execution

@dataclass(frozen=True)
class Execution:
    order_id: str                      # required; the Strix order_id this fill belongs to
    symbol: str                        # required; must match the order's symbol
    side: Side                         # required; must match the order's side
    qty: Decimal                       # required; positive — direction comes from side
    price: Decimal | None = None       # optional; without price, Position.avg_price is not updated
    timestamp: datetime | None = None  # broker timestamp; informational
    execution_id: str | None = None    # broker fill id; required for dedupe in strix.reconcile

A single fill (or partial fill) reported by the broker.

Pass to :func:strix.ingest_execution to update the parent order and position book. qty is the size of this fill (always positive — direction comes from side). Multiple Execution objects can target the same order_id for partial fills.

Attributes:

Name Type Description
order_id str

Strix order_id this execution belongs to.

symbol str

Trading symbol; must match the order's symbol.

side Side

BUY or SELL; must match the order's side.

qty Decimal

Filled quantity (positive). Decimal, int, or numeric string.

price Decimal | None

Fill price (optional). Without price, position avg_price is left unchanged.

timestamp datetime | None

Broker timestamp (optional, informational).

execution_id str | None

Broker-supplied fill id (optional, currently informational).

Position

@dataclass(frozen=True)
class Position:
    symbol: str                          # required
    qty: Decimal = Decimal(0)            # signed: positive long, negative short, zero flat
    avg_price: Decimal | None = None     # None when flat or no priced fills
    last_price: Decimal | None = None    # price of the most recent execution on this symbol

Net position in a symbol.

Sign convention: positive qty is long, negative is short, zero is flat. avg_price is the average entry price of the currently held size; it resets when the position flips sign and is None when flat. last_price is the price of the most recent execution that updated this symbol — useful when a position is built across multiple partial fills and avg_price (VWAP) doesn't reveal where the latest print landed.

Attributes:

Name Type Description
symbol str

Trading symbol.

qty Decimal

Net signed quantity. Long positive, short negative.

avg_price Decimal | None

Average entry price of the current position (None when flat or when no priced executions have arrived).

last_price Decimal | None

Price of the most recent execution on this symbol. None when the position is flat, when the latest execution arrived without a price (broker contract violation — Strix logs a WARNING and resets to None), or when no priced execution has been observed.

Session

Opaque handle returned by strix.init / strix.resume.

Holds in-memory projection of the event log: position book, order book, risk config, and the next-seq counter. Mutating operations emit events to the configured transport backend.

Session is an opaque handle returned by init and resume. The only public attribute is session_id: str (UUIDv7, useful for logging). All mutation goes through the module-level functions.

Configuration

InitialState

@dataclass(frozen=True)
class InitialState:
    positions: tuple[Position, ...] = ()
    open_orders: tuple[Order, ...] = ()

Explicit seed state for a new session.

Passing initial_state to :func:strix.init overrides the default carry-forward of positions and open orders from any prior session. The override is atomic: both fields are taken as the new session's starting truth. To start completely flat, pass InitialState() (the defaults are empty tuples).

Use this when:

  • First run against a fresh LocalTransport and you want to seed positions/orders from an external source.
  • Starting a paper-trading or backtest run that should ignore prior live state.

Attributes:

Name Type Description
positions tuple[Position, ...]

Positions to seed the new session with. Empty by default.

open_orders tuple[Order, ...]

Open orders to seed the new session with. Empty by default. Caller is responsible for ensuring these match broker reality — Strix does not reconcile them on entry.

RiskSettings

@dataclass(frozen=True)
class RiskSettings:
    # Phase 1 — user-data only
    max_qty_per_order: Numeric | None = None
    min_qty_per_order: Numeric | None = None
    max_orders: int | None = None
    max_open_orders: int | None = None
    max_shares_per_equity_symbol: Numeric | None = None
    position_limits: Mapping[str, Numeric] = {}
    # Phase 2 — market-data driven
    max_open_notional: Numeric | None = None
    max_share_price: Numeric | None = None
    min_share_price: Numeric | None = None
    min_share_price_short: Numeric | None = None
    session_stop_loss: SessionStopLoss | None = None
    cost_based_stop_loss: CostBasedStopLoss | None = None
    # Enforcement mode for pre-trade limit breaches
    on_breach: Literal["warn", "raise"] = "warn"
    # Mark freshness
    stale_mark_threshold: timedelta | None = timedelta(seconds=5)

on_breach decides how a pre-trade limit breach is handled. "warn" (default) records a RiskBreach event and logs a WARNING while the order still proceeds — Strix stays out of the order path. "raise" makes the breach raise StrixRiskError and record the order as REJECTED before the strix.order(...) block body runs. Stop-loss halts (StrixHaltedError) are not governed by on_breach — they always gate. See Risk controls.

SessionConfig

@dataclass(frozen=True)
class SessionConfig:
    on_invalid_execution: Literal["raise", "warn", "silent"] = "raise"
    on_missing_market_data: Literal["reject", "allow"] = "reject"
    pnl_snapshot_every_seconds: float = 15.0

on_invalid_execution decides how strix.ingest_execution reacts when the execution doesn't match the local order book (orphan, symbol/side mismatch, terminal-order target, overfill). In all cases the execution's position effect is applied to the position book (broker authoritative) and an ExecutionAnomalyDetected event is emitted. The setting governs the additional response — raise StrixInvalidExecutionError, log a WARNING, or proceed silently. strix.reconcile suppresses the raise branch during its broker-fetch loop regardless.

on_missing_market_data decides how pre-trade risk checks react when the configured MarketDataAdapter cannot supply a price reference. "reject" treats missing data as a breach (then handled per RiskSettings.on_breach — raised in "raise" mode, recorded as a RiskBreach in the default "warn" mode); "allow" logs a WARNING and skips the affected check.

pnl_snapshot_every_seconds controls the minimum interval between consecutive PnLSnapshot events. Snapshots are emitted from mark_to_market and ingest_execution when the interval has elapsed and something has actually changed; force-flushed on StopLossTriggered, StopLossRecovered, and SessionEnded. For CloudTransport users this field is overridden at session start by the server based on the active plan; local-only users fully control it.

SessionStopLoss

@dataclass(frozen=True)
class SessionStopLoss:
    threshold: Decimal                            # negative; e.g. Decimal("-5000")
    recovery_threshold: Decimal | None = None     # must be > threshold; None disables auto-recover
    mode: Literal["total", "realized"] = "total"  # total = realized + unrealized (needs marks)

Session-wide stop-loss configuration.

Continuous evaluation: when session P&L crosses threshold downward, Strix halts the session (new orders raise :class:StrixHaltedError). When P&L recovers past recovery_threshold (if set), Strix unhalts automatically.

Attributes:

Name Type Description
threshold Decimal

P&L floor — typically a negative Decimal like Decimal("-5000"). Crossing at or below this value triggers the halt.

recovery_threshold Decimal | None

P&L value that must be reached/exceeded to auto-unhalt. Must be > threshold if provided. None disables auto-recovery; resume manually via :func:strix.resume_trading.

mode Literal['total', 'realized']

"total" (default) uses realized + unrealized P&L. Requires marks via :func:strix.mark_to_market for an accurate unrealized component. "realized" uses only realized P&L (fills); no market data needed but misses unrealized drawdown.

CostBasedStopLoss

@dataclass(frozen=True)
class CostBasedStopLoss:
    threshold_pct: Decimal                            # must be negative; e.g. Decimal("-0.10") for 10% loss
    recovery_threshold_pct: Decimal | None = None     # must be > threshold_pct; None disables P&L-based recovery

Per-position stop-loss based on drawdown as a fraction of cost basis.

Continuous evaluation: for every non-zero position, compute pnl_pct = (mark - avg_price) * qty / (avg_price * abs(qty)). When this falls at or below threshold_pct, halt that symbol (orders on it raise :class:StrixHaltedError unless exposure-reducing). When the position closes or pnl_pct recovers past recovery_threshold_pct, the halt clears automatically.

Cost-based stop is always evaluated against unrealized P&L of the currently-held position. Realized-only semantics don't fit per-position (there is no position to halt once it's closed); use the session-wide :class:SessionStopLoss with mode="realized" for realized-only drawdown.

Attributes:

Name Type Description
threshold_pct Decimal

Fractional drawdown trigger — typically a negative Decimal like Decimal("-0.10") for "10% loss". Must be negative; positive values are nonsensical.

recovery_threshold_pct Decimal | None

Fractional drawdown above which an auto- recovery fires. Must be > threshold_pct if provided. None disables auto-recovery via P&L improvement; the halt still clears automatically when the position is closed.

Always evaluates unrealized drawdown on currently-held positions. Closing the position auto-clears the halt regardless of recovery_threshold_pct.

Pre-trade risk policy for a session.

Passed to :func:strix.init via the risk argument and to :func:strix.set_risk for mid-session updates. All fields are optional; an omitted field means "no limit on this dimension". The same RiskSettings instance can be reused across multiple init calls.

Attributes:

Name Type Description
max_qty_per_order Decimal | None

Reject any order whose qty exceeds this value.

min_qty_per_order Decimal | None

Reject any order whose qty is strictly less than this value. Useful for venues with minimum lot sizes or to block stray dust-sized orders.

max_orders int | None

Reject a new order if the session has already attempted this many orders. Counts every entry to strix.order(...) including rejections — gives true rate-limit semantics.

max_open_orders int | None

Reject a new order if the session already has this many orders in open status (PENDING_NEW, NEW, PARTIALLY_FILLED, PENDING_CANCEL).

max_shares_per_equity_symbol Decimal | None

Global per-symbol position cap, applied to every symbol not present in position_limits. An order is rejected if the projected abs(position) after the order would exceed this cap.

position_limits Mapping[str, Decimal]

Per-symbol overrides for the global cap. Where a symbol is in this mapping, its value is used and the global max_shares_per_equity_symbol is ignored for that symbol.

Market data (Phase 2)

MarketDataAdapter (Protocol)

class MarketDataAdapter(Protocol):
    def last(self, symbol: str) -> Decimal | None: ...
    def quote(self, symbol: str) -> Quote | None: ...
    def mark(self, symbol: str) -> Decimal | None: ...

Bases: Protocol

Structural type for market-data sources.

All three accessors return None when no data is available for the symbol. Strix interprets None according to the session's SessionConfig.on_missing_market_data policy (default: reject).

Implementations are responsible for freshness. Strix calls these synchronously on the order-placement hot path and on every continuous-risk evaluation; cache aggressively if your underlying source is slow.

last

last(symbol: str) -> Decimal | None

Last-trade price, or None if unknown.

mark

mark(symbol: str) -> Decimal | None

Mark price for P&L computation.

Often equal to last for actively-traded names, but may differ for end-of-day marks, illiquid names, or after-hours sessions. Implementations may default to last when no distinct mark is maintained.

quote

quote(symbol: str) -> Quote | None

Top-of-book quote (bid/ask), or None if unknown.

Structural type for market-data sources. All three accessors return None when no data is available; missing data is then interpreted per SessionConfig.on_missing_market_data for pre-trade checks (continuous-risk evaluation always skips with a WARNING).

Strix calls these synchronously on the order path and on every continuous-risk evaluation. Cache aggressively if your underlying source is slow.

Quote

@dataclass(frozen=True)
class Quote:
    bid: Decimal
    ask: Decimal
    ts: datetime | None = None

Best bid / best ask snapshot for a symbol.

ts is informational — Strix uses the value when present (e.g. for staleness warnings) but does not require it.

Attributes:

Name Type Description
bid Decimal

Best bid price. Must be <= ask.

ask Decimal

Best ask price. Must be >= bid.

ts datetime | None

Optional timestamp of the quote.

Top-of-book snapshot. Construction validates bid <= ask.

StaticMarketData

StaticMarketData(
    *,
    prices: Mapping[str, Numeric] | None = None,
    quotes: Mapping[str, Quote] | None = None,
    marks: Mapping[str, Numeric] | None = None,
)

In-memory, dict-backed :class:MarketDataAdapter.

Useful for tests, replays, paper-trading runs against a fixed snapshot, or as the source for a thin user-built wrapper that writes from a WebSocket feeder thread and reads from Strix's sync risk path.

mark falls back to last for symbols where no explicit mark has been set.

Example

from decimal import Decimal from strix import StaticMarketData, Quote md = StaticMarketData( ... prices={"AAPL": Decimal("150.10")}, ... quotes={"AAPL": Quote(Decimal("150.05"), Decimal("150.15"))}, ... ) md.last("AAPL") Decimal('150.10')

set_last

set_last(symbol: str, price: Numeric) -> None

Update the last-trade price for symbol.

set_mark

set_mark(symbol: str, mark: Numeric) -> None

Update the mark price for symbol.

Setting an explicit mark overrides the mark fallback to last.

set_quote

set_quote(symbol: str, quote: Quote) -> None

Update the top-of-book quote for symbol.

Dict-backed MarketDataAdapter for tests, replays, and simple use cases. set_last/set_quote/set_mark mutators allow incremental updates. mark() falls back to last() when no explicit mark is set.

Broker reconciliation

BrokerReadAdapter (Protocol)

class BrokerReadAdapter(Protocol):
    def fetch_executions(self, *, since: str | None) -> ExecutionBatch: ...
    def fetch_open_orders(self) -> Iterable[Order]: ...
    def fetch_positions(self) -> Iterable[Position]: ...

Bases: Protocol

Structural type for broker integrations. Used for static type checking only.

Implementations are user-owned — every algo has its own broker SDK. The Strix SDK ships only this Protocol and :class:StaticBrokerReadAdapter as a test fixture.

Implementations MUST set their own per-request HTTP/socket timeouts in the constructor or per-call. Strix does not wrap these methods in a timeout — a hung adapter hangs strix.reconcile. The constraint that network I/O must not block session-boundary calls indefinitely is satisfied because init / resume never invoke the adapter; reconcile is the only entry point, and it's user-explicit.

Exceptions raised by any method are wrapped in :class:BrokerFetchError by strix.reconcile (the original exception is preserved via __cause__).

fetch_executions

fetch_executions(*, since: str | None) -> ExecutionBatch

Return all broker fills since the supplied since cursor.

since is an adapter-defined opaque marker. None means "from the beginning of broker history that Strix should know about" — the adapter decides how far back that is.

fetch_open_orders

fetch_open_orders() -> Iterable[Order]

Return all orders currently open at the broker.

The adapter is expected to map the broker's status enum to Strix's :class:OrderStatus. Status values should be in the open set (PENDING_NEW, NEW, PARTIALLY_FILLED, PENDING_CANCEL); terminal-state orders should not appear here.

fetch_positions

fetch_positions() -> Iterable[Position]

Return current net positions held at the broker.

Symbols with zero net qty MAY be omitted (Strix treats absent symbols as flat). avg_price is optional; not used to detect mismatches but reported in :class:PositionMismatch for inspection and used by trust mode to adopt the broker's average.

Structural type for broker integrations. User-implemented against the broker's SDK. Exceptions raised inside any fetch_* method are wrapped in BrokerFetchError by strix.reconcile (original exception preserved via __cause__). The adapter is responsible for its own per-request timeouts; Strix does not wrap calls in a timeout.

StaticBrokerReadAdapter

StaticBrokerReadAdapter(
    *,
    executions: Iterable[Execution] = (),
    next_marker: str | None = None,
    open_orders: Iterable[Order] = (),
    positions: Iterable[Position] = (),
)

Test fixture. Returns pre-configured data without I/O.

Use in tests, examples, and dry-run / paper-trade harnesses. NOT a production broker. since is accepted but ignored — the adapter returns the full configured executions list every call, paired with the configured next_marker (default None).

Frozen data on construction; if a test needs different data, construct a new adapter. To exercise the :class:BrokerFetchError path, write a one-off Protocol-conformant class inline.

Frozen-data test fixture. Returns the configured lists on every fetch_* call. since is accepted but ignored. Not a production broker — write a Protocol-conformant class wrapping your real broker SDK.

ExecutionBatch

@dataclass(frozen=True)
class ExecutionBatch:
    executions: list[Execution]
    next_marker: str | None = None

Adapter response for :meth:BrokerReadAdapter.fetch_executions.

executions is the (possibly empty) list of fills since the requested marker. next_marker is the adapter-defined cursor representing "everything up to and including this point" — Strix stores it opaquely and feeds it back on the next fetch_executions call when the caller doesn't pass an explicit since=.

next_marker SHOULD be populated even when executions is empty so the cursor advances to the broker's current state and the next call doesn't re-scan from the same point. None is legal (the recorded marker simply stays None and the next call sends since=None).

Returned by BrokerReadAdapter.fetch_executions. next_marker is the adapter-defined opaque cursor representing "everything up to and including this point"; Strix stores it and passes it back on the next call when the caller doesn't supply since= explicitly.

PositionMismatch

@dataclass(frozen=True)
class PositionMismatch:
    symbol: str
    strix_qty: Decimal
    broker_qty: Decimal
    strix_avg_price: Decimal | None
    broker_avg_price: Decimal | None
    resolution: Literal["auto_adjusted", "unresolved"]

A position-book divergence between Strix's view and the broker's.

Both strix_qty and broker_qty are populated; either may be zero if the symbol is missing from one side (Strix's book treats absent symbols as flat). avg_price fields carry whatever each side reported; they are not used to detect mismatches (avg-price drift is expected and broker-specific) but are present so trust-mode can adopt the broker's average.

resolution records what reconcile did about this mismatch:

  • "auto_adjusted": on_mismatch="trust" emitted a PositionAdjusted event and the position book now matches the broker.
  • "unresolved": on_mismatch="raise" / "warn" modes always use this; the caller resolves manually.

A position-book divergence between Strix and the broker. avg_price fields are reported for inspection but do not trigger mismatches on their own. resolution="auto_adjusted" means trust-mode emitted a PositionAdjusted event to bring Strix's book in line with the broker; "unresolved" means the caller must handle it.

OrderMismatch

@dataclass(frozen=True)
class OrderMismatch:
    order_id: str
    strix_status: OrderStatus | None
    broker_status: OrderStatus | None
    resolution: Literal["auto_cancelled", "unresolved"]

An open-order divergence between Strix's view and the broker's.

Either side's status may be None if the order is unknown on that side.

resolution records what reconcile did:

  • "auto_cancelled": trust-mode emitted an OrderStatusChanged to mark the order CANCELLED (only the Strix-open + broker-absent case is auto-corrected).
  • "unresolved": raise/warn modes always; trust mode for cases outside the bounded auto-correct scope.

An open-order divergence. Either side's status may be None when the order is unknown there. resolution="auto_cancelled" covers the Strix-open + broker-absent case under trust mode; all other cases stay "unresolved".

ReconcileResult

@dataclass(frozen=True)
class ReconcileResult:
    executions_ingested: int
    executions_skipped_duplicate: int
    marker: str | None
    position_mismatches: list[PositionMismatch]
    order_mismatches: list[OrderMismatch]

Returned by :func:strix.reconcile on successful completion.

marker echoes the next_marker returned by the adapter for this call (also recorded internally via the ReconciliationCompleted event so subsequent reconcile calls auto-resume from it).

Returned by strix.reconcile when the call completes without raising. marker echoes the cursor returned by the adapter (also persisted via the ReconciliationCompleted event so subsequent calls auto-resume from it).

Transport

Transport (Protocol)

A structural type — any object implementing the methods can be passed as transport. The two concrete implementations that ship are below.

Bases: Protocol

Structural type for Strix transports (the persistence/sync port). Used for static type checking only.

start_session

start_session(session_id: str, *, ts: datetime | None = None, reason: str, seeded_positions: Iterable[Position] = (), seeded_open_orders: Iterable[Order] = (), risk: RiskSettings | None = None, config: SessionConfig | None = None) -> SessionStarted

end_session

end_session(session_id: str, *, ts: datetime | None = None, reason: str) -> SessionEnded

append

append(event: SessionEvent) -> None

replay

replay(*, session_id: str | None = None, since_seq: int | None = None) -> Iterator[SessionEvent]

active_session_id

active_session_id() -> str | None

flush

flush() -> None

close

close() -> None

InMemoryTransport

strix.InMemoryTransport()

Ephemeral, in-process event log. State lives until process exit. Default when strix.init() is called without a transport argument. Useful for tests, short scripts, and anything that doesn't need durability.

LocalTransport

strix.LocalTransport(data_dir="./strix_data")

File-backed JSONL event log. Survives process restart. data_dir is required — Strix won't pick a default location.

Disk layout under data_dir:

.strix-storage              # marker: {"format_version": 1}
strix.lock                  # OS file lock; auto-released on process exit
active_session              # one line: <session_id> of currently-open session
sessions/<session_id>/events.jsonl

Single-writer per data_dir (OS file lock). See Persistence for the full disk-format story.

Methods of interest to callers:

  • transport.close() — release the lock and close any open event log file. Auto-runs on process exit; eager call is fine.
  • transport.flush()fsync the currently-open event log file. Rarely necessary because append() already fsyncs.

Errors

All inherit from StrixError. Catch the base if you want a single handler; catch specifics if you want different reactions.

StrixError

Bases: Exception

Base class for Strix SDK errors.

StrixRiskError

Bases: StrixError

Raised when a pre-trade risk check blocks an order.

Raised when a pre-trade risk check blocks an order, inside the with strix.order(...) block before the body runs. The blocked order is recorded in the order book as REJECTED.

Attributes:

  • reason: str — human-readable reason (also the exception message).
  • order_id: str — the Strix order_id of the blocked order.

StrixInvalidExecutionError

Bases: StrixError

Raised by :func:strix.ingest_execution when an execution does not match the local order book and the session's on_invalid_execution policy is "raise".

The position effect of the execution has already been applied to the position book (the broker is authoritative on positions/cash) and an ExecutionAnomalyDetected event has been written to the event log before this exception is raised. Catching it does not undo those effects.

Attributes:

Name Type Description
category

Which kind of mismatch was detected. One of "missing-order", "symbol-mismatch", "side-mismatch", "terminal-order", "overfill".

detail

Human-readable description of the mismatch.

execution_id

execution_id of the offending fill if the broker supplied one; otherwise None.

order_id_ref

order_id carried by the execution (the order Strix tried to match). None only when the execution itself had no order_id — currently never (the dataclass requires it).

Raised by strix.ingest_execution when the execution does not match the local order book and SessionConfig.on_invalid_execution == "raise" (the default). The position effect has already been applied to the position book (broker authoritative) and an ExecutionAnomalyDetected event has been appended before the exception is raised — catching it does not undo those effects.

Attributes:

  • category: Literal["missing-order", "symbol-mismatch", "side-mismatch", "terminal-order", "overfill"] — which kind of mismatch was detected.
  • detail: str — human-readable description.
  • execution_id: str | None — broker-supplied fill id, if any.
  • order_id_ref: str | None — the order_id Strix tried to match.

StrixDuplicateOrderIdError

Bases: StrixError, ValueError

Raised by :func:strix.order when the caller-supplied order_id is already registered in the active session's order book.

The order book retains both open and terminal orders for the lifetime of a session, so an id is not reusable once registered, even after the order ends. Across sessions, carry-forward brings open orders' ids into the new session; terminal orders do not carry forward, so their ids become reusable.

When order_id is omitted from :func:strix.order, the SDK auto-generates a UUIDv7 and this error cannot fire; it is only reachable when the caller passes an explicit order_id that collides.

Inherits from :class:ValueError for back-compat with the original raise type — existing except ValueError: handlers keep working unchanged.

Attributes:

Name Type Description
order_id

The colliding order_id.

Raised by strix.order(...) when the caller-supplied order_id is already registered in the active session's order book. Inherits from StrixError and ValueError — existing except ValueError: handlers keep working unchanged.

Carry-forward semantics: open orders' ids carry forward into a new session via strix.init(...), so the id remains taken until the order terminates and the session boundary advances. Terminal orders are dropped from carry-forward; their ids become reusable in the next session.

Attributes:

  • order_id: str — the colliding order_id.

StrixCancelError

Bases: StrixError

Base class for errors raised by :func:strix.cancel.

Two concrete subclasses cover the validation paths:

  • :class:StrixUnknownOrderError — no order with the given order_id exists in the session's order book.
  • :class:StrixOrderNotCancellableError — the order exists but is in a status that doesn't permit cancellation (FILLED, REJECTED, CANCELLED, or already PENDING_CANCEL).

Both subclasses also inherit from the stdlib exception they historically were (KeyError and ValueError respectively), so existing except (KeyError, ValueError): handlers continue to work unchanged. New code should catch :class:StrixCancelError to handle both, or the specific subclass when the discrimination matters.

Attributes:

Name Type Description
order_id

The order_id passed to :func:strix.cancel.

Base class for errors raised by strix.cancel(...). Two concrete subclasses cover the validation paths; catch StrixCancelError to handle both (the typical race-handling shape) or the specific subclass when discrimination matters. Each subclass also inherits from its historical stdlib counterpart (KeyError / ValueError) so existing handlers keep working unchanged.

Attributes:

  • order_id: str — the order_id passed to strix.cancel(...).

StrixUnknownOrderError

Bases: StrixCancelError, KeyError

Raised by :func:strix.cancel when the order_id is not in the session's order book.

Inherits from :class:StrixCancelError (catch-all for cancel errors) and from :class:KeyError (back-compat with the original raise type — this was the only way the cancel CM previously signalled an unknown order).

Raised when the order_id passed to strix.cancel(...) is not in the session's order book. Inherits from StrixCancelError and KeyError.

StrixOrderNotCancellableError

Bases: StrixCancelError, ValueError

Raised by :func:strix.cancel when the order exists but is in a non-cancellable status.

Cancellable statuses are PENDING_NEW, NEW, and PARTIALLY_FILLED. Any other status (terminal FILLED/REJECTED/CANCELLED or already PENDING_CANCEL) triggers this error.

Inherits from :class:StrixCancelError (catch-all for cancel errors) and from :class:ValueError (back-compat with the original raise type).

Attributes:

Name Type Description
current_status

The order's status at the moment of the cancel call.

Raised when the order exists but is in a status that doesn't permit cancellation (FILLED, REJECTED, CANCELLED, or already PENDING_CANCEL). Inherits from StrixCancelError and ValueError.

Attributes (in addition to order_id from the base):

  • current_status: OrderStatus — the order's status at the moment of the cancel call.

StrixHaltedError

Bases: StrixError

Raised when an order is blocked because a stop-loss is currently triggered.

Distinct from :class:StrixRiskError: a regular risk check rejection is a per-order limit breach; a halt is a session-wide (or per-position) trading suspension that persists until the configured recovery threshold is crossed or :func:strix.resume_trading is called.

Attributes:

Name Type Description
reason

Human-readable description.

scope

"session" for a session-wide stop, "position" for a per-symbol stop.

subject

Symbol for scope == "position"; None for session-scope.

order_id

Strix order_id of the blocked order.

Raised when an order is blocked because a stop-loss is currently triggered (Phase 2). Distinct from StrixRiskError: a halt is a session-wide (or per-symbol) trading suspension that persists until the configured recovery threshold is crossed or strix.resume_trading() is called. Exposure-reducing orders bypass the halt.

Attributes:

  • reason: str — human-readable reason.
  • scope: str"session" (session-wide stop) or "position" (per-symbol stop).
  • subject: str | None — symbol for scope == "position"; None for session-scope.
  • order_id: str — the Strix order_id of the blocked order.

StrixStorageError

Bases: StrixError

Base class for storage-related errors.

Base for storage-related errors. The concrete subclasses below all inherit from it.

NoActiveSessionError

Bases: StrixStorageError

resume() was called but no open session exists in storage.

Raised by strix.resume(transport=...) when the transport has no open session. Handle by falling through to strix.init(transport=...) for the fresh-start case — see crash recovery.

StorageCorruptError

Bases: StrixStorageError

Storage state is internally inconsistent (bad pointer, malformed event, unknown type, etc.).

Raised when storage state is internally inconsistent: a malformed event log line, an unknown event type, a seq mismatch on append, an inconsistent active_session pointer, etc. These should not happen in normal operation; treat them as bugs to investigate.

StorageLockedError

Bases: StrixStorageError

Another process holds the data_dir lock.

Raised by LocalTransport(data_dir=...) when another process already holds the data_dir lock. Strix is single-writer per data_dir.

StorageVersionError

Bases: StrixStorageError

Storage on-disk format_version is unsupported by this SDK version.

Raised when the .strix-storage marker file has a format_version this SDK doesn't support. Will be relevant when post-v1 format migrations land.

StrixBrokerError

Bases: StrixError

Base class for broker-adapter-related errors.

Base for broker-adapter-related errors raised by strix.reconcile. Catch this if you want a single handler for any broker-side problem.

BrokerFetchError

Bases: StrixBrokerError

A :class:BrokerReadAdapter fetch_* call raised.

The original exception is preserved via __cause__. operation identifies which adapter method failed: "fetch_executions", "fetch_open_orders", or "fetch_positions".

Raised by strix.reconcile when a BrokerReadAdapter fetch_* method raises. The original exception is preserved via __cause__.

Attributes:

  • operation: str — which adapter method failed: "fetch_executions", "fetch_open_orders", or "fetch_positions".

BrokerReconciliationError

Bases: StrixBrokerError

Raised when :func:strix.reconcile detects mismatches between the resumed event-log state and the broker's view.

Carries the full lists of mismatches so callers can inspect and resolve. Executions ingested earlier in the same reconcile call remain applied; only the mismatch check raised. PositionAdjusted / OrderStatusChanged events emitted by trust mode before the raise also remain applied — the raise reports any unresolved mismatches that trust mode would not auto-correct.

Raised by strix.reconcile when divergences are detected: under on_mismatch="raise" (any mismatch) or on_mismatch="trust" (any unresolved order mismatch — trust mode auto-corrects only the bounded subset, see crash recovery). Executions ingested earlier in the same call remain applied; only the mismatch check raised.

Attributes:

  • position_mismatches: list[PositionMismatch]
  • order_mismatches: list[OrderMismatch]

Order ID contract

The order_id field on Order and Execution is a string with these rules:

  • Non-empty.
  • At most 128 characters.
  • No ASCII control characters (codepoints < 0x20 or = 0x7F).

Anything beyond that — letters, digits, punctuation, non-ASCII Unicode — is accepted. Violations raise ValueError from the Order / Execution constructor.

When order_id is omitted from strix.order(...), the SDK auto-generates a UUIDv7. Pass your own when you need stable IDs for broker reconciliation, replay, or multi-system attribution.

If you supply your own and care about cloud-DB index locality (when CloudTransport lands post-v1), prefer time-ordered schemes — UUIDv7, ULID, Snowflake. Pure UUIDv4 will work but degrades the secondary index when orders move to a hosted DB.

Numeric inputs

Wherever Strix accepts a quantity or price (qty, limit_price, price, RiskSettings.max_qty_per_order, RiskSettings.position_limits values), the type is:

Numeric = Decimal | int | str

Floats are rejected with TypeError. Strings parse via Decimal(str) — anything Decimal accepts is valid ("100", "100.5", "1.5e2", etc.). Non-finite values (NaN, Infinity) are rejected with ValueError.