feat: Add direction-specific quality thresholds and dynamic collateral display

- Split QUALITY_LEVERAGE_THRESHOLD into separate LONG and SHORT variants
- Added /api/drift/account-health endpoint for real-time collateral data
- Updated settings UI to show separate controls for LONG/SHORT thresholds
- Position size calculations now use dynamic collateral from Drift account
- Updated .env and docker-compose.yml with new environment variables
- LONG threshold: 95, SHORT threshold: 90 (configurable independently)

Files changed:
- app/api/drift/account-health/route.ts (NEW) - Account health API endpoint
- app/settings/page.tsx - Added collateral state, separate threshold inputs
- app/api/settings/route.ts - GET/POST handlers for LONG/SHORT thresholds
- .env - Added QUALITY_LEVERAGE_THRESHOLD_LONG/SHORT variables
- docker-compose.yml - Added new env vars with fallback defaults

Impact:
- Users can now configure quality thresholds independently for LONG vs SHORT signals
- Position size display dynamically updates based on actual Drift account collateral
- More flexible risk management with direction-specific leverage tiers
This commit is contained in:
mindesbunister
2025-12-01 09:09:30 +01:00
parent a294f44a06
commit 67ef5b1ac6
7 changed files with 90 additions and 19 deletions

View File

@@ -0,0 +1,36 @@
import { NextResponse } from 'next/server'
import { initializeDriftService } from '@/lib/drift/client'
/**
* GET /api/drift/account-health
*
* Returns current account health metrics from Drift Protocol
* Used by settings UI to dynamically display collateral and calculate position sizes
*/
export async function GET() {
try {
const driftService = await initializeDriftService()
if (!driftService) {
return NextResponse.json(
{ error: 'Drift service not initialized' },
{ status: 503 }
)
}
const health = await driftService.getAccountHealth()
return NextResponse.json({
totalCollateral: health.totalCollateral,
freeCollateral: health.freeCollateral,
totalLiability: health.totalLiability,
marginRatio: health.marginRatio,
})
} catch (error: any) {
console.error('❌ Error fetching account health:', error)
return NextResponse.json(
{ error: 'Failed to fetch account health', details: error.message },
{ status: 500 }
)
}
}

View File

@@ -132,7 +132,8 @@ export async function GET() {
USE_ADAPTIVE_LEVERAGE: env.USE_ADAPTIVE_LEVERAGE === 'true',
HIGH_QUALITY_LEVERAGE: parseFloat(env.HIGH_QUALITY_LEVERAGE || '5'),
LOW_QUALITY_LEVERAGE: parseFloat(env.LOW_QUALITY_LEVERAGE || '1'),
QUALITY_LEVERAGE_THRESHOLD: parseInt(env.QUALITY_LEVERAGE_THRESHOLD || '95'),
QUALITY_LEVERAGE_THRESHOLD_LONG: parseInt(env.QUALITY_LEVERAGE_THRESHOLD_LONG || env.QUALITY_LEVERAGE_THRESHOLD || '95'),
QUALITY_LEVERAGE_THRESHOLD_SHORT: parseInt(env.QUALITY_LEVERAGE_THRESHOLD_SHORT || '90'),
}
return NextResponse.json(settings)
@@ -207,7 +208,8 @@ export async function POST(request: NextRequest) {
USE_ADAPTIVE_LEVERAGE: settings.USE_ADAPTIVE_LEVERAGE.toString(),
HIGH_QUALITY_LEVERAGE: settings.HIGH_QUALITY_LEVERAGE.toString(),
LOW_QUALITY_LEVERAGE: settings.LOW_QUALITY_LEVERAGE.toString(),
QUALITY_LEVERAGE_THRESHOLD: settings.QUALITY_LEVERAGE_THRESHOLD.toString(),
QUALITY_LEVERAGE_THRESHOLD_LONG: settings.QUALITY_LEVERAGE_THRESHOLD_LONG.toString(),
QUALITY_LEVERAGE_THRESHOLD_SHORT: settings.QUALITY_LEVERAGE_THRESHOLD_SHORT.toString(),
}
const success = updateEnvFile(updates)

View File

@@ -59,8 +59,9 @@ interface TradingSettings {
USE_ADAPTIVE_LEVERAGE: boolean
HIGH_QUALITY_LEVERAGE: number
LOW_QUALITY_LEVERAGE: number
QUALITY_LEVERAGE_THRESHOLD: number
QUALITY_LEVERAGE_THRESHOLD_LONG: number // Quality threshold for LONG signals
QUALITY_LEVERAGE_THRESHOLD_SHORT: number // Quality threshold for SHORT signals
// Safety
MAX_DAILY_DRAWDOWN: number
MAX_TRADES_PER_HOUR: number
@@ -79,11 +80,30 @@ export default function SettingsPage() {
const [restarting, setRestarting] = useState(false)
const [testing, setTesting] = useState(false)
const [message, setMessage] = useState<{ type: 'success' | 'error', text: string } | null>(null)
const [collateral, setCollateral] = useState<number>(560) // Dynamic collateral from Drift account
useEffect(() => {
loadSettings()
loadCollateral()
}, [])
const loadCollateral = async () => {
try {
const response = await fetch('/api/drift/account-health')
if (response.ok) {
const data = await response.json()
setCollateral(data.freeCollateral)
console.log('✅ Loaded collateral from Drift:', data.freeCollateral)
} else {
console.warn('⚠️ Failed to load collateral, using fallback: 560')
setCollateral(560) // Fallback if API fails
}
} catch (error) {
console.error('❌ Error loading collateral:', error)
setCollateral(560) // Fallback on error
}
}
const loadSettings = async () => {
try {
const response = await fetch('/api/settings')
@@ -508,7 +528,7 @@ export default function SettingsPage() {
min={1}
max={20}
step={1}
description={`Leverage for exceptional signals (Quality ${settings.QUALITY_LEVERAGE_THRESHOLD}+ LONG, 90+ SHORT). Current: ${settings.HIGH_QUALITY_LEVERAGE}x leverage.`}
description={`Leverage for exceptional signals (Quality ${settings.QUALITY_LEVERAGE_THRESHOLD_LONG}+ LONG, ${settings.QUALITY_LEVERAGE_THRESHOLD_SHORT}+ SHORT). Current: ${settings.HIGH_QUALITY_LEVERAGE}x leverage.`}
/>
<Setting
label="Low Quality Leverage"
@@ -517,27 +537,36 @@ export default function SettingsPage() {
min={1}
max={20}
step={1}
description={`Leverage for borderline signals (Quality 90-${settings.QUALITY_LEVERAGE_THRESHOLD-1} LONG, 80-89 SHORT). Current: ${settings.LOW_QUALITY_LEVERAGE}x leverage.`}
description={`Leverage for borderline signals (Quality 90-${settings.QUALITY_LEVERAGE_THRESHOLD_LONG-1} LONG, 80-${settings.QUALITY_LEVERAGE_THRESHOLD_SHORT-1} SHORT). Current: ${settings.LOW_QUALITY_LEVERAGE}x leverage.`}
/>
<Setting
label="Quality Threshold"
value={settings.QUALITY_LEVERAGE_THRESHOLD}
onChange={(v) => updateSetting('QUALITY_LEVERAGE_THRESHOLD', v)}
label="Quality Threshold (LONG)"
value={settings.QUALITY_LEVERAGE_THRESHOLD_LONG}
onChange={(v) => updateSetting('QUALITY_LEVERAGE_THRESHOLD_LONG', v)}
min={80}
max={100}
step={1}
description="Minimum quality score for high-quality leverage tier (applies to LONG signals, SHORT uses 90+)."
description="Minimum quality score for high-quality leverage tier on LONG signals."
/>
<Setting
label="Quality Threshold (SHORT)"
value={settings.QUALITY_LEVERAGE_THRESHOLD_SHORT}
onChange={(v) => updateSetting('QUALITY_LEVERAGE_THRESHOLD_SHORT', v)}
min={80}
max={100}
step={1}
description="Minimum quality score for high-quality leverage tier on SHORT signals."
/>
<div className="p-4 bg-slate-700/50 rounded-lg">
<div className="text-sm text-slate-300 mb-2">Leverage Tiers (with $560 collateral)</div>
<div className="text-sm text-slate-300 mb-2">Leverage Tiers (with ${collateral.toFixed(0)} collateral)</div>
<div className="space-y-2 text-xs">
<div className="flex justify-between">
<span className="text-slate-400">🔥 High Quality (Q{settings.QUALITY_LEVERAGE_THRESHOLD}+ LONG, Q90+ SHORT):</span>
<span className="text-green-400 font-bold">{settings.HIGH_QUALITY_LEVERAGE}x = ${(560 * settings.HIGH_QUALITY_LEVERAGE).toFixed(0)} position</span>
<span className="text-slate-400">🔥 High Quality (Q{settings.QUALITY_LEVERAGE_THRESHOLD_LONG}+ LONG, Q{settings.QUALITY_LEVERAGE_THRESHOLD_SHORT}+ SHORT):</span>
<span className="text-green-400 font-bold">{settings.HIGH_QUALITY_LEVERAGE}x = ${(collateral * settings.HIGH_QUALITY_LEVERAGE).toFixed(0)} position</span>
</div>
<div className="flex justify-between">
<span className="text-slate-400">📊 Low Quality (Q90-{settings.QUALITY_LEVERAGE_THRESHOLD-1} LONG, Q80-89 SHORT):</span>
<span className="text-yellow-400 font-bold">{settings.LOW_QUALITY_LEVERAGE}x = ${(560 * settings.LOW_QUALITY_LEVERAGE).toFixed(0)} position</span>
<span className="text-slate-400">📊 Low Quality (Q90-{settings.QUALITY_LEVERAGE_THRESHOLD_LONG-1} LONG, Q80-{settings.QUALITY_LEVERAGE_THRESHOLD_SHORT-1} SHORT):</span>
<span className="text-yellow-400 font-bold">{settings.LOW_QUALITY_LEVERAGE}x = ${(collateral * settings.LOW_QUALITY_LEVERAGE).toFixed(0)} position</span>
</div>
<div className="flex justify-between">
<span className="text-slate-400">❌ Below Threshold:</span>