Core concepts
Three things define a run. Two of them do something you can not expect when the market starts to move.
import tradefloor as tf universe = tf.Universe.random(108, seed=7) macro = tf.Macro(federal_funds_rate=0.025, corporate_bond_yield=0.052, vix=16.0) engine = tf.Engine(seed=42, universe=universe, macro_state=macro)
Roster order changes the market
Universe subclasses list. The engine walks the instruments in index order and draws random numbers as it goes. A re-sorted roster is therefore a different market from the same seed.
What a reversed roster costs
On Universe.random(20, seed=11) at sim seed 42, AAA closes day 5 at:
Same names, same fundamentals, same seed. A sort of your tickers upstream does this without a warning.
There are two seeds
The economy moves on its own
The Macro that you give the engine is the state on day zero. Every close advances the chain. The economy updates, the cycle can turn, and the central bank acts. If you want a frozen economy, you must ask for one.
How often each field takes a new value
120 days, Universe.random(20, seed=11), sim seed 42Pinning a field does not freeze it
A pin replaces the endogenous step for that field one time. The chain then continues from the pinned value. Pin vix=30.0 after day 3. The next four days read:
The series pulled back toward its mean. It did not hold. For a day-by-day exogenous series, pin the field every day, or drive a scenario.
Four ways to build a universe
tf.Universe.random(108, seed=7) # generated, plausible per sector tf.Universe([tf.Instrument(...), ...]) # your own roster tf.Universe.from_edgar(snapshot) # real SEC fundamentals tf.Universe.from_json(saved) # one you saved with to_json universe.fingerprint # sha256 over the roster, order included
The generator fills twelve sectors round-robin, so a generated roster is more balanced than any real index. That costs nothing over one year. It shows in the second year, as the envelope states.