Compare commits

...

3 Commits

Author SHA1 Message Date
mindesbunister
d0cabeb911 chore: update database with recent trade data
- Updated SQLite database with latest trade records
- Includes data from testing price corrections and automation runs
2025-07-21 09:38:17 +02:00
mindesbunister
920cbbd117 fix: disable aggressive cleanup in development mode to prevent analysis interruption
- Completely disable automatic periodic cleanup in development mode
- Disable post-analysis cleanup in development mode
- Add multiple checks for active sessions (progress tracker, recent processes, API activity)
- Increase graceful shutdown timeout from 3s to 5s
- Add conservative fallbacks to prevent premature process termination

This prevents the cleanup system from killing browser processes during active analysis,
resolving the issue where automation analysis was being interrupted by cleanup.
2025-07-21 09:35:06 +02:00
mindesbunister
71e1a64b5d fix: correct entry prices and position sizing in trading system
- Fixed automation service to use real SOL price (~89) instead of hardcoded 00
- Updated position size calculation to properly convert USD investment to token amount
- Enhanced trade display to show separate entry/exit prices with price difference
- Added data quality warnings for trades with missing exit data
- Updated API to use current SOL price (189.50) and improved trade result determination
- Added detection and warnings for old trades with incorrect price data

Resolves issue where trades showed 9-100 entry prices instead of real SOL price of 89
and position sizes of 2.04 SOL instead of correct ~0.53 SOL for 00 investment
2025-07-21 09:26:48 +02:00
13 changed files with 854 additions and 543 deletions

View File

@@ -67,6 +67,14 @@ export async function GET(request) {
take: 5
})
// Get actual total trades count for consistency
const totalTradesCount = await prisma.trade.count({
where: {
userId: 'default-user',
symbol: symbol
}
})
// Get recent market context
const allTrades = await prisma.trade.findMany({
where: {
@@ -103,7 +111,7 @@ export async function GET(request) {
marketContext: {
recentPnL,
winRate: winRate.toFixed(1),
totalTrades: allTrades.length,
totalTrades: totalTradesCount, // Use actual total count
avgProfit: allTrades.length > 0 ? (recentPnL / allTrades.length).toFixed(2) : 0,
trend: sessions.length > 0 ? sessions[0].lastAnalysisData?.sentiment : 'NEUTRAL'
}

View File

@@ -46,7 +46,7 @@ export async function GET() {
const totalPnL = completedTrades.reduce((sum, trade) => sum + (trade.profit || 0), 0)
const winRate = completedTrades.length > 0 ? (successfulTrades.length / completedTrades.length * 100) : 0
const currentPrice = 175.82
const currentPrice = 189.50
const formattedTrades = recentTrades.map(trade => {
const priceChange = trade.side === 'BUY' ?
@@ -75,9 +75,51 @@ export async function GET() {
const tradingAmount = 100
const leverage = trade.leverage || 1
const correctTokenAmount = tradingAmount / trade.price
const displayAmount = correctTokenAmount
// Use real current price for calculation instead of stored wrong price
const correctTokenAmount = tradingAmount / currentPrice
const displayAmount = correctTokenAmount // Show corrected amount
const displayPositionSize = (tradingAmount * leverage).toFixed(2)
// Mark old trades with wrong data
const isOldWrongTrade = trade.price < 150 && trade.amount > 1.5 // Detect old wrong trades
// Enhanced entry/exit price handling
const entryPrice = trade.entryPrice || trade.price
let exitPrice = trade.exitPrice
let calculatedProfit = trade.profit
// If exit price is null but trade is completed, try to calculate from profit
if (trade.status === 'COMPLETED' && !exitPrice && calculatedProfit !== null && calculatedProfit !== undefined) {
// Calculate exit price from profit: profit = (exitPrice - entryPrice) * amount
if (trade.side === 'BUY') {
exitPrice = entryPrice + (calculatedProfit / trade.amount)
} else {
exitPrice = entryPrice - (calculatedProfit / trade.amount)
}
}
// If profit is null but we have both prices, calculate profit
if (trade.status === 'COMPLETED' && (calculatedProfit === null || calculatedProfit === undefined) && exitPrice && entryPrice) {
if (trade.side === 'BUY') {
calculatedProfit = (exitPrice - entryPrice) * trade.amount
} else {
calculatedProfit = (entryPrice - exitPrice) * trade.amount
}
}
// Determine result based on actual profit
let result = 'ACTIVE'
if (trade.status === 'COMPLETED') {
if (calculatedProfit === null || calculatedProfit === undefined) {
result = 'UNKNOWN' // When we truly don't have enough data
} else if (Math.abs(calculatedProfit) < 0.01) { // Within 1 cent
result = 'BREAKEVEN'
} else if (calculatedProfit > 0) {
result = 'WIN'
} else {
result = 'LOSS'
}
}
return {
id: trade.id,
@@ -98,21 +140,23 @@ export async function GET() {
actualDuration: durationMs,
durationText: formatDuration(durationMinutes) + (trade.status === 'OPEN' ? ' (Active)' : ''),
reason: `REAL: ${trade.side} signal with ${trade.confidence || 75}% confidence`,
entryPrice: trade.entryPrice || trade.price,
exitPrice: trade.exitPrice,
entryPrice: entryPrice,
exitPrice: exitPrice,
currentPrice: trade.status === 'OPEN' ? currentPrice : null,
unrealizedPnl: unrealizedPnL ? unrealizedPnL.toFixed(2) : null,
realizedPnl: realizedPnL ? realizedPnL.toFixed(2) : null,
calculatedProfit: calculatedProfit,
stopLoss: trade.stopLoss || (trade.side === 'BUY' ? (trade.price * 0.98).toFixed(2) : (trade.price * 1.02).toFixed(2)),
takeProfit: trade.takeProfit || (trade.side === 'BUY' ? (trade.price * 1.04).toFixed(2) : (trade.price * 0.96).toFixed(2)),
isActive: trade.status === 'OPEN' || trade.status === 'PENDING',
confidence: trade.confidence || 75,
result: trade.status === 'COMPLETED' ?
((trade.profit || 0) > 0 ? 'WIN' : (trade.profit || 0) < 0 ? 'LOSS' : 'BREAKEVEN') :
'ACTIVE',
result: result,
resultDescription: trade.status === 'COMPLETED' ?
`REAL: ${(trade.profit || 0) > 0 ? 'Profitable' : 'Loss'} ${trade.side} trade - Completed` :
`REAL: ${trade.side} position active - ${formatDuration(durationMinutes)}`
`REAL: ${result === 'WIN' ? 'Profitable' : result === 'LOSS' ? 'Loss' : result} ${trade.side} trade - Completed` :
`REAL: ${trade.side} position active - ${formatDuration(durationMinutes)}`,
isOldWrongTrade: isOldWrongTrade,
correctedAmount: isOldWrongTrade ? correctTokenAmount.toFixed(4) : null,
originalStoredPrice: trade.price
}
})

View File

@@ -0,0 +1,207 @@
import { NextResponse } from 'next/server'
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
export async function GET() {
try {
console.log('✅ API CORRECTED: Loading with fixed trade calculations...')
const sessions = await prisma.automationSession.findMany({
where: {
userId: 'default-user',
symbol: 'SOLUSD'
},
orderBy: { createdAt: 'desc' },
take: 10
})
if (sessions.length === 0) {
return NextResponse.json({
success: false,
message: 'No automation sessions found'
})
}
const latestSession = sessions[0]
const sessionsByTimeframe = {}
sessions.forEach(session => {
if (!sessionsByTimeframe[session.timeframe]) {
sessionsByTimeframe[session.timeframe] = session
}
})
const recentTrades = await prisma.trade.findMany({
where: {
userId: latestSession.userId,
symbol: latestSession.symbol
},
orderBy: { createdAt: 'desc' },
take: 10
})
const completedTrades = recentTrades.filter(t => t.status === 'COMPLETED')
const successfulTrades = completedTrades.filter(t => (t.profit || 0) > 0)
const totalPnL = completedTrades.reduce((sum, trade) => sum + (trade.profit || 0), 0)
const winRate = completedTrades.length > 0 ? (successfulTrades.length / completedTrades.length * 100) : 0
const currentPrice = 175.82
const formattedTrades = recentTrades.map(trade => {
const priceChange = trade.side === 'BUY' ?
(currentPrice - trade.price) :
(trade.price - currentPrice)
const realizedPnL = trade.status === 'COMPLETED' ? (trade.profit || 0) : null
const unrealizedPnL = trade.status === 'OPEN' ? (priceChange * trade.amount) : null
const entryTime = new Date(trade.createdAt)
const exitTime = trade.closedAt ? new Date(trade.closedAt) : null
const currentTime = new Date()
const durationMs = trade.status === 'COMPLETED' ?
(exitTime ? exitTime.getTime() - entryTime.getTime() : 0) :
(currentTime.getTime() - entryTime.getTime())
const durationMinutes = Math.floor(durationMs / (1000 * 60))
const formatDuration = (minutes) => {
if (minutes < 60) return `${minutes}m`
const hours = Math.floor(minutes / 60)
const mins = minutes % 60
return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`
}
// ✅ CORRECTED CALCULATION: Fix position size for $100 investment
const tradingAmount = 100
const leverage = trade.leverage || 1
const correctTokenAmount = tradingAmount / trade.price
const displayAmount = correctTokenAmount
const displayPositionSize = (tradingAmount * leverage).toFixed(2)
return {
id: trade.id,
type: 'MARKET',
side: trade.side,
amount: displayAmount,
tradingAmount: tradingAmount,
leverage: leverage,
positionSize: displayPositionSize,
price: trade.price,
status: trade.status,
pnl: realizedPnL ? realizedPnL.toFixed(2) : (unrealizedPnL ? unrealizedPnL.toFixed(2) : '0.00'),
pnlPercent: realizedPnL ? `${((realizedPnL / tradingAmount) * 100).toFixed(2)}%` :
(unrealizedPnL ? `${((unrealizedPnL / tradingAmount) * 100).toFixed(2)}%` : '0.00%'),
createdAt: trade.createdAt,
entryTime: trade.createdAt,
exitTime: trade.closedAt,
actualDuration: durationMs,
durationText: formatDuration(durationMinutes) + (trade.status === 'OPEN' ? ' (Active)' : ''),
reason: `REAL: ${trade.side} signal with ${trade.confidence || 75}% confidence`,
entryPrice: trade.entryPrice || trade.price,
exitPrice: trade.exitPrice,
currentPrice: trade.status === 'OPEN' ? currentPrice : null,
unrealizedPnl: unrealizedPnL ? unrealizedPnL.toFixed(2) : null,
realizedPnl: realizedPnL ? realizedPnL.toFixed(2) : null,
stopLoss: trade.stopLoss || (trade.side === 'BUY' ? (trade.price * 0.98).toFixed(2) : (trade.price * 1.02).toFixed(2)),
takeProfit: trade.takeProfit || (trade.side === 'BUY' ? (trade.price * 1.04).toFixed(2) : (trade.price * 0.96).toFixed(2)),
isActive: trade.status === 'OPEN' || trade.status === 'PENDING',
confidence: trade.confidence || 75,
result: trade.status === 'COMPLETED' ?
((trade.profit || 0) > 0 ? 'WIN' : (trade.profit || 0) < 0 ? 'LOSS' : 'BREAKEVEN') :
'ACTIVE',
resultDescription: trade.status === 'COMPLETED' ?
`REAL: ${(trade.profit || 0) > 0 ? 'Profitable' : 'Loss'} ${trade.side} trade - Completed` :
`REAL: ${trade.side} position active - ${formatDuration(durationMinutes)}`
}
})
return NextResponse.json({
success: true,
data: {
session: {
id: latestSession.id,
symbol: latestSession.symbol,
timeframe: latestSession.timeframe,
status: latestSession.status,
mode: latestSession.mode,
createdAt: latestSession.createdAt,
lastAnalysisAt: latestSession.lastAnalysis || new Date().toISOString(),
totalTrades: completedTrades.length,
successfulTrades: successfulTrades.length,
errorCount: latestSession.errorCount,
totalPnL: totalPnL
},
multiTimeframeSessions: sessionsByTimeframe,
analysis: {
decision: "HOLD",
confidence: 84,
summary: `🔥 REAL DATABASE: ${completedTrades.length} trades, ${successfulTrades.length} wins (${winRate.toFixed(1)}% win rate), P&L: $${totalPnL.toFixed(2)}`,
sentiment: "NEUTRAL",
testField: "CORRECTED_CALCULATIONS",
analysisContext: {
currentSignal: "HOLD",
explanation: `🎯 REAL DATA: ${recentTrades.length} database trades shown with corrected calculations`
},
timeframeAnalysis: {
"15m": { decision: "HOLD", confidence: 75 },
"1h": { decision: "HOLD", confidence: 70 },
"2h": { decision: "HOLD", confidence: 70 },
"4h": { decision: "HOLD", confidence: 70 }
},
multiTimeframeResults: [
{
timeframe: "1h",
status: "ACTIVE",
decision: "BUY",
confidence: 85,
sentiment: "BULLISH",
analysisComplete: true
},
{
timeframe: "2h",
status: "ACTIVE",
decision: "BUY",
confidence: 78,
sentiment: "BULLISH",
analysisComplete: true
},
{
timeframe: "4h",
status: "ACTIVE",
decision: "BUY",
confidence: 82,
sentiment: "BULLISH",
analysisComplete: true
}
],
layoutsAnalyzed: ["AI Layout", "DIY Layout"],
entry: {
price: currentPrice,
buffer: "±0.25",
rationale: "Current market level"
},
stopLoss: {
price: 174.5,
rationale: "Technical support level"
},
takeProfits: {
tp1: { price: 176.5, description: "First target" },
tp2: { price: 177.5, description: "Extended target" }
},
reasoning: `✅ CORRECTED DATA: ${completedTrades.length} completed trades, ${winRate.toFixed(1)}% win rate, $${totalPnL.toFixed(2)} P&L`,
timestamp: new Date().toISOString(),
processingTime: "~2.5 minutes"
},
recentTrades: formattedTrades
}
})
} catch (error) {
console.error('Error fetching analysis details:', error)
return NextResponse.json({
success: false,
error: 'Failed to fetch analysis details',
details: error.message
}, { status: 500 })
}
}

View File

@@ -33,6 +33,14 @@ export async function GET() {
}
// Get actual trade data to calculate real statistics
// Get ALL trades count for consistency
const totalTradesCount = await prisma.trade.count({
where: {
userId: session.userId,
symbol: session.symbol
}
})
const trades = await prisma.trade.findMany({
where: {
userId: session.userId,
@@ -62,7 +70,7 @@ export async function GET() {
mode: session.mode,
symbol: session.symbol,
timeframe: session.timeframe,
totalTrades: completedTrades.length,
totalTrades: totalTradesCount, // Use actual total count
successfulTrades: successfulTrades.length,
winRate: Math.round(winRate * 10) / 10, // Round to 1 decimal
totalPnL: Math.round(totalPnL * 100) / 100, // Round to 2 decimals

View File

@@ -2,43 +2,6 @@ import { NextResponse } from 'next/server'
import { enhancedScreenshotService } from '../../../lib/enhanced-screenshot'
import { aiAnalysisService } from '../../../lib/ai-analysis'
import { progressTracker } from '../../../lib/progress-tracker'
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
// 🧠 Generate enhanced recommendations based on automation insights
function generateEnhancedRecommendation(automationContext) {
if (!automationContext) return null
const { multiTimeframeSignals, topPatterns, marketContext } = automationContext
// Multi-timeframe consensus
const signals = multiTimeframeSignals.filter(s => s.decision)
const bullishSignals = signals.filter(s => s.decision === 'BUY').length
const bearishSignals = signals.filter(s => s.decision === 'SELL').length
// Pattern strength
const avgWinRate = signals.length > 0 ?
signals.reduce((sum, s) => sum + (s.winRate || 0), 0) / signals.length : 0
// Profitability insights
const avgProfit = topPatterns.length > 0 ?
topPatterns.reduce((sum, p) => sum + Number(p.profitPercent || 0), 0) / topPatterns.length : 0
let recommendation = '🤖 AUTOMATION-ENHANCED: '
if (bullishSignals > bearishSignals) {
recommendation += `BULLISH CONSENSUS (${bullishSignals}/${signals.length} timeframes)`
if (avgWinRate > 60) recommendation += ` ✅ Strong pattern (${avgWinRate.toFixed(1)}% win rate)`
if (avgProfit > 3) recommendation += ` 💰 High profit potential (~${avgProfit.toFixed(1)}%)`
} else if (bearishSignals > bullishSignals) {
recommendation += `BEARISH CONSENSUS (${bearishSignals}/${signals.length} timeframes)`
} else {
recommendation += 'NEUTRAL - Mixed signals across timeframes'
}
return recommendation
}
export async function POST(request) {
try {
@@ -51,101 +14,14 @@ export async function POST(request) {
const sessionId = `analysis_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
console.log('🔍 Created session ID:', sessionId)
// 🧠 LEVERAGE AUTOMATION INSIGHTS FOR MANUAL ANALYSIS
console.log('🤖 Gathering automation insights to enhance manual analysis...')
let automationContext = null
try {
const targetSymbol = symbol || 'SOLUSD'
// Get recent automation sessions for context
const sessions = await prisma.automationSession.findMany({
where: {
userId: 'default-user',
symbol: targetSymbol,
lastAnalysisData: { not: null }
},
orderBy: { createdAt: 'desc' },
take: 3
})
// Get top performing trades for pattern recognition
const successfulTrades = await prisma.trade.findMany({
where: {
userId: 'default-user',
symbol: targetSymbol,
status: 'COMPLETED',
profit: { gt: 0 }
},
orderBy: { profit: 'desc' },
take: 5
})
// Get recent market context
const allTrades = await prisma.trade.findMany({
where: {
userId: 'default-user',
symbol: targetSymbol,
status: 'COMPLETED'
},
orderBy: { createdAt: 'desc' },
take: 10
})
const recentPnL = allTrades.reduce((sum, t) => sum + (t.profit || 0), 0)
const winningTrades = allTrades.filter(t => (t.profit || 0) > 0)
const winRate = allTrades.length > 0 ? (winningTrades.length / allTrades.length * 100) : 0
automationContext = {
multiTimeframeSignals: sessions.map(s => ({
timeframe: s.timeframe,
decision: s.lastAnalysisData?.decision,
confidence: s.lastAnalysisData?.confidence,
sentiment: s.lastAnalysisData?.sentiment,
winRate: s.winRate,
totalPnL: s.totalPnL,
totalTrades: s.totalTrades
})),
topPatterns: successfulTrades.map(t => ({
side: t.side,
profit: t.profit,
confidence: t.confidence,
entryPrice: t.price,
exitPrice: t.exitPrice,
profitPercent: t.exitPrice ? ((t.exitPrice - t.price) / t.price * 100).toFixed(2) : null
})),
marketContext: {
recentPnL,
winRate: winRate.toFixed(1),
totalTrades: allTrades.length,
avgProfit: allTrades.length > 0 ? (recentPnL / allTrades.length).toFixed(2) : 0,
trend: sessions.length > 0 ? sessions[0].lastAnalysisData?.sentiment : 'NEUTRAL'
}
}
console.log('🧠 Automation insights gathered:', {
timeframes: automationContext.multiTimeframeSignals.length,
patterns: automationContext.topPatterns.length,
winRate: automationContext.marketContext.winRate + '%'
})
} catch (error) {
console.error('⚠️ Could not gather automation insights:', error.message)
automationContext = null
}
// Create progress tracking session with initial steps
const initialSteps = [
{
id: 'init',
title: 'Initializing Enhanced Analysis',
description: 'Starting AI-powered trading analysis with automation insights...',
title: 'Initializing Analysis',
description: 'Starting AI-powered trading analysis...',
status: 'pending'
},
{
id: 'insights',
title: 'Automation Intelligence',
description: 'Gathering multi-timeframe signals and profitable patterns...',
status: automationContext ? 'completed' : 'warning'
},
{
id: 'auth',
title: 'TradingView Authentication',
@@ -172,8 +48,8 @@ export async function POST(request) {
},
{
id: 'analysis',
title: 'Enhanced AI Analysis',
description: 'Analyzing screenshots with automation-enhanced AI insights',
title: 'AI Analysis',
description: 'Analyzing screenshots with AI',
status: 'pending'
}
]
@@ -189,7 +65,6 @@ export async function POST(request) {
timeframe: timeframe || timeframes?.[0] || '60', // Use single timeframe, fallback to first of array, then default
layouts: layouts || selectedLayouts || ['ai'],
sessionId, // Pass session ID for progress tracking
automationContext, // 🧠 Pass automation insights to enhance analysis
credentials: {
email: process.env.TRADINGVIEW_EMAIL,
password: process.env.TRADINGVIEW_PASSWORD
@@ -221,17 +96,6 @@ export async function POST(request) {
console.log('📸 Final screenshots:', screenshots)
// ⚠️ DISABLED: Don't cleanup browsers immediately after screenshots
// This was interrupting ongoing analysis processes
// Cleanup will happen automatically via periodic cleanup or manual trigger
// try {
// console.log('🧹 Triggering browser cleanup after screenshot completion...')
// await enhancedScreenshotService.cleanup()
// console.log('✅ Browser cleanup completed after screenshots')
// } catch (cleanupError) {
// console.error('Error in browser cleanup after screenshots:', cleanupError)
// }
const result = {
success: true,
sessionId, // Return session ID for progress tracking
@@ -246,46 +110,23 @@ export async function POST(request) {
timestamp: Date.now()
})),
analysis: analysis,
// 🧠 ENHANCED: Include automation insights in response
automationInsights: automationContext ? {
multiTimeframeConsensus: automationContext.multiTimeframeSignals.length > 0 ?
automationContext.multiTimeframeSignals[0].decision : null,
avgConfidence: automationContext.multiTimeframeSignals.length > 0 ?
(automationContext.multiTimeframeSignals.reduce((sum, s) => sum + (s.confidence || 0), 0) / automationContext.multiTimeframeSignals.length).toFixed(1) : null,
marketTrend: automationContext.marketContext.trend,
winRate: automationContext.marketContext.winRate + '%',
profitablePattern: automationContext.topPatterns.length > 0 ?
`${automationContext.topPatterns[0].side} signals with avg ${automationContext.topPatterns.reduce((sum, p) => sum + Number(p.profitPercent || 0), 0) / automationContext.topPatterns.length}% profit` : null,
recommendation: generateEnhancedRecommendation(automationContext)
} : null,
message: `Successfully captured ${screenshots.length} screenshot(s)${analysis ? ' with automation-enhanced AI analysis' : ''}${automationContext ? ' leveraging multi-timeframe insights' : ''}`
message: `Successfully captured ${screenshots.length} screenshot(s)${analysis ? ' with AI analysis' : ''}`
}
// ⚠️ DISABLED: Don't run post-analysis cleanup after every screenshot
// This was killing browser processes during ongoing analysis
// Cleanup should only happen after the ENTIRE automation cycle is complete
// try {
// const { default: aggressiveCleanup } = await import('../../../lib/aggressive-cleanup')
// // Run cleanup in background, don't block the response
// aggressiveCleanup.runPostAnalysisCleanup().catch(console.error)
// } catch (cleanupError) {
// console.error('Error triggering post-analysis cleanup:', cleanupError)
// }
// Trigger post-analysis cleanup in development mode
if (process.env.NODE_ENV === 'development') {
try {
const { default: aggressiveCleanup } = await import('../../../lib/aggressive-cleanup')
// Run cleanup in background, don't block the response
aggressiveCleanup.runPostAnalysisCleanup().catch(console.error)
} catch (cleanupError) {
console.error('Error triggering post-analysis cleanup:', cleanupError)
}
}
return NextResponse.json(result)
} catch (error) {
console.error('Enhanced screenshot API error:', error)
// ⚠️ DISABLED: Don't cleanup browsers on error during analysis
// This can interrupt ongoing processes that might recover
// try {
// console.log('🧹 Triggering browser cleanup after API error...')
// await enhancedScreenshotService.cleanup()
// console.log('✅ Browser cleanup completed after API error')
// } catch (cleanupError) {
// console.error('Error in browser cleanup after API error:', cleanupError)
// }
return NextResponse.json(
{
success: false,

View File

@@ -616,10 +616,10 @@ export default function AutomationPage() {
{/* Recent Trades */}
<div className="card card-gradient p-6">
<h2 className="text-xl font-bold text-white mb-4">Recent Automated Trades</h2>
<h2 className="text-xl font-bold text-white mb-4">Latest 4 Automated Trades</h2>
{recentTrades.length > 0 ? (
<div className="space-y-4">
{recentTrades.slice(0, 5).map((trade, idx) => (
{recentTrades.slice(0, 4).map((trade, idx) => (
<div
key={idx}
className="p-4 bg-gray-800 rounded-lg border border-gray-700 cursor-pointer hover:bg-gray-750 transition-colors"
@@ -685,12 +685,37 @@ export default function AutomationPage() {
<span className="text-gray-300">Position Size:</span>
<span className="text-white">${trade.positionSize}</span>
</div>
{/* Entry Price - Always show for completed trades */}
<div className="flex justify-between">
<span className="text-gray-300">Entry Price:</span>
<span className="text-white">${trade.entryPrice?.toFixed(2) || trade.price?.toFixed(2) || '0.00'}</span>
</div>
{/* Exit Price or Current Price */}
<div className="flex justify-between">
<span className="text-gray-300">{trade.isActive ? 'Current' : 'Exit'} Price:</span>
<span className="text-white">
${trade.isActive ? (trade.currentPrice?.toFixed(2) || '0.00') : (trade.exitPrice?.toFixed(2) || '0.00')}
{trade.isActive ?
`$${trade.currentPrice?.toFixed(2) || '0.00'}` :
(trade.exitPrice ?
`$${trade.exitPrice.toFixed(2)}` :
<span className="text-yellow-400">Not recorded</span>
)
}
</span>
</div>
{/* Price difference for completed trades */}
{!trade.isActive && trade.exitPrice && trade.entryPrice && (
<div className="flex justify-between col-span-2 pt-1 border-t border-gray-700">
<span className="text-gray-300">Price Difference:</span>
<span className={`font-medium ${
(trade.exitPrice - trade.entryPrice) > 0 ? 'text-green-400' :
(trade.exitPrice - trade.entryPrice) < 0 ? 'text-red-400' :
'text-gray-400'
}`}>
${((trade.exitPrice - trade.entryPrice) >= 0 ? '+' : '')}${(trade.exitPrice - trade.entryPrice).toFixed(2)}
</span>
</div>
)}
</div>
</div>
@@ -702,7 +727,8 @@ export default function AutomationPage() {
<span className={`font-bold ${
trade.isActive ?
(trade.unrealizedPnl && parseFloat(trade.unrealizedPnl) > 0 ? 'text-green-400' : 'text-red-400') :
(trade.realizedPnl && parseFloat(trade.realizedPnl) > 0 ? 'text-green-400' : 'text-red-400')
(trade.realizedPnl && parseFloat(trade.realizedPnl) > 0 ? 'text-green-400' :
trade.realizedPnl && parseFloat(trade.realizedPnl) < 0 ? 'text-red-400' : 'text-gray-400')
}`}>
${trade.isActive ?
(trade.unrealizedPnl || '0.00') :
@@ -713,7 +739,8 @@ export default function AutomationPage() {
<span className={`text-xs ${
trade.isActive ?
(trade.unrealizedPnl && parseFloat(trade.unrealizedPnl) > 0 ? 'text-green-400' : 'text-red-400') :
(trade.realizedPnl && parseFloat(trade.realizedPnl) > 0 ? 'text-green-400' : 'text-red-400')
(trade.realizedPnl && parseFloat(trade.realizedPnl) > 0 ? 'text-green-400' :
trade.realizedPnl && parseFloat(trade.realizedPnl) < 0 ? 'text-red-400' : 'text-gray-400')
}`}>
({trade.pnlPercent})
</span>
@@ -723,6 +750,18 @@ export default function AutomationPage() {
</span>
</div>
</div>
{/* Debug info for missing data */}
{trade.result === 'UNKNOWN' && (
<div className="text-xs text-yellow-400 mt-1">
Missing exit data: {!trade.exitPrice ? 'Exit Price ' : ''}{trade.calculatedProfit === null ? 'Profit' : ''}
</div>
)}
{/* Warning for old incorrect trades */}
{trade.isOldWrongTrade && (
<div className="text-xs text-orange-400 mt-1">
🔧 Old trade with incorrect price data (stored: ${trade.originalStoredPrice?.toFixed(2)}, should be ~$189)
</div>
)}
</div>
{/* Click hint */}

91
check-trade-counts.js Normal file
View File

@@ -0,0 +1,91 @@
const { PrismaClient } = require('@prisma/client')
const prisma = new PrismaClient()
async function checkTradeCounts() {
try {
console.log('🔍 Checking trade counts in database...')
// Total trades
const totalTrades = await prisma.trade.count({
where: {
userId: 'default-user',
symbol: 'SOLUSD'
}
})
// Completed trades
const completedTrades = await prisma.trade.count({
where: {
userId: 'default-user',
symbol: 'SOLUSD',
status: 'COMPLETED'
}
})
// Open/Active trades
const activeTrades = await prisma.trade.count({
where: {
userId: 'default-user',
symbol: 'SOLUSD',
status: 'OPEN'
}
})
// Pending trades
const pendingTrades = await prisma.trade.count({
where: {
userId: 'default-user',
symbol: 'SOLUSD',
status: 'PENDING'
}
})
console.log('\n📊 TRADE COUNTS:')
console.log(`Total Trades: ${totalTrades}`)
console.log(`Completed Trades: ${completedTrades}`)
console.log(`Active Trades: ${activeTrades}`)
console.log(`Pending Trades: ${pendingTrades}`)
// Get all trades with details
const allTrades = await prisma.trade.findMany({
where: {
userId: 'default-user',
symbol: 'SOLUSD'
},
orderBy: { createdAt: 'desc' },
select: {
id: true,
side: true,
amount: true,
price: true,
status: true,
createdAt: true,
leverage: true,
profit: true
}
})
console.log('\n📋 ALL TRADES:')
allTrades.forEach((trade, index) => {
console.log(`${index + 1}. ${trade.side} ${trade.amount} tokens @ $${trade.price} - ${trade.status} (${new Date(trade.createdAt).toLocaleString()})`)
})
// Check automation sessions
const sessions = await prisma.automationSession.count({
where: {
userId: 'default-user',
symbol: 'SOLUSD'
}
})
console.log(`\n🤖 Automation Sessions: ${sessions}`)
} catch (error) {
console.error('Error checking trade counts:', error)
} finally {
await prisma.$disconnect()
}
}
checkTradeCounts()

View File

@@ -9,6 +9,7 @@ class AggressiveCleanup {
private cleanupInterval: NodeJS.Timeout | null = null
private isRunning = false
private isInitialized = false
private lastApiCallTime = Date.now()
private constructor() {
// Don't auto-start - let startup.ts control it
@@ -30,10 +31,11 @@ class AggressiveCleanup {
this.isInitialized = true
console.log('🚀 Starting aggressive cleanup system')
// In development, use on-demand cleanup instead of periodic
// In development, completely disable automatic cleanup to prevent interference
if (process.env.NODE_ENV === 'development') {
console.log('🔧 Development mode: Using on-demand cleanup (triggered after analysis)')
console.log('✅ On-demand cleanup system ready')
console.log('🔧 Development mode: Automatic cleanup DISABLED to prevent analysis interference')
console.log('💡 Use manual cleanup via runPostAnalysisCleanup() or forceCleanup() when needed')
console.log('✅ Manual cleanup system ready')
return
}
@@ -55,16 +57,7 @@ class AggressiveCleanup {
}
async cleanupOrphanedProcesses(): Promise<void> {
if (this.isRunning) {
console.log('🔒 Cleanup already in progress, skipping...')
return
}
// Check if auto cleanup is disabled (for development)
if (process.env.DISABLE_AUTO_CLEANUP === 'true') {
console.log('🚫 Auto cleanup disabled via DISABLE_AUTO_CLEANUP environment variable')
return
}
if (this.isRunning) return
this.isRunning = true
const isDevelopment = process.env.NODE_ENV === 'development'
@@ -73,69 +66,62 @@ class AggressiveCleanup {
console.log(`🧹 Running ${cleanupType} cleanup for orphaned processes...`)
try {
// Check for active analysis sessions
// Multiple checks for active analysis sessions
let hasActiveSessions = false
// Check 1: Progress tracker
try {
const { progressTracker } = await import('./progress-tracker')
const activeSessions = progressTracker.getActiveSessions()
if (activeSessions.length > 0) {
console.log(`⚠️ Skipping cleanup - ${activeSessions.length} active analysis sessions detected:`)
activeSessions.forEach(session => {
const progress = progressTracker.getProgress(session)
if (progress) {
const activeStep = progress.steps.find(step => step.status === 'active')
const currentStep = activeStep ? activeStep.title : 'Unknown'
console.log(` - ${session}: ${currentStep} (Step ${progress.currentStep}/${progress.totalSteps})`)
} else {
console.log(` - ${session}: Session info not available`)
}
})
console.log(' Will retry cleanup after analysis completes')
return
console.log(`⚠️ Found ${activeSessions.length} active progress sessions: ${activeSessions.join(', ')}`)
hasActiveSessions = true
}
console.log('✅ No active analysis sessions detected, proceeding with cleanup')
} catch (importError) {
console.warn('⚠️ Could not check active sessions, proceeding cautiously with cleanup')
console.warn('Import error:', importError)
// In case of import errors, be extra cautious - only clean very old processes
if (isDevelopment) {
console.log('🔧 Development mode with import issues - using aggressive cleanup to clear stuck processes')
// In development, if we can't check sessions, assume they're stuck and clean aggressively
console.log('⚠️ Could not check progress tracker, being conservative')
hasActiveSessions = true // Be conservative if we can't check
}
// Check 2: Recent browser activity (processes less than 2 minutes old)
const chromiumProcesses = await this.findChromiumProcesses()
if (chromiumProcesses.length > 0) {
const recentProcesses = await this.checkProcessAge(chromiumProcesses)
if (recentProcesses.length > 0) {
console.log(`⚠️ Found ${recentProcesses.length} recent browser processes (< 2 min old)`)
hasActiveSessions = true
}
}
// Find and kill orphaned chromium processes
const chromiumProcesses = await this.findChromiumProcesses()
if (chromiumProcesses.length > 0) {
console.log(`🔍 Found ${chromiumProcesses.length} chromium processes, evaluating for cleanup...`)
// In development, be more selective about which processes to kill
let processesToKill = chromiumProcesses
if (isDevelopment) {
// Only kill processes that are likely orphaned (older than 5 minutes)
const oldProcesses = await this.filterOldProcesses(chromiumProcesses, 5 * 60 * 1000) // 5 minutes
processesToKill = oldProcesses
if (processesToKill.length === 0) {
console.log('✅ All chromium processes appear to be recent and potentially active - skipping cleanup')
return
}
console.log(`🔧 Development mode: Cleaning only ${processesToKill.length} old processes (older than 5 minutes)`)
// Check 3: In development, be extra conservative - only cleanup if no recent API calls
if (isDevelopment) {
const lastApiCall = this.getLastApiCallTime()
const timeSinceLastApi = Date.now() - lastApiCall
if (timeSinceLastApi < 30000) { // 30 seconds
console.log(`⚠️ Recent API activity detected (${Math.round(timeSinceLastApi/1000)}s ago), skipping cleanup`)
hasActiveSessions = true
}
}
if (hasActiveSessions) {
console.log(`⚠️ Skipping cleanup - active analysis detected`)
return
}
console.log('✅ No active analysis sessions detected, proceeding with cleanup')
// Find and kill orphaned chromium processes
if (chromiumProcesses.length > 0) {
console.log(`Found ${chromiumProcesses.length} chromium processes, cleaning up...`)
for (const pid of processesToKill) {
for (const pid of chromiumProcesses) {
try {
if (isDevelopment) {
// In development, use gentler SIGTERM first
console.log(`🔧 Dev mode: Gentle shutdown of process ${pid}`)
await execAsync(`kill -TERM ${pid}`)
// Give process 3 seconds to shut down gracefully
await new Promise(resolve => setTimeout(resolve, 3000))
// Give process 5 seconds to shut down gracefully (increased from 3)
await new Promise(resolve => setTimeout(resolve, 5000))
// Check if process is still running
try {
@@ -181,7 +167,6 @@ class AggressiveCleanup {
console.error(`Error in ${cleanupType} cleanup:`, error)
} finally {
this.isRunning = false
console.log(`🏁 ${cleanupType} cleanup completed`)
}
}
@@ -194,41 +179,31 @@ class AggressiveCleanup {
}
}
private async filterOldProcesses(pids: string[], maxAgeMs: number): Promise<string[]> {
const oldProcesses: string[] = []
private async checkProcessAge(pids: string[]): Promise<string[]> {
const recentProcesses: string[] = []
const twoMinutesAgo = Date.now() - (2 * 60 * 1000)
for (const pid of pids) {
try {
// Get process start time
const { stdout } = await execAsync(`ps -o pid,lstart -p ${pid} | tail -1`)
const processInfo = stdout.trim()
if (processInfo) {
// Parse the process start time
const parts = processInfo.split(/\s+/)
if (parts.length >= 6) {
// Format: PID Mon DD HH:MM:SS YYYY
const startTimeStr = parts.slice(1).join(' ')
const startTime = new Date(startTimeStr)
const now = new Date()
const processAge = now.getTime() - startTime.getTime()
if (processAge > maxAgeMs) {
console.log(`🕐 Process ${pid} is ${Math.round(processAge / 60000)} minutes old - marked for cleanup`)
oldProcesses.push(pid)
} else {
console.log(`🕐 Process ${pid} is ${Math.round(processAge / 60000)} minutes old - keeping alive`)
}
}
const { stdout } = await execAsync(`ps -o lstart= -p ${pid}`)
const startTime = new Date(stdout.trim()).getTime()
if (startTime > twoMinutesAgo) {
recentProcesses.push(pid)
}
} catch (error) {
// If we can't get process info, assume it's old and safe to clean
console.log(`❓ Could not get age info for process ${pid} - assuming it's old`)
oldProcesses.push(pid)
// Process might not exist anymore, skip
}
}
return oldProcesses
return recentProcesses
}
private getLastApiCallTime(): number {
return this.lastApiCallTime
}
updateApiCallTime(): void {
this.lastApiCallTime = Date.now()
}
async forceCleanup(): Promise<void> {
@@ -252,236 +227,22 @@ class AggressiveCleanup {
}
}
// New method for on-demand cleanup after complete automation cycle
// New method for on-demand cleanup after analysis
async runPostAnalysisCleanup(): Promise<void> {
// Check if auto cleanup is disabled (for development)
if (process.env.DISABLE_AUTO_CLEANUP === 'true') {
console.log('🚫 Post-analysis cleanup disabled via DISABLE_AUTO_CLEANUP environment variable')
const isDevelopment = process.env.NODE_ENV === 'development'
if (isDevelopment) {
console.log('🔧 Development mode: Skipping post-analysis cleanup to prevent interference')
console.log('💡 Manual cleanup available via forceCleanup() if needed')
return
}
console.log('🧹 Post-cycle cleanup triggered (analysis + decision complete)...')
console.log('🧹 Post-analysis cleanup triggered...')
// Wait for all browser processes to fully close
console.log('⏳ Waiting 3 seconds for all processes to close gracefully...')
await new Promise(resolve => setTimeout(resolve, 3000))
// Small delay to ensure analysis processes are fully closed
await new Promise(resolve => setTimeout(resolve, 2000))
// Always run cleanup after complete automation cycle - don't check for active sessions
// since the analysis is complete and we need to ensure all processes are cleaned up
console.log('🧹 Running comprehensive post-cycle cleanup (ignoring session status)...')
try {
// Find all chromium processes
const chromiumProcesses = await this.findChromiumProcesses()
if (chromiumProcesses.length === 0) {
console.log('✅ No chromium processes found to clean up')
return
}
console.log(`🔍 Found ${chromiumProcesses.length} chromium processes for post-analysis cleanup`)
// In post-analysis cleanup, we're more aggressive since analysis is complete
// Try graceful shutdown first
for (const pid of chromiumProcesses) {
try {
console.log(`🔧 Attempting graceful shutdown of process ${pid}`)
await execAsync(`kill -TERM ${pid}`)
} catch (error) {
console.log(` Process ${pid} may already be terminated`)
}
}
// Wait for graceful shutdown
await new Promise(resolve => setTimeout(resolve, 5000))
// Check which processes are still running and force kill them
const stillRunning = await this.findStillRunningProcesses(chromiumProcesses)
if (stillRunning.length > 0) {
console.log(`🗡️ Force killing ${stillRunning.length} stubborn processes`)
for (const pid of stillRunning) {
try {
await execAsync(`kill -9 ${pid}`)
console.log(`💀 Force killed process ${pid}`)
} catch (error) {
console.log(` Process ${pid} already terminated`)
}
}
} else {
console.log('✅ All processes shut down gracefully')
}
// Clean up temp directories and shared memory
try {
await execAsync('rm -rf /tmp/puppeteer_dev_chrome_profile-* 2>/dev/null || true')
await execAsync('rm -rf /dev/shm/.org.chromium.* 2>/dev/null || true')
await execAsync('rm -rf /tmp/.org.chromium.* 2>/dev/null || true')
console.log('✅ Cleaned up temporary files and shared memory')
} catch (error) {
console.error('Warning: Could not clean up temporary files:', error)
}
console.log('✅ Post-analysis cleanup completed successfully')
} catch (error) {
console.error('Error in post-analysis cleanup:', error)
}
// Clear any stuck progress sessions
try {
const { progressTracker } = await import('./progress-tracker')
const activeSessions = progressTracker.getActiveSessions()
if (activeSessions.length > 0) {
console.log(`🧹 Force clearing ${activeSessions.length} potentially stuck sessions`)
activeSessions.forEach(session => {
console.log(`🧹 Force clearing session: ${session}`)
progressTracker.deleteSession(session)
})
}
} catch (error) {
console.warn('Could not clear progress sessions:', error)
}
}
// Signal that an analysis cycle is complete and all processes should be cleaned up
async signalAnalysisCycleComplete(): Promise<void> {
console.log('🎯 Analysis cycle completion signal received')
// Wait for graceful shutdown of analysis-related processes
console.log('⏳ Waiting 5 seconds for graceful process shutdown...')
await new Promise(resolve => setTimeout(resolve, 5000))
// Check if there are any active progress sessions first
const activeSessions = await this.checkActiveAnalysisSessions()
if (activeSessions > 0) {
console.log(`⚠️ Found ${activeSessions} active analysis sessions, skipping aggressive cleanup`)
return
}
// Only run cleanup if no active sessions
console.log('🧹 No active sessions detected, running post-analysis cleanup...')
await this.cleanupPostAnalysisProcesses()
}
private async checkActiveAnalysisSessions(): Promise<number> {
// Check if progress tracker has any active sessions
try {
// This is a simple check - in a real scenario you might want to check actual session state
const { stdout } = await execAsync('pgrep -f "automation-.*-.*" | wc -l')
return parseInt(stdout.trim()) || 0
} catch (error) {
return 0
}
}
private async cleanupPostAnalysisProcesses(): Promise<void> {
console.log('🚨 Post-analysis cleanup - targeting orphaned browser processes')
try {
// Find all chromium processes
const chromiumProcesses = await this.findChromiumProcesses()
if (chromiumProcesses.length === 0) {
console.log('✅ No chromium processes found to clean up')
return
}
console.log(`🔍 Found ${chromiumProcesses.length} chromium processes`)
// Filter out processes that are too new (less than 2 minutes old)
const oldProcesses = await this.filterOldProcesses(chromiumProcesses, 2 * 60) // 2 minutes
if (oldProcesses.length === 0) {
console.log('✅ All chromium processes are recent, not cleaning up')
return
}
console.log(`🧹 Cleaning up ${oldProcesses.length} old chromium processes`)
// Try graceful shutdown first
for (const pid of oldProcesses) {
try {
console.log(`<EFBFBD> Attempting graceful shutdown of process ${pid}`)
await execAsync(`kill -TERM ${pid}`)
} catch (error) {
console.log(` Process ${pid} may already be terminated`)
}
}
// Wait for graceful shutdown
await new Promise(resolve => setTimeout(resolve, 3000))
// Check which processes are still running and force kill only those
const stillRunning = await this.findStillRunningProcesses(oldProcesses)
if (stillRunning.length > 0) {
console.log(`🗡️ Force killing ${stillRunning.length} stubborn processes`)
for (const pid of stillRunning) {
try {
await execAsync(`kill -9 ${pid}`)
console.log(`💀 Force killed process ${pid}`)
} catch (error) {
console.log(` Process ${pid} already terminated`)
}
}
}
console.log('✅ Post-analysis cleanup completed')
} catch (error) {
console.error('Error in post-analysis cleanup:', error)
}
}
private async findStillRunningProcesses(pids: string[]): Promise<string[]> {
const stillRunning: string[] = []
for (const pid of pids) {
try {
await execAsync(`kill -0 ${pid}`) // Check if process exists
stillRunning.push(pid)
} catch (error) {
// Process is already dead
}
}
return stillRunning
}
// Method to get detailed process information for debugging
async getProcessInfo(): Promise<void> {
try {
console.log('🔍 Current browser process information:')
// Get all chromium processes with detailed info
const { stdout } = await execAsync('ps aux | grep -E "(chromium|chrome)" | grep -v grep')
const processes = stdout.trim().split('\n').filter(line => line.length > 0)
if (processes.length === 0) {
console.log('✅ No browser processes currently running')
return
}
console.log(`📊 Found ${processes.length} browser processes:`)
processes.forEach((process, index) => {
const parts = process.split(/\s+/)
const pid = parts[1]
const cpu = parts[2]
const mem = parts[3]
const command = parts.slice(10).join(' ')
console.log(` ${index + 1}. PID: ${pid}, CPU: ${cpu}%, MEM: ${mem}%, CMD: ${command.substring(0, 100)}...`)
})
// Get memory usage
const { stdout: memInfo } = await execAsync('free -h')
console.log('💾 Memory usage:')
console.log(memInfo)
} catch (error) {
console.error('Error getting process info:', error)
}
await this.cleanupOrphanedProcesses()
}
stop(): void {

View File

@@ -714,34 +714,23 @@ Analyze all provided screenshots comprehensively and return only the JSON respon
if (sessionId) {
progressTracker.updateStep(sessionId, 'analysis', 'completed', 'AI analysis completed successfully!')
}
// Trigger browser cleanup immediately after analysis completes
try {
console.log('🧹 Triggering browser cleanup after analysis completion...')
const { enhancedScreenshotService } = await import('./enhanced-screenshot')
await enhancedScreenshotService.cleanup()
console.log('✅ Browser cleanup completed')
} catch (cleanupError) {
console.error('Error in browser cleanup:', cleanupError)
}
// Trigger system-wide cleanup
try {
// Dynamic import to avoid circular dependencies
const aggressiveCleanupModule = await import('./aggressive-cleanup')
const aggressiveCleanup = aggressiveCleanupModule.default
// Run cleanup in background, don't block the response
aggressiveCleanup.runPostAnalysisCleanup().catch(console.error)
} catch (cleanupError) {
console.error('Error triggering post-analysis cleanup:', cleanupError)
}
if (sessionId) {
// Mark session as complete after cleanup is initiated
// Mark session as complete
setTimeout(() => progressTracker.deleteSession(sessionId), 1000)
}
// Trigger post-analysis cleanup in development mode
if (process.env.NODE_ENV === 'development') {
try {
// Dynamic import to avoid circular dependencies
const aggressiveCleanupModule = await import('./aggressive-cleanup')
const aggressiveCleanup = aggressiveCleanupModule.default
// Run cleanup in background, don't block the response
aggressiveCleanup.runPostAnalysisCleanup().catch(console.error)
} catch (cleanupError) {
console.error('Error triggering post-analysis cleanup:', cleanupError)
}
}
return {
screenshots,
analysis
@@ -750,16 +739,6 @@ Analyze all provided screenshots comprehensively and return only the JSON respon
} catch (error) {
console.error('Automated capture and analysis with config failed:', error)
// Trigger browser cleanup even on error
try {
console.log('🧹 Triggering browser cleanup after analysis error...')
const { enhancedScreenshotService } = await import('./enhanced-screenshot')
await enhancedScreenshotService.cleanup()
console.log('✅ Browser cleanup completed after error')
} catch (cleanupError) {
console.error('Error in browser cleanup after error:', cleanupError)
}
if (sessionId) {
// Find the active step and mark it as error
const progress = progressTracker.getProgress(sessionId)

View File

@@ -568,7 +568,7 @@ ${validResults.map(r => `• ${r.timeframe}: ${r.analysis?.recommendation} (${r.
}
// Calculate position size based on risk percentage
const positionSize = this.calculatePositionSize(analysis)
const positionSize = await this.calculatePositionSize(analysis)
return {
direction: analysis.recommendation,
@@ -585,12 +585,33 @@ ${validResults.map(r => `• ${r.timeframe}: ${r.analysis?.recommendation} (${r.
}
}
private calculatePositionSize(analysis: any): number {
const baseAmount = this.config!.tradingAmount
private async calculatePositionSize(analysis: any): Promise<number> {
const baseAmount = this.config!.tradingAmount // This is the USD amount to invest
const riskAdjustment = this.config!.riskPercentage / 100
const confidenceAdjustment = analysis.confidence / 100
return baseAmount * riskAdjustment * confidenceAdjustment
// Calculate the USD amount to invest
const usdAmount = baseAmount * riskAdjustment * confidenceAdjustment
// Get current price to convert USD to token amount
let currentPrice = analysis.entry?.price || analysis.currentPrice
if (!currentPrice) {
try {
const { default: PriceFetcher } = await import('./price-fetcher')
currentPrice = await PriceFetcher.getCurrentPrice(this.config?.symbol || 'SOLUSD')
console.log(`📊 Using current ${this.config?.symbol || 'SOLUSD'} price for position size: $${currentPrice}`)
} catch (error) {
console.error('Error fetching price for position size, using fallback:', error)
currentPrice = this.config?.symbol === 'SOLUSD' ? 189 : 100
}
}
// Calculate token amount: USD investment / token price
const tokenAmount = usdAmount / currentPrice
console.log(`💰 Position calculation: $${usdAmount} ÷ $${currentPrice} = ${tokenAmount.toFixed(4)} tokens`)
return tokenAmount
}
private calculateStopLoss(analysis: any): number {
@@ -599,7 +620,7 @@ ${validResults.map(r => `• ${r.timeframe}: ${r.analysis?.recommendation} (${r.
return analysis.stopLoss.price
}
const currentPrice = analysis.entry?.price || 150 // Default SOL price
const currentPrice = analysis.entry?.price || 189 // Current SOL price
const stopLossPercent = this.config!.stopLossPercent / 100
if (analysis.recommendation === 'BUY') {
@@ -656,7 +677,21 @@ ${validResults.map(r => `• ${r.timeframe}: ${r.analysis?.recommendation} (${r.
private async executeSimulationTrade(decision: any): Promise<any> {
// Simulate trade execution with realistic parameters
const currentPrice = decision.currentPrice || 100 // Mock price
let currentPrice = decision.currentPrice
// If no current price provided, fetch real price
if (!currentPrice) {
try {
const { default: PriceFetcher } = await import('./price-fetcher')
currentPrice = await PriceFetcher.getCurrentPrice(this.config?.symbol || 'SOLUSD')
console.log(`📊 Fetched real ${this.config?.symbol || 'SOLUSD'} price: $${currentPrice}`)
} catch (error) {
console.error('Error fetching real price, using fallback:', error)
// Use a more realistic fallback based on symbol
currentPrice = this.config?.symbol === 'SOLUSD' ? 189 : 100
}
}
const slippage = Math.random() * 0.005 // 0-0.5% slippage
const executionPrice = currentPrice * (1 + (Math.random() > 0.5 ? slippage : -slippage))

Binary file not shown.

19
src/app/layout.tsx Normal file
View File

@@ -0,0 +1,19 @@
// Initialize cleanup system
import '../../lib/startup'
export const metadata = {
title: 'Next.js',
description: 'Generated by Next.js',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}

279
status_response.json Normal file
View File

@@ -0,0 +1,279 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/png" href="/static/favicon.png" />
<link rel="icon" type="image/png" href="/static/favicon-96x96.png" sizes="96x96" />
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg" />
<link rel="shortcut icon" href="/static/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png" />
<meta name="apple-mobile-web-app-title" content="Open WebUI" />
<link rel="manifest" href="/manifest.json" crossorigin="use-credentials" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover"
/>
<meta name="theme-color" content="#171717" />
<meta name="robots" content="noindex,nofollow" />
<meta name="description" content="Open WebUI" />
<link
rel="search"
type="application/opensearchdescription+xml"
title="Open WebUI"
href="/opensearch.xml"
/>
<script src="/static/loader.js" defer></script>
<link rel="stylesheet" href="/static/custom.css" />
<script>
function resizeIframe(obj) {
obj.style.height = obj.contentWindow.document.documentElement.scrollHeight + 'px';
}
</script>
<script>
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
(() => {
const metaThemeColorTag = document.querySelector('meta[name="theme-color"]');
const prefersDarkTheme = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (!localStorage?.theme) {
localStorage.theme = 'system';
}
if (localStorage.theme === 'system') {
document.documentElement.classList.add(prefersDarkTheme ? 'dark' : 'light');
metaThemeColorTag.setAttribute('content', prefersDarkTheme ? '#171717' : '#ffffff');
} else if (localStorage.theme === 'oled-dark') {
document.documentElement.style.setProperty('--color-gray-800', '#101010');
document.documentElement.style.setProperty('--color-gray-850', '#050505');
document.documentElement.style.setProperty('--color-gray-900', '#000000');
document.documentElement.style.setProperty('--color-gray-950', '#000000');
document.documentElement.classList.add('dark');
metaThemeColorTag.setAttribute('content', '#000000');
} else if (localStorage.theme === 'light') {
document.documentElement.classList.add('light');
metaThemeColorTag.setAttribute('content', '#ffffff');
} else if (localStorage.theme === 'her') {
document.documentElement.classList.add('dark');
document.documentElement.classList.add('her');
metaThemeColorTag.setAttribute('content', '#983724');
} else {
document.documentElement.classList.add('dark');
metaThemeColorTag.setAttribute('content', '#171717');
}
window.matchMedia('(prefers-color-scheme: dark)').addListener((e) => {
if (localStorage.theme === 'system') {
if (e.matches) {
document.documentElement.classList.add('dark');
document.documentElement.classList.remove('light');
metaThemeColorTag.setAttribute('content', '#171717');
} else {
document.documentElement.classList.add('light');
document.documentElement.classList.remove('dark');
metaThemeColorTag.setAttribute('content', '#ffffff');
}
}
});
function setSplashImage() {
const logo = document.getElementById('logo');
const isDarkMode = document.documentElement.classList.contains('dark');
if (isDarkMode) {
const darkImage = new Image();
darkImage.src = '/static/splash-dark.png';
darkImage.onload = () => {
logo.src = '/static/splash-dark.png';
logo.style.filter = ''; // Ensure no inversion is applied if splash-dark.png exists
};
darkImage.onerror = () => {
logo.style.filter = 'invert(1)'; // Invert image if splash-dark.png is missing
};
}
}
// Runs after classes are assigned
window.onload = setSplashImage;
})();
</script>
<title>Open WebUI</title>
<link rel="modulepreload" href="/_app/immutable/entry/start.DnmN7E71.js">
<link rel="modulepreload" href="/_app/immutable/chunks/B8l-1GK3.js">
<link rel="modulepreload" href="/_app/immutable/chunks/Bk--KVgx.js">
<link rel="modulepreload" href="/_app/immutable/entry/app.BNiCZCyD.js">
<link rel="modulepreload" href="/_app/immutable/chunks/C1FmrZbK.js">
<link rel="modulepreload" href="/_app/immutable/chunks/CoU9NT7-.js">
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">
<script>
{
__sveltekit_1bvgke6 = {
base: ""
};
const element = document.currentScript.parentElement;
Promise.all([
import("/_app/immutable/entry/start.DnmN7E71.js"),
import("/_app/immutable/entry/app.BNiCZCyD.js")
]).then(([kit, app]) => {
kit.start(app, element);
});
}
</script>
</div>
<div
id="splash-screen"
style="position: fixed; z-index: 100; top: 0; left: 0; width: 100%; height: 100%"
>
<style type="text/css" nonce="">
html {
overflow-y: scroll !important;
}
</style>
<img
id="logo"
style="
position: absolute;
width: auto;
height: 6rem;
top: 44%;
left: 50%;
transform: translateX(-50%);
"
src="/static/splash.png"
/>
<div
style="
position: absolute;
top: 33%;
left: 50%;
width: 24rem;
transform: translateX(-50%);
display: flex;
flex-direction: column;
align-items: center;
"
>
<img
id="logo-her"
style="width: auto; height: 13rem"
src="/static/splash.png"
class="animate-pulse-fast"
/>
<div style="position: relative; width: 24rem; margin-top: 0.5rem">
<div
id="progress-background"
style="
position: absolute;
width: 100%;
height: 0.75rem;
border-radius: 9999px;
background-color: #fafafa9a;
"
></div>
<div
id="progress-bar"
style="
position: absolute;
width: 0%;
height: 0.75rem;
border-radius: 9999px;
background-color: #fff;
"
class="bg-white"
></div>
</div>
</div>
<!-- <span style="position: absolute; bottom: 32px; left: 50%; margin: -36px 0 0 -36px">
Footer content
</span> -->
</div>
</body>
</html>
<style type="text/css" nonce="">
html {
overflow-y: hidden !important;
}
#splash-screen {
background: #fff;
}
html.dark #splash-screen {
background: #000;
}
html.her #splash-screen {
background: #983724;
}
#logo-her {
display: none;
}
#progress-background {
display: none;
}
#progress-bar {
display: none;
}
html.her #logo {
display: none;
}
html.her #logo-her {
display: block;
filter: invert(1);
}
html.her #progress-background {
display: block;
}
html.her #progress-bar {
display: block;
}
@media (max-width: 24rem) {
html.her #progress-background {
display: none;
}
html.her #progress-bar {
display: none;
}
}
@keyframes pulse {
50% {
opacity: 0.65;
}
}
.animate-pulse-fast {
animation: pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
</style>