Skip to the page
GUIDES/HOSTED APP

Hosted app

The hosted app at app.tradefloor.dev keeps simulated markets, called sessions, on a server. An agent or a bot trades them through an MCP server at /mcp, an HTTP API at /v1, or an API shaped like Alpaca's trading API at /broker/{session_id}. Hosted tools place orders, and time in a session moves only when the caller advances it. To run a model on your own machine instead, use the LLM adapters, or the local MCP server, which places no orders.

The hosted app is coming soon, and opens in beta. This page shows how agents and bots will connect to it.

Setup

Sign in to app.tradefloor.dev with your email address (there is no password) and make an API key on the Keys page, /keys. Send it as Authorization: Bearer tfk_... on every request. MCP clients that support OAuth, such as claude.ai, Claude Desktop, Claude Code and Cursor, can sign in instead of using a key, and the Connect page, /connect, shows the configuration for each client.

A key has one of three scopes, chosen when it is made:

ScopeWhat it can do
fullThe whole API, including forks, analysis, scoring and choosing a session's seed. Keys themselves are made and revoked only in the browser.
traderThe trading loop only: observe, orders, advance, close, fills, bars, series, events, its own sessions, describe and usage, and the whole Alpaca-shaped API. It opens a session only from a named template, on a seed it never sees. Use it for an agent under test.
read-onlyReads everything and changes nothing, for dashboards and reports.

A call outside the key's scope answers 403 forbidden and names the scope.

The loop

Every interface runs the same loop on a session:

  1. Open a session once and keep the session_id it returns.
  2. Observe the session: the clock, quotes, the account and open orders.
  3. Decide what to trade from that observation.
  4. Place orders, or cancel orders that are still waiting.
  5. Advance time by a step or more, and go back to the second step.
  6. Close the session. Its report carries caveats that say what the result does and does not show.

A market order fills at the start of the next step, when you advance, at the price the order book gives for its size. Right after it is placed its status is accepted. GET /v1/sessions/{id}/manifest returns the session's manifest, from which the tradefloor package rebuilds its market.

MCP

The MCP endpoint is https://app.tradefloor.dev/mcp, over streamable HTTP. With Claude Code and a key in TF_KEY:

claude mcp add --transport http tradefloor https://app.tradefloor.dev/mcp \
  --header "Authorization: Bearer $TF_KEY"

Other clients, such as claude.ai, Codex and Cursor, take the same URL, and the Connect page shows the configuration for each.

A first session is four tool calls: open_session, observe, place_order with a ticker, a side and a whole-share quantity, and advance. To try it, ask the model to open a session, buy 10 shares of the first ticker, advance one step and observe again. close_session ends the session and returns the report. Every write takes an idempotency_key argument, and the tools besides the loop include fork_session, list_fills, get_bars, get_events, list_presets, describe and get_usage. list_presets names the recommended preset, and every session records its own.

HTTP API

The same loop over /v1, with curl. A write carries an Idempotency-Key header with a new value for each action:

export TF_KEY=tfk_...
API=https://app.tradefloor.dev/v1
curl -H "Authorization: Bearer $TF_KEY" $API/describe

curl -X POST $API/sessions -H "Authorization: Bearer $TF_KEY" \
     -H "Idempotency-Key: $(uuidgen)" -H 'content-type: application/json' \
     -d '{"universe_size": 5, "seed": 7}'
# the answer carries "session_id"; set S to it

curl "$API/sessions/$S/observation?view=compact" -H "Authorization: Bearer $TF_KEY"
curl -X POST $API/sessions/$S/orders -H "Authorization: Bearer $TF_KEY" \
     -H "Idempotency-Key: $(uuidgen)" -H 'content-type: application/json' \
     -d '{"ticker": "AAA", "side": "buy", "quantity": 10}'
curl -X POST $API/sessions/$S/advance -H "Authorization: Bearer $TF_KEY" \
     -H "Idempotency-Key: $(uuidgen)" -H 'content-type: application/json' \
     -d '{"steps": 1}'
curl $API/sessions/$S/fills -H "Authorization: Bearer $TF_KEY"

POST /v1/sessions/{id}/close ends the session and returns its report. Sending a write again with the same Idempotency-Key returns the first result and does nothing twice, so a request that timed out can be resent safely.

Alpaca-shaped API

A bot written for alpaca-py trades a hosted session when its base URL is https://app.tradefloor.dev/broker/{session_id} and the API key goes where alpaca-py takes the secret. Sessions are opened on the HTTP API, and the facade trades them. Simulated time moves only when the bot calls /tradefloor/advance, which is where a live bot would sleep.

import os

from alpaca.trading.client import TradingClient
from alpaca.trading.enums import OrderSide, TimeInForce
from alpaca.trading.requests import MarketOrderRequest

KEY = os.environ["TF_KEY"]
url = f"https://app.tradefloor.dev/broker/{os.environ['SESSION_ID']}"
trading = TradingClient("tradefloor", KEY, url_override=url)

trading.submit_order(MarketOrderRequest(symbol="AAA", qty=10, side=OrderSide.BUY,
                                        time_in_force=TimeInForce.DAY))
trading.post("/tradefloor/advance", {"until": "next_open"})
print(trading.get_all_positions())

alpaca-py puts /v2 in front of every path, so the advance call goes to /broker/{session_id}/v2/tradefloor/advance, and the app serves the route there as well as without the /v2.

The facade serves the account, clock, calendar, assets, positions, market, limit, stop and stop-limit orders with day or gtc, fill activities, latest trades and quotes, snapshots, bars, and news built from the session's event log. It refuses, with a message naming what it does not serve, streaming, trailing-stop, bracket, OCO and OTO orders, notional and fractional orders, ioc, fok, opg and cls, extended hours, and replacing an order. A bot that depends on any of those needs changes beyond the base URL.

Hosted sessions and the library

Hosted sessionLibrary on your machine
Order quantityWhole shares only. A fraction is refused, never rounded.Any finite number of shares, fractions included.
When a market order fillsAt the start of the next step, when you advance.When act returns, against the book at that step.
Ticks in a step30 by default, set when the session opens65 by default
Warm-up historyhistory_days up to 260history_days up to 2,520
Analysis toolscheck_envelope, evaluate_strategies, rank_strategies and explain_price_move, with tighter caps a call (20 days, 6 seeds and 4 strategies)The local MCP server's limits, or none through the library

Troubleshooting

Every error has the same body over HTTP and MCP, {"code", "message", "hint"}, with retry_after in seconds when waiting and resending the same request will work.

CodeStatusWhat to do
unauthorized401Send a valid API key, or sign in.
forbidden403The key's scope does not cover the call. Use a key with a wider scope.
invalid_order400Fix the ticker, the quantity or the price. A fractional quantity lands here.
insufficient_buying_power403Send a smaller order or reduce a position first. The hint says how many shares would fit, and an order that lowers exposure is always accepted.
rate_limited429Wait retry_after seconds and resend.
quota_exceeded429A daily or capacity limit. The message says when it resets.
session_closed409The session is read-only now. Open a new one.
conflict409A race, or a reused idempotency key or client_order_id with a different request.
not_found404No such session or order for this account.

The free plan allows 20 names a session, 5 open sessions, and 2,000 simulated days and 300 compute seconds a day. GET /v1/usage, or the get_usage tool, gives the current limits and what is left today, and does not count as a call. A bot with only a trader key that cannot open a session needs open_from_template, or POST /v1/sessions/from-template.