Description
Retrieves real-time market prices for specified cryptocurrency symbols. Used to calculate unrealized P&L, display current market values, and update position valuations in real-time.Input Schema
string[]
Array of cryptocurrency trading pair symbols to fetch prices for (e.g.,
["BTC-USD", "ETH-USD", "SOL-USD"]). If omitted or empty, returns prices for all available symbols.TypeScript Type
type CryptoPricesInput = {
symbols?: string[];
};
Output Schema
CryptoPrice[]
Array of cryptocurrency price objects.
TypeScript Type
type CryptoPricesResponse = {
prices: {
symbol: string;
price: number;
message?: string;
}[];
};
Example Usage
Basic Query
import { useQuery } from "@tanstack/react-query";
import { orpc } from "@/server/orpc/client";
function CryptoPrices() {
const { data, isLoading } = useQuery(
orpc.trading.getCryptoPrices.queryOptions({
input: {
symbols: ["BTC-USD", "ETH-USD", "SOL-USD"]
}
})
);
if (isLoading) return <div>Loading prices...</div>;
return (
<div>
<h3>Current Prices</h3>
<ul>
{data?.prices.map((crypto) => (
<li key={crypto.symbol}>
{crypto.symbol}: ${crypto.price.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
{crypto.message && <span style={{ color: 'orange' }}> - {crypto.message}</span>}
</li>
))}
</ul>
</div>
);
}
Real-time Price Ticker
import { useQuery } from "@tanstack/react-query";
import { orpc } from "@/server/orpc/client";
function PriceTicker() {
// Poll every 2 seconds for real-time updates
const { data } = useQuery({
...orpc.trading.getCryptoPrices.queryOptions({
input: {
symbols: ["BTC-USD", "ETH-USD"]
}
}),
refetchInterval: 2000,
staleTime: 1000,
});
return (
<div style={{ display: 'flex', gap: '2rem', padding: '1rem', backgroundColor: '#000' }}>
{data?.prices.map((crypto) => (
<div key={crypto.symbol} style={{ color: '#fff' }}>
<div style={{ fontSize: '0.875rem', opacity: 0.7 }}>{crypto.symbol}</div>
<div style={{ fontSize: '1.5rem', fontWeight: 'bold' }}>
${crypto.price.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
</div>
</div>
))}
</div>
);
}
Calculate Position Values
import { useQuery } from "@tanstack/react-query";
import { orpc } from "@/server/orpc/client";
function PositionValuation() {
const positionsQuery = useQuery(
orpc.trading.getPositions.queryOptions({ input: {} })
);
// Extract unique symbols from positions
const symbols = Array.from(
new Set(
positionsQuery.data?.positions.flatMap(account =>
account.positions.map(pos => pos.symbol)
) ?? []
)
);
const pricesQuery = useQuery(
orpc.trading.getCryptoPrices.queryOptions({
input: { symbols }
})
);
if (positionsQuery.isLoading || pricesQuery.isLoading) {
return <div>Loading...</div>;
}
// Create price lookup map
const priceMap = new Map(
pricesQuery.data?.prices.map(p => [p.symbol, p.price]) ?? []
);
return (
<div>
<h3>Position Valuations</h3>
{positionsQuery.data?.positions.map(account => (
<div key={account.modelId}>
<h4>{account.modelName}</h4>
{account.positions.map((pos, idx) => {
const currentPrice = priceMap.get(pos.symbol) ?? 0;
const value = pos.quantity * currentPrice;
const pnl = pos.side === 'long'
? (currentPrice - pos.entryPrice) * pos.quantity
: (pos.entryPrice - currentPrice) * pos.quantity;
return (
<div key={`${pos.symbol}-${idx}`}>
<p>
{pos.symbol}: {pos.quantity.toFixed(4)} × ${currentPrice.toFixed(2)} =
<strong> ${value.toFixed(2)}</strong>
<span style={{ color: pnl >= 0 ? 'green' : 'red', marginLeft: '1rem' }}>
({pnl >= 0 ? '+' : ''}${pnl.toFixed(2)})
</span>
</p>
</div>
);
})}
</div>
))}
</div>
);
}
Price Alert System
import { useQuery } from "@tanstack/react-query";
import { orpc } from "@/server/orpc/client";
import { useEffect, useState } from "react";
type PriceAlert = {
symbol: string;
threshold: number;
condition: 'above' | 'below';
};
function PriceAlerts() {
const [alerts, setAlerts] = useState<PriceAlert[]>([
{ symbol: 'BTC-USD', threshold: 100000, condition: 'above' },
{ symbol: 'ETH-USD', threshold: 3000, condition: 'below' },
]);
const [triggered, setTriggered] = useState<string[]>([]);
const { data } = useQuery({
...orpc.trading.getCryptoPrices.queryOptions({
input: {
symbols: alerts.map(a => a.symbol)
}
}),
refetchInterval: 5000,
});
useEffect(() => {
if (!data?.prices) return;
const newTriggered: string[] = [];
alerts.forEach(alert => {
const price = data.prices.find(p => p.symbol === alert.symbol);
if (!price) return;
const isTriggered = alert.condition === 'above'
? price.price > alert.threshold
: price.price < alert.threshold;
if (isTriggered) {
const alertKey = `${alert.symbol}-${alert.threshold}-${alert.condition}`;
if (!triggered.includes(alertKey)) {
newTriggered.push(alertKey);
// Trigger notification
console.log(`🚨 ALERT: ${alert.symbol} is ${alert.condition} ${alert.threshold}!`);
}
}
});
if (newTriggered.length > 0) {
setTriggered(prev => [...prev, ...newTriggered]);
}
}, [data, alerts, triggered]);
return (
<div>
<h3>Price Alerts</h3>
{alerts.map((alert, idx) => {
const price = data?.prices.find(p => p.symbol === alert.symbol);
return (
<div key={idx}>
<p>
{alert.symbol}: ${price?.price.toFixed(2) ?? 'N/A'}
{alert.condition === 'above' ? ' > ' : ' < '}
${alert.threshold.toFixed(2)}
</p>
</div>
);
})}
</div>
);
}
Fetch All Prices
import { useQuery } from "@tanstack/react-query";
import { orpc } from "@/server/orpc/client";
function AllCryptoPrices() {
// Omit symbols parameter to fetch all available prices
const { data, isLoading } = useQuery(
orpc.trading.getCryptoPrices.queryOptions({
input: {}
})
);
if (isLoading) return <div>Loading all prices...</div>;
// Sort by price descending
const sortedPrices = [...(data?.prices ?? [])].sort((a, b) => b.price - a.price);
return (
<div>
<h3>All Cryptocurrency Prices</h3>
<p>Total: {sortedPrices.length} symbols</p>
<table>
<thead>
<tr>
<th>Symbol</th>
<th>Price (USD)</th>
</tr>
</thead>
<tbody>
{sortedPrices.map((crypto) => (
<tr key={crypto.symbol}>
<td>{crypto.symbol}</td>
<td>${crypto.price.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
Implementation Notes
- Normalization: Input symbols are normalized using
parseSymbols()before fetching (handles various formats). - Error Handling: Returns empty array (
prices: []) on fetch failure instead of throwing an error. - Data Source: Prices are fetched from
@/server/features/trading/queries.server.fetchCryptoPrices(). - Caching: Recommended to use short
staleTime(1-5 seconds) for near real-time updates.
Related Endpoints
- trading.getPositions - Fetch open positions (uses prices for P&L calculations)
- trading.getTrades - Fetch closed trades

