LEARN/CONVENTIONS

Conventions

Units and input rules. Read these before you hit them, because each one raises rather than guesses.

Nothing is silently clamped. A simulator that repairs your inputs gives you a market you did not specify, so a malformed scenario raises ValidationError and a rejected order raises OrderError.

The rules

Rates are fractional

0.052 not 5.2

0.052 means 5.2 percent. Passing 5.2 raises, with an error that says so.

Absence differs from zero

None not 0.0

corporate_bond_yield=None falls through to the policy rate. 0.0 is a real observation and is used as given. In a columnar read, where a column can not carry None, absence is NaN and never zero, because zero is a real maker inventory, a real mispricing and a real return.

Negative EPS is legal

eps=-1.20 not filter them out

Loss-makers are valued off book value, and a universe without them is not realistic. It also never exercises that valuation path, so a study on such a roster covers half the model.

Short interest is a share count

3_000_000 not 0.03

The squeeze rule divides it by the float. A value strictly between 0 and 1 raises for a company with a real share count.

Roster order is contractual

universe.fingerprint not sorted(tickers)

A re-sorted universe is a different market. The fingerprint covers order as well as content.

Coefficients ship as a preset

model="pt-v14" not Engine(garch_alpha=...)

They are named and versioned rather than passed as constructor keywords, so two published results can be compared. pt-v14 is the default as of the 2026-08-28 era boundary, pt-v12 was the default before it, and every earlier name from pt-v1 on still reproduces bit for bit.

Every numeric column is f64

float64 not mixed dtypes

No integer columns, no downcasting. A join that produces a different dtype is a sign the read went wrong.

Short interest, in detail

This is the convention that bites hardest, because the mistake would otherwise be silent. The squeeze rule divides short interest by the float, so 3% of a hundred million shares is 3,000,000.

Why 0.03 raises

Three hundredths of one share against a real float is a squeeze ratio of 3e-10, and a squeeze that can never fire. So a value strictly between 0 and 1 raises for a company with a real share count.

Zero is legal. It means no short interest, which is a real observation.

What the generator draws

Log-uniform between 0.4% and 30% of shares outstanding, so the median is the geometric mean of those bounds.

Median, whole ticker space 3.45%
Above the 20% threshold 9.54%
Median on random(108, seed=7) 2.53%

A hundred names is a small sample of a draw whose top and bottom differ by a factor of seventy-five, so a roster the size these docs use runs lower and noisier.

What a preset does and does not carry

In the dictionary

The mispricing and crowd model: the half-life, mispricing_phi, momentum_theta, the mispricing and daily-shock caps, and the three crowd terms. Eight numbers, and every one of them live.

Live, and absent from it

The GARCH parameters, the market and sector factor sigmas, and the order-flow coefficient. None of them appear, and nothing forces a preset name to change when one of them moves.

So the preset name is necessary and not sufficient. A fully specified run also names the package version, which pins the implementation.

All pages