Skip to main content

Overview

The analytics.getRunInfo endpoint provides information about the current trading session, including when trading began. This is useful for calculating session duration, determining data availability, and contextualizing performance metrics.

Endpoint

orpc.analytics.getRunInfo.queryOptions({ input: {} })

Input Parameters

This endpoint takes no input parameters.

Response Schema

runStartTime
Date | null
The timestamp when trading began, determined by the earliest portfolio snapshot.Returns null if no portfolio snapshots exist (trading has not started).

How Run Start Time is Determined

The run start time is calculated by finding the earliest portfolio snapshot in the database:
export async function getRunStartTime(): Promise<Date | null> {
  const result = await db
    .select({ createdAt: portfolioSize.createdAt })
    .from(portfolioSize)
    .orderBy(asc(portfolioSize.createdAt))
    .limit(1);

  return result[0]?.createdAt ?? null;
}
Key points:
  • Uses the portfolioSize table, which tracks portfolio values over time
  • Finds the earliest createdAt timestamp
  • Returns null if the table is empty (no trading activity yet)
  • Does not distinguish between live trading and simulator modes

Use Cases

Calculate Session Duration

Determine how long the trading session has been running:
const { data } = useQuery(orpc.analytics.getRunInfo.queryOptions({ input: {} }));

if (data?.runStartTime) {
  const durationMs = Date.now() - data.runStartTime.getTime();
  const durationDays = durationMs / (1000 * 60 * 60 * 24);
  console.log(`Trading for ${durationDays.toFixed(2)} days`);
}

Contextualize Performance Metrics

Provide context for Sharpe ratio and other time-sensitive metrics:
if (data?.runStartTime) {
  const durationHours = (Date.now() - data.runStartTime.getTime()) / (1000 * 60 * 60);
  
  if (durationHours < 24) {
    console.warn('Sharpe ratio may be unreliable - less than 24 hours of data');
  }
}

Determine Data Availability

Check if historical data is available for analysis:
if (data?.runStartTime === null) {
  return <div>No trading data available yet. Start trading to see analytics.</div>;
}

Usage Examples

Display Session Uptime

import { useQuery } from '@tanstack/react-query';
import { orpc } from '@/server/orpc/client';
import { formatDistanceToNow } from 'date-fns';

function SessionUptime() {
  const { data, isLoading } = useQuery(
    orpc.analytics.getRunInfo.queryOptions({ input: {} })
  );

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

  if (!data?.runStartTime) {
    return <div>Trading session not started</div>;
  }

  return (
    <div>
      <h3>Session Info</h3>
      <p>Started: {data.runStartTime.toLocaleString()}</p>
      <p>Uptime: {formatDistanceToNow(data.runStartTime, { addSuffix: true })}</p>
    </div>
  );
}

Calculate Session Statistics

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

function SessionStats() {
  const { data } = useQuery(
    orpc.analytics.getRunInfo.queryOptions({ input: {} })
  );

  if (!data?.runStartTime) {
    return <div>No data</div>;
  }

  const startTime = data.runStartTime.getTime();
  const now = Date.now();
  
  const durationMs = now - startTime;
  const durationMinutes = durationMs / (1000 * 60);
  const durationHours = durationMinutes / 60;
  const durationDays = durationHours / 24;

  return (
    <div>
      <h3>Session Duration</h3>
      <p>Minutes: {durationMinutes.toFixed(0)}</p>
      <p>Hours: {durationHours.toFixed(1)}</p>
      <p>Days: {durationDays.toFixed(2)}</p>
    </div>
  );
}

Conditional Rendering Based on Data Availability

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

function AnalyticsDashboard() {
  const { data: runInfo } = useQuery(
    orpc.analytics.getRunInfo.queryOptions({ input: {} })
  );

  const { data: modelStats } = useQuery(
    orpc.analytics.getModelStats.queryOptions({
      input: { mode: 'overall', variant: 'all' }
    }),
    {
      enabled: !!runInfo?.runStartTime // Only fetch if session started
    }
  );

  if (!runInfo?.runStartTime) {
    return (
      <div>
        <h2>Welcome to Autonome</h2>
        <p>Start trading to see analytics and performance metrics.</p>
        <button>Start Trading Session</button>
      </div>
    );
  }

  return (
    <div>
      <h2>Analytics Dashboard</h2>
      <p>Session started: {runInfo.runStartTime.toLocaleString()}</p>
      {/* Render analytics components */}
    </div>
  );
}

Session Reliability Warning

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

function ReliabilityWarning() {
  const { data } = useQuery(
    orpc.analytics.getRunInfo.queryOptions({ input: {} })
  );

  if (!data?.runStartTime) return null;

  const durationHours = (Date.now() - data.runStartTime.getTime()) / (1000 * 60 * 60);

  if (durationHours < 24) {
    return (
      <div className="warning">
        <strong>Limited Data Warning</strong>
        <p>
          Session has been running for only {durationHours.toFixed(1)} hours.
          Statistical metrics like Sharpe ratio may not be reliable until
          at least 24 hours of trading data is available.
        </p>
      </div>
    );
  }

  if (durationHours < 72) {
    return (
      <div className="info">
        <strong>Note:</strong> Metrics become more reliable after 72+ hours of trading.
      </div>
    );
  }

  return null;
}

Combine with Leaderboard for Context

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

function LeaderboardWithContext() {
  const { data: runInfo } = useQuery(
    orpc.analytics.getRunInfo.queryOptions({ input: {} })
  );
  
  const { data: leaderboard } = useQuery(
    orpc.analytics.getLeaderboard.queryOptions({
      input: { window: '7d', sortBy: 'pnlPercent', variant: 'all' }
    })
  );

  if (!runInfo?.runStartTime) {
    return <div>No trading data</div>;
  }

  const durationDays = (Date.now() - runInfo.runStartTime.getTime()) / (1000 * 60 * 60 * 24);
  const effectiveWindow = durationDays < 7 ? `${durationDays.toFixed(1)}d` : '7d';

  return (
    <div>
      <h2>7-Day Leaderboard</h2>
      <p>Session started {durationDays.toFixed(1)} days ago</p>
      {durationDays < 7 && (
        <p className="info">Showing all available data ({effectiveWindow})</p>
      )}
      
      <table>
        {/* Render leaderboard entries */}
      </table>
    </div>
  );
}

Implementation Notes

Why Portfolio Snapshots?

The endpoint uses portfolio snapshots rather than other timestamps (like first order or first model creation) because:
  1. Portfolio snapshots are consistent: They’re created regularly by the system
  2. Meaningful start time: Trading effectively begins when portfolio tracking starts
  3. Available for both modes: Works in both live trading and simulator modes
  4. Database reliability: The portfolioSize table is a stable source of truth

Null Handling

The endpoint returns null instead of throwing an error when no data exists. This allows:
  • Graceful handling in UI components
  • Conditional rendering based on data availability
  • Clear distinction between “no data” and “error”

Time Zones

The returned Date object is in UTC. Convert to local time in your UI:
const localTime = data?.runStartTime?.toLocaleString();
const utcTime = data?.runStartTime?.toISOString();

Source Code Reference

Implementation details:
  • Router: src/server/orpc/router/analytics.ts:243-250
  • Query function: src/server/features/analytics/queries.server.ts:666-674
  • Database schema: src/db/schema.ts (portfolioSize table)

Build docs developers (and LLMs) love