From 77eb727f8d9cc5fc6fde71970689c6bb4075c862 Mon Sep 17 00:00:00 2001 From: mindesbunister Date: Wed, 16 Jul 2025 11:36:58 +0200 Subject: [PATCH] Fix spot trade logic: remove incorrect positions and enhance trade history display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove incorrect open positions for spot swaps (instant settlements) - Add DELETE API route for position removal (/api/trading/positions/[positionId]) - Update existing SOL/USDC trade to clearly mark as SPOT_SWAP - Enhance TradesHistoryPanel with visual trade type indicators: * SPOT_SWAP: Purple badge with ⚡ icon * MARKET: Blue badge with 📈 icon * LIMIT: Orange badge with 🎯 icon * STOP: Red badge with 🛑 icon - Add trade history update functionality for modifying existing trades - Fix container communication URLs in execute-dex route Result: Spot trades no longer create open positions, trade history clearly shows trade types --- app/api/trading/execute-dex/route.js | 43 ++++------- app/api/trading/history/route.js | 25 +++++++ .../trading/positions/[positionId]/route.js | 72 +++++++++++++++++++ components/TradesHistoryPanel.js | 25 ++++++- 4 files changed, 134 insertions(+), 31 deletions(-) create mode 100644 app/api/trading/positions/[positionId]/route.js diff --git a/app/api/trading/execute-dex/route.js b/app/api/trading/execute-dex/route.js index d1bc998..5ad7898 100644 --- a/app/api/trading/execute-dex/route.js +++ b/app/api/trading/execute-dex/route.js @@ -191,8 +191,9 @@ export async function POST(request) { message: `${side.toUpperCase()} order executed on Jupiter DEX${stopLoss || takeProfit ? ' with TP/SL monitoring' : ''}` } - // Add trade to history + // Add trade to history with clear spot swap indication try { + // Use localhost for internal container communication await fetch('http://localhost:3000/api/trading/history', { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -202,44 +203,26 @@ export async function POST(request) { side: side.toUpperCase(), amount: amount, price: 168.1, // Get from actual execution - type: 'market', + type: 'SPOT_SWAP', // Clearly indicate this is a spot swap status: 'executed', txId: tradeResult.txId, dex: 'JUPITER', - notes: closePosition ? 'Position closing trade' : null + tradingMode: 'SPOT', + notes: closePosition ? 'Position closing trade' : `Spot swap: ${amount} ${fromCoin || symbol} → ${toCoin || 'USDC'}` }) }) - console.log('✅ Trade added to history') + console.log('✅ Spot trade added to history') } catch (error) { console.error('❌ Failed to add trade to history:', error) } - // Create position only if not closing an existing position - if (!closePosition) { - try { - const positionResponse = await fetch('http://localhost:3000/api/trading/positions', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - action: 'add', - symbol: tradingPair || `${fromCoin || symbol}/${toCoin || 'USDC'}`, - side: side.toUpperCase(), - amount: amount, - entryPrice: 168.1, // Get from actual execution - stopLoss: stopLoss, - takeProfit: takeProfit, - txId: tradeResult.txId, - leverage: 1 - }) - }) - - if (positionResponse.ok) { - console.log('✅ Position created for DEX trade') - } - } catch (error) { - console.error('❌ Failed to create position:', error) - } - } + // DON'T create positions for spot swaps - they're instant settlements + // Only leverage/perpetual trades should create positions + // For spot trades, we only need trade history entries + + // Note: Position creation is intentionally removed for spot trades + // If this trade had stop loss or take profit, those would be handled as separate limit orders + console.log('✅ Spot trade completed - no position created (instant settlement)') return NextResponse.json(tradeResponse) diff --git a/app/api/trading/history/route.js b/app/api/trading/history/route.js index 12b1ac7..805ff11 100644 --- a/app/api/trading/history/route.js +++ b/app/api/trading/history/route.js @@ -98,6 +98,31 @@ export async function POST(request) { message: `Trade added to history: ${newTrade.side} ${newTrade.amount} ${newTrade.symbol}` }) + } else if (action === 'update') { + // Load existing trades + const tradesHistory = loadTrades() + const { tradeId, updates } = tradeData + + // Find and update the trade + const tradeIndex = tradesHistory.findIndex(trade => trade.id === tradeId) + + if (tradeIndex === -1) { + return NextResponse.json({ + success: false, + error: 'Trade not found' + }, { status: 404 }) + } + + // Update the trade with new data + tradesHistory[tradeIndex] = { ...tradesHistory[tradeIndex], ...updates } + saveTrades(tradesHistory) + + return NextResponse.json({ + success: true, + trade: tradesHistory[tradeIndex], + message: `Trade updated: ${tradeId}` + }) + } else if (action === 'clear') { // Clear trade history saveTrades([]) diff --git a/app/api/trading/positions/[positionId]/route.js b/app/api/trading/positions/[positionId]/route.js new file mode 100644 index 0000000..aa9cd93 --- /dev/null +++ b/app/api/trading/positions/[positionId]/route.js @@ -0,0 +1,72 @@ +import { NextResponse } from 'next/server' +import fs from 'fs' +import path from 'path' + +// Persistent storage for positions using JSON file +const POSITIONS_FILE = path.join(process.cwd(), 'data', 'positions.json') + +// Helper functions for persistent storage +function loadPositions() { + try { + if (fs.existsSync(POSITIONS_FILE)) { + const data = fs.readFileSync(POSITIONS_FILE, 'utf8') + return JSON.parse(data) + } + } catch (error) { + console.error('Error loading positions:', error) + } + return [] +} + +function savePositions(positions) { + try { + fs.writeFileSync(POSITIONS_FILE, JSON.stringify(positions, null, 2)) + } catch (error) { + console.error('Error saving positions:', error) + } +} + +export async function DELETE(request, { params }) { + try { + const { positionId } = params + + if (!positionId) { + return NextResponse.json({ + success: false, + error: 'Position ID is required' + }, { status: 400 }) + } + + // Load existing positions + const activePositions = loadPositions() + + // Find and remove the position + const positionIndex = activePositions.findIndex(pos => pos.id === positionId) + + if (positionIndex === -1) { + return NextResponse.json({ + success: false, + error: 'Position not found' + }, { status: 404 }) + } + + const removedPosition = activePositions[positionIndex] + activePositions.splice(positionIndex, 1) + savePositions(activePositions) + + console.log(`🗑️ Removed incorrect position: ${removedPosition.symbol} (${positionId})`) + + return NextResponse.json({ + success: true, + message: `Position ${positionId} removed successfully`, + removedPosition + }) + + } catch (error) { + console.error('Error deleting position:', error) + return NextResponse.json({ + success: false, + error: 'Failed to delete position' + }, { status: 500 }) + } +} diff --git a/components/TradesHistoryPanel.js b/components/TradesHistoryPanel.js index 2701531..6dd49b5 100644 --- a/components/TradesHistoryPanel.js +++ b/components/TradesHistoryPanel.js @@ -43,6 +43,21 @@ export default function TradesHistoryPanel() { } } + const getTradeTypeInfo = (trade) => { + switch (trade.type) { + case 'SPOT_SWAP': + return { label: 'SPOT SWAP', color: 'bg-purple-600', icon: '⚡' } + case 'market': + return { label: 'MARKET', color: 'bg-blue-600', icon: '📈' } + case 'limit': + return { label: 'LIMIT', color: 'bg-orange-600', icon: '🎯' } + case 'stop': + return { label: 'STOP', color: 'bg-red-600', icon: '🛑' } + default: + return { label: trade.type?.toUpperCase() || 'TRADE', color: 'bg-gray-600', icon: '💱' } + } + } + const getSideColor = (side) => { return side === 'BUY' ? 'text-green-400' : 'text-red-400' } @@ -122,7 +137,15 @@ export default function TradesHistoryPanel() { }`}> {trade.side} - + {(() => { + const typeInfo = getTradeTypeInfo(trade) + return ( + + {typeInfo.icon} {typeInfo.label} + + ) + })()} + {trade.dex}