Files
trading_bot_v3/app/automation-v2/page.js
mindesbunister c687562ecf 🏖️ BEACH MODE: Complete Autonomous Trading System
Features Added:
- 🤖 Autonomous AI Risk Management System
- 🛡️ Smart Stop Loss Proximity Monitoring
- 📊 Real-time Position Monitor with Dark Theme
- 🚨 Emergency Stop Buttons on All Pages
- 🏖️ Full Beach Mode Operation

- Emergency exit analysis (< 1% from SL)
- Position review and adjustments (1-2% from SL)
- Enhanced monitoring (2-5% from SL)
- Opportunity scanning (> 5% from SL)

- Beautiful dark theme Position Monitor
- Emergency stop buttons on automation pages
- Real-time P&L tracking with trend indicators
- Beach mode demo script

- Autonomous risk manager integration
- Position monitoring API endpoints
- Enhanced automation with AI leverage calculator
- CLI monitoring tools with enhanced display

Now you can truly relax on the beach while your AI handles everything! 🏖️🤖💰
2025-07-25 11:57:02 +02:00

483 lines
20 KiB
JavaScript
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.
'use client'
import React, { useState, useEffect } from 'react'
import PositionMonitor from '../components/PositionMonitor.tsx'
// 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',
selectedTimeframes: ['60'], // Multi-timeframe support
tradingAmount: 100,
balancePercentage: 50, // Default to 50% of available balance
})
const [status, setStatus] = useState(null)
const [balance, setBalance] = useState(null)
const [positions, setPositions] = useState([])
const [loading, setLoading] = useState(false)
useEffect(() => {
fetchStatus()
fetchBalance()
fetchPositions()
const interval = setInterval(() => {
fetchStatus()
fetchBalance()
fetchPositions()
}, 30000)
return () => clearInterval(interval)
}, [])
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 response:', data) // Debug log
if (response.ok && !data.error) {
setStatus(data) // Status data is returned directly, not wrapped in 'success'
} else {
console.error('Status API error:', data.error || 'Unknown error')
}
} 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('🚀 Starting automation...')
setLoading(true)
try {
if (config.selectedTimeframes.length === 0) {
console.error('No timeframes selected')
setLoading(false)
return
}
const automationConfig = {
symbol: config.symbol,
selectedTimeframes: config.selectedTimeframes,
mode: config.mode,
tradingAmount: config.tradingAmount,
leverage: config.leverage,
stopLoss: config.stopLoss,
takeProfit: config.takeProfit
}
const response = await fetch('/api/automation/start', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(automationConfig)
})
const data = await response.json()
if (data.success) {
console.log('✅ Automation started successfully')
fetchStatus()
} else {
console.error('Failed to start automation:', data.error)
}
} catch (error) {
console.error('Failed to start automation:', error)
} finally {
setLoading(false)
}
}
const handleStop = async () => {
console.log('🛑 Stopping automation...')
setLoading(true)
try {
const response = await fetch('/api/automation/stop', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
})
const data = await response.json()
if (data.success) {
console.log('✅ Automation stopped successfully')
fetchStatus()
} else {
console.error('Failed to stop automation:', data.error)
}
} catch (error) {
console.error('Failed to stop automation:', error)
} finally {
setLoading(false)
}
}
return (
<div className="space-y-8">
{/* Position Monitor - Real-time Trading Overview */}
<PositionMonitor />
<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">
{/* Header with Start/Stop Button */}
<div className="flex items-center justify-between mb-6">
<h3 className="text-xl font-bold text-white">Configuration</h3>
<div className="flex space-x-3">
{/* Emergency Stop Button - Always Available */}
<button
onClick={handleStop}
disabled={loading}
className="px-4 py-2 bg-orange-600 text-white rounded-lg hover:bg-orange-700 transition-colors disabled:opacity-50 font-semibold text-sm border-2 border-orange-500"
title="Emergency Stop - Immediately stops all automation"
>
🚨 EMERGENCY STOP
</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...' : status?.rateLimitHit ? 'RESTART' : 'START'}
</button>
)}
</div>
</div>
{/* Trading Mode - Side by Side Radio Buttons with Logos */}
<div className="mb-6">
<label className="block text-sm font-bold text-blue-400 mb-3">Trading Mode</label>
<div className="grid grid-cols-2 gap-4">
<label className="flex items-center space-x-3 cursor-pointer p-4 rounded-lg border border-gray-600 hover:border-blue-500 transition-colors">
<input
type="radio"
className="w-5 h-5 text-blue-600"
name="mode"
checked={config.mode === 'SIMULATION'}
onChange={() => setConfig({...config, mode: 'SIMULATION'})}
disabled={status?.isActive}
/>
<div className="flex items-center space-x-2">
<span className="text-2xl">📊</span>
<span className="text-white font-medium">Paper Trading</span>
</div>
</label>
<label className="flex items-center space-x-3 cursor-pointer p-4 rounded-lg border border-gray-600 hover:border-green-500 transition-colors">
<input
type="radio"
className="w-5 h-5 text-green-600"
name="mode"
checked={config.mode === 'LIVE'}
onChange={() => setConfig({...config, mode: 'LIVE'})}
disabled={status?.isActive}
/>
<div className="flex items-center space-x-2">
<span className="text-2xl">💰</span>
<span className="text-white font-semibold">Live Trading</span>
</div>
</label>
</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 || tf).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 - Made Bigger */}
<div className="grid grid-cols-3 gap-3">
<button
type="button"
onClick={() => setConfig({...config, selectedTimeframes: ['5', '15', '30']})}
disabled={status?.isActive}
className="py-3 px-4 rounded-lg text-sm font-medium bg-green-600/20 text-green-300 hover:bg-green-600/30 transition-all disabled:opacity-50 disabled:cursor-not-allowed border border-green-600/30 hover:border-green-600/50"
>
<div className="text-lg mb-1">📈</div>
<div>Scalping</div>
<div className="text-xs opacity-75">5m, 15m, 30m</div>
</button>
<button
type="button"
onClick={() => setConfig({...config, selectedTimeframes: ['60', '120']})}
disabled={status?.isActive}
className="py-3 px-4 rounded-lg text-sm font-medium bg-blue-600/20 text-blue-300 hover:bg-blue-600/30 transition-all disabled:opacity-50 disabled:cursor-not-allowed border border-blue-600/30 hover:border-blue-600/50"
>
<div className="text-lg mb-1"></div>
<div>Day Trading</div>
<div className="text-xs opacity-75">1h, 2h</div>
</button>
<button
type="button"
onClick={() => setConfig({...config, selectedTimeframes: ['240', 'D']})}
disabled={status?.isActive}
className="py-3 px-4 rounded-lg text-sm font-medium bg-purple-600/20 text-purple-300 hover:bg-purple-600/30 transition-all disabled:opacity-50 disabled:cursor-not-allowed border border-purple-600/30 hover:border-purple-600/50"
>
<div className="text-lg mb-1">🎯</div>
<div>Swing Trading</div>
<div className="text-xs opacity-75">4h, 1d</div>
</button>
</div>
</div>
</div>
</div>
{/* Status and Info Panel */}
<div className="space-y-6">
{/* Status */}
<div className="bg-gray-800 p-6 rounded-lg border border-gray-700">
<h3 className="text-lg font-bold text-white mb-4">Bot Status</h3>
<div className="space-y-3">
<div className="flex justify-between items-center">
<span className="text-gray-400">Status:</span>
<span className={`px-2 py-1 rounded text-xs font-semibold ${
status?.isActive ? 'bg-green-600 text-white' : 'bg-gray-600 text-gray-300'
}`}>
{status?.isActive ? 'RUNNING' : 'STOPPED'}
</span>
</div>
{status?.isActive && (
<>
<div className="flex justify-between items-center">
<span className="text-gray-400">Symbol:</span>
<span className="text-white font-medium">{status.symbol}</span>
</div>
<div className="flex justify-between items-center">
<span className="text-gray-400">Mode:</span>
<span className={`px-2 py-1 rounded text-xs font-semibold ${
status.mode === 'LIVE' ? 'bg-red-600 text-white' : 'bg-blue-600 text-white'
}`}>
{status.mode}
</span>
</div>
<div className="flex justify-between items-center">
<span className="text-gray-400">Timeframes:</span>
<span className="text-cyan-400 text-xs">
{status.timeframes?.map(tf => timeframes.find(t => t.value === tf)?.label || tf).join(', ')}
</span>
</div>
</>
)}
{/* Rate Limit Notification */}
{status?.rateLimitHit && (
<div className="mt-4 p-3 bg-red-900 border border-red-600 rounded-lg">
<div className="flex items-center space-x-2">
<span className="text-red-400 font-semibold"> Rate Limit Reached</span>
</div>
{status.rateLimitMessage && (
<p className="text-red-300 text-sm mt-1">{status.rateLimitMessage}</p>
)}
<p className="text-red-200 text-xs mt-2">
Automation stopped automatically. Please recharge your OpenAI account to continue.
</p>
</div>
)}
</div>
</div>
{/* Balance */}
{balance && (
<div className="bg-gray-800 p-6 rounded-lg border border-gray-700">
<h3 className="text-lg font-bold text-white mb-4">Account Balance</h3>
<div className="space-y-3">
<div className="flex justify-between items-center">
<span className="text-gray-400">Available:</span>
<span className="text-green-400 font-semibold">${balance.availableBalance}</span>
</div>
<div className="flex justify-between items-center">
<span className="text-gray-400">Total:</span>
<span className="text-white font-medium">${balance.totalCollateral}</span>
</div>
<div className="flex justify-between items-center">
<span className="text-gray-400">Positions:</span>
<span className="text-yellow-400">{balance.positions || 0}</span>
</div>
</div>
</div>
)}
{/* Positions */}
{positions.length > 0 && (
<div className="bg-gray-800 p-6 rounded-lg border border-gray-700">
<h3 className="text-lg font-bold text-white mb-4">Open Positions</h3>
<div className="space-y-2">
{positions.map((position, index) => (
<div key={index} className="flex justify-between items-center p-2 bg-gray-700 rounded">
<span className="text-white">{position.symbol}</span>
<span className={`font-semibold ${
position.side === 'LONG' ? 'text-green-400' : 'text-red-400'
}`}>
{position.side} ${position.size}
</span>
</div>
))}
</div>
</div>
)}
</div>
</div>
</div>
)
}