feat: implement dynamic position calculator with leverage slider
- Added comprehensive PositionCalculator component with real-time PnL calculations - Implemented dynamic leverage adjustment with slider (1x to 100x) - Added investment amount input for position sizing - Integrated liquidation price calculations based on leverage and maintenance margin - Added real-time price fetching from multiple sources (CoinGecko, CoinCap, Binance) - Implemented automatic stop loss and take profit extraction from AI analysis - Added risk/reward ratio calculations and position metrics - Included trading fee calculations and net investment display - Added position type selection (Long/Short) with dynamic PnL calculation - Integrated high leverage warning system for risk management - Added advanced settings for customizable trading fees and maintenance margins - Automatically updates calculations when analysis parameters change - Supports both manual price input and real-time market data - Fully responsive design with gradient styling matching app theme
This commit is contained in:
152
lib/price-fetcher.ts
Normal file
152
lib/price-fetcher.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
// Price fetcher utility for getting current market prices
|
||||
export class PriceFetcher {
|
||||
private static cache = new Map<string, { price: number; timestamp: number }>()
|
||||
private static readonly CACHE_DURATION = 30000 // 30 seconds
|
||||
|
||||
static async getCurrentPrice(symbol: string): Promise<number> {
|
||||
const cacheKey = symbol.toUpperCase()
|
||||
const cached = this.cache.get(cacheKey)
|
||||
|
||||
// Return cached price if recent
|
||||
if (cached && Date.now() - cached.timestamp < this.CACHE_DURATION) {
|
||||
return cached.price
|
||||
}
|
||||
|
||||
try {
|
||||
// Try multiple price sources
|
||||
let price = await this.fetchFromCoinGecko(symbol)
|
||||
|
||||
if (!price) {
|
||||
price = await this.fetchFromCoinCap(symbol)
|
||||
}
|
||||
|
||||
if (!price) {
|
||||
price = await this.fetchFromBinance(symbol)
|
||||
}
|
||||
|
||||
if (price) {
|
||||
this.cache.set(cacheKey, { price, timestamp: Date.now() })
|
||||
return price
|
||||
}
|
||||
|
||||
return 0
|
||||
} catch (error) {
|
||||
console.error('Error fetching price:', error)
|
||||
return cached?.price || 0
|
||||
}
|
||||
}
|
||||
|
||||
private static async fetchFromCoinGecko(symbol: string): Promise<number | null> {
|
||||
try {
|
||||
const coinId = this.getCoinGeckoId(symbol)
|
||||
if (!coinId) return null
|
||||
|
||||
const response = await fetch(
|
||||
`https://api.coingecko.com/api/v3/simple/price?ids=${coinId}&vs_currencies=usd`,
|
||||
{ cache: 'no-cache' }
|
||||
)
|
||||
|
||||
if (!response.ok) return null
|
||||
|
||||
const data = await response.json()
|
||||
return data[coinId]?.usd || null
|
||||
} catch (error) {
|
||||
console.error('CoinGecko fetch error:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private static async fetchFromCoinCap(symbol: string): Promise<number | null> {
|
||||
try {
|
||||
const asset = this.getCoinCapAsset(symbol)
|
||||
if (!asset) return null
|
||||
|
||||
const response = await fetch(
|
||||
`https://api.coincap.io/v2/assets/${asset}`,
|
||||
{ cache: 'no-cache' }
|
||||
)
|
||||
|
||||
if (!response.ok) return null
|
||||
|
||||
const data = await response.json()
|
||||
return parseFloat(data.data?.priceUsd) || null
|
||||
} catch (error) {
|
||||
console.error('CoinCap fetch error:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private static async fetchFromBinance(symbol: string): Promise<number | null> {
|
||||
try {
|
||||
const binanceSymbol = this.getBinanceSymbol(symbol)
|
||||
if (!binanceSymbol) return null
|
||||
|
||||
const response = await fetch(
|
||||
`https://api.binance.com/api/v3/ticker/price?symbol=${binanceSymbol}`,
|
||||
{ cache: 'no-cache' }
|
||||
)
|
||||
|
||||
if (!response.ok) return null
|
||||
|
||||
const data = await response.json()
|
||||
return parseFloat(data.price) || null
|
||||
} catch (error) {
|
||||
console.error('Binance fetch error:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private static getCoinGeckoId(symbol: string): string | null {
|
||||
const mapping: Record<string, string> = {
|
||||
'BTCUSD': 'bitcoin',
|
||||
'ETHUSD': 'ethereum',
|
||||
'SOLUSD': 'solana',
|
||||
'SUIUSD': 'sui',
|
||||
'ADAUSD': 'cardano',
|
||||
'DOTUSD': 'polkadot',
|
||||
'AVAXUSD': 'avalanche-2',
|
||||
'LINKUSD': 'chainlink',
|
||||
'MATICUSD': 'matic-network',
|
||||
'UNIUSD': 'uniswap'
|
||||
}
|
||||
return mapping[symbol.toUpperCase()] || null
|
||||
}
|
||||
|
||||
private static getCoinCapAsset(symbol: string): string | null {
|
||||
const mapping: Record<string, string> = {
|
||||
'BTCUSD': 'bitcoin',
|
||||
'ETHUSD': 'ethereum',
|
||||
'SOLUSD': 'solana',
|
||||
'SUIUSD': 'sui',
|
||||
'ADAUSD': 'cardano',
|
||||
'DOTUSD': 'polkadot',
|
||||
'AVAXUSD': 'avalanche',
|
||||
'LINKUSD': 'chainlink',
|
||||
'MATICUSD': 'polygon',
|
||||
'UNIUSD': 'uniswap'
|
||||
}
|
||||
return mapping[symbol.toUpperCase()] || null
|
||||
}
|
||||
|
||||
private static getBinanceSymbol(symbol: string): string | null {
|
||||
const mapping: Record<string, string> = {
|
||||
'BTCUSD': 'BTCUSDT',
|
||||
'ETHUSD': 'ETHUSDT',
|
||||
'SOLUSD': 'SOLUSDT',
|
||||
'SUIUSD': 'SUIUSDT',
|
||||
'ADAUSD': 'ADAUSDT',
|
||||
'DOTUSD': 'DOTUSDT',
|
||||
'AVAXUSD': 'AVAXUSDT',
|
||||
'LINKUSD': 'LINKUSDT',
|
||||
'MATICUSD': 'MATICUSDT',
|
||||
'UNIUSD': 'UNIUSDT'
|
||||
}
|
||||
return mapping[symbol.toUpperCase()] || null
|
||||
}
|
||||
|
||||
static clearCache(): void {
|
||||
this.cache.clear()
|
||||
}
|
||||
}
|
||||
|
||||
export default PriceFetcher
|
||||
Reference in New Issue
Block a user