USE IT/AGENT INTEGRATIONS

Agent integrations

Bring your agent. Tradefloor keeps the market and the evaluation loop fixed. Adapters connect plain Python, the OpenAI Agents SDK, PydanticAI, LangGraph and FinRobot to the same observation, decision and execution loop.

Tradefloor owns the simulated market, execution, portfolio accounting, scenarios, checkpoints, forks and evaluation. The external agent owns model calls, prompts, tools, orchestration and the portfolio decision. The Tradefloor market and evaluation loop stay fixed across adapters, which isolates framework-specific behaviour to the agent and adapter boundary.

INTEGRATIONSUPPORTINSTALL EXTRA
Plain PythonGeneric callablenone
OpenAI Agents SDKAdapteropenai-agents
PydanticAIAdapterpydantic-ai
LangGraphAdapterlanggraph
FinRobotExisting financial-agent integrationfinrobot

The two halves

THE AGENT RUNTIME OWNS

Model, prompts, tools, orchestration and the decision.

GENERAL
Plain PythonOpenAI Agents SDKPydanticAILangGraph
FINANCIAL
FinRobot
observation decision execution ONE LOOP, EVERY ADAPTER
TRADEFLOOR OWNS

Market, execution, portfolio, scenarios, forks and scoring.

order bookportfolio accountingscenarioscheckpoints + forksevaluation

Adapter parity check. The four offline examples drive the real framework with a deterministic function standing where a model would sit, so what runs offline is the adapter and the framework. Parity lives in the library's own tests: tests/test_integrations.py holds one market fixed and runs a shared contract list against every adapter, and tests/test_integration_examples.py executes all four examples and asserts the scorecards they print. Each example sizes its own book, so two of their rows differ by market as well as by framework, and the contract checks speak to the boundary the adapters share and not to how an agent of yours will behave.

The decision boundary

Every integration returns a decision in one shape, and none of them touches engine state.

{"actions": [{"symbol": "TECH_A",
              "side": "BUY",
              "quantity": 1200}],
 "rationale": "one line, for the record"}

Tradefloor validates the decision against the current market before it executes, so a well-formed decision this market cannot take is refused rather than partly filled. The signatures, the exception classes and the participation cap are on the integrations API.

RULEWHAT IT MEANS
actions: []A hold. The agent looked at the market and declined to trade.
no actions keyInvalid, and refused by name. A malformed decision must never score as a legitimate hold, because no scorecard afterwards can tell the two apart.
unknown symbolRefused. Every symbol is checked against the listed universe.
unknown fieldRefused by name. A silently dropped stop_loss would leave an agent believing it has protection this market cannot give.
limit price, order typeRefused with a message naming the missing capability. Orders sweep the live book with a signed quantity.

Plain Python

from tradefloor.integrations.callable import (
    callable_agent,
)

def rule(payload):
    return {"actions": [...]}

agent = callable_agent(rule)
scores = tf.evaluate({"mine": agent},
    seed=4242, universe=roster, days=5)
no extra dependency

The callable receives the allowlisted serialized observation, which is what keeps hidden simulator state out of reach. A policy that genuinely needs the Observation is a native Tradefloor agent and implements act directly: see Agents.

Example: examples/integrations/callable/five_days.py. Signatures: the integrations API.

OpenAI Agents SDK

from agents import Agent
from tradefloor.integrations.openai_agents import (
    OpenAIAgentsAdapter,
)

pm = Agent(name="Portfolio Manager",
    instructions="You manage a concentrated"
                 " equity book.")

agent = OpenAIAgentsAdapter(pm)
scores = tf.evaluate({"pm": agent},
    seed=4242, universe=roster, days=5)
pip install "tradefloor[openai-agents]"

Tradefloor binds the shared decision schema to the agent you supply without mutating it. Existing instructions, tools, model settings, hooks and guardrails stay part of the agent runtime and still run inside the decision.

A run can be recorded to a transcript and replayed later without another model call.

Example: examples/integrations/openai_agents/five_days.py. Compatibility, error behaviour, tracing and version notes: the integrations API.

PydanticAI

from dataclasses import dataclass
from pydantic_ai import Agent, RunContext
from tradefloor.integrations.pydantic_ai import (
    PydanticAIAdapter,
)

@dataclass
class Desk:
    risk_limit: float

pm = Agent("openai:gpt-5.2", deps_type=Desk,
    instructions="You are a systematic"
                 " portfolio manager.")

@pm.tool
def house_risk_limit(ctx: RunContext[Desk]) -> float:
    return ctx.deps.risk_limit

agent = PydanticAIAdapter(pm,
    deps=Desk(risk_limit=0.25))
scores = tf.evaluate({"pm": agent},
    seed=4242, universe=roster, days=5)
pip install "tradefloor[pydantic-ai]"

The adapter uses PydanticAI's typed-output path for Tradefloor decisions while preserving the agent's tools and its dependency object. Tradefloor does not replace deps, so a tool that needs market data receives it through your own dependency design.

Example: examples/integrations/pydantic_ai/rate_shock.py. Output binding, error behaviour and instrumentation: the integrations API.

LangGraph

from typing import TypedDict
from langgraph.graph import END, START, StateGraph
from tradefloor.integrations.langgraph import (
    LangGraphAdapter,
)

class TradeState(TypedDict):
    observation: dict
    decision: dict

def decide(state):
    payload = state["observation"]
    return {"decision": {"actions": [],
                         "rationale": "held"}}

builder = StateGraph(TradeState)
builder.add_node("decide", decide)
builder.add_edge(START, "decide")
builder.add_edge("decide", END)

agent = LangGraphAdapter(builder.compile())
scores = tf.evaluate({"graph": agent},
    seed=4242, universe=roster, days=5)
pip install "tradefloor[langgraph]"

Tradefloor invokes a compiled graph and extracts a decision from the state it returns. A graph with its own state shape is supported through input_builder and output_parser. Use input_builder when the graph expects a different state schema.

Example: examples/integrations/langgraph/rate_shock.py. Input and output mapping, interrupts and tracing: the integrations API.

TRADEFLOOR CHECKPOINT

Simulated market state: prices, the book, the macro path, the variance process and the RNG.

LANGGRAPH CHECKPOINT

Workflow state: the graph's channel values and which node runs next. It carries no engine, no book, no prices and no RNG.

Neither reconstructs the other, and both directions fail quietly, so keep the pair together by run.

FinRobot

FinRobot is Tradefloor's existing financial-agent integration. It predates the general adapter layer, and the shared layer the other adapters sit on was derived from it.

import tradefloor as tf
from tradefloor.counterfactual import (
    World, agree, compare,
)
from tradefloor.integrations.finrobot import (
    FinRobotAdapter, Transcript,
)

agent = FinRobotAdapter(mode="replay",
    transcript=Transcript.load(FIXTURE),
    fundamentals=FUNDAMENTALS,
    objective=OBJECTIVE, every=6)

world = World(seed=4242, universe=roster,
    agent=agent, pins=BASE_PINS)
world.run(days=20)

control, shock = world.fork("control", "+200bps")
started = agree(control, shock)

shock.intervene(federal_funds_rate=0.06,
                corporate_bond_yield=0.075)
control.run(days=20)
shock.run(days=20)

report = compare(control, shock,
                 agreement=started)
print(report.render())
pip install "tradefloor[finrobot]"

The adapter implements the agent protocol World runs, so swapping it in changes no line of the market, the fork, the intervention or the comparison. mode="replay" replays a recorded run with no API key, no network and no FinRobot install; mode="live" with an llm_config calls FinRobot and can record a new Transcript.

The full example runs twenty days of shared history, checkpoints the world, forks it, applies the +200bps shock to one branch and compares what the same recorded agent did next.

# run the full example
python examples/integrations/finrobot/rate_shock.py

Full example: examples/integrations/finrobot/rate_shock.py. Study: Counterfactuals. Reference: integrations API.

Reproducibility

simulator determinismTradefloor is deterministic given the same simulator configuration and the same sequence of agent actions.
model nondeterminismA Tradefloor seed controls the simulated market, not the external model's sampling behaviour.
recorded replayA transcript records decisions and replays them later with no framework, provider, key or network.

A published result citing an external agent records the framework, the model, the provider, the generation configuration, the agent configuration, and the Tradefloor seed, preset and scenario. adapter.provenance() collects the first half. What a run has to carry to be reproducible at all is on citing a run, and an adapter's provenance belongs in the same manifest.

A recording is keyed to the market it was taken against, so a change to the roster, the seed, the cadence or the instructions ends the replay and names the step it stopped at. The transcript format and the key are on the integrations API.

All pages