Add Position Sync feature for recovering tracking after partial fills

- New /api/trading/sync-positions endpoint (no auth)
- Fetches actual Drift positions and compares with Position Manager
- Removes stale tracking, adds missing positions with calculated TP/SL
- Settings UI: Orange 'Sync Positions' button added
- CLI script: scripts/sync-positions.sh for terminal access
- Full documentation in docs/guides/POSITION_SYNC_GUIDE.md
- Quick reference: POSITION_SYNC_QUICK_REF.md
- Updated AI instructions with pitfall #23

Problem solved: Manual Telegram trades with partial fills can cause
Position Manager to lose tracking, leaving positions without software-
based stop loss protection. This feature restores dual-layer protection.

Note: Docker build not picking up route yet (cache issue), needs investigation
This commit is contained in:
mindesbunister
2025-11-10 17:05:32 +01:00
parent 2e47731e8e
commit 089308a07e
6 changed files with 440 additions and 0 deletions

View File

@@ -125,6 +125,33 @@ export default function SettingsPage() {
setRestarting(false)
}
const syncPositions = async () => {
setLoading(true)
setMessage(null)
try {
const response = await fetch('/api/trading/sync-positions', {
method: 'POST',
})
const data = await response.json()
if (data.success) {
const { results } = data
let msg = '✅ Position sync complete! '
if (results.added.length > 0) msg += `Added: ${results.added.join(', ')}. `
if (results.removed.length > 0) msg += `Removed: ${results.removed.join(', ')}. `
if (results.unchanged.length > 0) msg += `Already tracking: ${results.unchanged.join(', ')}. `
if (results.errors.length > 0) msg += `⚠️ Errors: ${results.errors.length}`
setMessage({ type: 'success', text: msg })
} else {
setMessage({ type: 'error', text: `Sync failed: ${data.error || data.message}` })
}
} catch (error) {
setMessage({ type: 'error', text: `Sync failed: ${error instanceof Error ? error.message : 'Unknown error'}` })
}
setLoading(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
@@ -793,6 +820,14 @@ export default function SettingsPage() {
>
{restarting ? '🔄 Restarting...' : '🔄 Restart Bot'}
</button>
<button
onClick={syncPositions}
disabled={loading}
className="flex-1 bg-gradient-to-r from-orange-500 to-red-500 text-white font-bold py-4 px-6 rounded-lg hover:from-orange-600 hover:to-red-600 transition-all disabled:opacity-50 disabled:cursor-not-allowed"
title="Re-sync Position Manager with actual Drift positions"
>
{loading ? '🔄 Syncing...' : '🔄 Sync Positions'}
</button>
<button
onClick={loadSettings}
className="bg-slate-700 text-white font-bold py-4 px-6 rounded-lg hover:bg-slate-600 transition-all"