/** * Trading Bot Settings UI * * Beautiful interface for managing trading parameters */ 'use client' import { useState, useEffect } from 'react' interface TradingSettings { // Global fallback settings MAX_POSITION_SIZE_USD: number LEVERAGE: number // Per-symbol settings SOLANA_ENABLED: boolean SOLANA_POSITION_SIZE: number SOLANA_LEVERAGE: number ETHEREUM_ENABLED: boolean ETHEREUM_POSITION_SIZE: number ETHEREUM_LEVERAGE: number // Risk management STOP_LOSS_PERCENT: number TAKE_PROFIT_1_PERCENT: number TAKE_PROFIT_1_SIZE_PERCENT: number TAKE_PROFIT_2_PERCENT: number TAKE_PROFIT_2_SIZE_PERCENT: number EMERGENCY_STOP_PERCENT: number BREAKEVEN_TRIGGER_PERCENT: number PROFIT_LOCK_TRIGGER_PERCENT: number PROFIT_LOCK_PERCENT: number USE_TRAILING_STOP: boolean TRAILING_STOP_PERCENT: number TRAILING_STOP_ACTIVATION: number MAX_DAILY_DRAWDOWN: number MAX_TRADES_PER_HOUR: number MIN_TIME_BETWEEN_TRADES: number MIN_QUALITY_SCORE: number SLIPPAGE_TOLERANCE: number DRY_RUN: boolean } export default function SettingsPage() { const [settings, setSettings] = useState(null) const [loading, setLoading] = useState(true) const [saving, setSaving] = useState(false) const [restarting, setRestarting] = useState(false) const [testing, setTesting] = useState(false) const [message, setMessage] = useState<{ type: 'success' | 'error', text: string } | null>(null) useEffect(() => { loadSettings() }, []) const loadSettings = async () => { try { const response = await fetch('/api/settings') const data = await response.json() setSettings(data) setLoading(false) } catch (error) { setMessage({ type: 'error', text: 'Failed to load settings' }) setLoading(false) } } const saveSettings = async () => { setSaving(true) setMessage(null) try { const response = await fetch('/api/settings', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(settings), }) if (response.ok) { setMessage({ type: 'success', text: 'Settings saved! Click "Restart Bot" to apply changes.' }) } else { setMessage({ type: 'error', text: 'Failed to save settings' }) } } catch (error) { setMessage({ type: 'error', text: 'Failed to save settings' }) } setSaving(false) } const restartBot = async () => { setRestarting(true) setMessage(null) try { const response = await fetch('/api/restart', { method: 'POST', }) if (response.ok) { setMessage({ type: 'success', text: 'Bot is restarting... Settings will be applied in ~10 seconds.' }) } else { setMessage({ type: 'error', text: 'Failed to restart bot. Please restart manually with: docker restart trading-bot' }) } } catch (error) { setMessage({ type: 'error', text: 'Failed to restart bot. Please restart manually with: docker restart trading-bot' }) } setRestarting(false) } const testTrade = async (direction: 'long' | 'short', symbol: string = 'SOLUSDT') => { if (!confirm(`⚠️ This will execute a REAL ${direction.toUpperCase()} trade on ${symbol} with current settings. Continue?`)) { return } setTesting(true) setMessage(null) try { const response = await fetch('/api/trading/test', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ symbol: symbol, direction: direction, }), }) const data = await response.json() if (data.success) { const dualStopsMsg = data.useDualStops ? `Dual stops: Soft $${data.softStopPrice?.toFixed(4)} | Hard $${data.hardStopPrice?.toFixed(4)}` : `SL: $${data.stopLoss?.toFixed(4)}` setMessage({ type: 'success', text: `✅ ${symbol} ${direction.toUpperCase()} test trade executed! Size: $${data.positionSize?.toFixed(2)} | Entry: $${data.entryPrice?.toFixed(4)} | ${dualStopsMsg} | TX: ${data.positionId?.substring(0, 8)}...` }) } else { setMessage({ type: 'error', text: `Failed: ${data.error || data.message}` }) } } catch (error) { setMessage({ type: 'error', text: `Test trade failed: ${error instanceof Error ? error.message : 'Unknown error'}` }) } setTesting(false) } const updateSetting = (key: keyof TradingSettings, value: any) => { if (!settings) return setSettings({ ...settings, [key]: value }) } const calculateRisk = (baseSize?: number, leverage?: number) => { if (!settings) return null const size = baseSize ?? settings.MAX_POSITION_SIZE_USD const lev = leverage ?? settings.LEVERAGE const maxLoss = size * lev * (Math.abs(settings.STOP_LOSS_PERCENT) / 100) const tp1Gain = size * lev * (settings.TAKE_PROFIT_1_PERCENT / 100) * (settings.TAKE_PROFIT_1_SIZE_PERCENT / 100) const tp2Gain = size * lev * (settings.TAKE_PROFIT_2_PERCENT / 100) * (settings.TAKE_PROFIT_2_SIZE_PERCENT / 100) const fullWin = tp1Gain + tp2Gain return { maxLoss, tp1Gain, tp2Gain, fullWin } } if (loading) { return (
Loading settings...
) } if (!settings) return null const risk = calculateRisk() return (
{/* Header */}

⚙️ Trading Bot Settings

Configure your automated trading parameters

{/* Message */} {message && (
{message.text}
)} {/* Risk Calculator */} {risk && (

📊 Risk Calculator

Max Loss (SL)
-${risk.maxLoss.toFixed(2)}
TP1 Gain ({settings.TAKE_PROFIT_1_SIZE_PERCENT}%)
+${risk.tp1Gain.toFixed(2)}
TP2 Gain ({settings.TAKE_PROFIT_2_SIZE_PERCENT}%)
+${risk.tp2Gain.toFixed(2)}
Full Win
+${risk.fullWin.toFixed(2)}
Risk/Reward Ratio: 1:{(risk.fullWin / risk.maxLoss).toFixed(2)}
)} {/* Settings Sections */}
{/* Per-Symbol Position Sizing */}

Enable/disable Solana trading and set symbol-specific position sizing. When enabled, these settings override global defaults for SOL trades.

🟢 Enable Solana Trading
Accept SOL-PERP trade signals from TradingView
updateSetting('SOLANA_POSITION_SIZE', v)} min={1} max={10000} step={1} description={`Base capital for SOL trades. With ${settings.SOLANA_LEVERAGE}x leverage = $${(settings.SOLANA_POSITION_SIZE * settings.SOLANA_LEVERAGE).toFixed(0)} notional position.`} /> updateSetting('SOLANA_LEVERAGE', v)} min={1} max={20} step={1} description="Leverage multiplier for Solana positions only." /> {(() => { const solRisk = calculateRisk(settings.SOLANA_POSITION_SIZE, settings.SOLANA_LEVERAGE) return solRisk && (
SOL Risk/Reward
Max Loss: ${solRisk.maxLoss.toFixed(2)}
Full Win: ${solRisk.fullWin.toFixed(2)}
R:R 1:{(solRisk.fullWin / solRisk.maxLoss).toFixed(2)}
) })()}

Enable/disable Ethereum trading and set symbol-specific position sizing. When enabled, these settings override global defaults for ETH trades.

🟢 Enable Ethereum Trading
Accept ETH-PERP trade signals from TradingView
updateSetting('ETHEREUM_POSITION_SIZE', v)} min={1} max={10000} step={1} description={`Base capital for ETH trades. With ${settings.ETHEREUM_LEVERAGE}x leverage = $${(settings.ETHEREUM_POSITION_SIZE * settings.ETHEREUM_LEVERAGE).toFixed(0)} notional position. Drift minimum: ~$38-40 (0.01 ETH).`} /> updateSetting('ETHEREUM_LEVERAGE', v)} min={1} max={20} step={1} description="Leverage multiplier for Ethereum positions only." /> {(() => { const ethRisk = calculateRisk(settings.ETHEREUM_POSITION_SIZE, settings.ETHEREUM_LEVERAGE) return ethRisk && (
ETH Risk/Reward
Max Loss: ${ethRisk.maxLoss.toFixed(2)}
Full Win: ${ethRisk.fullWin.toFixed(2)}
R:R 1:{(ethRisk.fullWin / ethRisk.maxLoss).toFixed(2)}
) })()}
{/* Global Position Sizing (Fallback) */}

These are fallback values used for any symbol that doesn't have its own specific settings (like BTC-PERP). SOL and ETH ignore these when their own settings are configured above.

updateSetting('MAX_POSITION_SIZE_USD', v)} min={10} max={10000} step={10} description="Base USD amount per trade for unspecified symbols." /> updateSetting('LEVERAGE', v)} min={1} max={20} step={1} description="Leverage multiplier for unspecified symbols." />
{/* Risk Management */}
updateSetting('STOP_LOSS_PERCENT', v)} min={-10} max={-0.1} step={0.1} description="Close 100% of position when price drops this much. Protects from large losses." /> updateSetting('TAKE_PROFIT_1_PERCENT', v)} min={0.1} max={10} step={0.1} description="Price level for first take profit exit." /> updateSetting('TAKE_PROFIT_1_SIZE_PERCENT', v)} min={1} max={100} step={1} description="What % of position to close at TP1. Example: 50 = close half." /> updateSetting('TAKE_PROFIT_2_PERCENT', v)} min={0.1} max={20} step={0.1} description="Price level for second take profit exit." /> updateSetting('TAKE_PROFIT_2_SIZE_PERCENT', v)} min={1} max={100} step={1} description="What % of remaining position to close at TP2. Example: 100 = close rest." /> updateSetting('EMERGENCY_STOP_PERCENT', v)} min={-20} max={-0.1} step={0.1} description="Hard stop for flash crashes. Should be wider than regular SL." />
{/* Dynamic Adjustments */}
updateSetting('BREAKEVEN_TRIGGER_PERCENT', v)} min={0} max={5} step={0.1} description="After TP1 closes, move SL to this profit level. Should be between 0% (breakeven) and TP1%. Example: 0.4% = locks in +4% account profit on remaining position." /> updateSetting('PROFIT_LOCK_TRIGGER_PERCENT', v)} min={0} max={10} step={0.1} description="After TP1, if price continues to this level, move SL higher to lock more profit. Must be > TP1 and < TP2." /> updateSetting('PROFIT_LOCK_PERCENT', v)} min={0} max={5} step={0.1} description="When Profit Lock Trigger hits, move SL to this profit level. Should be > Breakeven Trigger." />
{/* Trailing Stop */}

After TP2 closes, the remaining position (your "runner") can use a trailing stop loss that follows price. This lets you capture big moves while protecting profit.

updateSetting('USE_TRAILING_STOP', v === 1)} min={0} max={1} step={1} description="Enable trailing stop for runner position after TP2. 0 = disabled, 1 = enabled." /> updateSetting('TRAILING_STOP_PERCENT', v)} min={0.1} max={2} step={0.1} description="How far below peak price (for longs) to trail the stop loss. Example: 0.3% = SL trails 0.3% below highest price reached." /> updateSetting('TRAILING_STOP_ACTIVATION', v)} min={0.1} max={5} step={0.1} description="Runner must reach this profit % before trailing stop activates. Prevents premature stops. Example: 0.5% = wait until runner is +0.5% profit." />
{/* Trade Limits */}
updateSetting('MAX_DAILY_DRAWDOWN', v)} min={-1000} max={-10} step={10} description="Stop trading if daily loss exceeds this amount." /> updateSetting('MAX_TRADES_PER_HOUR', v)} min={1} max={20} step={1} description="Maximum number of trades allowed per hour." /> updateSetting('MIN_TIME_BETWEEN_TRADES', v)} min={0} max={60} step={1} description="Minimum wait time between trades to prevent overtrading." /> updateSetting('MIN_QUALITY_SCORE', v)} min={0} max={100} step={5} description="Minimum signal quality score required to accept a trade. Signals below this score will be blocked." />
{/* Execution */}
updateSetting('SLIPPAGE_TOLERANCE', v)} min={0.1} max={5} step={0.1} description="Maximum acceptable price slippage on market orders." />
🧪 Dry Run Mode
Simulate trades without executing. Enable for testing.
{/* Action Buttons */}
{/* Primary Actions */}
{/* Test Trade Buttons */}
🧪 Test Trades (REAL MONEY)
💡 Save settings first, then click Restart Bot to apply changes
) } function Section({ title, description, children }: { title: string, description: string, children: React.ReactNode }) { return (

{title}

{description}

{children}
) } function Setting({ label, value, onChange, min, max, step, description }: { label: string value: number onChange: (value: number) => void min: number max: number step: number description: string }) { return (
onChange(parseFloat(e.target.value))} min={min} max={max} step={step} className="w-24 bg-slate-700 text-white px-3 py-2 rounded-lg border border-slate-600 focus:border-blue-500 focus:outline-none" />
onChange(parseFloat(e.target.value))} min={min} max={max} step={step} className="w-full h-2 bg-slate-700 rounded-lg appearance-none cursor-pointer slider" />

{description}

) }