Skip to main content

Overview

The simulator.getCompletedTradesFromDB endpoint retrieves historical completed trades for a specific AI model from the database, along with calculated performance statistics. Unlike the in-memory simulator state, this endpoint queries persistent database records to provide historical trade analysis.

Request

Parameters

modelId
string
The AI model ID to filter trades by. If not provided or empty, returns an empty result set.Model ID corresponds to records in the Models table and links trades to specific AI agents.
limit
number
default:"200"
Maximum number of trades to return. Must be a positive integer.
  • Default: 200
  • Maximum: 500 (hard cap enforced by the endpoint)
  • Used to prevent large result sets from impacting performance

Response

trades
array
Array of completed trades, ordered by close date (most recent first).
id
string
Unique trade identifier in format {toolCallId}:{index}.
modelId
string
AI model ID that executed the trade.
modelName
string | null
Human-readable model name (e.g., "Apex", "Trendsurfer").
symbol
string
Trading pair symbol (e.g., "BTC-USD").
direction
enum<string>
Trade direction: "LONG" or "SHORT".
notional
number | null
Position notional value in USD: quantity * exitPrice.
realizedPnl
number | null
Realized profit/loss from closing the position.
leverage
number | null
Leverage multiplier used when opening the position (if tracked).
confidence
number | null
AI model confidence level (0-1) when opening the position (if tracked).
closedAt
string | null
ISO 8601 timestamp when the position was closed.
invocationId
string
AI invocation ID that triggered the trade.
stats
object
Aggregated performance statistics calculated from the returned trades.
tradeCount
number
Total number of completed trades.
totalRealized
number
Sum of all realized P&L values.
expectancy
number | null
Average P&L per trade: totalRealized / tradeCount.null if no trades.
averageLeverage
number | null
Mean leverage across all trades with non-null leverage values.null if no leverage data.
medianLeverage
number | null
Median leverage across all trades with non-null leverage values.null if no leverage data.
maxLeverage
number | null
Maximum leverage value observed across all trades.null if no leverage data.
leverageValues
array<number>
Array of all non-null leverage values (used for calculations).
averageConfidence
number | null
Mean confidence level across all trades with non-null confidence values.null if no confidence data.
medianConfidence
number | null
Median confidence level across all trades with non-null confidence values.null if no confidence data.
confidenceValues
array<number>
Array of all non-null confidence values (used for calculations).

Code Example

import { useQuery } from "@tanstack/react-query";
import { orpc } from "@/server/orpc/client";

export function TradeHistory({ modelId }: { modelId: string }) {
  const { data, isLoading, error } = useQuery(
    orpc.simulator.getCompletedTradesFromDB.queryOptions({
      input: {
        modelId,
        limit: 100, // optional, defaults to 200
      },
    })
  );

  if (isLoading) return <div>Loading trades...</div>;
  if (error) return <div>Error: {error.message}</div>;
  if (!data) return null;

  const { trades, stats } = data;

  return (
    <div className="space-y-6">
      {/* Performance Summary */}
      <div className="grid grid-cols-4 gap-4">
        <div>
          <h3 className="text-sm text-gray-500">Total Trades</h3>
          <p className="text-2xl font-bold">{stats.tradeCount}</p>
        </div>
        <div>
          <h3 className="text-sm text-gray-500">Total P&L</h3>
          <p className={`text-2xl font-bold ${
            stats.totalRealized >= 0 ? "text-green-500" : "text-red-500"
          }`}>
            ${stats.totalRealized.toFixed(2)}
          </p>
        </div>
        <div>
          <h3 className="text-sm text-gray-500">Expectancy</h3>
          <p className="text-2xl">
            {stats.expectancy !== null 
              ? `$${stats.expectancy.toFixed(2)}` 
              : "N/A"}
          </p>
        </div>
        <div>
          <h3 className="text-sm text-gray-500">Avg Leverage</h3>
          <p className="text-2xl">
            {stats.averageLeverage !== null
              ? `${stats.averageLeverage.toFixed(2)}x`
              : "N/A"}
          </p>
        </div>
      </div>

      {/* Trade List */}
      <div>
        <h3 className="text-lg font-bold mb-4">Recent Trades</h3>
        <div className="space-y-2">
          {trades.map((trade) => (
            <div key={trade.id} className="border p-4 rounded">
              <div className="flex justify-between items-start">
                <div>
                  <span className="font-bold">{trade.symbol}</span>
                  <span className={`ml-2 px-2 py-1 rounded text-xs ${
                    trade.direction === "LONG" 
                      ? "bg-green-100 text-green-700" 
                      : "bg-red-100 text-red-700"
                  }`}>
                    {trade.direction}
                  </span>
                </div>
                <div className="text-right">
                  <p className={`font-bold ${
                    (trade.realizedPnl ?? 0) >= 0 
                      ? "text-green-500" 
                      : "text-red-500"
                  }`}>
                    {trade.realizedPnl !== null
                      ? `$${trade.realizedPnl.toFixed(2)}`
                      : "N/A"}
                  </p>
                  {trade.closedAt && (
                    <p className="text-xs text-gray-500">
                      {new Date(trade.closedAt).toLocaleString()}
                    </p>
                  )}
                </div>
              </div>
              <div className="mt-2 grid grid-cols-3 gap-2 text-sm text-gray-600">
                {trade.notional !== null && (
                  <div>Notional: ${trade.notional.toFixed(2)}</div>
                )}
                {trade.leverage !== null && (
                  <div>Leverage: {trade.leverage}x</div>
                )}
                {trade.confidence !== null && (
                  <div>Confidence: {(trade.confidence * 100).toFixed(0)}%</div>
                )}
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

Data Source

This endpoint queries the database, not the in-memory simulator state:

Tool Calls Table

Trades are reconstructed from tool call records:
  • CLOSE_POSITION tool calls contain closed position data
  • CREATE_POSITION tool calls contain leverage and confidence metadata
  • Trades are linked via invocationId to match opens with closes

Metadata Structure

Tool call metadata is JSON containing:
// CLOSE_POSITION metadata
{
  "closedPositions": [
    {
      "symbol": "BTC-USD",
      "side": "LONG",
      "quantity": 0.5,
      "exitPrice": 45000,
      "realizedPnl": 1250.50,
      "closedAt": "2025-03-15T10:30:00Z"
    }
  ]
}

// CREATE_POSITION metadata
{
  "decisions": [
    {
      "symbol": "BTC-USD",
      "leverage": 3,
      "confidence": 0.85
    }
  ]
}

Matching Logic

  1. Query all CLOSE_POSITION calls for the model (ordered by date)
  2. Extract closed position records from metadata
  3. Query all CREATE_POSITION calls for the same invocations
  4. Build an index of leverage/confidence by symbol from CREATE calls
  5. Match each closed position with its creation metadata

Statistics Calculation

Expectancy

Average profit per trade:
expectancy = totalRealized / tradeCount
Positive expectancy indicates profitable trading on average.

Leverage Statistics

  • Average: Mean of all non-null leverage values
  • Median: Middle value of sorted leverage values
  • Max: Highest leverage used across all trades
Only trades with tracked leverage are included in calculations.

Confidence Statistics

  • Average: Mean of all non-null confidence values (0-1)
  • Median: Middle value of sorted confidence values
Only trades with tracked confidence are included in calculations.

Null Handling

Statistics gracefully handle missing data:
  • Fields with null values are excluded from calculations
  • If no valid data exists, statistics return null
  • Empty arrays are returned when no values are available

Use Cases

Performance Analytics Dashboard

Display historical performance metrics for AI trading models:
const { data } = useQuery(
  orpc.simulator.getCompletedTradesFromDB.queryOptions({
    input: { modelId: "model-apex" }
  })
);

// Show win rate, expectancy, total P&L

Model Comparison

Compare performance across different AI models:
const apexTrades = useQuery(
  orpc.simulator.getCompletedTradesFromDB.queryOptions({
    input: { modelId: "apex-model-id" }
  })
);

const sovereignTrades = useQuery(
  orpc.simulator.getCompletedTradesFromDB.queryOptions({
    input: { modelId: "sovereign-model-id" }
  })
);

// Compare stats.expectancy, stats.averageLeverage, etc.

Trade Audit Trail

Review individual trade execution history:
const { data } = useQuery(
  orpc.simulator.getCompletedTradesFromDB.queryOptions({
    input: { modelId: "model-id", limit: 500 }
  })
);

// Export trades for analysis
const csv = data.trades.map(t => 
  `${t.symbol},${t.direction},${t.realizedPnl},${t.closedAt}`
).join('\n');

Leverage Risk Analysis

Analyze leverage usage patterns:
const { data } = useQuery(
  orpc.simulator.getCompletedTradesFromDB.queryOptions({
    input: { modelId: "model-id" }
  })
);

const { averageLeverage, maxLeverage, leverageValues } = data.stats;

// Visualize leverage distribution

Performance Considerations

Query Optimization

  • Database query uses indexed columns (modelId, toolCallType, createdAt)
  • Limit parameter caps result set size (max 500)
  • Results ordered by createdAt DESC for recent trades first

Metadata Parsing

JSON metadata is parsed on-the-fly:
  • Expect ~1-5ms parsing time per trade
  • Large result sets (500 trades) may take 100-500ms total
  • Consider pagination for very large datasets

Caching Strategy

// Cache for 5 minutes (trades don't change frequently)
const { data } = useQuery({
  ...orpc.simulator.getCompletedTradesFromDB.queryOptions({
    input: { modelId }
  }),
  staleTime: 5 * 60 * 1000, // 5 minutes
});

Error Handling

ScenarioBehavior
No modelId providedReturns empty trades: [] and zero stats
Invalid modelIdReturns empty result set (no error)
limit exceeds 500Capped at 500 automatically
Database connection errorThrows error (caught by React Query)
Malformed JSON metadataSkips invalid records, logs error
This endpoint queries historical database records, not the current simulator state. Trades shown here may not reflect positions currently open in the simulator.

Build docs developers (and LLMs) love