Initial commit
This commit is contained in:
727
commands/track-price.md
Normal file
727
commands/track-price.md
Normal file
@@ -0,0 +1,727 @@
|
||||
---
|
||||
description: Track real-time prices across crypto, stocks, forex, and commodities with multi-source feeds
|
||||
shortcut: 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
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
```javascript
|
||||
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.
|
||||
Reference in New Issue
Block a user