> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/fatelessdev/autonome/llms.txt
> Use this file to discover all available pages before exploring further.

# trading.getPortfolioHistory

> Fetch time-series portfolio value data with adaptive downsampling

## Description

Retrieves historical portfolio net asset value (NAV) over time, grouped by model. Supports date range filtering and automatic downsampling to optimize chart rendering performance. Essential for displaying portfolio performance charts, calculating drawdowns, and analyzing historical returns.

## Input Schema

<ParamField path="variant" type="VariantId" optional>
  Filter portfolio history by model variant. Valid values: `"Apex"`, `"Trendsurfer"`, `"Contrarian"`, `"Sovereign"`. Omit to fetch history from all variants.
</ParamField>

<ParamField path="startDate" type="string" optional>
  ISO 8601 datetime string for the start of the date range (e.g., `"2024-01-01T00:00:00.000Z"`). Defaults to earliest available data if omitted.
</ParamField>

<ParamField path="endDate" type="string" optional>
  ISO 8601 datetime string for the end of the date range (e.g., `"2024-12-31T23:59:59.999Z"`). Defaults to current time if omitted.
</ParamField>

<ParamField path="maxPoints" type="number" optional>
  Maximum number of data points to return. Used for downsampling when dataset is large. Must be between 100 and 15,000. Defaults to an appropriate value based on the query (aggregate mode needs more points since data spans multiple model-variant combinations).
</ParamField>

### TypeScript Type

```typescript theme={null}
type PortfolioHistoryInput = {
  variant?: "Apex" | "Trendsurfer" | "Contrarian" | "Sovereign";
  startDate?: string; // ISO 8601 datetime
  endDate?: string;   // ISO 8601 datetime
  maxPoints?: number; // min: 100, max: 15000
};
```

## Output Schema

<ResponseField name="history" type="PortfolioSnapshot[]">
  Array of portfolio value snapshots over time.

  <Expandable title="PortfolioSnapshot properties">
    <ResponseField name="id" type="string" required>
      Unique identifier for the portfolio snapshot (UUID).
    </ResponseField>

    <ResponseField name="modelId" type="string" required>
      Database ID of the model this snapshot belongs to.
    </ResponseField>

    <ResponseField name="netPortfolio" type="string" required>
      Total portfolio value in USD as a string (e.g., `"10234.56"`). Stored as TEXT to preserve precision. Convert to number for calculations: `parseFloat(netPortfolio)`.
    </ResponseField>

    <ResponseField name="createdAt" type="string" required>
      ISO 8601 timestamp when this snapshot was first created.
    </ResponseField>

    <ResponseField name="updatedAt" type="string" required>
      ISO 8601 timestamp when this snapshot was last updated.
    </ResponseField>

    <ResponseField name="model" type="ModelInfo" optional>
      Information about the model that owns this portfolio.

      <Expandable title="ModelInfo properties">
        <ResponseField name="name" type="string" required>
          Display name of the model (e.g., `"gpt-4o"`, `"claude-3-5-sonnet-20241022"`).
        </ResponseField>

        <ResponseField name="variant" type="VariantId" optional>
          Variant classification: `"Apex"`, `"Trendsurfer"`, `"Contrarian"`, or `"Sovereign"`.
        </ResponseField>

        <ResponseField name="openRouterModelName" type="string" optional>
          OpenRouter model identifier if applicable (e.g., `"openai/gpt-4o"`).
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="resolution" type="DownsampleResolution" required>
  Time resolution of the returned data after downsampling. Indicates the time bucket size used:

  * `"1m"` - 1-minute intervals
  * `"5m"` - 5-minute intervals
  * `"15m"` - 15-minute intervals
  * `"1h"` - 1-hour intervals
  * `"4h"` - 4-hour intervals

  The resolution is automatically determined based on the data range and `maxPoints` parameter.
</ResponseField>

### TypeScript Type

```typescript theme={null}
type DownsampleResolution = "1m" | "5m" | "15m" | "1h" | "4h";

type PortfolioHistoryResponse = {
  history: {
    id: string;
    modelId: string;
    netPortfolio: string;
    createdAt: string;
    updatedAt: string;
    model?: {
      name: string;
      variant?: "Apex" | "Trendsurfer" | "Contrarian" | "Sovereign";
      openRouterModelName?: string;
    };
  }[];
  resolution: DownsampleResolution;
};
```

## Example Usage

### Basic Portfolio Chart

```typescript theme={null}
import { useQuery } from "@tanstack/react-query";
import { orpc } from "@/server/orpc/client";
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend } from 'recharts';

function PortfolioChart() {
  const { data, isLoading } = useQuery(
    orpc.trading.getPortfolioHistory.queryOptions({
      input: {}
    })
  );

  if (isLoading) return <div>Loading chart...</div>;

  // Transform data for chart
  const chartData = data?.history.map(snapshot => ({
    timestamp: new Date(snapshot.createdAt).getTime(),
    value: parseFloat(snapshot.netPortfolio),
    model: snapshot.model?.name ?? 'Unknown',
  })) ?? [];

  return (
    <div>
      <h3>Portfolio Performance</h3>
      <p>Resolution: {data?.resolution}</p>
      <LineChart width={800} height={400} data={chartData}>
        <CartesianGrid strokeDasharray="3 3" />
        <XAxis 
          dataKey="timestamp" 
          tickFormatter={(ts) => new Date(ts).toLocaleDateString()}
        />
        <YAxis 
          tickFormatter={(value) => `$${value.toLocaleString()}`}
        />
        <Tooltip 
          labelFormatter={(ts) => new Date(ts).toLocaleString()}
          formatter={(value) => [`$${Number(value).toFixed(2)}`, 'Portfolio Value']}
        />
        <Legend />
        <Line type="monotone" dataKey="value" stroke="#8884d8" name="Portfolio" />
      </LineChart>
    </div>
  );
}
```

### Filter by Date Range

```typescript theme={null}
import { useQuery } from "@tanstack/react-query";
import { orpc } from "@/server/orpc/client";
import { useState } from "react";

function DateRangeChart() {
  const [startDate] = useState(() => {
    const date = new Date();
    date.setDate(date.getDate() - 7); // Last 7 days
    return date.toISOString();
  });

  const { data } = useQuery(
    orpc.trading.getPortfolioHistory.queryOptions({
      input: {
        startDate,
        endDate: new Date().toISOString(),
        maxPoints: 500,
      }
    })
  );

  const totalReturn = data?.history.length ? 
    parseFloat(data.history[data.history.length - 1].netPortfolio) - parseFloat(data.history[0].netPortfolio)
    : 0;

  const returnPct = data?.history.length ?
    (totalReturn / parseFloat(data.history[0].netPortfolio)) * 100
    : 0;

  return (
    <div>
      <h3>Last 7 Days Performance</h3>
      <p>Resolution: {data?.resolution}</p>
      <p style={{ color: totalReturn >= 0 ? 'green' : 'red' }}>
        Return: ${totalReturn.toFixed(2)} ({returnPct >= 0 ? '+' : ''}{returnPct.toFixed(2)}%)
      </p>
      {/* Chart component */}
    </div>
  );
}
```

### Filter by Variant

```typescript theme={null}
import { useQuery } from "@tanstack/react-query";
import { orpc } from "@/server/orpc/client";

function ApexPerformance() {
  const { data } = useQuery(
    orpc.trading.getPortfolioHistory.queryOptions({
      input: {
        variant: "Apex",
        maxPoints: 1000,
      }
    })
  );

  // Group by model for multi-line chart
  const seriesData = data?.history.reduce((acc, snapshot) => {
    const modelName = snapshot.model?.name ?? 'Unknown';
    if (!acc[modelName]) acc[modelName] = [];
    acc[modelName].push({
      timestamp: snapshot.createdAt,
      value: parseFloat(snapshot.netPortfolio),
    });
    return acc;
  }, {} as Record<string, Array<{ timestamp: string; value: number }>>);

  return (
    <div>
      <h3>Apex Variant Performance</h3>
      <p>Resolution: {data?.resolution}</p>
      <p>Models tracked: {Object.keys(seriesData ?? {}).length}</p>
      {/* Multi-line chart showing each Apex model */}
    </div>
  );
}
```

### Calculate Performance Metrics

```typescript theme={null}
import { useQuery } from "@tanstack/react-query";
import { orpc } from "@/server/orpc/client";

function PerformanceMetrics() {
  const { data } = useQuery(
    orpc.trading.getPortfolioHistory.queryOptions({
      input: { maxPoints: 5000 }
    })
  );

  if (!data?.history.length) return <div>No data available</div>;

  const values = data.history.map(s => parseFloat(s.netPortfolio));
  const initialValue = values[0];
  const currentValue = values[values.length - 1];
  const totalReturn = currentValue - initialValue;
  const returnPct = (totalReturn / initialValue) * 100;

  // Calculate max drawdown
  let peak = values[0];
  let maxDrawdown = 0;
  values.forEach(value => {
    if (value > peak) peak = value;
    const drawdown = ((value - peak) / peak) * 100;
    if (drawdown < maxDrawdown) maxDrawdown = drawdown;
  });

  // Calculate daily returns for Sharpe ratio
  const dailyReturns: number[] = [];
  for (let i = 1; i < values.length; i++) {
    const ret = (values[i] - values[i - 1]) / values[i - 1];
    dailyReturns.push(ret);
  }

  const avgReturn = dailyReturns.reduce((sum, r) => sum + r, 0) / dailyReturns.length;
  const variance = dailyReturns.reduce((sum, r) => sum + Math.pow(r - avgReturn, 2), 0) / dailyReturns.length;
  const stdDev = Math.sqrt(variance);
  const sharpe = stdDev !== 0 ? (avgReturn / stdDev) * Math.sqrt(252) : 0; // Annualized

  return (
    <div>
      <h3>Performance Metrics</h3>
      <p>Resolution: {data.resolution}</p>
      <p>Data Points: {data.history.length}</p>
      <p>Initial Value: ${initialValue.toFixed(2)}</p>
      <p>Current Value: ${currentValue.toFixed(2)}</p>
      <p style={{ color: totalReturn >= 0 ? 'green' : 'red' }}>
        Total Return: ${totalReturn.toFixed(2)} ({returnPct >= 0 ? '+' : ''}{returnPct.toFixed(2)}%)
      </p>
      <p>Max Drawdown: {maxDrawdown.toFixed(2)}%</p>
      <p>Sharpe Ratio: {sharpe.toFixed(2)}</p>
    </div>
  );
}
```

### Compare Multiple Variants

```typescript theme={null}
import { useQueries } from "@tanstack/react-query";
import { orpc } from "@/server/orpc/client";

function VariantComparison() {
  const variants = ["Apex", "Trendsurfer", "Contrarian", "Sovereign"] as const;

  const queries = useQueries({
    queries: variants.map(variant => 
      orpc.trading.getPortfolioHistory.queryOptions({
        input: { variant, maxPoints: 1000 }
      })
    ),
  });

  const isLoading = queries.some(q => q.isLoading);
  if (isLoading) return <div>Loading comparison...</div>;

  return (
    <div>
      <h3>Variant Performance Comparison</h3>
      {variants.map((variant, idx) => {
        const data = queries[idx].data;
        if (!data?.history.length) return null;

        const initialValue = parseFloat(data.history[0].netPortfolio);
        const currentValue = parseFloat(data.history[data.history.length - 1].netPortfolio);
        const returnPct = ((currentValue - initialValue) / initialValue) * 100;

        return (
          <div key={variant}>
            <h4>{variant}</h4>
            <p>Resolution: {data.resolution}</p>
            <p>Return: <span style={{ color: returnPct >= 0 ? 'green' : 'red' }}>
              {returnPct >= 0 ? '+' : ''}{returnPct.toFixed(2)}%
            </span></p>
          </div>
        );
      })}
    </div>
  );
}
```

## Implementation Notes

* **Precision**: `netPortfolio` is stored as TEXT in the database to preserve decimal precision. Always use `parseFloat()` for calculations.
* **Downsampling**: Server automatically downsamples based on data range and `maxPoints` to optimize performance. The `resolution` field indicates the time bucket size used.
* **Retention Policy**: Raw data is retained for 7 days, then aggregated into hourly buckets for 30 days. Older data is aggregated into daily buckets.
* **Aggregate Mode**: When querying without a `variant`, data spans all model-variant combinations, requiring higher `maxPoints` for sufficient granularity.
* **Error Handling**: Throws an error on fetch failure with the original error message.

## Related Endpoints

* [trading.getPositions](/api/trading/get-positions) - Fetch current portfolio positions
* [trading.getTrades](/api/trading/get-trades) - Fetch closed trades for performance analysis
