Trades
The Trade endpoint returns the fill history for an account — every executed order that resulted in a position change. Use this to analyze your strategy’s trading activity, calculate performance metrics, or build a trade journal.
Trades vs. Orders
- Orders are instructions to buy or sell (may be pending, partially filled, filled, or canceled)
- Trades (also called “fills”) are the actual executions — completed transactions where contracts changed hands
An order with status 2 (Filled) will have a corresponding trade. Pending and canceled orders do not generate trades.
POST Trade/search
Get the trade/fill history for an account. Returns all trades executed during the current session.
URL: POST /api/topstep-sim/Trade/search
Authentication: Bearer token required.
Request body
{ "accountId": 12345, "startTimestamp": "2025-01-15T00:00:00Z", "endTimestamp": "2025-01-16T00:00:00Z"}| Field | Type | Required | Description |
|---|---|---|---|
accountId | number | Yes | Account ID |
startTimestamp | string | No | Only return fills at or after this time |
endTimestamp | string | No | Only return fills at or before this time |
Response
Returns up to 500 fills, newest first.
{ "success": true, "errorCode": 0, "errorMessage": null, "trades": [ { "id": 2, "accountId": 12345, "contractId": "CON.F.US.ENQ.H25", "creationTimestamp": "2025-01-15T14:45:12.000Z", "price": 21518.50, "profitAndLoss": null, "fees": 1.24, "side": 1, "size": 1, "voided": false, "orderId": 1000000043 }, { "id": 1, "accountId": 12345, "contractId": "CON.F.US.ENQ.H25", "creationTimestamp": "2025-01-15T14:30:05.000Z", "price": 21503.25, "profitAndLoss": null, "fees": 1.24, "side": 0, "size": 1, "voided": false, "orderId": 1000000042 } ]}| Field | Type | Description |
|---|---|---|
trades | array | List of trade/fill objects, newest first |
trades[].id | number | Trade index within the response |
trades[].accountId | number | Account ID |
trades[].contractId | string | Contract ID |
trades[].creationTimestamp | string | ISO 8601 timestamp of the fill |
trades[].price | number | Execution price |
trades[].profitAndLoss | null | Always null — fills are half-turns; compute round-trip P&L yourself |
trades[].fees | number | Commission/fees charged on the fill |
trades[].side | number | 0 = Buy, 1 = Sell |
trades[].size | number | Number of contracts filled |
trades[].voided | boolean | Always false |
trades[].orderId | number | The order ID that generated this trade |
Example
result = api("Trade/search", {"accountId": ACCOUNT_ID})
if result["success"]: trades = result.get("trades", []) print(f"Total trades: {len(trades)}")
for trade in trades: side = "BUY" if trade["side"] == 0 else "SELL" print(f" {trade['creationTimestamp']} — {side} {trade['size']} @ {trade['price']}")curl -X POST https://api.test-max.com/api/topstep-sim/Trade/search \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{"accountId": 12345}'Calculating performance from trades
You can compute key performance metrics directly from the trade history:
Total P&L
result = api("Trade/search", {"accountId": ACCOUNT_ID})# The API returns newest first — sort oldest first before pairingtrades = sorted(result.get("trades", []), key=lambda t: t["creationTimestamp"])
# Pair up buy/sell trades to calculate round-trip P&Ltotal_pnl = 0.0buys = []
for trade in trades: if trade["side"] == 0: # Buy buys.append(trade) elif trade["side"] == 1 and buys: # Sell (close) entry = buys.pop(0) pnl = (trade["price"] - entry["price"]) * trade["size"] * TICK_VALUE / TICK_SIZE total_pnl += pnl
print(f"Total P&L: ${total_pnl:,.2f}")Win rate
wins = 0losses = 0round_trips = []
# Pair trades into round tripsfor i in range(0, len(trades) - 1, 2): entry = trades[i] exit_trade = trades[i + 1]
if entry["side"] == 0: # Long trade pnl = exit_trade["price"] - entry["price"] else: # Short trade pnl = entry["price"] - exit_trade["price"]
if pnl > 0: wins += 1 elif pnl < 0: losses += 1
round_trips.append(pnl)
total_trades = wins + losseswin_rate = (wins / total_trades * 100) if total_trades > 0 else 0print(f"Win rate: {win_rate:.1f}% ({wins}W / {losses}L)")Trade count and frequency
from datetime import datetime
trades = sorted(result.get("trades", []), key=lambda t: t["creationTimestamp"])if len(trades) >= 2: first = datetime.fromisoformat(trades[0]["creationTimestamp"].replace("Z", "+00:00")) last = datetime.fromisoformat(trades[-1]["creationTimestamp"].replace("Z", "+00:00")) duration = last - first print(f"Session duration: {duration}") print(f"Trades: {len(trades)} over {duration}")Usage notes
- Trades are returned newest first (up to 500). Sort by
creationTimestampif you need chronological order. - Each fill is a separate trade object, even if the order was partially filled across multiple prices.
- The trade list is scoped to the current replay session. Starting a new session resets the trade history.
- Use
Trade/searchafter callingReplay/endto get the complete trade log for a session. The session results include an aggregate summary, whileTrade/searchgives you the individual fill details.