Running a market
Build a universe, run a market, and do a test of a strategy. For most people this page is the whole library.
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)
engine.run_days(252)
Three things define a run
Pick who exists
There are four constructors. A generated roster uses synthetic tickers, AAA to AAZ and on. The generator assigns them by position.
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 earlier
One thing that will bite you: roster order is contractual
Reorder the roster and you get a different market from the same seed. Sorting your tickers alphabetically upstream changes the world without meaning to. On Universe.random(20, seed=11) at sim seed 42, AAA closes day 5 at 143.03 in roster order and 134.88 reversed. Check with universe.fingerprint.
Read what happened
A run gives you five Arrow tables. polars, pandas, pyarrow and duckdb read them zero-copy, and the package needs none of them.
Orders move the price
Orders match against a simulated limit order book with price-time priority. A big order gets worse prices, because it took the levels that rested there. There is no slippage formula in the code. The one momentum signal below changes only how often it rebalances.
One market is one sample
Before you call a winner, run the comparison across twelve seeded markets. Then do a check of how often the lead holds.
ranking = tf.rank(
lambda: tf.reference_agents(seed=3),
seeds=range(12), universe=universe,
days=10, workers=4)
ranking.separation("mean_reversion",
"momentum")
# {'wins': 9, 'losses': 3,
# 'ties': 0, 'p_value': 0.1460}/ste100-writer
Pooled, mean-reversion captures +0.783 against momentum's +0.259. Paired across the same twelve markets it wins 9 to 3, at p = 0.15, which is a lead the sign test does not call settled.
Its lead is in the size of its wins, not in how often they come. A single seed picks the pooled leader 5 times in 12.
A year from start to finish
Build a universe, run a year, and read the results. Score a strategy, then run the same comparison across twelve markets. Each step prints something.
import tradefloor as tf
# 1. a universe and a market
universe = tf.Universe.random(30, seed=11)
engine = tf.Engine(seed=42, universe=universe)
engine.run_days(252) # one trading year
# 2. what happened, and what was actually true
bars = engine.bars(grain="day") # OHLCV for every name
truth = engine.truth() # true value + what drove each move
# 3. score a strategy against a fresh market
spec = tf.StrategySpec.momentum(lookback_days=1.0, top_k=5)
scores = tf.evaluate({"momentum": spec}, seed=7, universe=universe, days=10)
print(scores["momentum"].return_pct, scores["momentum"].strategy_fingerprint)
# 4. the same comparison across twelve markets
ranking = tf.rank(lambda: tf.reference_agents(seed=3), seeds=range(12),
universe=universe, days=10, workers=4)
print(ranking.separation("momentum", "mean_reversion"))
Use this to rule strategies out. A strategy can die under a rate shock. Its edge can disappear when you charge it correctly for each trade. Both results are worth the five seconds of CPU time.
Read this before you conclude
Good results here do not predict real returns
The price process comes from a known model, so a strategy that fits that model's structure will look excellent and teach you nothing. A strategy that fails here is the informative case, because it broke against a live order book under honest impact costs.
Momentum can work here for a reason real markets do not supply
Returns trend, because the mispricing process has a herding term with a dial on it. The shipped pt-v14 turns that dial well down: momentum_theta sits at 0.0186 and return autocorrelation at lag one reads +0.0114 against a real band of -0.08 to 0.06, so it is in band. pt-v1 ships the same knob at 0.25 and measures +0.249. Either way the dial exists and real markets have no equivalent.
pt-v14: the certified panel, 30 seeds, 40 instruments, 252 days.
One venue, no latency, no strategic counterparties
Orders arrive instantly, there is one book per name, and you trade against a market maker and aggregate flow, never against agents that adapt to you.