Files
trading_bot_v3/page-temp.js
mindesbunister ade5610ba2 chore: update database after trade limit removal testing
- Updated dev.db with latest automation runs without trade limits
- Added page-temp.js for development reference
- System now running unrestricted with unlimited daily trades
2025-07-24 15:54:18 +02:00

712 lines
30 KiB
JavaScript
Raw Permalink 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.
'use client'
import React, { useState, useEffect } from 'react'
// Available timeframes for automation (matching analysis page format)
const timeframes = [
{ label: '5m', value: '5' },
{ label: '15m', value: '15' },
{ label: '30m', value: '30' },
{ label: '1h', value: '60' },
{ label: '2h', value: '120' },
{ label: '4h', value: '240' },
{ label: '1d', value: 'D' },
]
export default function AutomationPageV2() {
const [config, setConfig] = useState({
mode: 'SIMULATION',
dexProvider: 'DRIFT',
symbol: 'SOLUSD',
timeframe: '1h', // Primary timeframe for backwards compatibility
selectedTimeframes: ['60'], // Multi-timeframe support
tradingAmount: 100,
balancePercentage: 50, // Default to 50% of available balance
// stopLossPercent and takeProfitPercent removed - AI calculates these automatically
})
const [status, setStatus] = useState(null)
const [balance, setBalance] = useState(null)
const [positions, setPositions] = useState([])
const [loading, setLoading] = useState(false)
const [nextAnalysisCountdown, setNextAnalysisCountdown] = useState(0)
useEffect(() => {
fetchStatus()
fetchBalance()
fetchPositions()
const interval = setInterval(() => {
fetchStatus()
fetchBalance()
fetchPositions()
}, 30000)
return () => clearInterval(interval)
}, [])
// Timer effect for countdown
useEffect(() => {
let countdownInterval = null
if (status?.isActive && status?.nextAnalysisIn > 0) {
setNextAnalysisCountdown(status.nextAnalysisIn)
countdownInterval = setInterval(() => {
setNextAnalysisCountdown(prev => {
if (prev <= 1) {
// Refresh status when timer reaches 0
fetchStatus()
return 0
}
return prev - 1
})
}, 1000)
} else {
setNextAnalysisCountdown(0)
}
return () => {
if (countdownInterval) {
clearInterval(countdownInterval)
}
}
}, [status?.nextAnalysisIn, status?.isActive])
// Helper function to format countdown time
const formatCountdown = (seconds) => {
if (seconds <= 0) return 'Analyzing now...'
const hours = Math.floor(seconds / 3600)
const minutes = Math.floor((seconds % 3600) / 60)
const secs = seconds % 60
if (hours > 0) {
return `${hours}h ${minutes}m ${secs}s`
} else if (minutes > 0) {
return `${minutes}m ${secs}s`
} else {
return `${secs}s`
}
}
const toggleTimeframe = (timeframe) => {
setConfig(prev => ({
...prev,
selectedTimeframes: prev.selectedTimeframes.includes(timeframe)
? prev.selectedTimeframes.filter(tf => tf !== timeframe)
: [...prev.selectedTimeframes, timeframe]
}))
}
const fetchStatus = async () => {
try {
const response = await fetch('/api/automation/status')
const data = await response.json()
console.log('Status fetched:', data) // Debug log
if (data.success) {
setStatus(data.status)
}
} catch (error) {
console.error('Failed to fetch status:', error)
}
}
const fetchBalance = async () => {
try {
const response = await fetch('/api/drift/balance')
const data = await response.json()
if (data.success) {
setBalance(data)
}
} catch (error) {
console.error('Failed to fetch balance:', error)
}
}
const fetchPositions = async () => {
try {
const response = await fetch('/api/drift/positions')
const data = await response.json()
if (data.success) {
setPositions(data.positions || [])
}
} catch (error) {
console.error('Failed to fetch positions:', error)
}
}
const handleStart = async () => {
console.log('Start button clicked') // Debug log
setLoading(true)
try {
// Ensure we have selectedTimeframes before starting
if (config.selectedTimeframes.length === 0) {
alert('Please select at least one timeframe for analysis')
setLoading(false)
return
}
console.log('Starting automation with config:', {
...config,
selectedTimeframes: config.selectedTimeframes
})
const response = await fetch('/api/automation/start', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(config)
})
const data = await response.json()
if (data.success) {
fetchStatus()
} else {
alert('Failed to start automation: ' + data.error)
}
} catch (error) {
console.error('Failed to start automation:', error)
alert('Failed to start automation')
} finally {
setLoading(false)
}
}
const handleStop = async () => {
console.log('Stop button clicked') // Debug log
setLoading(true)
try {
const response = await fetch('/api/automation/stop', {
method: 'POST'
})
const data = await response.json()
console.log('Stop response:', data) // Debug log
if (data.success) {
fetchStatus()
} else {
alert('Failed to stop automation: ' + data.error)
}
} catch (error) {
console.error('Failed to stop automation:', error)
alert('Failed to stop automation')
} finally {
setLoading(false)
}
}
return (
<div className="space-y-6">
<div className="bg-green-500 p-3 text-white text-center font-bold rounded">
🚀 NEW AUTOMATION V2 - MULTI-TIMEFRAME READY 🚀
</div>
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold text-white">Automated Trading V2</h1>
<p className="text-gray-400 mt-1">Drift Protocol - Multi-Timeframe Analysis</p>
</div>
<div className="flex space-x-4">
<button
onClick={() => console.log('TEST BUTTON CLICKED')}
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
>
Test Click
</button>
{status?.isActive ? (
<button
onClick={handleStop}
disabled={loading}
className="px-6 py-3 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors disabled:opacity-50 font-semibold"
>
{loading ? 'Stopping...' : 'STOP'}
</button>
) : (
<button
onClick={handleStart}
disabled={loading}
className="px-6 py-3 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors disabled:opacity-50 font-semibold"
>
{loading ? 'Starting...' : 'START'}
</button>
)}
</div>
</div>
<div className="grid grid-cols-1 xl:grid-cols-3 gap-6">
{/* Configuration Panel */}
<div className="xl:col-span-2 space-y-6">
<div className="bg-gray-800 p-6 rounded-lg border border-gray-700">
<h3 className="text-xl font-bold text-white mb-6">Configuration</h3>
{/* Trading Mode */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
<div className="space-y-3">
<label className="block text-sm font-bold text-blue-400">Trading Mode</label>
<div className="space-y-2">
<label className="flex items-center space-x-3 cursor-pointer p-3 rounded-lg border border-gray-600 hover:border-blue-500 transition-colors">
<input
type="radio"
className="w-4 h-4 text-blue-600"
name="mode"
checked={config.mode === 'SIMULATION'}
onChange={() => setConfig({...config, mode: 'SIMULATION'})}
disabled={status?.isActive}
/>
<span className="text-white">Paper Trading</span>
</label>
<label className="flex items-center space-x-3 cursor-pointer p-3 rounded-lg border border-gray-600 hover:border-green-500 transition-colors">
<input
type="radio"
className="w-4 h-4 text-green-600"
name="mode"
checked={config.mode === 'LIVE'}
onChange={() => setConfig({...config, mode: 'LIVE'})}
disabled={status?.isActive}
/>
<span className="text-white font-semibold">Live Trading</span>
</label>
</div>
</div>
</div>
{/* Symbol and Position Size */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
<div>
<label className="block text-sm font-medium text-gray-300 mb-2">Symbol</label>
<select
className="w-full p-3 bg-gray-700 border border-gray-600 rounded-lg text-white focus:border-blue-500"
value={config.symbol}
onChange={(e) => setConfig({...config, symbol: e.target.value})}
disabled={status?.isActive}
>
<option value="SOLUSD">SOL/USD</option>
<option value="BTCUSD">BTC/USD</option>
<option value="ETHUSD">ETH/USD</option>
<option value="APTUSD">APT/USD</option>
<option value="AVAXUSD">AVAX/USD</option>
<option value="DOGEUSD">DOGE/USD</option>
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-300 mb-2">
Balance to Use: {config.balancePercentage}%
{balance && ` ($${(parseFloat(balance.availableBalance) * config.balancePercentage / 100).toFixed(2)})`}
</label>
<input
type="range"
className="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer"
style={{
background: `linear-gradient(to right, #3b82f6 0%, #3b82f6 ${config.balancePercentage}%, #374151 ${config.balancePercentage}%, #374151 100%)`
}}
min="10"
max="100"
step="5"
value={config.balancePercentage}
onChange={(e) => {
const percentage = parseFloat(e.target.value);
const newAmount = balance ? (parseFloat(balance.availableBalance) * percentage / 100) : 100;
setConfig({
...config,
balancePercentage: percentage,
tradingAmount: Math.round(newAmount)
});
}}
disabled={status?.isActive}
/>
<div className="flex justify-between text-xs text-gray-400 mt-1">
<span>10%</span>
<span>50%</span>
<span>100%</span>
</div>
</div>
</div>
{/* MULTI-TIMEFRAME SELECTION */}
<div className="mb-6">
<label className="block text-sm font-medium text-gray-300 mb-2">
Analysis Timeframes
<span className="text-xs text-cyan-400 ml-2">({config.selectedTimeframes.length} selected)</span>
{config.selectedTimeframes.length === 0 && (
<span className="text-xs text-red-400 ml-2"> At least one timeframe required</span>
)}
</label>
{/* Timeframe Checkboxes */}
<div className="grid grid-cols-4 gap-2 mb-3">
{timeframes.map(tf => (
<label key={tf.value} className="group relative cursor-pointer">
<input
type="checkbox"
checked={config.selectedTimeframes.includes(tf.value)}
onChange={() => toggleTimeframe(tf.value)}
disabled={status?.isActive}
className="sr-only"
/>
<div className={`flex items-center justify-center p-2 rounded-lg border transition-all text-xs font-medium ${
config.selectedTimeframes.includes(tf.value)
? 'border-cyan-500 bg-cyan-500/10 text-cyan-300 shadow-lg shadow-cyan-500/20'
: status?.isActive
? 'border-gray-700 bg-gray-800/30 text-gray-500 cursor-not-allowed'
: 'border-gray-700 bg-gray-800/30 text-gray-400 hover:border-gray-600 hover:bg-gray-800/50 hover:text-gray-300'
}`}>
{tf.label}
{config.selectedTimeframes.includes(tf.value) && (
<div className="absolute top-0.5 right-0.5 w-1.5 h-1.5 bg-cyan-400 rounded-full"></div>
)}
</div>
</label>
))}
</div>
{/* Selected Timeframes Display */}
{config.selectedTimeframes.length > 0 && (
<div className="p-2 bg-gray-800/30 rounded-lg mb-3">
<div className="text-xs text-gray-400">
Selected: <span className="text-cyan-400">
{config.selectedTimeframes.map(tf => timeframes.find(t => t.value === tf)?.label).filter(Boolean).join(', ')}
</span>
</div>
<div className="text-xs text-gray-500 mt-1">
💡 Multiple timeframes provide more robust analysis
</div>
</div>
)}
{/* Quick Selection Buttons */}
<div className="flex gap-2">
<button
type="button"
onClick={() => setConfig({...config, selectedTimeframes: ['5', '15', '30']})}
disabled={status?.isActive}
className="py-1 px-2 rounded text-xs font-medium bg-green-600/20 text-green-300 hover:bg-green-600/30 transition-all disabled:opacity-50 disabled:cursor-not-allowed"
>
📈 Scalping
</button>
<button
type="button"
onClick={() => setConfig({...config, selectedTimeframes: ['60', '120']})}
disabled={status?.isActive}
className="py-1 px-2 rounded text-xs font-medium bg-blue-600/20 text-blue-300 hover:bg-blue-600/30 transition-all disabled:opacity-50 disabled:cursor-not-allowed"
>
Day Trading
</button>
<button
type="button"
onClick={() => setConfig({...config, selectedTimeframes: ['240', 'D']})}
disabled={status?.isActive}
className="py-1 px-2 rounded text-xs font-medium bg-purple-600/20 text-purple-300 hover:bg-purple-600/30 transition-all disabled:opacity-50 disabled:cursor-not-allowed"
>
🎯 Swing Trading
</button>
</div>
</div>
{/* AI Risk Management Notice */}
<div className="bg-blue-600/10 border border-blue-600/30 rounded-lg p-4">
<div className="flex items-center space-x-2 mb-2">
<span className="text-blue-400 text-lg">🧠</span>
<h4 className="text-blue-300 font-semibold">AI-Powered Risk Management</h4>
</div>
<p className="text-gray-300 text-sm">
Stop loss and take profit levels are automatically calculated by the AI based on:
</p>
<ul className="text-gray-400 text-xs mt-2 space-y-1 ml-4">
<li> Multi-timeframe technical analysis</li>
<li> Market volatility and support/resistance levels</li>
<li> Real-time risk assessment and position sizing</li>
<li> Learning from previous trade outcomes</li>
</ul>
<div className="mt-3 p-2 bg-green-600/10 border border-green-600/30 rounded">
<p className="text-green-300 text-xs">
Ultra-tight scalping enabled (0.5%+ stop losses proven effective)
</p>
</div>
</div>
</div>
</div>
{/* Status Panels */}
<div className="space-y-6">
{/* Account Status */}
<div className="bg-gray-800 p-6 rounded-lg border border-gray-700">
<div className="flex items-center justify-between mb-4">
<h3 className="text-xl font-bold text-white">Account Status</h3>
<button
onClick={fetchBalance}
className="px-3 py-1 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors disabled:opacity-50 text-sm"
>
Sync
</button>
</div>
{balance ? (
<div className="space-y-3">
<div className="flex justify-between">
<span className="text-gray-300">Available Balance:</span>
<span className="text-green-400 font-semibold">${parseFloat(balance.availableBalance).toFixed(2)}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-300">Account Value:</span>
<span className="text-green-400 font-semibold">${parseFloat(balance.accountValue || balance.availableBalance).toFixed(2)}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-300">Unrealized P&L:</span>
<span className={`font-semibold ${balance.unrealizedPnl > 0 ? 'text-green-400' : balance.unrealizedPnl < 0 ? 'text-red-400' : 'text-gray-400'}`}>
${parseFloat(balance.unrealizedPnl || 0).toFixed(2)}
</span>
</div>
<div className="flex justify-between">
<span className="text-gray-300">Open Positions:</span>
<span className="text-yellow-400 font-semibold">{positions.length}</span>
</div>
</div>
) : (
<div className="text-center py-4">
<div className="text-gray-400">Loading account data...</div>
</div>
)}
</div>
{/* Bot Status */}
<div className="bg-gray-800 p-6 rounded-lg border border-gray-700">
<h3 className="text-xl font-bold text-white mb-4">Bot Status</h3>
{status ? (
<div className="space-y-3">
<div className="flex justify-between">
<span className="text-gray-300">Status:</span>
<span className={`font-semibold px-2 py-1 rounded ${
status.isActive ? 'bg-green-600 text-white' : 'bg-red-600 text-white'
}`}>
{status.isActive ? 'ACTIVE' : 'STOPPED'}
</span>
</div>
<div className="flex justify-between">
<span className="text-gray-300">Mode:</span>
<span className={`font-semibold ${
status.mode === 'LIVE' ? 'text-red-400' : 'text-blue-400'
}`}>
{status.mode}
</span>
</div>
<div className="flex justify-between">
<span className="text-gray-300">Protocol:</span>
<span className="text-green-400 font-semibold">DRIFT</span>
</div>
<div className="flex justify-between">
<span className="text-gray-300">Symbol:</span>
<span className="text-white font-semibold">{status.symbol}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-300">Timeframes:</span>
<span className="text-cyan-400 font-semibold text-xs">
{config.selectedTimeframes.map(tf => timeframes.find(t => t.value === tf)?.label).filter(Boolean).join(', ')}
</span>
</div>
</div>
) : (
<p className="text-gray-400">Loading bot status...</p>
)}
</div>
{/* Analysis Progress */}
{status?.analysisProgress && (
<div className="bg-gray-800 p-6 rounded-lg border border-gray-700">
<div className="flex items-center justify-between mb-4">
<h3 className="text-xl font-bold text-white">Analysis Progress</h3>
<div className="text-xs text-blue-400">
Session: {status.analysisProgress.sessionId.split('-').pop()}
</div>
</div>
<div className="space-y-4">
{/* Overall Progress */}
<div className="flex items-center justify-between">
<span className="text-gray-300">Step {status.analysisProgress.currentStep} of {status.analysisProgress.totalSteps}</span>
<span className="text-blue-400 font-semibold">
{Math.round((status.analysisProgress.currentStep / status.analysisProgress.totalSteps) * 100)}%
</span>
</div>
<div className="bg-gray-700 rounded-full h-2">
<div
className="bg-blue-500 h-2 rounded-full transition-all duration-500"
style={{
width: `${(status.analysisProgress.currentStep / status.analysisProgress.totalSteps) * 100}%`
}}
></div>
</div>
{/* Timeframe Progress */}
{status.analysisProgress.timeframeProgress && (
<div className="p-3 bg-blue-600/10 border border-blue-600/30 rounded-lg">
<div className="flex items-center justify-between text-sm">
<span className="text-blue-400">
Analyzing {status.analysisProgress.timeframeProgress.currentTimeframe || 'timeframes'}
</span>
<span className="text-blue-300">
{status.analysisProgress.timeframeProgress.current}/{status.analysisProgress.timeframeProgress.total}
</span>
</div>
</div>
)}
{/* Detailed Steps */}
<div className="space-y-2">
{status.analysisProgress.steps.map((step, index) => (
<div key={step.id} className={`flex items-center space-x-3 p-2 rounded-lg ${
step.status === 'active' ? 'bg-blue-600/20 border border-blue-600/30' :
step.status === 'completed' ? 'bg-green-600/20 border border-green-600/30' :
step.status === 'error' ? 'bg-red-600/20 border border-red-600/30' :
'bg-gray-700/30'
}`}>
{/* Status Icon */}
<div className={`w-6 h-6 rounded-full flex items-center justify-center text-xs font-bold ${
step.status === 'active' ? 'bg-blue-500 text-white animate-pulse' :
step.status === 'completed' ? 'bg-green-500 text-white' :
step.status === 'error' ? 'bg-red-500 text-white' :
'bg-gray-600 text-gray-300'
}`}>
{step.status === 'active' ? '⏳' :
step.status === 'completed' ? '✓' :
step.status === 'error' ? '✗' :
index + 1}
</div>
{/* Step Info */}
<div className="flex-1">
<div className={`font-semibold text-sm ${
step.status === 'active' ? 'text-blue-300' :
step.status === 'completed' ? 'text-green-300' :
step.status === 'error' ? 'text-red-300' :
'text-gray-400'
}`}>
{step.title}
</div>
<div className="text-xs text-gray-400">
{step.details || step.description}
</div>
</div>
{/* Duration */}
{step.duration && (
<div className="text-xs text-gray-500">
{(step.duration / 1000).toFixed(1)}s
</div>
)}
</div>
))}
</div>
</div>
</div>
)}
{/* Analysis Timer */}
{status?.isActive && !status?.analysisProgress && (
<div className="bg-gray-800 p-6 rounded-lg border border-gray-700">
<div className="flex items-center justify-between mb-4">
<h3 className="text-xl font-bold text-white">Analysis Timer</h3>
<div className="text-xs text-gray-400">
Cycle #{status.currentCycle || 0}
</div>
</div>
<div className="space-y-3">
<div className="text-center">
<div className="text-3xl font-bold text-blue-400 mb-2">
{formatCountdown(nextAnalysisCountdown)}
</div>
<div className="text-sm text-gray-400">
{nextAnalysisCountdown > 0 ? 'Next Analysis In' : 'Analysis Starting Soon'}
</div>
</div>
<div className="bg-gray-700 rounded-full h-2">
<div
className="bg-blue-500 h-2 rounded-full transition-all duration-1000"
style={{
width: status.analysisInterval > 0 ?
`${Math.max(0, 100 - (nextAnalysisCountdown / status.analysisInterval) * 100)}%` :
'0%'
}}
></div>
</div>
<div className="text-xs text-gray-400 text-center">
Analysis Interval: {Math.floor((status.analysisInterval || 0) / 60)}m
</div>
</div>
</div>
)}
{/* Individual Timeframe Results */}
{status?.individualTimeframeResults && status.individualTimeframeResults.length > 0 && (
<div className="bg-gray-800 p-6 rounded-lg border border-gray-700">
<h3 className="text-xl font-bold text-white mb-4">Timeframe Analysis</h3>
<div className="space-y-2">
{status.individualTimeframeResults.map((result, index) => (
<div key={index} className="flex items-center justify-between p-3 bg-gray-700/50 rounded-lg">
<div className="flex items-center space-x-3">
<span className="text-cyan-400 font-bold text-sm w-8">
{timeframes.find(tf => tf.value === result.timeframe)?.label || result.timeframe}
</span>
<span className={`font-semibold text-sm px-2 py-1 rounded ${
result.recommendation === 'BUY' ? 'bg-green-600/20 text-green-400' :
result.recommendation === 'SELL' ? 'bg-red-600/20 text-red-400' :
'bg-gray-600/20 text-gray-400'
}`}>
{result.recommendation}
</span>
</div>
<div className="text-right">
<div className="text-white font-semibold text-sm">
{result.confidence}%
</div>
<div className="text-xs text-gray-400">
confidence
</div>
</div>
</div>
))}
</div>
<div className="mt-4 p-3 bg-blue-600/10 border border-blue-600/30 rounded-lg">
<div className="text-xs text-blue-400">
Last Updated: {status.individualTimeframeResults[0]?.timestamp ?
new Date(status.individualTimeframeResults[0].timestamp).toLocaleTimeString() :
'N/A'
}
</div>
</div>
</div>
)}
{/* Trading Metrics */}
<div className="bg-gray-800 p-6 rounded-lg border border-gray-700">
<h3 className="text-xl font-bold text-white mb-4">Trading Metrics</h3>
<div className="grid grid-cols-2 gap-4">
<div className="text-center">
<div className="text-2xl font-bold text-green-400">
${balance ? parseFloat(balance.accountValue || balance.availableBalance).toFixed(2) : '0.00'}
</div>
<div className="text-xs text-gray-400">Portfolio</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-blue-400">
{balance ? parseFloat(balance.leverage || 0).toFixed(1) : '0.0'}%
</div>
<div className="text-xs text-gray-400">Leverage Used</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-red-400">
${balance ? parseFloat(balance.unrealizedPnl || 0).toFixed(2) : '0.00'}
</div>
<div className="text-xs text-gray-400">Unrealized P&L</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-yellow-400">
{positions.length}
</div>
<div className="text-xs text-gray-400">Open Positions</div>
</div>
</div>
</div>
</div>
</div>
</div>
)
}