Files
trading_bot_v4/app/settings/page.tsx
mindesbunister 2405bff68a feat: Complete Trading Bot v4 with Drift Protocol integration
Features:
- Autonomous trading system with Drift Protocol on Solana
- Real-time position monitoring with Pyth price feeds
- Dynamic stop-loss and take-profit management
- n8n workflow integration for TradingView signals
- Beautiful web UI for settings management
- REST API for trade execution and monitoring

- Next.js 15 with standalone output mode
- TypeScript with strict typing
- Docker containerization with multi-stage builds
- PostgreSQL database for trade history
- Singleton pattern for Drift client connection pooling
- BN.js for BigNumber handling (Drift SDK requirement)

- Configurable stop-loss and take-profit levels
- Breakeven trigger and profit locking
- Daily loss limits and trade cooldowns
- Slippage tolerance controls
- DRY_RUN mode for safe testing

- Real-time risk calculator
- Interactive sliders for all parameters
- Live preview of trade outcomes
- Position sizing and leverage controls
- Beautiful gradient design with Tailwind CSS

- POST /api/trading/execute - Execute trades
- POST /api/trading/close - Close positions
- GET /api/trading/positions - Monitor active trades
- GET /api/trading/check-risk - Validate trade signals
- GET /api/settings - View configuration
- POST /api/settings - Update configuration

- Fixed Borsh serialization errors (simplified order params)
- Resolved RPC rate limiting with singleton pattern
- Fixed BigInt vs BN type mismatches
- Corrected order execution flow
- Improved position state management

- Complete setup guides
- Docker deployment instructions
- n8n workflow configuration
- API reference documentation
- Risk management guidelines

- Runs on port 3001 (external), 3000 (internal)
- Uses Helius RPC for optimal performance
- Production-ready with error handling
- Health monitoring and logging
2025-10-24 14:24:36 +02:00

385 lines
14 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.
/**
* Trading Bot Settings UI
*
* Beautiful interface for managing trading parameters
*/
'use client'
import { useState, useEffect } from 'react'
interface TradingSettings {
MAX_POSITION_SIZE_USD: number
LEVERAGE: number
STOP_LOSS_PERCENT: number
TAKE_PROFIT_1_PERCENT: number
TAKE_PROFIT_2_PERCENT: number
EMERGENCY_STOP_PERCENT: number
BREAKEVEN_TRIGGER_PERCENT: number
PROFIT_LOCK_TRIGGER_PERCENT: number
PROFIT_LOCK_PERCENT: number
MAX_DAILY_DRAWDOWN: number
MAX_TRADES_PER_HOUR: number
MIN_TIME_BETWEEN_TRADES: number
SLIPPAGE_TOLERANCE: number
DRY_RUN: boolean
}
export default function SettingsPage() {
const [settings, setSettings] = useState<TradingSettings | null>(null)
const [loading, setLoading] = useState(true)
const [saving, setSaving] = 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! Restart the 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 updateSetting = (key: keyof TradingSettings, value: any) => {
if (!settings) return
setSettings({ ...settings, [key]: value })
}
const calculateRisk = () => {
if (!settings) return null
const maxLoss = settings.MAX_POSITION_SIZE_USD * settings.LEVERAGE * (Math.abs(settings.STOP_LOSS_PERCENT) / 100)
const tp1Gain = settings.MAX_POSITION_SIZE_USD * settings.LEVERAGE * (settings.TAKE_PROFIT_1_PERCENT / 100)
const tp2Gain = settings.MAX_POSITION_SIZE_USD * settings.LEVERAGE * (settings.TAKE_PROFIT_2_PERCENT / 100)
const fullWin = tp1Gain / 2 + tp2Gain / 2 // 50% at each TP
return { maxLoss, tp1Gain, tp2Gain, fullWin }
}
if (loading) {
return (
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 flex items-center justify-center">
<div className="text-white text-xl">Loading settings...</div>
</div>
)
}
if (!settings) return null
const risk = calculateRisk()
return (
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 py-8 px-4">
<div className="max-w-5xl mx-auto">
{/* Header */}
<div className="mb-8">
<h1 className="text-4xl font-bold text-white mb-2"> Trading Bot Settings</h1>
<p className="text-slate-400">Configure your automated trading parameters</p>
</div>
{/* Message */}
{message && (
<div className={`mb-6 p-4 rounded-lg ${
message.type === 'success' ? 'bg-green-500/20 text-green-400 border border-green-500/50' : 'bg-red-500/20 text-red-400 border border-red-500/50'
}`}>
{message.text}
</div>
)}
{/* Risk Calculator */}
{risk && (
<div className="mb-8 bg-slate-800/50 backdrop-blur-sm border border-slate-700 rounded-xl p-6">
<h2 className="text-xl font-bold text-white mb-4">📊 Risk Calculator</h2>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<div className="bg-red-500/10 border border-red-500/50 rounded-lg p-4">
<div className="text-red-400 text-sm mb-1">Max Loss (SL)</div>
<div className="text-white text-2xl font-bold">-${risk.maxLoss.toFixed(2)}</div>
</div>
<div className="bg-blue-500/10 border border-blue-500/50 rounded-lg p-4">
<div className="text-blue-400 text-sm mb-1">TP1 Gain (50%)</div>
<div className="text-white text-2xl font-bold">+${(risk.tp1Gain / 2).toFixed(2)}</div>
</div>
<div className="bg-green-500/10 border border-green-500/50 rounded-lg p-4">
<div className="text-green-400 text-sm mb-1">TP2 Gain (50%)</div>
<div className="text-white text-2xl font-bold">+${(risk.tp2Gain / 2).toFixed(2)}</div>
</div>
<div className="bg-purple-500/10 border border-purple-500/50 rounded-lg p-4">
<div className="text-purple-400 text-sm mb-1">Full Win</div>
<div className="text-white text-2xl font-bold">+${risk.fullWin.toFixed(2)}</div>
</div>
</div>
<div className="mt-4 text-slate-400 text-sm">
Risk/Reward Ratio: 1:{(risk.fullWin / risk.maxLoss).toFixed(2)}
</div>
</div>
)}
{/* Settings Sections */}
<div className="space-y-6">
{/* Position Sizing */}
<Section title="💰 Position Sizing" description="Control your trade size and leverage">
<Setting
label="Position Size (USD)"
value={settings.MAX_POSITION_SIZE_USD}
onChange={(v) => updateSetting('MAX_POSITION_SIZE_USD', v)}
min={10}
max={10000}
step={10}
description="Base USD amount per trade. With 5x leverage, $50 = $250 position."
/>
<Setting
label="Leverage"
value={settings.LEVERAGE}
onChange={(v) => updateSetting('LEVERAGE', v)}
min={1}
max={20}
step={1}
description="Multiplier for your position. Higher = more profit AND more risk."
/>
</Section>
{/* Risk Management */}
<Section title="🛡️ Risk Management" description="Stop loss and take profit levels">
<Setting
label="Stop Loss (%)"
value={settings.STOP_LOSS_PERCENT}
onChange={(v) => 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."
/>
<Setting
label="Take Profit 1 (%)"
value={settings.TAKE_PROFIT_1_PERCENT}
onChange={(v) => updateSetting('TAKE_PROFIT_1_PERCENT', v)}
min={0.1}
max={10}
step={0.1}
description="Close 50% of position at this profit level. Locks in early gains."
/>
<Setting
label="Take Profit 2 (%)"
value={settings.TAKE_PROFIT_2_PERCENT}
onChange={(v) => updateSetting('TAKE_PROFIT_2_PERCENT', v)}
min={0.1}
max={20}
step={0.1}
description="Close remaining 50% at this profit level. Captures larger moves."
/>
<Setting
label="Emergency Stop (%)"
value={settings.EMERGENCY_STOP_PERCENT}
onChange={(v) => 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."
/>
</Section>
{/* Dynamic Adjustments */}
<Section title="🎯 Dynamic Stop Loss" description="Automatically adjust SL as trade moves in profit">
<Setting
label="Breakeven Trigger (%)"
value={settings.BREAKEVEN_TRIGGER_PERCENT}
onChange={(v) => updateSetting('BREAKEVEN_TRIGGER_PERCENT', v)}
min={0}
max={5}
step={0.1}
description="Move SL to breakeven (entry price) when profit reaches this level."
/>
<Setting
label="Profit Lock Trigger (%)"
value={settings.PROFIT_LOCK_TRIGGER_PERCENT}
onChange={(v) => updateSetting('PROFIT_LOCK_TRIGGER_PERCENT', v)}
min={0}
max={10}
step={0.1}
description="When profit reaches this level, lock in profit by moving SL."
/>
<Setting
label="Profit Lock Amount (%)"
value={settings.PROFIT_LOCK_PERCENT}
onChange={(v) => updateSetting('PROFIT_LOCK_PERCENT', v)}
min={0}
max={5}
step={0.1}
description="Move SL to this profit level when lock trigger is hit."
/>
</Section>
{/* Trade Limits */}
<Section title="⚠️ Safety Limits" description="Prevent overtrading and excessive losses">
<Setting
label="Max Daily Loss (USD)"
value={settings.MAX_DAILY_DRAWDOWN}
onChange={(v) => updateSetting('MAX_DAILY_DRAWDOWN', v)}
min={-1000}
max={-10}
step={10}
description="Stop trading if daily loss exceeds this amount."
/>
<Setting
label="Max Trades Per Hour"
value={settings.MAX_TRADES_PER_HOUR}
onChange={(v) => updateSetting('MAX_TRADES_PER_HOUR', v)}
min={1}
max={20}
step={1}
description="Maximum number of trades allowed per hour."
/>
<Setting
label="Cooldown Between Trades (seconds)"
value={settings.MIN_TIME_BETWEEN_TRADES}
onChange={(v) => updateSetting('MIN_TIME_BETWEEN_TRADES', v)}
min={0}
max={3600}
step={60}
description="Minimum wait time between trades to prevent overtrading."
/>
</Section>
{/* Execution */}
<Section title="⚡ Execution Settings" description="Order execution parameters">
<Setting
label="Slippage Tolerance (%)"
value={settings.SLIPPAGE_TOLERANCE}
onChange={(v) => updateSetting('SLIPPAGE_TOLERANCE', v)}
min={0.1}
max={5}
step={0.1}
description="Maximum acceptable price slippage on market orders."
/>
<div className="flex items-center justify-between p-4 bg-slate-700/30 rounded-lg">
<div className="flex-1">
<div className="text-white font-medium mb-1">🧪 Dry Run Mode</div>
<div className="text-slate-400 text-sm">
Simulate trades without executing. Enable for testing.
</div>
</div>
<button
onClick={() => updateSetting('DRY_RUN', !settings.DRY_RUN)}
className={`relative inline-flex h-8 w-14 items-center rounded-full transition-colors ${
settings.DRY_RUN ? 'bg-blue-500' : 'bg-slate-600'
}`}
>
<span
className={`inline-block h-6 w-6 transform rounded-full bg-white transition-transform ${
settings.DRY_RUN ? 'translate-x-7' : 'translate-x-1'
}`}
/>
</button>
</div>
</Section>
</div>
{/* Save Button */}
<div className="mt-8 flex gap-4">
<button
onClick={saveSettings}
disabled={saving}
className="flex-1 bg-gradient-to-r from-blue-500 to-purple-500 text-white font-bold py-4 px-6 rounded-lg hover:from-blue-600 hover:to-purple-600 transition-all disabled:opacity-50 disabled:cursor-not-allowed"
>
{saving ? '💾 Saving...' : '💾 Save Settings'}
</button>
<button
onClick={loadSettings}
className="bg-slate-700 text-white font-bold py-4 px-6 rounded-lg hover:bg-slate-600 transition-all"
>
🔄 Reset
</button>
</div>
<div className="mt-4 text-center text-slate-500 text-sm">
Changes require bot restart to take effect
</div>
</div>
</div>
)
}
function Section({ title, description, children }: { title: string, description: string, children: React.ReactNode }) {
return (
<div className="bg-slate-800/50 backdrop-blur-sm border border-slate-700 rounded-xl p-6">
<h2 className="text-xl font-bold text-white mb-1">{title}</h2>
<p className="text-slate-400 text-sm mb-6">{description}</p>
<div className="space-y-4">
{children}
</div>
</div>
)
}
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 (
<div className="space-y-2">
<div className="flex items-center justify-between">
<label className="text-white font-medium">{label}</label>
<input
type="number"
value={value}
onChange={(e) => 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"
/>
</div>
<input
type="range"
value={value}
onChange={(e) => 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"
/>
<p className="text-slate-400 text-sm">{description}</p>
</div>
)
}