Forks and counterfactuals
Reference for World, agree, compare, their result types, and the tools for checkpoints and replay. A counterfactual runs one agent in two copies of a market that differ in one variable.
Checkpoints and forks
A fork copies a running market so you can run two futures from one past. Everything before the fork is identical in both copies, bit for bit, so a difference that appears afterwards comes from the input you changed.
import tradefloor as tf universe = tf.Universe.random(40, seed=7) engine = tf.Engine(seed=42, universe=universe) engine.run_days(60) mark = tf.Checkpoint.of(engine, universe=universe, seed=42) text = mark.to_json() # outlives the process calm, hiked = mark.branch(2) # two engines, identical up to day 60 hiked.pin_macro(corporate_bond_yield=0.09) # they diverge only from here calm.run_days(20) hiked.run_days(20)
| Cost | Survives the process | |
|---|---|---|
tf.branch(engine, 2) | under 1 ms | no |
Checkpoint.resume() | seconds, growing with the order log | yes |
branch copies the engine state, every column and each random stream's position. Checkpoint stores the order log and rebuilds the engine by replaying it, so cite the checkpoint in a published result. A checkpoint records the roster's fingerprint and refuses to load against a roster that changed, because two rosters can share tickers and differ in fundamentals. Two futures from one past runs a fork with its output.
Counterfactuals
A World holds the market, one agent, its portfolio and the macro path. Forking a world copies all of them, so the same agent meets two markets that differ in one input:
class Mine:
def act(self, obs):
return {"AAA": 300} if obs.step == 0 else None
u = tf.Universe.random(24, seed=7)
world = tf.World(seed=7, universe=u, agent=Mine())
world.run(50) # a shared past
control, stress = world.fork("control", "stress")
stress.apply(tf.Scenario.load("liquidity_crisis"))
started = tf.agree(control, stress) # identical, bit for bit
control.run(80)
stress.run(80)
result = tf.compare(control, stress, agreement=started)
print(result.render())agree checks that two arms match in engine state, order books, portfolio, agent state and stream positions. compare finds the first step where they differ, separately for the macro path, the agent's decisions, its orders, the prices and the portfolios, and summarizes both arms. world.manifest() records the run like any other, and an external agent runs here the same way, through an adapter. A counterfactual with an agent runs this example with its output and reads the comparison.
The answer describes this simulated market under the one change you made. A claim about a real market needs evidence from a real market.
World
A market, the agent or agents trading in it, and the macro path they run under. It is available at the top level as tradefloor.World.
World(
*,
seed: int,
universe: Sequence[Instrument],
agent: Any = None,
agents: dict[str, Any] | None = None,
pins: dict[str, Any] | None = None,
macro: Macro | None = None,
cash: float = 1_000_000.0,
max_leverage: float | None = 2.0,
steps_per_day: int = 6,
ticks_per_step: int = 65,
start: tuple[int, int, int] = (9, 30, 3),
model: str | ModelParams | None = None,
label: str = "",
on_refusal: str = "raise",
)
agree and compare
tf.agree(a: World, b: World) -> Agreement
tf.compare(
control: World,
treatment: World,
*,
agreement: Agreement | None = None,
) -> Comparison
Checkpointing and replay
Checkpoint.of(engine, *, universe: Sequence[Instrument],
seed: int, macro: Macro | None = None,
label: str = "") -> Checkpoint
tf.branch(engine, count: int = 2, *,
universe: Sequence[Instrument] | None = None,
seed: int | None = None,
macro: Macro | None = None) -> list[Engine]
tf.replay(log, *, seed: int,
universe: Sequence[Instrument],
macro: Macro | None = None,
model: str | ModelParams | None = None,
until: int | None = None) -> Engine
See also
Scenarios builds what World.apply takes, and RunManifest documents what a manifest carries.