Files
trading_bot_v3/app/paper-trading/page.js.dangerous.backup
mindesbunister 33690f51fa feat: implement real data paper trading system
- Replace mock data with real market analysis in paper trading
- Safe paper trading API now uses live TradingView screenshots and OpenAI analysis
- Maintain complete isolation from live trading while using real market conditions
- Fix Docker build error in automation trade route (removed unreachable code)
- Add safety redirects to prevent accidental live trading access
- Real data includes: live charts, technical indicators, current market conditions
- Analysis time: 30-180s for genuine market analysis vs 5s for mock data
- All safety blocks maintained for zero trading risk learning environment

Tested and verified:
 Container builds and runs successfully
 Real screenshot capture working (TradingView integration)
 OpenAI analysis processing real market data
 Safety systems prevent any actual trading
 Paper trading provides realistic learning experience
2025-08-02 10:22:36 +02:00

621 lines
26 KiB
Plaintext

'use client'
import React, { useState, useEffect } from 'react'
export default function PaperTradingPage() {
const [paperBalance, setPaperBalance] = useState(1000) // Start with $1000 paper money
const [paperTrades, setPaperTrades] = useState([])
const [currentAnalysis, setCurrentAnalysis] = useState(null)
const [loading, setLoading] = useState(false)
const [symbol, setSymbol] = useState('SOLUSD')
const [timeframe, setTimeframe] = useState('240') // 4H default for cost efficiency
const [analysisHistory, setAnalysisHistory] = useState([])
const [usageStats, setUsageStats] = useState({ dailyCount: 0, estimatedDailyCost: 0 })
const [lastAnalysisTime, setLastAnalysisTime] = useState(null)
// User's selected timeframes (cost-effective)
const selectedTimeframes = [
{ label: '5m', value: '5', riskLevel: 'HIGH', maxDaily: 10, cost: 'High' },
{ label: '30m', value: '30', riskLevel: 'MEDIUM', maxDaily: 20, cost: 'Medium' },
{ label: '1h', value: '60', riskLevel: 'MEDIUM', maxDaily: 15, cost: 'Medium' },
{ label: '4h', value: '240', riskLevel: 'LOW', maxDaily: 8, cost: 'Low' }
]
// Paper trading settings
const [settings, setSettings] = useState({
riskPerTrade: 1.0, // 1% risk per trade
enableAntiChasing: true,
minConfidence: 80, // High confidence required
requireMultiConfirmation: true,
paperMode: true
})
useEffect(() => {
// Load paper trading history from localStorage
const savedTrades = localStorage.getItem('paperTrades')
if (savedTrades) {
setPaperTrades(JSON.parse(savedTrades))
}
const savedBalance = localStorage.getItem('paperBalance')
if (savedBalance) {
setPaperBalance(parseFloat(savedBalance))
}
const savedHistory = localStorage.getItem('analysisHistory')
if (savedHistory) {
setAnalysisHistory(JSON.parse(savedHistory))
}
}, [])
// Save to localStorage whenever trades or balance changes
useEffect(() => {
localStorage.setItem('paperTrades', JSON.stringify(paperTrades))
localStorage.setItem('paperBalance', paperBalance.toString())
}, [paperTrades, paperBalance])
useEffect(() => {
localStorage.setItem('analysisHistory', JSON.stringify(analysisHistory))
}, [analysisHistory])
const runEnhancedAnalysis = async () => {
// Cost control check
const now = Date.now()
if (lastAnalysisTime && (now - lastAnalysisTime) < 300000) { // 5 minute cooldown
const remainingTime = Math.ceil((300000 - (now - lastAnalysisTime)) / 1000 / 60)
alert(`⏳ Cooldown active. Wait ${remainingTime} minutes to prevent excessive OpenAI costs.`)
return
}
// Daily limit check
const timeframeConfig = selectedTimeframes.find(tf => tf.value === timeframe)
if (usageStats.dailyCount >= (timeframeConfig?.maxDaily || 10)) {
alert(`📊 Daily limit reached for ${timeframeConfig?.label} (${timeframeConfig?.maxDaily} analyses/day). This prevents excessive OpenAI costs.`)
return
}
setLoading(true)
try {
console.log('🛡️ Running Enhanced Anti-Chasing Analysis...')
console.log(`💰 Current usage: ${usageStats.dailyCount} analyses today (~$${usageStats.estimatedDailyCost.toFixed(3)} cost)`)
// First, capture fresh screenshots
const screenshotResponse = await fetch('/api/enhanced-screenshot', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
symbol,
timeframe,
layouts: ['ai', 'diy'],
analyze: true
})
})
if (!screenshotResponse.ok) {
throw new Error('Failed to capture screenshots')
}
const screenshotData = await screenshotResponse.json()
console.log('📸 Screenshots captured:', screenshotData.screenshots?.length || 0)
// Then run enhanced anti-chasing analysis
const analysisResponse = await fetch('/api/enhanced-anti-chasing', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
symbol,
timeframe,
layouts: ['ai', 'diy'],
currentBalance: paperBalance
})
})
if (!analysisResponse.ok) {
throw new Error('Enhanced analysis failed')
}
const analysisData = await analysisResponse.json()
console.log('🧠 Enhanced analysis complete:', analysisData.data)
const analysis = {
timestamp: new Date().toISOString(),
symbol,
timeframe,
...analysisData.data.analysis,
riskAssessment: analysisData.data.riskAssessment,
tradeDecision: analysisData.data.tradeDecision,
antiChasingInsights: analysisData.data.antiChasingInsights,
screenshots: screenshotData.screenshots || []
}
setCurrentAnalysis(analysis)
setLastAnalysisTime(now)
// Update usage stats
setUsageStats(prev => ({
dailyCount: prev.dailyCount + 1,
estimatedDailyCost: (prev.dailyCount + 1) * 0.006 // ~$0.006 per analysis
}))
// Add to history
setAnalysisHistory(prev => [analysis, ...prev.slice(0, 9)]) // Keep last 10
} catch (error) {
console.error('❌ Analysis failed:', error)
alert('Analysis failed: ' + error.message)
} finally {
setLoading(false)
}
}
const executePaperTrade = (signal) => {
if (!currentAnalysis) return
const trade = {
id: Date.now(),
timestamp: new Date().toISOString(),
symbol: currentAnalysis.symbol,
timeframe: currentAnalysis.timeframe,
side: signal,
entryPrice: currentAnalysis.entry?.price || 100,
stopLoss: currentAnalysis.stopLoss?.price,
takeProfit: currentAnalysis.takeProfits?.tp1?.price,
confidence: currentAnalysis.confidence,
reasoning: currentAnalysis.reasoning,
riskReward: currentAnalysis.riskToReward,
status: 'OPEN',
momentumStatus: currentAnalysis.momentumStatus?.type,
entryQuality: currentAnalysis.entryQuality?.score,
riskAssessment: currentAnalysis.riskAssessment
}
// Calculate position size based on risk management
const riskAmount = paperBalance * (settings.riskPerTrade / 100)
const stopDistance = Math.abs(trade.entryPrice - (trade.stopLoss || trade.entryPrice * 0.95))
trade.positionSize = Math.min(riskAmount / stopDistance, paperBalance * 0.1) // Max 10% of balance
setPaperTrades(prev => [trade, ...prev])
console.log('📄 Paper trade executed:', trade)
alert(`Paper trade executed: ${signal} ${trade.symbol} at $${trade.entryPrice}`)
}
const closePaperTrade = (tradeId, exitPrice, reason = 'Manual close') => {
setPaperTrades(prev => prev.map(trade => {
if (trade.id === tradeId && trade.status === 'OPEN') {
const pnl = trade.side === 'BUY'
? (exitPrice - trade.entryPrice) * (trade.positionSize / trade.entryPrice)
: (trade.entryPrice - exitPrice) * (trade.positionSize / trade.entryPrice)
setPaperBalance(current => current + pnl)
return {
...trade,
status: 'CLOSED',
exitPrice,
exitTime: new Date().toISOString(),
pnl,
exitReason: reason
}
}
return trade
}))
}
const resetPaperTrading = () => {
if (confirm('Reset all paper trading data? This cannot be undone.')) {
setPaperBalance(1000)
setPaperTrades([])
setAnalysisHistory([])
localStorage.removeItem('paperTrades')
localStorage.removeItem('paperBalance')
localStorage.removeItem('analysisHistory')
}
}
const openTrades = paperTrades.filter(t => t.status === 'OPEN')
const closedTrades = paperTrades.filter(t => t.status === 'CLOSED')
const totalPnL = closedTrades.reduce((sum, trade) => sum + (trade.pnl || 0), 0)
const winRate = closedTrades.length > 0
? (closedTrades.filter(t => (t.pnl || 0) > 0).length / closedTrades.length * 100).toFixed(1)
: 0
return (
<div className="space-y-6">
{/* Header */}
<div className="bg-gray-800/50 rounded-lg p-6 border border-gray-700">
<div className="flex items-center justify-between mb-4">
<h1 className="text-2xl font-bold text-white">📄 Enhanced Paper Trading</h1>
<div className="flex items-center space-x-4">
<div className="text-right">
<p className="text-sm text-gray-400">Paper Balance</p>
<p className={`text-lg font-bold ${paperBalance >= 1000 ? 'text-green-400' : 'text-red-400'}`}>
${paperBalance.toFixed(2)}
</p>
</div>
<div className="text-right">
<p className="text-sm text-gray-400">Total P&L</p>
<p className={`text-lg font-bold ${totalPnL >= 0 ? 'text-green-400' : 'text-red-400'}`}>
${totalPnL.toFixed(2)}
</p>
</div>
<div className="text-right">
<p className="text-sm text-gray-400">Win Rate</p>
<p className="text-lg font-bold text-blue-400">{winRate}%</p>
</div>
</div>
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm">
<div className="bg-gray-700/50 rounded p-3 text-center">
<p className="text-gray-400">Open Trades</p>
<p className="text-white font-bold text-lg">{openTrades.length}</p>
</div>
<div className="bg-gray-700/50 rounded p-3 text-center">
<p className="text-gray-400">Closed Trades</p>
<p className="text-white font-bold text-lg">{closedTrades.length}</p>
</div>
<div className="bg-gray-700/50 rounded p-3 text-center">
<p className="text-gray-400">Wins</p>
<p className="text-green-400 font-bold text-lg">
{closedTrades.filter(t => (t.pnl || 0) > 0).length}
</p>
</div>
<div className="bg-gray-700/50 rounded p-3 text-center">
<p className="text-gray-400">Losses</p>
<p className="text-red-400 font-bold text-lg">
{closedTrades.filter(t => (t.pnl || 0) < 0).length}
</p>
</div>
</div>
</div>
{/* Enhanced Analysis Panel */}
<div className="bg-gray-800/50 rounded-lg p-6 border border-gray-700">
<h2 className="text-xl font-bold text-white mb-4">🛡️ Enhanced Anti-Chasing Analysis</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
<div>
<label className="block text-sm text-gray-400 mb-2">Symbol</label>
<select
value={symbol}
onChange={(e) => setSymbol(e.target.value)}
className="w-full bg-gray-700 text-white rounded px-3 py-2"
>
<option value="SOLUSD">SOL/USD</option>
<option value="BTCUSD">BTC/USD</option>
<option value="ETHUSD">ETH/USD</option>
</select>
</div>
<div>
<label className="block text-sm text-gray-400 mb-2">Timeframe (Your Selection)</label>
<select
value={timeframe}
onChange={(e) => setTimeframe(e.target.value)}
className="w-full bg-gray-700 text-white rounded px-3 py-2"
>
{selectedTimeframes.map(tf => (
<option key={tf.value} value={tf.value}>
{tf.label} - {tf.riskLevel} Risk ({tf.cost} Cost)
</option>
))}
</select>
</div>
<div>
<label className="block text-sm text-gray-400 mb-2">Risk Per Trade</label>
<select
value={settings.riskPerTrade}
onChange={(e) => setSettings(prev => ({ ...prev, riskPerTrade: parseFloat(e.target.value) }))}
className="w-full bg-gray-700 text-white rounded px-3 py-2"
>
<option value="0.5">0.5% (Very Conservative)</option>
<option value="1.0">1.0% (Conservative)</option>
<option value="1.5">1.5% (Moderate)</option>
<option value="2.0">2.0% (Aggressive)</option>
</select>
</div>
</div>
{/* Cost Control Warning */}
<div className="bg-yellow-900/30 border border-yellow-700 rounded-lg p-4 mb-4">
<div className="flex items-center justify-between mb-2">
<h4 className="text-yellow-400 font-medium">💰 Cost Control Active</h4>
<div className="text-yellow-300 text-sm">
Today: {usageStats.dailyCount} analyses (~${usageStats.estimatedDailyCost.toFixed(3)})
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm">
<div>
<p className="text-yellow-300 mb-1">• 5-minute cooldown between analyses</p>
<p className="text-yellow-300 mb-1">• Daily limits per timeframe prevent overspending</p>
<p className="text-yellow-300">• Manual trigger only (no auto-run)</p>
</div>
<div>
<p className="text-yellow-300 mb-1">Selected Timeframe Limits:</p>
{selectedTimeframes.map(tf => (
<p key={tf.value} className="text-yellow-200 text-xs">
{tf.label}: {tf.maxDaily}/day max
</p>
))}
</div>
</div>
</div>
<div className="space-y-3">
<button
onClick={runEnhancedAnalysis}
disabled={loading || isOnCooldown}
className={`w-full py-3 px-4 rounded-lg font-medium transition-all duration-200 ${
loading || isOnCooldown
? 'bg-gray-600 text-gray-400 cursor-not-allowed'
: 'bg-blue-600 hover:bg-blue-700 text-white'
}`}
>
{loading ? '🔄 Analyzing Market...' : '🛡️ Run Enhanced Analysis'}
</button>
{/* Status indicators */}
<div className="flex justify-between text-sm">
<div className="flex items-center space-x-4">
{isOnCooldown && (
<span className="text-orange-400">
⏱️ Cooldown: {Math.ceil(cooldownRemaining / 1000)}s
</span>
)}
{usageStats.hitDailyLimit && (
<span className="text-red-400">
🚫 Daily limit reached
</span>
)}
</div>
<span className="text-gray-400">
Cost: ~$0.006 per analysis
</span>
</div>
</div>
</div>
{/* Current Analysis Results */}
{currentAnalysis && (
<div className="bg-gray-800/50 rounded-lg p-6 border border-gray-700">
<h3 className="text-lg font-bold text-white mb-4">📊 Latest Analysis Results</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-4">
<div className="bg-gray-700/50 rounded p-4">
<h4 className="text-sm text-gray-400 mb-2">Recommendation</h4>
<p className={`text-lg font-bold ${
currentAnalysis.recommendation === 'BUY' ? 'text-green-400' :
currentAnalysis.recommendation === 'SELL' ? 'text-red-400' : 'text-yellow-400'
}`}>
{currentAnalysis.recommendation}
</p>
<p className="text-sm text-gray-300">Confidence: {currentAnalysis.confidence}%</p>
</div>
<div className="bg-gray-700/50 rounded p-4">
<h4 className="text-sm text-gray-400 mb-2">Momentum Status</h4>
<p className={`text-lg font-bold ${
currentAnalysis.momentumStatus?.type === 'EXHAUSTED' ? 'text-green-400' :
currentAnalysis.momentumStatus?.type === 'BUILDING' ? 'text-blue-400' : 'text-yellow-400'
}`}>
{currentAnalysis.momentumStatus?.type || 'UNKNOWN'}
</p>
<p className="text-sm text-gray-300">
Direction: {currentAnalysis.momentumStatus?.direction || 'N/A'}
</p>
</div>
<div className="bg-gray-700/50 rounded p-4">
<h4 className="text-sm text-gray-400 mb-2">Entry Quality</h4>
<p className={`text-lg font-bold ${
(currentAnalysis.entryQuality?.score || 0) >= 80 ? 'text-green-400' :
(currentAnalysis.entryQuality?.score || 0) >= 60 ? 'text-yellow-400' : 'text-red-400'
}`}>
{currentAnalysis.entryQuality?.score || 0}/100
</p>
<p className="text-sm text-gray-300">
Risk: {currentAnalysis.entryQuality?.riskLevel || 'Unknown'}
</p>
</div>
</div>
{/* Trading Levels */}
{currentAnalysis.entry && (
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
<div className="bg-blue-900/30 rounded p-4 border border-blue-700">
<h4 className="text-sm text-blue-400 mb-2">Entry Price</h4>
<p className="text-lg font-bold text-white">${currentAnalysis.entry.price}</p>
<p className="text-xs text-gray-400">{currentAnalysis.entry.rationale}</p>
</div>
{currentAnalysis.stopLoss && (
<div className="bg-red-900/30 rounded p-4 border border-red-700">
<h4 className="text-sm text-red-400 mb-2">Stop Loss</h4>
<p className="text-lg font-bold text-white">${currentAnalysis.stopLoss.price}</p>
<p className="text-xs text-gray-400">{currentAnalysis.stopLoss.rationale}</p>
</div>
)}
{currentAnalysis.takeProfits?.tp1 && (
<div className="bg-green-900/30 rounded p-4 border border-green-700">
<h4 className="text-sm text-green-400 mb-2">Take Profit</h4>
<p className="text-lg font-bold text-white">${currentAnalysis.takeProfits.tp1.price}</p>
<p className="text-xs text-gray-400">R:R {currentAnalysis.riskToReward || 'N/A'}</p>
</div>
)}
</div>
)}
{/* Anti-Chasing Insights */}
{currentAnalysis.antiChasingInsights && (
<div className="bg-purple-900/30 rounded p-4 border border-purple-700 mb-4">
<h4 className="text-sm text-purple-400 mb-2">🛡️ Anti-Chasing Insights</h4>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm">
<div>
<p className="text-gray-300 mb-1">
<span className="text-purple-400">Momentum:</span> {currentAnalysis.momentumStatus?.type}
</p>
<p className="text-gray-300 mb-1">
<span className="text-purple-400">Timeframe Alignment:</span> {currentAnalysis.timeframeAlignment?.alignment}
</p>
</div>
<div>
<p className="text-gray-300 mb-1">
<span className="text-purple-400">Entry Quality:</span> {currentAnalysis.entryQuality?.score}/100
</p>
<p className="text-gray-300">
<span className="text-purple-400">Reversal Probability:</span> {currentAnalysis.momentumStatus?.reversalProbability || 'N/A'}%
</p>
</div>
</div>
</div>
)}
{/* Trade Decision */}
{currentAnalysis.tradeDecision && (
<div className={`rounded p-4 border mb-4 ${
currentAnalysis.tradeDecision.allowed
? 'bg-green-900/30 border-green-700'
: 'bg-red-900/30 border-red-700'
}`}>
<h4 className={`text-sm mb-2 ${
currentAnalysis.tradeDecision.allowed ? 'text-green-400' : 'text-red-400'
}`}>
Risk Management Decision
</h4>
<p className="text-white font-medium mb-2">
{currentAnalysis.tradeDecision.allowed ? '✅ TRADE APPROVED' : '❌ TRADE REJECTED'}
</p>
<p className="text-sm text-gray-300">{currentAnalysis.tradeDecision.reason}</p>
</div>
)}
{/* Action Buttons */}
{currentAnalysis.recommendation !== 'HOLD' && currentAnalysis.tradeDecision?.allowed && (
<div className="flex space-x-4">
<button
onClick={() => executePaperTrade('BUY')}
disabled={currentAnalysis.recommendation !== 'BUY'}
className={`flex-1 py-2 px-4 rounded font-medium ${
currentAnalysis.recommendation === 'BUY'
? 'bg-green-600 hover:bg-green-700 text-white'
: 'bg-gray-600 text-gray-400 cursor-not-allowed'
}`}
>
📄 Paper BUY
</button>
<button
onClick={() => executePaperTrade('SELL')}
disabled={currentAnalysis.recommendation !== 'SELL'}
className={`flex-1 py-2 px-4 rounded font-medium ${
currentAnalysis.recommendation === 'SELL'
? 'bg-red-600 hover:bg-red-700 text-white'
: 'bg-gray-600 text-gray-400 cursor-not-allowed'
}`}
>
📄 Paper SELL
</button>
</div>
)}
{/* Reasoning */}
<div className="mt-4 p-4 bg-gray-700/30 rounded">
<h4 className="text-sm text-gray-400 mb-2">Analysis Reasoning</h4>
<p className="text-sm text-gray-300">{currentAnalysis.reasoning}</p>
</div>
</div>
)}
{/* Open Trades */}
{openTrades.length > 0 && (
<div className="bg-gray-800/50 rounded-lg p-6 border border-gray-700">
<h3 className="text-lg font-bold text-white mb-4">📊 Open Paper Trades</h3>
<div className="space-y-3">
{openTrades.map(trade => (
<div key={trade.id} className="bg-gray-700/50 rounded p-4 flex justify-between items-center">
<div className="flex-1">
<div className="flex items-center space-x-4">
<span className={`px-2 py-1 rounded text-xs font-medium ${
trade.side === 'BUY' ? 'bg-green-600 text-white' : 'bg-red-600 text-white'
}`}>
{trade.side}
</span>
<span className="text-white font-medium">{trade.symbol}</span>
<span className="text-gray-400">${trade.entryPrice}</span>
<span className="text-gray-400">Size: ${trade.positionSize?.toFixed(2)}</span>
{trade.momentumStatus && (
<span className={`px-2 py-1 rounded text-xs ${
trade.momentumStatus === 'EXHAUSTED' ? 'bg-purple-600 text-white' : 'bg-gray-600 text-gray-300'
}`}>
{trade.momentumStatus}
</span>
)}
</div>
<div className="text-xs text-gray-400 mt-1">
Entry: {new Date(trade.timestamp).toLocaleString()} |
Confidence: {trade.confidence}% |
Quality: {trade.entryQuality}/100
</div>
</div>
<button
onClick={() => {
const exitPrice = prompt(`Exit price for ${trade.symbol}:`, trade.entryPrice)
if (exitPrice) closePaperTrade(trade.id, parseFloat(exitPrice))
}}
className="bg-yellow-600 hover:bg-yellow-700 text-white px-3 py-1 rounded text-sm"
>
Close
</button>
</div>
))}
</div>
</div>
)}
{/* Trade History */}
{closedTrades.length > 0 && (
<div className="bg-gray-800/50 rounded-lg p-6 border border-gray-700">
<div className="flex justify-between items-center mb-4">
<h3 className="text-lg font-bold text-white">📈 Paper Trade History</h3>
<button
onClick={resetPaperTrading}
className="bg-red-600 hover:bg-red-700 text-white px-3 py-2 rounded text-sm"
>
Reset All Data
</button>
</div>
<div className="space-y-2 max-h-96 overflow-y-auto">
{closedTrades.slice(0, 20).map(trade => (
<div key={trade.id} className={`p-3 rounded border-l-4 ${
(trade.pnl || 0) >= 0 ? 'bg-green-900/20 border-green-500' : 'bg-red-900/20 border-red-500'
}`}>
<div className="flex justify-between items-center">
<div className="flex items-center space-x-4">
<span className={`px-2 py-1 rounded text-xs font-medium ${
trade.side === 'BUY' ? 'bg-green-600 text-white' : 'bg-red-600 text-white'
}`}>
{trade.side}
</span>
<span className="text-white">{trade.symbol}</span>
<span className="text-gray-400">${trade.entryPrice} → ${trade.exitPrice}</span>
<span className={`font-medium ${
(trade.pnl || 0) >= 0 ? 'text-green-400' : 'text-red-400'
}`}>
${(trade.pnl || 0).toFixed(2)}
</span>
</div>
<div className="text-xs text-gray-400">
{new Date(trade.timestamp).toLocaleDateString()}
</div>
</div>
<div className="text-xs text-gray-400 mt-1">
Confidence: {trade.confidence}% | Quality: {trade.entryQuality}/100 | {trade.exitReason}
</div>
</div>
))}
</div>
</div>
)}
</div>
)
}