LEARN/CORE TYPES

Core types

The five types you need to run a market. Every keyword below is keyword-only unless the signature shows otherwise.

Engine

A whole market, stepped through time.
Engine(*, seed: int,
       universe: Sequence[Instrument],
       macro_state: Macro | None = None,
       model: str | ModelParams | None = None)
run_days(n) Runs n whole trading days: open, the session, then close. It records a day before closing it.
open_market() / close_market() The day boundary by hand. The close does the momentum roll, GARCH and the macro step, so it advances the chain into the next day.
run_session(hour, minute, day_of_week, ticks, *, order_flow=None) One block of ticks. order_flow is how you tell the market you traded.
tick(hour, minute, day_of_week, *, volatility, news, news_impacts, order_flow) A single minute. Everything run_session does, one step at a time.
bars(*, grain="day") An Arrow stream of OHLCV. Day or tick grain.
truth(*, day=None) An Arrow stream of the ground truth: fair value, mispricing and the nine factors.
macro_table() / book_table() The economy day by day, and the resting book level by level.
book(ticker) A detached OrderBook snapshot. Reading it leaves prices() byte-identical.
pin_macro(**fields) Overrides the endogenous step for those fields once. The chain then continues from the pinned value.
model_fingerprint The preset name, or custom-7f290e34 once anything settable moves.
draws_consumed Total draws from the market, economy and external streams. Equal counts between two runs mean an identical noise sequence.
order_log Every input the engine consumed, as JSON-serialisable dicts. An unknown entry raises on replay.
FACTORS The nine factor names, at runtime, in the order the truth table carries them.
The engine owns the seeded generator, the per-company price state, the economy and the central bank, and nothing beyond those four.

Universe

A list of Instrument, whose order is contractual.
Universe(instruments: Sequence[Instrument])

Universe.random(n: int, *, seed: int)
Universe.from_edgar(snapshot)
Universe.from_json(text)
fingerprint A sha256 over the roster in canonical form, order included.
to_json() Round-trips through from_json.
index_of(ticker) The roster index, or None.
tickers The roster order as a list.
It subclasses list, so len, iteration and indexing all work. random() fills twelve sectors round-robin and assigns synthetic tickers by position: AAA, AAB, AAC and on.

Instrument

One tradable company.
Instrument(ticker: str, sector: str, *,
           initial_price: float,
           shares_outstanding: float,
           eps: float | None = None,
           book_value_per_share: float | None = None,
           revenue_growth: float | None = None,
           avg_volume: float = ...,
           beta: float = ...,
           short_interest: float = ...)
short_interest A share count, not a fraction. A value strictly between 0 and 1 is refused for a company with a real share count.
eps=None Marks a loss-maker. It is valued off book value and never reprices when the discount rate moves.
avg_volume Held fixed through a run, and what a participation cap is measured against.
beta The loading on the shared market factor.
market_cap Derived and read-only: price times shares outstanding.
Ticker and sector are positional. Everything else is keyword-only, so a call can not silently swap two floats.

Macro

The economy on day zero.
Macro(*, vix: float = ...,
      federal_funds_rate: float = ...,
      corporate_bond_yield: float | None = None,
      inflation_rate: float = ...,
      qe_pe_boost: float = ...,
      fear_greed_index: float = ...,
      cycle: CycleName = ...)
Rates are fractional 0.052 means 5.2 percent. Passing 5.2 raises.
corporate_bond_yield=None Falls through to the policy rate plus the spread. 0.0 is a real observation and is used as given.
cycle expansion, peak, contraction, trough or recovery. It lives on Macro only, never in the macro table.
This is the state on day zero, not the whole run. Every close advances the chain, so vix takes 118 distinct values over a 120-day run.

OrderBook

One book, with price-time priority.
book = engine.book("AAA")

book.best_bid / book.best_ask
book.mid_price / book.spread
book.depth_buy / book.depth_sell
book.bids / book.asks
bids / asks The resting orders, each carrying price, remaining quantity, owner and sequence.
sequence Arrival order, which is what breaks a tie at the same price. Post behind someone and you fill behind them.
best_bid / best_ask The touch, or None when that side is empty.
depth_buy / depth_sell Total resting size per side.
A snapshot from engine.book() is detached. Filling against it prices your execution at the levels you consume, and leaves the untraded run byte-identical.

Two errors, and what each one means

ValidationError

A malformed input: a rate passed as a percentage, a short interest between 0 and 1 on a real share count, an unknown sector key, a scenario that chains a shape onto a built scenario.

OrderError

A rejected order: a zero, negative or NaN quantity, or a NaN price on a limit. Nothing is clamped, because a simulator that repairs your order gives you a fill you did not ask for.

All pages