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:
7
.env
7
.env
@@ -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
|
||||
|
||||
36
app/api/drift/account-health/route.ts
Normal file
36
app/api/drift/account-health/route.ts
Normal 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 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -59,7 +59,8 @@ 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
|
||||
@@ -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.
@@ -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}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user