Compare commits
4 Commits
497e9ed0be
...
b7e4801e45
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7e4801e45 | ||
|
|
ccf73db63d | ||
|
|
ab717ea2fb | ||
|
|
f0845d0fd1 |
@@ -7,6 +7,7 @@ export async function POST(request) {
|
|||||||
symbol,
|
symbol,
|
||||||
side,
|
side,
|
||||||
amount,
|
amount,
|
||||||
|
amountUSD,
|
||||||
stopLoss,
|
stopLoss,
|
||||||
takeProfit,
|
takeProfit,
|
||||||
useRealDEX = false,
|
useRealDEX = false,
|
||||||
@@ -21,6 +22,7 @@ export async function POST(request) {
|
|||||||
symbol,
|
symbol,
|
||||||
side,
|
side,
|
||||||
amount,
|
amount,
|
||||||
|
amountUSD,
|
||||||
stopLoss,
|
stopLoss,
|
||||||
takeProfit,
|
takeProfit,
|
||||||
useRealDEX,
|
useRealDEX,
|
||||||
@@ -64,13 +66,14 @@ export async function POST(request) {
|
|||||||
console.log('🔍 Validating wallet balance before DEX trade...')
|
console.log('🔍 Validating wallet balance before DEX trade...')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const validationResponse = await fetch('http://localhost:3000/api/trading/validate', {
|
const validationResponse = await fetch('http://localhost:3002/api/trading/validate', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
symbol,
|
symbol,
|
||||||
side,
|
side,
|
||||||
amount,
|
amount,
|
||||||
|
amountUSD,
|
||||||
tradingMode: 'SPOT',
|
tradingMode: 'SPOT',
|
||||||
fromCoin,
|
fromCoin,
|
||||||
toCoin
|
toCoin
|
||||||
@@ -194,7 +197,7 @@ export async function POST(request) {
|
|||||||
// Add trade to history with clear spot swap indication
|
// Add trade to history with clear spot swap indication
|
||||||
try {
|
try {
|
||||||
// Use localhost for internal container communication
|
// Use localhost for internal container communication
|
||||||
await fetch('http://localhost:3000/api/trading/history', {
|
await fetch('http://localhost:3002/api/trading/history', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ import { NextResponse } from 'next/server'
|
|||||||
export async function POST(request) {
|
export async function POST(request) {
|
||||||
try {
|
try {
|
||||||
const body = await request.json()
|
const body = await request.json()
|
||||||
const { symbol, side, amount, price, tradingMode = 'SPOT', fromCoin, toCoin } = body
|
const { symbol, side, amount, amountUSD, price, tradingMode = 'SPOT', fromCoin, toCoin } = body
|
||||||
|
|
||||||
console.log(`🔍 Validating trade: ${side} ${amount} ${symbol}`)
|
console.log(`🔍 Validating trade: ${side} ${amount} ${symbol} (USD: ${amountUSD})`)
|
||||||
|
|
||||||
// Fetch real wallet balance from the wallet API
|
// Fetch real wallet balance from the wallet API
|
||||||
let walletBalance
|
let walletBalance
|
||||||
try {
|
try {
|
||||||
const walletResponse = await fetch('http://localhost:3000/api/wallet/balance')
|
const walletResponse = await fetch('http://localhost:3002/api/wallet/balance')
|
||||||
const walletData = await walletResponse.json()
|
const walletData = await walletResponse.json()
|
||||||
|
|
||||||
if (walletData.success && walletData.wallet) {
|
if (walletData.success && walletData.wallet) {
|
||||||
@@ -42,15 +42,16 @@ export async function POST(request) {
|
|||||||
|
|
||||||
if (tradingMode === 'SPOT') {
|
if (tradingMode === 'SPOT') {
|
||||||
if (side.toUpperCase() === 'BUY') {
|
if (side.toUpperCase() === 'BUY') {
|
||||||
// For BUY orders, need USDC or USD equivalent
|
// For BUY orders, use the USD amount directly (not amount * price)
|
||||||
const tradePrice = price || 166.5 // Use provided price or current SOL price
|
requiredBalance = amountUSD || (amount * (price || 166.5))
|
||||||
requiredBalance = amount * tradePrice
|
|
||||||
requiredCurrency = 'USD'
|
requiredCurrency = 'USD'
|
||||||
availableBalance = walletBalance.usdValue
|
availableBalance = walletBalance.usdValue
|
||||||
|
|
||||||
|
console.log(`💰 BUY validation: Need $${requiredBalance} USD, Have $${availableBalance}`)
|
||||||
} else {
|
} else {
|
||||||
// For SELL orders, need the actual token
|
// For SELL orders, need the actual token amount
|
||||||
requiredBalance = amount
|
requiredBalance = amount
|
||||||
requiredCurrency = fromCoin || symbol
|
requiredCurrency = fromCoin || symbol.replace('USD', '')
|
||||||
|
|
||||||
// Find the token balance
|
// Find the token balance
|
||||||
const tokenPosition = walletBalance.positions.find(pos =>
|
const tokenPosition = walletBalance.positions.find(pos =>
|
||||||
@@ -59,14 +60,16 @@ export async function POST(request) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
availableBalance = tokenPosition ? tokenPosition.amount : walletBalance.solBalance
|
availableBalance = tokenPosition ? tokenPosition.amount : walletBalance.solBalance
|
||||||
|
console.log(`💰 SELL validation: Need ${requiredBalance} ${requiredCurrency}, Have ${availableBalance}`)
|
||||||
}
|
}
|
||||||
} else if (tradingMode === 'PERP') {
|
} else if (tradingMode === 'PERP') {
|
||||||
// For perpetuals, only need margin
|
// For perpetuals, only need margin
|
||||||
const leverage = 10 // Default leverage
|
const leverage = 10 // Default leverage
|
||||||
const tradePrice = price || 166.5
|
requiredBalance = (amountUSD || (amount * (price || 166.5))) / leverage
|
||||||
requiredBalance = (amount * tradePrice) / leverage
|
|
||||||
requiredCurrency = 'USD'
|
requiredCurrency = 'USD'
|
||||||
availableBalance = walletBalance.usdValue
|
availableBalance = walletBalance.usdValue
|
||||||
|
|
||||||
|
console.log(`💰 PERP validation: Need $${requiredBalance} USD margin, Have $${availableBalance}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`💰 Balance check: Need ${requiredBalance} ${requiredCurrency}, Have ${availableBalance}`)
|
console.log(`💰 Balance check: Need ${requiredBalance} ${requiredCurrency}, Have ${availableBalance}`)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import React, { useState, useRef, useEffect } from 'react'
|
import React, { useState, useRef, useEffect } from 'react'
|
||||||
import SimpleChart from '../../components/SimpleChart'
|
// import SimpleChart from '../../components/SimpleChart'
|
||||||
|
|
||||||
interface Position {
|
interface Position {
|
||||||
id: string
|
id: string
|
||||||
@@ -419,7 +419,14 @@ export default function ChartTradingDemo() {
|
|||||||
<div className="flex-1 flex">
|
<div className="flex-1 flex">
|
||||||
{/* Chart Area (70% width) */}
|
{/* Chart Area (70% width) */}
|
||||||
<div className="flex-1 p-4">
|
<div className="flex-1 p-4">
|
||||||
<SimpleChart symbol={selectedSymbol} positions={positions} />
|
<div className="bg-gray-800 rounded-lg p-4 h-full flex items-center justify-center">
|
||||||
|
<div className="text-gray-400 text-center">
|
||||||
|
<div className="text-lg mb-2">📊</div>
|
||||||
|
<div>Chart Component Loading...</div>
|
||||||
|
<div className="text-sm">Symbol: {selectedSymbol}</div>
|
||||||
|
<div className="text-sm">Positions: {positions.length}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Trading Panel (30% width) */}
|
{/* Trading Panel (30% width) */}
|
||||||
|
|||||||
@@ -496,6 +496,7 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
|
|||||||
symbol: tradeData.symbol || symbol,
|
symbol: tradeData.symbol || symbol,
|
||||||
side: 'BUY', // Could be derived from analysis
|
side: 'BUY', // Could be derived from analysis
|
||||||
amount: parseFloat(tradeData.positionSize) || parseFloat(tradeData.size),
|
amount: parseFloat(tradeData.positionSize) || parseFloat(tradeData.size),
|
||||||
|
amountUSD: parseFloat(tradeData.amountUSD || tradeData.positionSize || tradeData.size),
|
||||||
stopLoss: parseFloat(tradeData.sl),
|
stopLoss: parseFloat(tradeData.sl),
|
||||||
takeProfit: parseFloat(tradeData.tp1), // Use TP1 as primary target
|
takeProfit: parseFloat(tradeData.tp1), // Use TP1 as primary target
|
||||||
useRealDEX: true, // Enable real trading for manual execution
|
useRealDEX: true, // Enable real trading for manual execution
|
||||||
|
|||||||
@@ -22,6 +22,12 @@ const navItems = [
|
|||||||
icon: '💰',
|
icon: '💰',
|
||||||
description: 'Execute trades'
|
description: 'Execute trades'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Chart Trading',
|
||||||
|
href: '/chart-trading-demo',
|
||||||
|
icon: '📈',
|
||||||
|
description: 'Advanced chart trading'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'Automation',
|
name: 'Automation',
|
||||||
href: '/automation',
|
href: '/automation',
|
||||||
|
|||||||
0
components/SimpleChart.tsx
Normal file
0
components/SimpleChart.tsx
Normal file
@@ -39,6 +39,7 @@ export default function TradeModal({ isOpen, onClose, tradeData, onExecute }: Tr
|
|||||||
console.log('🚀 TradeModal loaded with enhanced features - Version 2.0')
|
console.log('🚀 TradeModal loaded with enhanced features - Version 2.0')
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [walletBalance, setWalletBalance] = useState<any>(null)
|
const [walletBalance, setWalletBalance] = useState<any>(null)
|
||||||
|
const [balanceLoading, setBalanceLoading] = useState(false)
|
||||||
const [formData, setFormData] = useState<FormData>({
|
const [formData, setFormData] = useState<FormData>({
|
||||||
entry: tradeData?.entry || '',
|
entry: tradeData?.entry || '',
|
||||||
tp1: tradeData?.tp || '',
|
tp1: tradeData?.tp || '',
|
||||||
@@ -62,25 +63,57 @@ export default function TradeModal({ isOpen, onClose, tradeData, onExecute }: Tr
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (tradeData) {
|
if (tradeData) {
|
||||||
console.log('🔄 TradeModal updating form with new tradeData:', tradeData)
|
console.log('🔄 TradeModal updating form with new tradeData:', tradeData)
|
||||||
|
|
||||||
|
// Extract the base symbol (remove USD suffix)
|
||||||
|
let baseSymbol = 'SOL' // Default
|
||||||
|
if (tradeData.symbol) {
|
||||||
|
if (tradeData.symbol.endsWith('USD')) {
|
||||||
|
baseSymbol = tradeData.symbol.replace('USD', '')
|
||||||
|
} else {
|
||||||
|
baseSymbol = tradeData.symbol
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`🔄 Setting trading coin to: ${baseSymbol} (from symbol: ${tradeData.symbol})`)
|
||||||
|
|
||||||
setFormData(prev => ({
|
setFormData(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
entry: tradeData.entry || '',
|
entry: tradeData.entry || '',
|
||||||
tp1: tradeData.tp || '',
|
tp1: tradeData.tp || '',
|
||||||
tp2: tradeData.tp2 || '',
|
tp2: tradeData.tp2 || '',
|
||||||
sl: tradeData.sl || '',
|
sl: tradeData.sl || '',
|
||||||
tradingCoin: tradeData.symbol ? tradeData.symbol.replace('USD', '') : 'SOL'
|
tradingCoin: baseSymbol
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}, [tradeData])
|
}, [tradeData])
|
||||||
|
|
||||||
const fetchWalletBalance = async () => {
|
const fetchWalletBalance = async () => {
|
||||||
try {
|
try {
|
||||||
|
setBalanceLoading(true)
|
||||||
|
console.log('💰 Fetching wallet balance...')
|
||||||
const response = await fetch('/api/wallet/balance')
|
const response = await fetch('/api/wallet/balance')
|
||||||
const data = await response.json()
|
const data = await response.json()
|
||||||
setWalletBalance(data)
|
|
||||||
|
if (data.success) {
|
||||||
|
setWalletBalance(data)
|
||||||
|
console.log('✅ Wallet balance loaded:', data)
|
||||||
|
} else {
|
||||||
|
console.error('❌ Wallet balance API error:', data.error)
|
||||||
|
// Set fallback balance
|
||||||
|
setWalletBalance({
|
||||||
|
wallet: { solBalance: 2.5 },
|
||||||
|
balance: { availableBalance: 420.0 }
|
||||||
|
})
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch wallet balance:', error)
|
console.error('❌ Failed to fetch wallet balance:', error)
|
||||||
setWalletBalance({ solBalance: 2.5, usdcBalance: 150.0 }) // Fallback
|
// Set fallback balance
|
||||||
|
setWalletBalance({
|
||||||
|
wallet: { solBalance: 2.5 },
|
||||||
|
balance: { availableBalance: 420.0 }
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
setBalanceLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,8 +161,30 @@ export default function TradeModal({ isOpen, onClose, tradeData, onExecute }: Tr
|
|||||||
try {
|
try {
|
||||||
console.log('🎯 Executing trade with data:', formData)
|
console.log('🎯 Executing trade with data:', formData)
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
if (!formData.positionValue || parseFloat(formData.positionValue) <= 0) {
|
||||||
|
alert('Please enter a valid position size')
|
||||||
|
setLoading(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formData.entry || parseFloat(formData.entry) <= 0) {
|
||||||
|
alert('Please enter a valid entry price')
|
||||||
|
setLoading(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const tradingData = {
|
const tradingData = {
|
||||||
...formData,
|
symbol: formData.tradingCoin + 'USD', // e.g., 'SOLUSD'
|
||||||
|
positionSize: formData.positionValue, // API expects 'positionSize'
|
||||||
|
size: formData.positionValue, // Fallback field name
|
||||||
|
amount: positionSizeSOL, // Send actual SOL amount, not USD amount
|
||||||
|
amountUSD: parseFloat(formData.positionValue), // USD amount for validation
|
||||||
|
sl: formData.sl,
|
||||||
|
tp1: formData.tp1,
|
||||||
|
tp2: formData.tp2,
|
||||||
|
entry: formData.entry,
|
||||||
|
leverage: formData.leverage,
|
||||||
positionSizeSOL: formatNumber(positionSizeSOL, 4),
|
positionSizeSOL: formatNumber(positionSizeSOL, 4),
|
||||||
leveragedValue: formatNumber(leveragedValue, 2),
|
leveragedValue: formatNumber(leveragedValue, 2),
|
||||||
profitTP1: formatNumber(profitTP1, 2),
|
profitTP1: formatNumber(profitTP1, 2),
|
||||||
@@ -138,20 +193,27 @@ export default function TradeModal({ isOpen, onClose, tradeData, onExecute }: Tr
|
|||||||
lossAtSL: formatNumber(lossAtSL, 2)
|
lossAtSL: formatNumber(lossAtSL, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('🚀 Sending trading data to API:', tradingData)
|
||||||
await onExecute(tradingData)
|
await onExecute(tradingData)
|
||||||
onClose()
|
onClose()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Trade execution failed:', error)
|
console.error('Trade execution failed:', error)
|
||||||
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
|
||||||
|
alert(`Trade execution failed: ${errorMessage}`)
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const setPositionPercentage = (percentage: number) => {
|
const setPositionPercentage = (percentage: number) => {
|
||||||
if (walletBalance && walletBalance.solBalance) {
|
if (walletBalance && walletBalance.balance) {
|
||||||
const availableBalance = walletBalance.solBalance * coinPrice // Convert SOL to USD
|
// Use the available balance in USD from the API
|
||||||
|
const availableBalance = walletBalance.balance.availableBalance || 0
|
||||||
const newPosition = (availableBalance * percentage / 100).toFixed(0)
|
const newPosition = (availableBalance * percentage / 100).toFixed(0)
|
||||||
setFormData(prev => ({ ...prev, positionValue: newPosition }))
|
setFormData(prev => ({ ...prev, positionValue: newPosition }))
|
||||||
|
console.log(`🎯 Set position to ${percentage}% of available balance: $${newPosition}`)
|
||||||
|
} else {
|
||||||
|
console.warn('⚠️ Wallet balance not available for percentage calculation')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,11 +271,19 @@ export default function TradeModal({ isOpen, onClose, tradeData, onExecute }: Tr
|
|||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<div className="flex justify-between items-center mb-3">
|
<div className="flex justify-between items-center mb-3">
|
||||||
<label className="text-sm font-medium text-gray-300">Position Size (USD)</label>
|
<label className="text-sm font-medium text-gray-300">Position Size (USD)</label>
|
||||||
{walletBalance && (
|
<div className="text-xs text-gray-400">
|
||||||
<span className="text-xs text-gray-400">
|
{balanceLoading ? (
|
||||||
Available: ${formatNumber(walletBalance.solBalance * coinPrice, 2)}
|
<span className="animate-pulse">Loading balance...</span>
|
||||||
</span>
|
) : walletBalance ? (
|
||||||
)}
|
<span>
|
||||||
|
Available: <span className="text-green-400 font-medium">
|
||||||
|
${formatNumber(walletBalance.balance?.availableBalance || 0, 2)}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span className="text-red-400">Balance unavailable</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<input
|
<input
|
||||||
@@ -230,8 +300,13 @@ export default function TradeModal({ isOpen, onClose, tradeData, onExecute }: Tr
|
|||||||
<button
|
<button
|
||||||
key={percent}
|
key={percent}
|
||||||
type="button"
|
type="button"
|
||||||
|
disabled={balanceLoading || !walletBalance}
|
||||||
onClick={() => setPositionPercentage(percent)}
|
onClick={() => setPositionPercentage(percent)}
|
||||||
className="py-2 px-3 bg-gray-700 hover:bg-gray-600 rounded-lg text-xs text-gray-300 hover:text-white transition-all"
|
className={`py-2 px-3 rounded-lg text-xs transition-all ${
|
||||||
|
balanceLoading || !walletBalance
|
||||||
|
? 'bg-gray-800 text-gray-500 cursor-not-allowed'
|
||||||
|
: 'bg-gray-700 hover:bg-gray-600 text-gray-300 hover:text-white'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
{percent}%
|
{percent}%
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
39
test-trade-validation.js
Normal file
39
test-trade-validation.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// Quick test to verify the trade validation fix
|
||||||
|
const testTradeValidation = async () => {
|
||||||
|
console.log('🧪 Testing trade validation with fixed amountUSD...')
|
||||||
|
|
||||||
|
const tradeData = {
|
||||||
|
symbol: 'USDCUSD',
|
||||||
|
side: 'BUY',
|
||||||
|
amount: 5,
|
||||||
|
amountUSD: 5, // This should be passed through correctly now
|
||||||
|
useRealDEX: false, // Use simulation for testing
|
||||||
|
tradingPair: 'USDCUSD/USDC'
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('🚀 Sending test trade:', tradeData)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('http://localhost:3001/api/trading/execute-dex', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(tradeData)
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await response.json()
|
||||||
|
|
||||||
|
console.log('📊 Response status:', response.status)
|
||||||
|
console.log('📊 Response body:', result)
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
console.log('✅ Trade validation fix is working!')
|
||||||
|
} else {
|
||||||
|
console.log('❌ Trade validation still has issues:', result.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Test failed:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testTradeValidation()
|
||||||
Reference in New Issue
Block a user