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

7
.env
View File

@@ -395,11 +395,14 @@ MIN_SIGNAL_QUALITY_SCORE=91
MIN_SIGNAL_QUALITY_SCORE_LONG=90
MIN_SIGNAL_QUALITY_SCORE_SHORT=80
# Adaptive Leverage System (Nov 24, 2025)
# ENABLED Dec 1, 2025: 10x for high-quality signals (Q≥95 LONGs, Q≥90 SHORTs), 5x for borderline
# ENABLED Dec 1, 2025: 10x for high-quality signals, 5x for borderline
# Direction-specific thresholds: LONG ≥95, SHORT ≥90
USE_ADAPTIVE_LEVERAGE=true
HIGH_QUALITY_LEVERAGE=10
LOW_QUALITY_LEVERAGE=5
QUALITY_LEVERAGE_THRESHOLD=95
QUALITY_LEVERAGE_THRESHOLD_LONG=95
QUALITY_LEVERAGE_THRESHOLD_SHORT=90
QUALITY_LEVERAGE_THRESHOLD=95 # Kept for backward compatibility
SOLANA_ENABLED=true
SOLANA_POSITION_SIZE=100
SOLANA_LEVERAGE=5

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>

Binary file not shown.

View File

@@ -49,7 +49,9 @@ services:
USE_ADAPTIVE_LEVERAGE: ${USE_ADAPTIVE_LEVERAGE:-true}
HIGH_QUALITY_LEVERAGE: ${HIGH_QUALITY_LEVERAGE:-5}
LOW_QUALITY_LEVERAGE: ${LOW_QUALITY_LEVERAGE:-1}
QUALITY_LEVERAGE_THRESHOLD: ${QUALITY_LEVERAGE_THRESHOLD:-95}
QUALITY_LEVERAGE_THRESHOLD_LONG: ${QUALITY_LEVERAGE_THRESHOLD_LONG:-95}
QUALITY_LEVERAGE_THRESHOLD_SHORT: ${QUALITY_LEVERAGE_THRESHOLD_SHORT:-90}
QUALITY_LEVERAGE_THRESHOLD: ${QUALITY_LEVERAGE_THRESHOLD:-95} # Backward compatibility
# Database (if using PostgreSQL)
DATABASE_URL: ${DATABASE_URL:-postgresql://postgres:postgres@postgres:5432/trading_bot_v4}

View File

@@ -13,7 +13,6 @@
import { getMarketDataCache } from './market-data-cache'
import { getMergedConfig } from '../../config/trading'
import { getPrismaClient } from '../database/client'
import { sendValidationNotification } from '../notifications/telegram'
interface QueuedSignal {