Files
trading_bot_v3/components/PositionCalculator.tsx
mindesbunister ba354c609d 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
2025-07-18 13:16:11 +02:00

408 lines
15 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Dynamic Position Calculator Component
"use client"
import React, { useState, useEffect } from 'react'
interface PositionCalculatorProps {
analysis?: any // The AI analysis results
currentPrice?: number
symbol?: string
onPositionChange?: (position: PositionCalculation) => void
}
interface PositionCalculation {
investmentAmount: number
leverage: number
positionSize: number
entryPrice: number
stopLoss: number
takeProfit: number
liquidationPrice: number
maxLoss: number
maxProfit: number
riskRewardRatio: number
marginRequired: number
tradingFee: number
netInvestment: number
}
export default function PositionCalculator({
analysis,
currentPrice = 0,
symbol = 'BTCUSD',
onPositionChange
}: PositionCalculatorProps) {
const [investmentAmount, setInvestmentAmount] = useState<number>(100)
const [leverage, setLeverage] = useState<number>(10)
const [positionType, setPositionType] = useState<'long' | 'short'>('long')
const [calculation, setCalculation] = useState<PositionCalculation | null>(null)
const [showAdvanced, setShowAdvanced] = useState(false)
// Trading parameters
const [tradingFee, setTradingFee] = useState<number>(0.1) // 0.1% fee
const [maintenanceMargin, setMaintenanceMargin] = useState<number>(0.5) // 0.5% maintenance margin
// Calculate position metrics
const calculatePosition = () => {
if (!currentPrice || currentPrice <= 0) return null
const positionSize = investmentAmount * leverage
const marginRequired = investmentAmount
const fee = positionSize * (tradingFee / 100)
const netInvestment = investmentAmount + fee
// Get AI analysis targets if available
let entryPrice = currentPrice
let stopLoss = 0
let takeProfit = 0
if (analysis && analysis.analysis) {
// Try to extract entry, stop loss, and take profit from AI analysis
const analysisText = analysis.analysis.toLowerCase()
// Look for entry price
const entryMatch = analysisText.match(/entry[:\s]*[\$]?(\d+\.?\d*)/i)
if (entryMatch) {
entryPrice = parseFloat(entryMatch[1])
}
// Look for stop loss
const stopMatch = analysisText.match(/stop[:\s]*[\$]?(\d+\.?\d*)/i)
if (stopMatch) {
stopLoss = parseFloat(stopMatch[1])
}
// Look for take profit
const profitMatch = analysisText.match(/(?:take profit|target)[:\s]*[\$]?(\d+\.?\d*)/i)
if (profitMatch) {
takeProfit = parseFloat(profitMatch[1])
}
// If no specific targets found, use percentage-based defaults
if (!stopLoss) {
stopLoss = positionType === 'long'
? entryPrice * 0.95 // 5% stop loss for long
: entryPrice * 1.05 // 5% stop loss for short
}
if (!takeProfit) {
takeProfit = positionType === 'long'
? entryPrice * 1.10 // 10% take profit for long
: entryPrice * 0.90 // 10% take profit for short
}
} else {
// Default targets if no analysis
stopLoss = positionType === 'long'
? currentPrice * 0.95
: currentPrice * 1.05
takeProfit = positionType === 'long'
? currentPrice * 1.10
: currentPrice * 0.90
}
// Calculate liquidation price
const liquidationPrice = positionType === 'long'
? entryPrice * (1 - (1 / leverage) + (maintenanceMargin / 100))
: entryPrice * (1 + (1 / leverage) - (maintenanceMargin / 100))
// Calculate PnL
const stopLossChange = positionType === 'long'
? (stopLoss - entryPrice) / entryPrice
: (entryPrice - stopLoss) / entryPrice
const takeProfitChange = positionType === 'long'
? (takeProfit - entryPrice) / entryPrice
: (entryPrice - takeProfit) / entryPrice
const maxLoss = positionSize * Math.abs(stopLossChange)
const maxProfit = positionSize * Math.abs(takeProfitChange)
const riskRewardRatio = maxProfit / maxLoss
const result: PositionCalculation = {
investmentAmount,
leverage,
positionSize,
entryPrice,
stopLoss,
takeProfit,
liquidationPrice,
maxLoss,
maxProfit,
riskRewardRatio,
marginRequired,
tradingFee: fee,
netInvestment
}
setCalculation(result)
onPositionChange?.(result)
return result
}
// Recalculate when parameters change
useEffect(() => {
calculatePosition()
}, [investmentAmount, leverage, positionType, currentPrice, analysis, tradingFee, maintenanceMargin])
const formatCurrency = (amount: number, decimals: number = 2) => {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: decimals,
maximumFractionDigits: decimals
}).format(amount)
}
const formatPrice = (price: number) => {
return new Intl.NumberFormat('en-US', {
style: 'decimal',
minimumFractionDigits: 2,
maximumFractionDigits: 2
}).format(price)
}
return (
<div className="bg-gradient-to-br from-gray-800/50 to-gray-900/50 border border-gray-700 rounded-lg p-6">
<div className="flex items-center justify-between mb-6">
<h3 className="text-xl font-bold text-white flex items-center">
<span className="w-6 h-6 bg-gradient-to-br from-green-400 to-green-600 rounded-lg flex items-center justify-center mr-3 text-sm">
📊
</span>
Position Calculator
</h3>
<button
onClick={() => setShowAdvanced(!showAdvanced)}
className="text-sm text-gray-400 hover:text-white transition-colors"
>
{showAdvanced ? '🔽 Hide Advanced' : '🔼 Show Advanced'}
</button>
</div>
{/* Input Controls */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
{/* Investment Amount */}
<div>
<label className="block text-sm font-medium text-gray-300 mb-2">
Investment Amount ($)
</label>
<input
type="number"
value={investmentAmount}
onChange={(e) => setInvestmentAmount(Number(e.target.value))}
className="w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-blue-500"
min="1"
step="1"
/>
</div>
{/* Position Type */}
<div>
<label className="block text-sm font-medium text-gray-300 mb-2">
Position Type
</label>
<div className="flex space-x-2">
<button
onClick={() => setPositionType('long')}
className={`flex-1 px-3 py-2 rounded-lg text-sm font-medium transition-all ${
positionType === 'long'
? 'bg-green-600 text-white'
: 'bg-gray-700 text-gray-300 hover:bg-gray-600'
}`}
>
📈 Long
</button>
<button
onClick={() => setPositionType('short')}
className={`flex-1 px-3 py-2 rounded-lg text-sm font-medium transition-all ${
positionType === 'short'
? 'bg-red-600 text-white'
: 'bg-gray-700 text-gray-300 hover:bg-gray-600'
}`}
>
📉 Short
</button>
</div>
</div>
{/* Leverage Slider */}
<div className="md:col-span-2">
<label className="block text-sm font-medium text-gray-300 mb-2">
Leverage: {leverage}x
</label>
<div className="relative">
<input
type="range"
min="1"
max="100"
value={leverage}
onChange={(e) => setLeverage(Number(e.target.value))}
className="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer slider"
style={{
background: `linear-gradient(to right, #10B981 0%, #10B981 ${leverage}%, #374151 ${leverage}%, #374151 100%)`
}}
/>
<div className="flex justify-between text-xs text-gray-400 mt-1">
<span>1x</span>
<span>25x</span>
<span>50x</span>
<span>100x</span>
</div>
</div>
</div>
</div>
{/* Advanced Settings */}
{showAdvanced && (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6 p-4 bg-gray-800/30 rounded-lg border border-gray-600">
<div>
<label className="block text-sm font-medium text-gray-300 mb-2">
Trading Fee (%)
</label>
<input
type="number"
value={tradingFee}
onChange={(e) => setTradingFee(Number(e.target.value))}
className="w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-blue-500"
min="0"
max="1"
step="0.01"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-300 mb-2">
Maintenance Margin (%)
</label>
<input
type="number"
value={maintenanceMargin}
onChange={(e) => setMaintenanceMargin(Number(e.target.value))}
className="w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-blue-500"
min="0.1"
max="5"
step="0.1"
/>
</div>
</div>
)}
{/* Calculation Results */}
{calculation && (
<div className="space-y-4">
{/* Position Summary */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="bg-gray-800/30 rounded-lg p-4 border border-gray-600">
<div className="text-sm text-gray-400 mb-1">Position Size</div>
<div className="text-lg font-bold text-white">
{formatCurrency(calculation.positionSize)}
</div>
</div>
<div className="bg-gray-800/30 rounded-lg p-4 border border-gray-600">
<div className="text-sm text-gray-400 mb-1">Entry Price</div>
<div className="text-lg font-bold text-white">
${formatPrice(calculation.entryPrice)}
</div>
</div>
<div className="bg-gray-800/30 rounded-lg p-4 border border-gray-600">
<div className="text-sm text-gray-400 mb-1">Margin Required</div>
<div className="text-lg font-bold text-white">
{formatCurrency(calculation.marginRequired)}
</div>
</div>
</div>
{/* Risk Metrics */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="bg-red-900/20 border border-red-700 rounded-lg p-4">
<div className="text-sm text-red-400 mb-2">🚨 Risk Metrics</div>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-gray-300">Stop Loss:</span>
<span className="text-red-400 font-bold">${formatPrice(calculation.stopLoss)}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-300">Max Loss:</span>
<span className="text-red-400 font-bold">{formatCurrency(calculation.maxLoss)}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-300">Liquidation:</span>
<span className="text-red-500 font-bold">${formatPrice(calculation.liquidationPrice)}</span>
</div>
</div>
</div>
<div className="bg-green-900/20 border border-green-700 rounded-lg p-4">
<div className="text-sm text-green-400 mb-2">💰 Profit Potential</div>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-gray-300">Take Profit:</span>
<span className="text-green-400 font-bold">${formatPrice(calculation.takeProfit)}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-300">Max Profit:</span>
<span className="text-green-400 font-bold">{formatCurrency(calculation.maxProfit)}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-300">Risk/Reward:</span>
<span className="text-green-400 font-bold">1:{calculation.riskRewardRatio.toFixed(2)}</span>
</div>
</div>
</div>
</div>
{/* Fee Breakdown */}
<div className="bg-gray-800/30 rounded-lg p-4 border border-gray-600">
<div className="text-sm text-gray-400 mb-2">💸 Fee Breakdown</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="flex justify-between">
<span className="text-gray-300">Trading Fee:</span>
<span className="text-yellow-400">{formatCurrency(calculation.tradingFee)}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-300">Net Investment:</span>
<span className="text-white font-bold">{formatCurrency(calculation.netInvestment)}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-300">Leverage:</span>
<span className="text-blue-400 font-bold">{leverage}x</span>
</div>
</div>
</div>
{/* Risk Warning */}
{leverage > 50 && (
<div className="bg-red-900/30 border border-red-600 rounded-lg p-4">
<div className="flex items-center space-x-2">
<span className="text-red-400 text-lg"></span>
<span className="text-red-400 font-bold">High Leverage Warning</span>
</div>
<p className="text-red-300 text-sm mt-2">
Using {leverage}x leverage is extremely risky. A small price movement against your position could result in liquidation.
</p>
</div>
)}
</div>
)}
<style jsx>{`
.slider::-webkit-slider-thumb {
appearance: none;
height: 20px;
width: 20px;
border-radius: 50%;
background: #10B981;
cursor: pointer;
border: 2px solid #065F46;
box-shadow: 0 0 0 1px #065F46;
}
.slider::-moz-range-thumb {
height: 20px;
width: 20px;
border-radius: 50%;
background: #10B981;
cursor: pointer;
border: 2px solid #065F46;
box-shadow: 0 0 0 1px #065F46;
}
`}</style>
</div>
)
}