Skip to the page
GETTING STARTED/QUICKSTART

Quickstart

tradefloor is a simulated stock market with a limit order book and an economy, for testing trading strategies and AI agents. This page installs it, runs a small market, scores a simple agent on that market and explains the result, in about five minutes.

The snippets on this page run in order in one Python session, and each one builds on the last. A block labeled Output shows what the code above it prints on tradefloor 0.8.6 with the default preset. To run the whole page at once, download quickstart.py and run python quickstart.py.

Install

pip install tradefloor

tradefloor needs Python 3.11 or later and installs no other packages. Install lists the platforms with prebuilt wheels and the extras that some features need.

import tradefloor as tf

print(tf.__version__, tf.preset_record()["preset"])
0.8.6 pt-v20

The first value is the package version. The second is the default preset, the named and frozen set of model coefficients a market runs on when you do not choose one.

Run a small market

import struct

universe = tf.Universe.random(5, seed=11)       # five made-up companies
engine = tf.Engine(seed=42, universe=universe)  # a market on the default preset
engine.run_days(20)                             # run it for 20 trading days

bars = engine.bars(grain="day")                 # one row per company per day
print(bars.num_rows, "daily bars:", ", ".join(bars.columns))

closes = struct.unpack("<5d", engine.prices())  # each company's price now
for company, close in zip(universe, closes):
    print(f"{company.ticker}  {company.sector:<22} "
          f"{company.initial_price:7.2f} -> {close:7.2f}")
100 daily bars: day, bar, instrument_id, open, high, low, close, volume
AAA  technology              135.37 ->  133.98
AAB  financial_services      282.62 ->  290.24
AAC  healthcare               28.59 ->   29.34
AAD  energy                   12.73 ->   13.42
AAE  consumer_discretionary   31.22 ->   32.41

The universe is the list of companies the market trades, in order. Universe.random(5, seed=11) makes five companies with made-up tickers, sectors and fundamentals, and it makes the same five every time. The engine's seed, 42, fixes every random draw the market takes, so this block prints the same numbers on every run.

The first line of output counts 100 daily bars: one for each of the 5 companies on each of the 20 days, each with an open, high, low, close and volume. Each line after it is one company: its ticker, its sector, its price on day zero and its price when the 20 days ended. AAA, a technology company, started at 135.37 and ended at 133.98.

bars is an Arrow table, which pandas, polars, pyarrow and duckdb read without copying. engine.prices() returns each company's last price as little-endian float64 bytes in roster order, and struct.unpack("<5d", ...) reads the five of them with no extra package. engine.truth() gives each company's fair value and the factors behind every price move. Engine and data lists every table.

Score a simple agent

An agent is any object with an act(obs) method. The harness calls it at every decision step, six a day by default, and act returns the orders to send: a dict of ticker to a number of shares, positive to buy and negative to sell, or None to send nothing.

class EqualWeight:
    """Put about 100,000 into each company at the first step, then hold."""

    def act(self, obs):
        if obs.step == 0:
            return {ticker: int(100_000 / price)      # a number of shares
                    for ticker, price in zip(obs.tickers, obs.prices)}
        return None                                  # hold


scores = tf.evaluate({"equal": EqualWeight()},
                     seed=42, universe=universe, days=20)
print(scores["equal"])
Scorecard('equal', pnl=13,036, return=+1.30%, trades=5, impact=+0.38bps, sharpe=+1.62, vol=10.4%, in_market=100%, exposure=0.51x)

tf.evaluate builds its own market from the seed and universe you pass, gives the agent 1,000,000 in cash and runs it for 20 days of six steps each. The orders fill against the simulated order book, so every trade pays the spread and moves the price a little. The quantities are shares, and a value such as 0.2 buys a fifth of one share (shares and portfolio weights).

Understand the result

FieldHereWhat it means
pnl13,036Net worth at the end minus the 1,000,000 the agent started with.
return+1.30%The same profit as a percentage of the starting cash.
trades5Fills. Each of the five market orders here, one per company, filled at once and counts once. A limit order counts once for each part that fills.
impact+0.38bpsHow far the agent's own trades moved the prices of what it traded, against the same market with nobody trading, weighted by the money traded. Positive means the move cost the agent.
sharpe+1.62The mean daily return over its standard deviation, annualized over 252 days, with no risk-free rate subtracted. Under 20 days it prints as n/a (short run).
vol10.4%The annualized volatility of the daily returns.
in_market100%The share of steps that ended with a position open.
exposure0.51xThe average of all positions, long and short, as a multiple of net worth. 1.0 is fully invested.

The scorecard adds errors=N when a step raised or the market refused an order, and the lines are in scores["equal"].errors. Rejected orders explains each kind of line and what to change.

A profit on its own says little, so score a reference agent on the same market:

scores = tf.evaluate({"equal": EqualWeight(),
                      "buy_and_hold": tf.baselines.BuyAndHold()},
                     seed=42, universe=universe, days=20)
print(scores["buy_and_hold"])
print({name: round(gap) for name, gap in tf.versus_buy_and_hold(scores).items()})
Scorecard('buy_and_hold', pnl=23,765, return=+2.38%, trades=5, impact=+0.43bps, sharpe=+1.63, vol=19.3%, in_market=100%, exposure=0.94x)
{'equal': -10729}

Each agent trades its own copy of the same starting market. Buy-and-hold put nearly all its cash into the five companies (exposure 0.94x) and made 23,765. The equal-weight agent put in about half (0.51x) and made 13,036, so versus_buy_and_hold puts it 10,729 behind. This is one market on one seed, and another seed can reverse the order, so compare agents across seeds before you draw a conclusion.

Reproducing a run

Run this page again on tradefloor 0.8.6 and every number comes out the same. The market depends on the universe, the economy on day zero, the seed, the preset and the package version. It also depends on the agent's orders, because orders move prices. Reproducibility lists each condition, says which ones hold across platforms and releases, and shows how to save a run as a manifest that someone else can replay.

Next steps

Compare agents across seeds

One seed is one sample. tf.rank runs the same agents on many seeds and counts, seed by seed, which one did better, as Compare strategies shows.

Fork a market

Copy a running market, change one input in one copy and run both on. The copies share their past exactly, so a later difference comes from the change. Fork a market walks through it.

Connect an LLM

Run an agent built with the OpenAI Agents SDK, PydanticAI, LangGraph or your own model call, and record its answers so the run replays without the model. LLM adapters shows how.

Train an RL policy

tradefloor.gym wraps a market as a Gymnasium environment, after pip install "tradefloor[rl]", and Gym environment documents it.

If something on this page fails, Troubleshooting lists the usual causes, and the Glossary defines the terms used here.