24 KiB
24 KiB
description, shortcut
| description | shortcut |
|---|---|
| Track real-time prices across crypto, stocks, forex, and commodities with multi-source feeds | tp |
Track Market Price
Real-time price tracking system with institutional-grade data feeds from multiple sources for accuracy and reliability.
Usage
When the user wants to track market prices, implement a comprehensive price monitoring system with these capabilities:
Required Information
- Symbol: Asset ticker (BTC, AAPL, EUR/USD, etc.)
- Asset Type: crypto, stock, forex, commodity
- Interval: 1s, 5s, 30s, 1m, 5m (real-time streaming)
- Exchanges: Specific exchanges or "ALL" for aggregate
- Alert Conditions: Price thresholds, percentage changes
- Duration: How long to track (continuous or time-limited)
Implementation
1. Multi-Source Price Aggregator
class MarketPriceTracker {
constructor() {
this.dataSources = {
crypto: {
binance: 'wss://stream.binance.com:9443/ws',
coinbase: 'wss://ws-feed.exchange.coinbase.com',
kraken: 'wss://ws.kraken.com',
ftx: 'wss://ftx.com/ws/',
coingecko: 'https://api.coingecko.com/api/v3',
messari: 'https://data.messari.io/api/v1'
},
stocks: {
alphaVantage: process.env.ALPHA_VANTAGE_API,
iex: 'https://api.iextrading.com/1.0',
polygon: 'wss://socket.polygon.io',
finnhub: 'wss://ws.finnhub.io',
yahoo: 'https://query1.finance.yahoo.com/v8'
},
forex: {
oanda: 'wss://stream-fxtrade.oanda.com',
forexConnect: 'https://api-fxtrade.oanda.com/v3',
currencyLayer: process.env.CURRENCY_LAYER_API,
exchangeRates: 'https://api.exchangerate.host'
},
commodities: {
quandl: process.env.QUANDL_API,
metalsPrices: 'https://api.metals.live/v1',
oilPrices: 'https://api.oilpriceapi.com/v1'
}
};
this.priceCache = new Map();
this.connections = new Map();
this.aggregationStrategy = 'VWAP'; // Volume Weighted Average Price
}
async trackPrice(params) {
const {
symbol,
assetType,
interval = '1s',
exchanges = 'ALL',
alertConditions = [],
duration = 'continuous'
} = params;
// Validate symbol format
this.validateSymbol(symbol, assetType);
// Initialize tracking
const trackingSession = {
id: crypto.randomUUID(),
symbol,
assetType,
interval,
startTime: Date.now(),
duration,
exchanges: this.selectExchanges(exchanges, assetType),
alerts: this.parseAlertConditions(alertConditions),
priceHistory: [],
statistics: {
high: 0,
low: Infinity,
open: 0,
volume: 0,
vwap: 0,
changes: {
'1m': 0,
'5m': 0,
'15m': 0,
'1h': 0,
'24h': 0
}
}
};
// Start real-time tracking
await this.initializeConnections(trackingSession);
// Begin price aggregation
this.startPriceAggregation(trackingSession);
// Monitor alerts
this.startAlertMonitoring(trackingSession);
return trackingSession;
}
async initializeConnections(session) {
const connections = [];
for (const exchange of session.exchanges) {
try {
const connection = await this.connectToExchange(
exchange,
session.symbol,
session.assetType
);
connections.push({
exchange,
connection,
status: 'connected',
latency: 0,
lastUpdate: null
});
// Subscribe to price updates
this.subscribeToPrice(connection, session);
} catch (error) {
console.error(`Failed to connect to ${exchange}:`, error);
connections.push({
exchange,
status: 'failed',
error: error.message
});
}
}
session.connections = connections;
return connections;
}
async connectToExchange(exchange, symbol, assetType) {
const sourceConfig = this.dataSources[assetType][exchange];
if (sourceConfig.startsWith('wss://')) {
// WebSocket connection
return this.createWebSocketConnection(sourceConfig, symbol, exchange);
} else {
// REST API polling
return this.createPollingConnection(sourceConfig, symbol, exchange);
}
}
createWebSocketConnection(url, symbol, exchange) {
return new Promise((resolve, reject) => {
const ws = new WebSocket(url);
ws.on('open', () => {
// Subscribe to symbol
const subscribeMsg = this.getSubscribeMessage(exchange, symbol);
ws.send(JSON.stringify(subscribeMsg));
resolve(ws);
});
ws.on('error', reject);
ws.on('message', (data) => {
this.handlePriceUpdate(exchange, data);
});
});
}
getSubscribeMessage(exchange, symbol) {
const messages = {
binance: {
method: 'SUBSCRIBE',
params: [`${symbol.toLowerCase()}@trade`, `${symbol.toLowerCase()}@depth`],
id: 1
},
coinbase: {
type: 'subscribe',
product_ids: [symbol],
channels: ['ticker', 'level2']
},
kraken: {
event: 'subscribe',
pair: [symbol],
subscription: { name: 'ticker' }
}
};
return messages[exchange] || {};
}
handlePriceUpdate(exchange, data) {
const parsed = JSON.parse(data);
const price = this.extractPrice(exchange, parsed);
if (price) {
const update = {
exchange,
price: price.price,
volume: price.volume,
timestamp: Date.now(),
bid: price.bid,
ask: price.ask,
spread: price.ask - price.bid
};
// Update cache
if (!this.priceCache.has(exchange)) {
this.priceCache.set(exchange, []);
}
this.priceCache.get(exchange).push(update);
// Trigger aggregation
this.aggregatePrices();
}
}
extractPrice(exchange, data) {
const extractors = {
binance: (d) => ({
price: parseFloat(d.p),
volume: parseFloat(d.q),
bid: parseFloat(d.b),
ask: parseFloat(d.a)
}),
coinbase: (d) => ({
price: parseFloat(d.price),
volume: parseFloat(d.volume_24h),
bid: parseFloat(d.best_bid),
ask: parseFloat(d.best_ask)
}),
kraken: (d) => ({
price: parseFloat(d[1].c[0]),
volume: parseFloat(d[1].v[1]),
bid: parseFloat(d[1].b[0]),
ask: parseFloat(d[1].a[0])
})
};
return extractors[exchange]?.(data);
}
aggregatePrices() {
const allPrices = [];
for (const [exchange, prices] of this.priceCache.entries()) {
if (prices.length > 0) {
const recent = prices.filter(p =>
Date.now() - p.timestamp < 5000 // Last 5 seconds
);
allPrices.push(...recent);
}
}
if (allPrices.length === 0) return null;
// Calculate aggregated price based on strategy
let aggregatedPrice;
switch (this.aggregationStrategy) {
case 'VWAP':
aggregatedPrice = this.calculateVWAP(allPrices);
break;
case 'MEDIAN':
aggregatedPrice = this.calculateMedian(allPrices);
break;
case 'WEIGHTED':
aggregatedPrice = this.calculateWeightedAverage(allPrices);
break;
default:
aggregatedPrice = this.calculateSimpleAverage(allPrices);
}
return {
price: aggregatedPrice,
sources: allPrices.length,
timestamp: Date.now(),
spread: this.calculateSpread(allPrices),
confidence: this.calculateConfidence(allPrices)
};
}
calculateVWAP(prices) {
let totalValue = 0;
let totalVolume = 0;
for (const p of prices) {
totalValue += p.price * p.volume;
totalVolume += p.volume;
}
return totalVolume > 0 ? totalValue / totalVolume : 0;
}
calculateConfidence(prices) {
if (prices.length < 2) return 0;
const values = prices.map(p => p.price);
const mean = values.reduce((a, b) => a + b) / values.length;
const variance = values.reduce((sum, val) =>
sum + Math.pow(val - mean, 2), 0) / values.length;
const stdDev = Math.sqrt(variance);
const coefficientOfVariation = (stdDev / mean) * 100;
// Lower CV means higher confidence
if (coefficientOfVariation < 0.5) return 99;
if (coefficientOfVariation < 1) return 95;
if (coefficientOfVariation < 2) return 90;
if (coefficientOfVariation < 5) return 75;
return 50;
}
startAlertMonitoring(session) {
const checkInterval = this.parseInterval(session.interval);
session.alertMonitor = setInterval(() => {
const currentPrice = this.getCurrentPrice(session.symbol);
if (!currentPrice) return;
for (const alert of session.alerts) {
if (this.checkAlertCondition(alert, currentPrice, session)) {
this.triggerAlert(alert, currentPrice, session);
}
}
}, checkInterval);
}
checkAlertCondition(alert, price, session) {
switch (alert.type) {
case 'PRICE_ABOVE':
return price.price > alert.threshold;
case 'PRICE_BELOW':
return price.price < alert.threshold;
case 'PERCENT_CHANGE':
const changePercent = this.calculatePercentChange(
session.statistics.open,
price.price
);
return Math.abs(changePercent) > alert.threshold;
case 'VOLUME_SPIKE':
return price.volume > session.statistics.avgVolume * alert.multiplier;
case 'SPREAD_WIDE':
return price.spread > alert.threshold;
case 'VOLATILITY':
return this.calculateVolatility(session) > alert.threshold;
default:
return false;
}
}
triggerAlert(alert, price, session) {
const alertData = {
id: crypto.randomUUID(),
type: alert.type,
symbol: session.symbol,
price: price.price,
threshold: alert.threshold,
timestamp: Date.now(),
message: this.formatAlertMessage(alert, price, session),
severity: alert.severity || 'INFO',
action: alert.action || 'NOTIFY'
};
// Send alert through notification channels
this.sendAlert(alertData);
// Log alert
if (!session.triggeredAlerts) {
session.triggeredAlerts = [];
}
session.triggeredAlerts.push(alertData);
// Execute automated actions if configured
if (alert.action === 'AUTO_TRADE') {
this.executeAutomatedAction(alert, price, session);
}
}
}
2. Price Display Interface
class PriceDisplay {
constructor() {
this.displayMode = 'detailed'; // 'simple', 'detailed', 'professional'
this.updateFrequency = 1000; // milliseconds
}
displayPrice(session, aggregatedPrice) {
const display = `
╔════════════════════════════════════════════════════════════════╗
║ REAL-TIME PRICE TRACKER ║
╠════════════════════════════════════════════════════════════════╣
║ Symbol: ${session.symbol.padEnd(48)} ║
║ Asset Type: ${session.assetType.padEnd(48)} ║
║ Current Price: ${this.formatPrice(aggregatedPrice.price).padEnd(48)} ║
║ Confidence: ${this.formatConfidence(aggregatedPrice.confidence).padEnd(48)} ║
╠════════════════════════════════════════════════════════════════╣
║ PRICE METRICS ║
╠════════════════════════════════════════════════════════════════╣
║ 24H High: ${this.formatPrice(session.statistics.high).padEnd(48)} ║
║ 24H Low: ${this.formatPrice(session.statistics.low).padEnd(48)} ║
║ 24H Change: ${this.formatChange(session.statistics.changes['24h']).padEnd(48)} ║
║ Volume: ${this.formatVolume(session.statistics.volume).padEnd(48)} ║
║ VWAP: ${this.formatPrice(session.statistics.vwap).padEnd(48)} ║
╠════════════════════════════════════════════════════════════════╣
║ EXCHANGE PRICES ║
╠════════════════════════════════════════════════════════════════╣
${this.formatExchangePrices(session)}
╠════════════════════════════════════════════════════════════════╣
║ ALERTS ║
╠════════════════════════════════════════════════════════════════╣
${this.formatAlerts(session)}
╚════════════════════════════════════════════════════════════════╝
`;
return display;
}
formatExchangePrices(session) {
const lines = [];
for (const conn of session.connections) {
if (conn.status === 'connected' && conn.lastPrice) {
lines.push(`║ ${conn.exchange.padEnd(15)} ${this.formatPrice(conn.lastPrice).padEnd(15)} ${this.formatLatency(conn.latency).padEnd(26)} ║`);
}
}
return lines.join('\n');
}
formatAlerts(session) {
if (!session.alerts || session.alerts.length === 0) {
return '║ No active alerts ║';
}
const lines = [];
for (const alert of session.alerts) {
const status = alert.triggered ? '' : '⏳';
lines.push(`║ ${status} ${alert.type}: ${alert.threshold} ║`);
}
return lines.join('\n');
}
formatPrice(price) {
if (price > 1000) {
return `$${price.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
} else if (price > 1) {
return `$${price.toFixed(4)}`;
} else {
return `$${price.toFixed(8)}`;
}
}
formatConfidence(confidence) {
const bars = '█'.repeat(Math.floor(confidence / 10));
const empty = '░'.repeat(10 - Math.floor(confidence / 10));
return `${bars}${empty} ${confidence}%`;
}
}
3. Advanced Analytics
class PriceAnalytics {
constructor() {
this.indicators = {};
this.patterns = [];
}
analyzePrice(priceHistory) {
return {
technicalIndicators: this.calculateTechnicalIndicators(priceHistory),
pricePatterns: this.detectPatterns(priceHistory),
supportResistance: this.findSupportResistance(priceHistory),
volatility: this.analyzeVolatility(priceHistory),
momentum: this.analyzeMomentum(priceHistory),
marketStructure: this.analyzeMarketStructure(priceHistory)
};
}
calculateTechnicalIndicators(prices) {
return {
sma: {
sma20: this.calculateSMA(prices, 20),
sma50: this.calculateSMA(prices, 50),
sma200: this.calculateSMA(prices, 200)
},
ema: {
ema12: this.calculateEMA(prices, 12),
ema26: this.calculateEMA(prices, 26)
},
rsi: this.calculateRSI(prices, 14),
macd: this.calculateMACD(prices),
bollingerBands: this.calculateBollingerBands(prices, 20, 2),
atr: this.calculateATR(prices, 14),
obv: this.calculateOBV(prices),
vwap: this.calculateDailyVWAP(prices)
};
}
detectPatterns(prices) {
const patterns = [];
// Head and Shoulders
if (this.detectHeadAndShoulders(prices)) {
patterns.push({
type: 'HEAD_AND_SHOULDERS',
direction: 'BEARISH',
confidence: 85
});
}
// Double Top/Bottom
const doublePattern = this.detectDoubleTopBottom(prices);
if (doublePattern) {
patterns.push(doublePattern);
}
// Triangle Patterns
const triangle = this.detectTriangle(prices);
if (triangle) {
patterns.push(triangle);
}
// Flag/Pennant
const flagPattern = this.detectFlagPennant(prices);
if (flagPattern) {
patterns.push(flagPattern);
}
return patterns;
}
findSupportResistance(prices) {
const levels = [];
const priceValues = prices.map(p => p.price);
// Find local maxima and minima
for (let i = 2; i < priceValues.length - 2; i++) {
// Resistance (local maximum)
if (priceValues[i] > priceValues[i-1] &&
priceValues[i] > priceValues[i-2] &&
priceValues[i] > priceValues[i+1] &&
priceValues[i] > priceValues[i+2]) {
levels.push({
type: 'RESISTANCE',
price: priceValues[i],
strength: this.calculateLevelStrength(prices, priceValues[i]),
touches: this.countTouches(prices, priceValues[i])
});
}
// Support (local minimum)
if (priceValues[i] < priceValues[i-1] &&
priceValues[i] < priceValues[i-2] &&
priceValues[i] < priceValues[i+1] &&
priceValues[i] < priceValues[i+2]) {
levels.push({
type: 'SUPPORT',
price: priceValues[i],
strength: this.calculateLevelStrength(prices, priceValues[i]),
touches: this.countTouches(prices, priceValues[i])
});
}
}
// Cluster similar levels
return this.clusterLevels(levels);
}
}
4. Alert Configuration
class AlertConfiguration {
parseAlertConditions(conditions) {
const parsed = [];
for (const condition of conditions) {
if (typeof condition === 'string') {
// Parse string format: "above 50000", "below 45000", "change 5%"
const match = condition.match(/(\w+)\s+([0-9.]+)(%)?/);
if (match) {
parsed.push(this.createAlert(match[1], parseFloat(match[2]), match[3] === '%'));
}
} else {
// Object format already
parsed.push(condition);
}
}
return parsed;
}
createAlert(type, value, isPercent) {
const alertTypes = {
'above': 'PRICE_ABOVE',
'below': 'PRICE_BELOW',
'change': 'PERCENT_CHANGE',
'volume': 'VOLUME_SPIKE',
'spread': 'SPREAD_WIDE',
'volatility': 'VOLATILITY'
};
return {
type: alertTypes[type] || 'CUSTOM',
threshold: value,
isPercent,
enabled: true,
cooldown: 300000, // 5 minutes between repeat alerts
lastTriggered: 0
};
}
}
5. WebSocket Manager
class WebSocketManager {
constructor() {
this.connections = new Map();
this.reconnectAttempts = new Map();
this.maxReconnectAttempts = 5;
this.reconnectDelay = 1000;
}
async manage(url, handlers) {
const ws = new WebSocket(url);
const connectionId = crypto.randomUUID();
ws.on('open', () => {
console.log(`WebSocket connected: ${url}`);
this.connections.set(connectionId, ws);
this.reconnectAttempts.set(connectionId, 0);
handlers.onOpen?.(ws);
});
ws.on('message', (data) => {
handlers.onMessage?.(data);
});
ws.on('error', (error) => {
console.error(`WebSocket error: ${url}`, error);
handlers.onError?.(error);
});
ws.on('close', () => {
console.log(`WebSocket closed: ${url}`);
this.connections.delete(connectionId);
this.attemptReconnect(url, handlers, connectionId);
});
return connectionId;
}
attemptReconnect(url, handlers, connectionId) {
const attempts = this.reconnectAttempts.get(connectionId) || 0;
if (attempts < this.maxReconnectAttempts) {
const delay = this.reconnectDelay * Math.pow(2, attempts);
console.log(`Reconnecting in ${delay}ms... (attempt ${attempts + 1})`);
setTimeout(() => {
this.reconnectAttempts.set(connectionId, attempts + 1);
this.manage(url, handlers);
}, delay);
} else {
console.error(`Max reconnection attempts reached for ${url}`);
handlers.onMaxReconnectFailed?.();
}
}
}
Error Handling
try {
const tracker = new MarketPriceTracker();
const session = await tracker.trackPrice({
symbol: 'BTC/USDT',
assetType: 'crypto',
interval: '1s',
exchanges: ['binance', 'coinbase', 'kraken'],
alertConditions: [
'above 50000',
'below 45000',
'change 5%',
'volume 2x'
],
duration: '24h'
});
// Display real-time updates
const display = new PriceDisplay();
setInterval(async () => {
const price = tracker.getCurrentPrice(session.symbol);
console.clear();
console.log(display.displayPrice(session, price));
}, 1000);
} catch (error) {
console.error('Price tracking failed:', error);
process.exit(1);
}
This command provides institutional-grade real-time price tracking with multi-exchange aggregation, advanced alerts, and comprehensive analytics.