From 4f6859368233b0f781384c2b9424456528dc540f Mon Sep 17 00:00:00 2001 From: mindesbunister Date: Sun, 27 Jul 2025 10:32:27 +0200 Subject: [PATCH] feat: enhance position display with proper formatting and value calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix price formatting to show exactly 2 decimal places - Display position size in SOL units (16.40 SOL) instead of incorrect dollar amount - Add new Value field showing total dollar value of position (Size ร— Current Price) - Improve Open Positions section with accurate financial data display - Maintain enhanced styling and responsive layout - All prices now formatted professionally with consistent decimal places --- app/api/automation/analyze-position/route.js | 97 +- app/automation-v2/page.js | 357 +++--- app/automation-v2/page.js.backup | 541 ++++++++- app/automation-v2/page.js.before-fix | 1064 ++++++++++++++++++ app/automation-v2/page.js.working | 1064 ++++++++++++++++++ merge-positions-script.js | 141 +++ merge-positions-v2.js | 147 +++ show-position-analysis.js | 70 ++ 8 files changed, 3282 insertions(+), 199 deletions(-) create mode 100644 app/automation-v2/page.js.before-fix create mode 100644 app/automation-v2/page.js.working create mode 100644 merge-positions-script.js create mode 100644 merge-positions-v2.js create mode 100644 show-position-analysis.js diff --git a/app/api/automation/analyze-position/route.js b/app/api/automation/analyze-position/route.js index 68ec64f..853f28e 100644 --- a/app/api/automation/analyze-position/route.js +++ b/app/api/automation/analyze-position/route.js @@ -16,12 +16,74 @@ export async function POST(request) { stopLossPrice: 178.06 }; - // Calculate some metrics - handle missing stop loss - const hasStopLoss = position.stopLossPrice && position.stopLossPrice > 0; - const estimatedStopLoss = hasStopLoss ? position.stopLossPrice : (position.entryPrice * 0.95); // 5% default - const stopLossDistance = Math.abs(position.entryPrice - estimatedStopLoss); + // Fetch actual Drift orders to get real stop loss and take profit + let actualStopLoss = null; + let actualTakeProfit = null; + let orderAnalysis = "Orders not accessible"; + + try { + const ordersResponse = await fetch('http://localhost:3000/api/drift/orders'); + if (ordersResponse.ok) { + const ordersData = await ordersResponse.json(); + + if (ordersData.success && ordersData.orders) { + const relevantOrders = ordersData.orders.filter(order => + order.symbol === position.symbol && + order.reduceOnly && + order.status === 'OPEN' + ); + + // Find stop loss (price below entry for long, above for short) + const stopLossOrders = relevantOrders.filter(order => { + const isStopDirection = position.side.toLowerCase() === 'long' ? + (order.direction === 'SHORT' || order.direction === 'SELL') : + (order.direction === 'LONG' || order.direction === 'BUY'); + + const hasStopPrice = position.side.toLowerCase() === 'long' ? + (order.triggerPrice && parseFloat(order.triggerPrice) < position.entryPrice) : + (order.triggerPrice && parseFloat(order.triggerPrice) > position.entryPrice); + + return isStopDirection && hasStopPrice; + }); + + // Find take profit (price above entry for long, below for short) + const takeProfitOrders = relevantOrders.filter(order => { + const isTpDirection = position.side.toLowerCase() === 'long' ? + (order.direction === 'SHORT' || order.direction === 'SELL') : + (order.direction === 'LONG' || order.direction === 'BUY'); + + const hasTpPrice = position.side.toLowerCase() === 'long' ? + (order.triggerPrice && parseFloat(order.triggerPrice) > position.entryPrice) : + (order.triggerPrice && parseFloat(order.triggerPrice) < position.entryPrice); + + return isTpDirection && hasTpPrice; + }); + + if (stopLossOrders.length > 0) { + actualStopLoss = parseFloat(stopLossOrders[0].triggerPrice); + } + + if (takeProfitOrders.length > 0) { + actualTakeProfit = parseFloat(takeProfitOrders[0].triggerPrice); + } + + orderAnalysis = `Found ${relevantOrders.length} reduce-only orders: ${stopLossOrders.length} stop loss, ${takeProfitOrders.length} take profit`; + } + } + } catch (orderError) { + console.log('Could not fetch orders for analysis:', orderError.message); + orderAnalysis = "Order fetch failed - using estimates"; + } + + // Use actual orders if available, otherwise estimate + const hasRealStopLoss = actualStopLoss !== null; + const hasRealTakeProfit = actualTakeProfit !== null; + const effectiveStopLoss = hasRealStopLoss ? actualStopLoss : (position.entryPrice * 0.95); + const effectiveTakeProfit = hasRealTakeProfit ? actualTakeProfit : (position.entryPrice * 1.10); + + const stopLossDistance = Math.abs(position.entryPrice - effectiveStopLoss); const stopLossPercent = ((stopLossDistance / position.entryPrice) * 100).toFixed(1); - const leverage = (position.size * position.entryPrice) / (position.size * position.entryPrice * 0.08); // Estimate based on position + const leverage = (position.size * position.entryPrice) / (position.size * position.entryPrice * 0.08); const estimatedLeverage = Math.round(leverage * 10) / 10; // Generate realistic AI reasoning based on the position @@ -33,9 +95,10 @@ export async function POST(request) { โ€ข Position size of ${position.size} SOL indicates moderate conviction ๐Ÿ“Š Risk Management Assessment: -โ€ข Stop loss at $${estimatedStopLoss.toFixed(2)} (${stopLossPercent}% protection)${hasStopLoss ? '' : ' - ESTIMATED'} -โ€ข Risk/reward setup suggests ${stopLossPercent}% stop with potential 2-3x reward -โ€ข Position sizing appears conservative for risk tolerance +โ€ข Stop loss at $${effectiveStopLoss.toFixed(2)} (${stopLossPercent}% protection)${hasRealStopLoss ? ' โœ… CONFIRMED' : ' โš ๏ธ ESTIMATED'} +โ€ข Take profit at $${effectiveTakeProfit.toFixed(2)}${hasRealTakeProfit ? ' โœ… CONFIRMED' : ' โš ๏ธ ESTIMATED'} +โ€ข Risk/reward ratio: ${((Math.abs(effectiveTakeProfit - position.entryPrice) / stopLossDistance)).toFixed(1)}:1 +โ€ข ${orderAnalysis} โšก Leverage Analysis: โ€ข Estimated leverage: ~${estimatedLeverage}x (based on position metrics) @@ -44,14 +107,15 @@ export async function POST(request) { ๐Ÿ›ก๏ธ Current Status: โ€ข Position currently ${position.currentPrice > position.entryPrice ? 'profitable' : 'underwater'} -โ€ข Distance to ${hasStopLoss ? 'stop loss' : 'estimated stop'}: ${((Math.abs(position.currentPrice - estimatedStopLoss) / position.currentPrice) * 100).toFixed(1)}% +โ€ข Distance to stop loss: ${((Math.abs(position.currentPrice - effectiveStopLoss) / position.currentPrice) * 100).toFixed(1)}% +โ€ข Distance to take profit: ${((Math.abs(position.currentPrice - effectiveTakeProfit) / position.currentPrice) * 100).toFixed(1)}% โ€ข Monitoring recommended for further developments`; // Create a decision object for the existing position const retroactiveDecision = { timestamp: new Date().toISOString(), recommendation: `${position.side.toUpperCase()} (Executed)`, - confidence: 82, // Estimated confidence based on position size and setup + confidence: hasRealStopLoss && hasRealTakeProfit ? 92 : 82, // Higher confidence with real orders minConfidenceRequired: 75, reasoning: aiReasoning, executed: true, @@ -60,11 +124,16 @@ export async function POST(request) { amount: Math.round(position.size * position.entryPrice), leverage: estimatedLeverage, currentPrice: position.entryPrice, - stopLoss: estimatedStopLoss, - takeProfit: position.entryPrice + (stopLossDistance * 2.5), // Estimate 2.5:1 RR - aiReasoning: `Retrospective analysis: ${estimatedLeverage}x leverage with ${stopLossPercent}% stop loss provides balanced risk/reward. Position sizing suggests moderate risk appetite with professional risk management principles applied.${hasStopLoss ? '' : ' Note: Stop loss estimated as not visible in position data.'}`, + stopLoss: effectiveStopLoss, + takeProfit: effectiveTakeProfit, + aiReasoning: `Retrospective analysis: ${estimatedLeverage}x leverage with ${stopLossPercent}% stop loss provides balanced risk/reward. Position sizing suggests moderate risk appetite with professional risk management principles applied.${hasRealStopLoss ? ' Actual stop loss orders detected and confirmed.' : ' Stop loss estimated - actual orders may differ.'}`, txId: 'existing_position_analysis', - aiStopLossPercent: `${stopLossPercent}% protective stop` + aiStopLossPercent: `${stopLossPercent}% protective stop`, + orderStatus: { + realStopLoss: hasRealStopLoss, + realTakeProfit: hasRealTakeProfit, + orderAnalysis: orderAnalysis + } }, executionError: null, isRetrospective: true // Flag to indicate this is retroactive analysis diff --git a/app/automation-v2/page.js b/app/automation-v2/page.js index 5865659..327c682 100644 --- a/app/automation-v2/page.js +++ b/app/automation-v2/page.js @@ -684,10 +684,10 @@ export default function AutomationPageV2() { {/* Enhanced Sidebar */}
- {/* Bot Status Card */} + {/* Unified Trading Dashboard Card */}
-
{status?.isActive ? '๐ŸŸข' : 'โšช'}
-

Bot Status

-

Real-time monitoring

+

Trading Dashboard

+

Status โ€ข Positions โ€ข Risk Monitor

-
-
- Status: - - {status?.isActive ? 'RUNNING' : 'STOPPED'} - + {/* Bot Status Section */} +
+

+ ๐Ÿค–Bot Status +

+
+
+ Status: + + {status?.isActive ? 'RUNNING' : 'STOPPED'} + +
+ + {status?.isActive && ( + <> +
+ Symbol: + {status.symbol} +
+ +
+ Mode: + + {status.mode} + +
+ +
+
Timeframes:
+
+ {status.timeframes?.map((tf, index) => ( + + {timeframes.find(t => t.value === tf)?.label || tf} + + ))} +
+
+ + )} + + {/* Rate Limit Warning */} + {status?.rateLimitHit && ( +
+
+ โš ๏ธ + Rate Limit Reached +
+ {status.rateLimitMessage && ( +

{status.rateLimitMessage}

+ )} +

+ Automation stopped. Recharge OpenAI account to continue. +

+
+ )}
- - {status?.isActive && ( - <> +
+ + {/* Position Monitor Section */} + {monitorData && ( +
+

+ ๐Ÿ“ŠPosition Monitor +

+
- Symbol: - {status.symbol} + Has Position: + + {monitorData.hasPosition ? 'โœ… YES' : 'โŒ NO'} +
- Mode: + Risk Level: - {status.mode} + {monitorData.riskLevel}
-
Timeframes:
-
- {status.timeframes?.map((tf, index) => ( - - {timeframes.find(t => t.value === tf)?.label || tf} - - ))} +
Next Action:
+
{monitorData.nextAction}
+
+ + {monitorData.orphanedOrderCleanup && ( +
+
+ {monitorData.orphanedOrderCleanup.success ? 'โœ… Cleanup Success' : 'โŒ Cleanup Failed'} +
+
+ {monitorData.orphanedOrderCleanup.message} +
-
- - )} - - {/* Rate Limit Warning */} - {status?.rateLimitHit && ( -
-
- โš ๏ธ - Rate Limit Reached -
- {status.rateLimitMessage && ( -

{status.rateLimitMessage}

)} -

- Automation stopped automatically. Please recharge your OpenAI account to continue. -

- )} -
+
+ )} + + {/* Open Positions Section */} + {positions.length > 0 && ( +
+

+ ๐Ÿ“ˆOpen Positions + + {positions.length} + +

+
+ {positions.map((position, index) => ( +
+
+ {position.symbol} + + {position.side} + +
+ +
+
+ Size: + + {position.symbol?.includes('SOL') ? + `${parseFloat(position.size).toFixed(2)} SOL` : + `$${parseFloat(position.size).toFixed(2)}` + } + +
+ +
+ Value: + + ${((parseFloat(position.size) || 0) * (parseFloat(position.markPrice) || parseFloat(position.entryPrice) || 0)).toFixed(2)} + +
+ + {position.entryPrice && ( +
+ Entry: + ${parseFloat(position.entryPrice).toFixed(2)} +
+ )} + + {position.markPrice && ( +
+ Mark: + ${parseFloat(position.markPrice).toFixed(2)} +
+ )} + + {position.pnl !== undefined && ( +
+ PnL: + = 0 ? 'text-green-400' : 'text-red-400' + }`}> + ${position.pnl >= 0 ? '+' : ''}${parseFloat(position.pnl).toFixed(2)} + +
+ )} +
+
+ ))} +
+
+ )}
{/* Account Balance Card */} @@ -787,140 +917,13 @@ export default function AutomationPageV2() {
-
Open Positions
+
Total Positions
{balance.positions || 0}
)} - - {/* Position Monitor Card */} - {monitorData && ( -
-
-
- ๐Ÿ“Š -
-
-

Position Monitor

-

Risk assessment

-
-
- -
-
- Has Position: - - {monitorData.hasPosition ? 'โœ… YES' : 'โŒ NO'} - -
- -
- Risk Level: - - {monitorData.riskLevel} - -
- -
-
Next Action:
-
{monitorData.nextAction}
-
- - {monitorData.orphanedOrderCleanup && ( -
-
- {monitorData.orphanedOrderCleanup.success ? 'โœ… Cleanup Success' : 'โŒ Cleanup Failed'} -
-
- {monitorData.orphanedOrderCleanup.message} -
-
- )} -
-
- )} - - {/* Open Positions Card */} - {positions.length > 0 && ( -
-
-
- ๐Ÿ“ˆ -
-
-

Open Positions

-

{positions.length} active trade{positions.length !== 1 ? 's' : ''}

-
-
- -
- {positions.map((position, index) => ( -
-
- {position.symbol} - - {position.side} - -
- -
-
- Size: - ${position.size} -
- - {position.entryPrice && ( -
- Entry: - ${position.entryPrice} -
- )} - - {position.markPrice && ( -
- Mark: - ${position.markPrice} -
- )} - - {position.pnl !== undefined && ( -
- PnL: - = 0 ? 'text-green-400' : 'text-red-400' - }`}> - ${position.pnl >= 0 ? '+' : ''}${position.pnl} - -
- )} -
-
- ))} -
-
- )} diff --git a/app/automation-v2/page.js.backup b/app/automation-v2/page.js.backup index 018f119..80f9124 100644 --- a/app/automation-v2/page.js.backup +++ b/app/automation-v2/page.js.backup @@ -26,17 +26,21 @@ export default function AutomationPageV2() { const [balance, setBalance] = useState(null) const [positions, setPositions] = useState([]) const [loading, setLoading] = useState(false) + const [monitorData, setMonitorData] = useState(null) useEffect(() => { fetchStatus() fetchBalance() fetchPositions() + fetchMonitorData() + fetchMonitorData() const interval = setInterval(() => { fetchStatus() fetchBalance() fetchPositions() - }, 30000) + fetchMonitorData() + }, 300000) // 5 minutes instead of 30 seconds return () => clearInterval(interval) }, []) @@ -77,6 +81,18 @@ export default function AutomationPageV2() { } } + const fetchMonitorData = async () => { + try { + const response = await fetch('/api/automation/position-monitor') + const data = await response.json() + if (data.success) { + setMonitorData(data.monitor) + } + } catch (error) { + console.error('Failed to fetch monitor data:', error) + } + } + const fetchPositions = async () => { try { const response = await fetch('/api/drift/positions') @@ -169,6 +185,8 @@ export default function AutomationPageV2() { console.log('โœ… Emergency stop completed successfully') fetchStatus() fetchPositions() + fetchMonitorData() + fetchMonitorData() } else { console.error('Emergency stop failed:', data.error) } @@ -179,16 +197,117 @@ export default function AutomationPageV2() { } } + const generateTestDecision = async () => { + console.log('๐Ÿงช Generating test AI decision...') + setLoading(true) + try { + const response = await fetch('/api/automation/test-decision', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + action: 'generate_test_decision', + analysis: { + recommendation: 'STRONG BUY', + confidence: 89, + reasoning: `๐ŸŽฏ BULLISH CONVERGENCE DETECTED: + +๐Ÿ“ˆ Technical Analysis: +โ€ข RSI bounced from oversold (28โ†’54) showing strong recovery momentum +โ€ข MACD histogram turning positive with bullish crossover confirmed +โ€ข Price broke above key resistance at $185.40 with 3x normal volume +โ€ข 20 EMA (184.92) providing strong support, price trending above all major EMAs + +๐Ÿ“Š Market Structure: +โ€ข Higher lows pattern intact since yesterday's session +โ€ข Volume profile shows accumulation at current levels +โ€ข Order book depth favoring buyers (67% buy-side liquidity) + +โšก Entry Trigger: +โ€ข Breakout candle closed above $186.00 resistance with conviction +โ€ข Next resistance target: $189.75 (2.1% upside potential) +โ€ข Risk/Reward ratio: 1:2.3 (excellent risk management setup) + +๐Ÿ›ก๏ธ Risk Management: +โ€ข Stop loss at $184.20 (1.0% below entry) protects against false breakout +โ€ข Position sizing optimized for 2% account risk tolerance`, + stopLoss: 184.20, + takeProfit: 189.75, + currentPrice: 186.12, + stopLossPercent: '1.0% protective stop' + }, + config: { + selectedTimeframes: config.selectedTimeframes, + symbol: config.symbol, + mode: config.mode, + enableTrading: config.enableTrading, + tradingAmount: 62 + } + }) + }) + + const data = await response.json() + + if (data.success) { + console.log('โœ… Test decision generated successfully') + fetchStatus() // Refresh to show the decision + } else { + console.error('Failed to generate test decision:', data.error) + } + } catch (error) { + console.error('Test decision error:', error) + } finally { + setLoading(false) + } + } + + const analyzeExistingPosition = async () => { + console.log('๐Ÿ” Analyzing existing position...') + setLoading(true) + try { + // First get the current position data + const positionResponse = await fetch('/api/automation/position-monitor') + const positionData = await positionResponse.json() + + if (positionData.success && positionData.monitor.hasPosition) { + // Analyze the existing position + const response = await fetch('/api/automation/analyze-position', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + action: 'analyze_existing_position', + positionData: positionData.monitor.position + }) + }) + + const data = await response.json() + + if (data.success) { + console.log('โœ… Position analysis generated successfully') + fetchStatus() // Refresh to show the analysis + } else { + console.error('Failed to analyze position:', data.error) + } + } else { + console.log('โ„น๏ธ No position found to analyze') + alert('No active position found to analyze') + } + } catch (error) { + console.error('Position analysis error:', error) + } finally { + setLoading(false) + } + } + return (
- {/* Configuration Panel */} + {/* ๐Ÿค– Automation Control Panel */}
{/* Header with Start/Stop Button */} {/* Header with Start/Stop Button */}
-

Configuration

+

๐Ÿค– Automation Control

{status?.isActive ? ( <> @@ -197,7 +316,7 @@ export default function AutomationPageV2() { disabled={loading} className="px-6 py-3 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors disabled:opacity-50 font-semibold" > - {loading ? 'Stopping...' : 'STOP'} + {loading ? 'Stopping...' : '๐Ÿ›‘ STOP'} )} + + {/* Always available buttons */} + +
@@ -400,7 +537,7 @@ export default function AutomationPageV2() {
{/* Status */}
-

Bot Status

+

๐Ÿค– Bot Status

@@ -454,10 +591,398 @@ export default function AutomationPageV2() {
+ {/* AI Reasoning & Decision Analysis Panel - Improved Layout */} +
+
+

+ ๐Ÿง  AI Trading Analysis +

+
+
+ + {status?.lastDecision ? 'Analysis Available' : 'Waiting for Analysis'} + +
+
+ + {status?.lastDecision ? ( +
+ {/* Quick Summary Row */} +
+
+
Recommendation
+
+ {status.lastDecision.recommendation || 'HOLD'} +
+ {status.lastDecision.isRetrospective && ( +
๐Ÿ“Š Retroactive
+ )} +
+ +
+
Confidence
+
= 80 ? 'text-green-300' : + status.lastDecision.confidence >= 70 ? 'text-yellow-300' : + 'text-red-300' + }`}> + {status.lastDecision.confidence}% +
+
Min: {status.lastDecision.minConfidenceRequired}%
+
+ +
+
Status
+
+ {status.lastDecision.executed ? 'โœ… EXECUTED' : 'โŒ NOT EXECUTED'} +
+
+ {new Date(status.lastDecision.timestamp).toLocaleTimeString()} +
+
+ + {status.lastDecision.executed && status.lastDecision.executionDetails && ( +
+
Leverage
+
+ {status.lastDecision.executionDetails.leverage}x +
+
AI Calculated
+
+ )} +
+ + {/* Main Content Grid */} +
+ {/* AI Reasoning - Left Column */} +
+
+

+ ๐ŸŽฏ + Why This Decision? +

+
+
+ {status.lastDecision.reasoning} +
+
+
+ + {/* Execution Error (if any) */} + {!status.lastDecision.executed && status.lastDecision.executionError && ( +
+

โŒ Execution Failed

+ {status.lastDecision.executionError} +
+ )} +
+ + {/* Trade Details - Right Column */} + {status.lastDecision.executed && status.lastDecision.executionDetails && ( +
+ {/* Entry & Exit Strategy */} +
+

+ ๐Ÿ“ˆ + Entry & Exit Strategy +

+
+
+
+ Entry Price: + + ${status.lastDecision.executionDetails.currentPrice?.toFixed(4)} + +
+
+ Position Size: + ${status.lastDecision.executionDetails.amount} +
+
+ Side: + + {status.lastDecision.executionDetails.side} + +
+
+
+
+ Stop Loss: + + ${status.lastDecision.executionDetails.stopLoss?.toFixed(4)} + +
+
+ Take Profit: + + ${status.lastDecision.executionDetails.takeProfit?.toFixed(4)} + +
+ {status.lastDecision.executionDetails.txId && ( +
+ TX ID: + + {status.lastDecision.executionDetails.txId.substring(0, 8)}... + +
+ )} +
+
+
+ + {/* AI Leverage Reasoning */} +
+

+ โšก + AI Leverage Calculation +

+
+
+ {status.lastDecision.executionDetails.aiReasoning} +
+
+
+
+ )} +
+
+ ) : ( +
+
๐Ÿค–
+

AI Analysis Standby

+

+ The AI will analyze market conditions and provide detailed reasoning for all trading decisions. +

+
+
What you'll see when analysis starts:
+
+
    +
  • โ€ขEntry Strategy: Why AI chose this entry point
  • +
  • โ€ขStop Loss Logic: Risk management reasoning
  • +
  • โ€ขTake Profit Target: Profit-taking strategy
  • +
+
    +
  • โ€ขLeverage Calculation: AI's risk assessment
  • +
  • โ€ขConfidence Analysis: Probability scoring
  • +
  • โ€ขExecution Status: Trade confirmation
  • +
+
+
+
+ )} +
+ + {/* Legacy Last Decision Panel - Hidden when new panel is active */} + {status?.lastDecision && false && ( +
+

๐Ÿง  Last Decision

+ +
+ {/* Decision Header */} +
+
+ + + {status.lastDecision.executed ? 'โœ… EXECUTED' : 'โŒ NOT EXECUTED'} + +
+ + {new Date(status.lastDecision.timestamp).toLocaleTimeString()} + +
+ + {/* Analysis Details */} +
+
+ Recommendation: + + {status.lastDecision.recommendation || 'HOLD'} + +
+ +
+ Confidence: +
+ = 80 ? 'text-green-400' : + status.lastDecision.confidence >= 70 ? 'text-yellow-400' : + 'text-red-400' + }`}> + {status.lastDecision.confidence}% + + + (min: {status.lastDecision.minConfidenceRequired}%) + +
+
+ +
+ Reasoning: + {status.lastDecision.reasoning} +
+
+ + {/* Execution Details (if executed) */} + {status.lastDecision.executed && status.lastDecision.executionDetails && ( +
+

๐Ÿ’ฐ Execution Details

+ +
+
+ Side: + + {status.lastDecision.executionDetails.side} + +
+ +
+ Amount: + ${status.lastDecision.executionDetails.amount} +
+ +
+ Entry: + ${status.lastDecision.executionDetails.currentPrice?.toFixed(2)} +
+ +
+ Leverage: + {status.lastDecision.executionDetails.leverage}x +
+
+ + {/* SL/TP Details */} + {(status.lastDecision.executionDetails.stopLoss || status.lastDecision.executionDetails.takeProfit) && ( +
+
๐Ÿ›ก๏ธ Risk Management
+
+ {status.lastDecision.executionDetails.stopLoss && ( +
+ Stop Loss: + + ${status.lastDecision.executionDetails.stopLoss.toFixed(2)} + {status.lastDecision.executionDetails.aiStopLossPercent && ( + ({status.lastDecision.executionDetails.aiStopLossPercent}) + )} + +
+ )} + + {status.lastDecision.executionDetails.takeProfit && ( +
+ Take Profit: + + ${status.lastDecision.executionDetails.takeProfit.toFixed(2)} + +
+ )} +
+ + {status.lastDecision.executionDetails.stopLoss && status.lastDecision.executionDetails.takeProfit && ( +
+ Risk/Reward: 1:2 ratio +
+ )} +
+ )} + + {/* AI Leverage Reasoning */} + {status.lastDecision.executionDetails.aiReasoning && ( +
+
๐Ÿง  AI Leverage Decision
+
+ {status.lastDecision.executionDetails.aiReasoning} +
+
+ )} + + {/* Transaction ID */} + {status.lastDecision.executionDetails.txId && ( +
+ TX ID: + + {status.lastDecision.executionDetails.txId.substring(0, 20)}... + +
+ )} +
+ )} + + {/* Execution Error (if failed) */} + {!status.lastDecision.executed && status.lastDecision.executionError && ( +
+

โŒ Execution Failed

+ {status.lastDecision.executionError} +
+ )} +
+
+ )} + + {/* Position Monitor */} + {monitorData && ( +
+

๐Ÿ“Š Position Monitor

+ +
+
+ Has Position: + + {monitorData.hasPosition ? 'โœ… YES' : 'โŒ NO'} + +
+ +
+ Risk Level: + + {monitorData.riskLevel} + +
+ +
+ Next Action: {monitorData.nextAction} +
+ + {monitorData.orphanedOrderCleanup && ( +
+
+ {monitorData.orphanedOrderCleanup.success ? 'โœ… Cleanup Success' : 'โŒ Cleanup Failed'} +
+
+ {monitorData.orphanedOrderCleanup.message} +
+
+ )} +
+
+ )} + {/* Balance */} {balance && (
-

Account Balance

+

๏ฟฝ Account Balance

@@ -481,7 +1006,7 @@ export default function AutomationPageV2() { {/* Positions */} {positions.length > 0 && (
-

Open Positions

+

๐Ÿ“ˆ Open Positions

{positions.map((position, index) => ( diff --git a/app/automation-v2/page.js.before-fix b/app/automation-v2/page.js.before-fix new file mode 100644 index 0000000..8a15d81 --- /dev/null +++ b/app/automation-v2/page.js.before-fix @@ -0,0 +1,1064 @@ +'use client' +import React, { useState, useEffect } from 'react' + +// Available timeframes for automation (matching analysis page format) +const timeframes = [ + { label: '5m', value: '5' }, + { label: '15m', value: '15' }, + { label: '30m', value: '30' }, + { label: '1h', value: '60' }, + { label: '2h', value: '120' }, + { label: '4h', value: '240' }, + { label: '1d', value: 'D' }, +] + +export default function AutomationPageV2() { + const [config, setConfig] = useState({ + mode: 'SIMULATION', + dexProvider: 'DRIFT', + symbol: 'SOLUSD', + selectedTimeframes: ['60'], // Multi-timeframe support + tradingAmount: 100, + balancePercentage: 50, // Default to 50% of available balance + }) + + const [status, setStatus] = useState(null) + const [balance, setBalance] = useState(null) + const [positions, setPositions] = useState([]) + const [loading, setLoading] = useState(false) + const [monitorData, setMonitorData] = useState(null) + + useEffect(() => { + fetchStatus() + fetchBalance() + fetchPositions() + fetchMonitorData() + fetchMonitorData() + + const interval = setInterval(() => { + fetchStatus() + fetchBalance() + fetchPositions() + fetchMonitorData() + }, 300000) // 5 minutes instead of 30 seconds + return () => clearInterval(interval) + }, []) + + const toggleTimeframe = (timeframe) => { + setConfig(prev => ({ + ...prev, + selectedTimeframes: prev.selectedTimeframes.includes(timeframe) + ? prev.selectedTimeframes.filter(tf => tf !== timeframe) + : [...prev.selectedTimeframes, timeframe] + })) + } + + const fetchStatus = async () => { + try { + const response = await fetch('/api/automation/status') + const data = await response.json() + console.log('Status response:', data) // Debug log + + if (response.ok && !data.error) { + setStatus(data) // Status data is returned directly, not wrapped in 'success' + } else { + console.error('Status API error:', data.error || 'Unknown error') + } + } catch (error) { + console.error('Failed to fetch status:', error) + } + } + + const fetchBalance = async () => { + try { + const response = await fetch('/api/drift/balance') + const data = await response.json() + if (data.success) { + setBalance(data) + } + } catch (error) { + console.error('Failed to fetch balance:', error) + } + } + + const fetchMonitorData = async () => { + try { + const response = await fetch('/api/automation/position-monitor') + const data = await response.json() + if (data.success) { + setMonitorData(data.monitor) + } + } catch (error) { + console.error('Failed to fetch monitor data:', error) + } + } + + const fetchPositions = async () => { + try { + const response = await fetch('/api/drift/positions') + const data = await response.json() + if (data.success) { + setPositions(data.positions || []) + } + } catch (error) { + console.error('Failed to fetch positions:', error) + } + } + + const handleStart = async () => { + console.log('๐Ÿš€ Starting automation...') + setLoading(true) + try { + if (config.selectedTimeframes.length === 0) { + console.error('No timeframes selected') + setLoading(false) + return + } + + const automationConfig = { + symbol: config.symbol, + selectedTimeframes: config.selectedTimeframes, + mode: config.mode, + tradingAmount: config.tradingAmount, + leverage: config.leverage, + stopLoss: config.stopLoss, + takeProfit: config.takeProfit + } + + const response = await fetch('/api/automation/start', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(automationConfig) + }) + + const data = await response.json() + + if (data.success) { + console.log('โœ… Automation started successfully') + fetchStatus() + } else { + console.error('Failed to start automation:', data.error) + } + } catch (error) { + console.error('Failed to start automation:', error) + } finally { + setLoading(false) + } + } + + const handleStop = async () => { + console.log('๐Ÿ›‘ Stopping automation...') + setLoading(true) + try { + const response = await fetch('/api/automation/stop', { + method: 'POST', + headers: { 'Content-Type': 'application/json' } + }) + + const data = await response.json() + + if (data.success) { + console.log('โœ… Automation stopped successfully') + fetchStatus() + } else { + console.error('Failed to stop automation:', data.error) + } + } catch (error) { + console.error('Failed to stop automation:', error) + } finally { + setLoading(false) + } + } + + const handleEmergencyStop = async () => { + console.log('๐Ÿšจ Emergency stop triggered!') + setLoading(true) + try { + const response = await fetch('/api/automation/emergency-stop', { + method: 'POST', + headers: { 'Content-Type': 'application/json' } + }) + + const data = await response.json() + + if (data.success) { + console.log('โœ… Emergency stop completed successfully') + fetchStatus() + fetchPositions() + fetchMonitorData() + fetchMonitorData() + } else { + console.error('Emergency stop failed:', data.error) + } + } catch (error) { + console.error('Emergency stop error:', error) + } finally { + setLoading(false) + } + } + + const generateTestDecision = async () => { + console.log('๐Ÿงช Generating test AI decision...') + setLoading(true) + try { + const response = await fetch('/api/automation/test-decision', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + action: 'generate_test_decision', + analysis: { + recommendation: 'STRONG BUY', + confidence: 89, + reasoning: `๐ŸŽฏ BULLISH CONVERGENCE DETECTED: + +๐Ÿ“ˆ Technical Analysis: +โ€ข RSI bounced from oversold (28โ†’54) showing strong recovery momentum +โ€ข MACD histogram turning positive with bullish crossover confirmed +โ€ข Price broke above key resistance at $185.40 with 3x normal volume +โ€ข 20 EMA (184.92) providing strong support, price trending above all major EMAs + +๐Ÿ“Š Market Structure: +โ€ข Higher lows pattern intact since yesterday's session +โ€ข Volume profile shows accumulation at current levels +โ€ข Order book depth favoring buyers (67% buy-side liquidity) + +โšก Entry Trigger: +โ€ข Breakout candle closed above $186.00 resistance with conviction +โ€ข Next resistance target: $189.75 (2.1% upside potential) +โ€ข Risk/Reward ratio: 1:2.3 (excellent risk management setup) + +๐Ÿ›ก๏ธ Risk Management: +โ€ข Stop loss at $184.20 (1.0% below entry) protects against false breakout +โ€ข Position sizing optimized for 2% account risk tolerance`, + stopLoss: 184.20, + takeProfit: 189.75, + currentPrice: 186.12, + stopLossPercent: '1.0% protective stop' + }, + config: { + selectedTimeframes: config.selectedTimeframes, + symbol: config.symbol, + mode: config.mode, + enableTrading: config.enableTrading, + tradingAmount: 62 + } + }) + }) + + const data = await response.json() + + if (data.success) { + console.log('โœ… Test decision generated successfully') + fetchStatus() // Refresh to show the decision + } else { + console.error('Failed to generate test decision:', data.error) + } + } catch (error) { + console.error('Test decision error:', error) + } finally { + setLoading(false) + } + } + + const analyzeExistingPosition = async () => { + console.log('๐Ÿ” Analyzing existing position...') + setLoading(true) + try { + // First get the current position data + const positionResponse = await fetch('/api/automation/position-monitor') + const positionData = await positionResponse.json() + + if (positionData.success && positionData.monitor.hasPosition) { + // Analyze the existing position + const response = await fetch('/api/automation/analyze-position', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + action: 'analyze_existing_position', + positionData: positionData.monitor.position + }) + }) + + const data = await response.json() + + if (data.success) { + console.log('โœ… Position analysis generated successfully') + fetchStatus() // Refresh to show the analysis + } else { + console.error('Failed to analyze position:', data.error) + } + } else { + console.log('โ„น๏ธ No position found to analyze') + alert('No active position found to analyze') + } + } catch (error) { + console.error('Position analysis error:', error) + } finally { + setLoading(false) + } + } + + return ( +
+ {/* Top Grid: Automation Control (3 cols) + Bot Status (1 col) */} +
+ {/* ๐Ÿค– Automation Control Panel */} +
+
+ {/* Header with Start/Stop Button */} + {/* Header with Start/Stop Button */} +
+

๐Ÿค– Automation Control

+
+ {status?.isActive ? ( + <> + + + + ) : ( + + )} + + {/* Always available buttons */} + + +
+
+ + {/* Trading Mode - Side by Side Radio Buttons with Logos */} +
+ +
+ + +
+
+ + {/* Symbol and Position Size */} +
+
+ + +
+ +
+ + { + const percentage = parseFloat(e.target.value); + const newAmount = balance ? (parseFloat(balance.availableBalance) * percentage / 100) : 100; + setConfig({ + ...config, + balancePercentage: percentage, + tradingAmount: Math.round(newAmount) + }); + }} + disabled={status?.isActive} + /> +
+ 10% + 50% + 100% +
+
+
+ + {/* MULTI-TIMEFRAME SELECTION */} +
+ + + {/* Timeframe Checkboxes */} +
+ {timeframes.map(tf => ( + + ))} +
+ + {/* Selected Timeframes Display */} + {config.selectedTimeframes.length > 0 && ( +
+
+ Selected: + {config.selectedTimeframes.map(tf => timeframes.find(t => t.value === tf)?.label || tf).filter(Boolean).join(', ')} + +
+
+ ๐Ÿ’ก Multiple timeframes provide more robust analysis +
+
+ )} + + {/* Quick Selection Buttons - Made Bigger */} +
+ + + +
+
+
+
+ + {/* Status and Info Panel */} +
+ {/* Status */} +
+

๐Ÿค– Bot Status

+ +
+
+ Status: + + {status?.isActive ? 'RUNNING' : 'STOPPED'} + +
+ + {status?.isActive && ( + <> +
+ Symbol: + {status.symbol} +
+ +
+ Mode: + + {status.mode} + +
+ +
+ Timeframes: + + {status.timeframes?.map(tf => timeframes.find(t => t.value === tf)?.label || tf).join(', ')} + +
+ + )} + + {/* Rate Limit Notification */} + {status?.rateLimitHit && ( +
+
+ โš ๏ธ Rate Limit Reached +
+ {status.rateLimitMessage && ( +

{status.rateLimitMessage}

+ )} +

+ Automation stopped automatically. Please recharge your OpenAI account to continue. +

+
+ )} +
+
+ + {/* AI Reasoning & Decision Analysis Panel - Improved Layout */} +
+
+

+ ๐Ÿง  AI Trading Analysis +

+
+
+ + {status?.lastDecision ? 'Analysis Available' : 'Waiting for Analysis'} + +
+
+ + {status?.lastDecision ? ( +
+ {/* Quick Summary Row */} +
+
+
Recommendation
+
+ {status.lastDecision.recommendation || 'HOLD'} +
+ {status.lastDecision.isRetrospective && ( +
๐Ÿ“Š Retroactive
+ )} +
+ +
+
Confidence
+
= 80 ? 'text-green-300' : + status.lastDecision.confidence >= 70 ? 'text-yellow-300' : + 'text-red-300' + }`}> + {status.lastDecision.confidence}% +
+
Min: {status.lastDecision.minConfidenceRequired}%
+
+ +
+
Status
+
+ {status.lastDecision.executed ? 'โœ… EXECUTED' : 'โŒ NOT EXECUTED'} +
+
+ {new Date(status.lastDecision.timestamp).toLocaleTimeString()} +
+
+ + {status.lastDecision.executed && status.lastDecision.executionDetails && ( +
+
Leverage
+
+ {status.lastDecision.executionDetails.leverage}x +
+
AI Calculated
+
+ )} +
+ + {/* Main Content Grid */} +
+ {/* AI Reasoning - Left Column */} +
+
+

+ ๐ŸŽฏ + Why This Decision? +

+
+
+ {status.lastDecision.reasoning} +
+
+
+ + {/* Execution Error (if any) */} + {!status.lastDecision.executed && status.lastDecision.executionError && ( +
+

โŒ Execution Failed

+ {status.lastDecision.executionError} +
+ )} +
+ + {/* Trade Details - Right Column */} + {status.lastDecision.executed && status.lastDecision.executionDetails && ( +
+ {/* Entry & Exit Strategy */} +
+

+ ๐Ÿ“ˆ + Entry & Exit Strategy +

+
+
+
+ Entry Price: + + ${status.lastDecision.executionDetails.currentPrice?.toFixed(4)} + +
+
+ Position Size: + ${status.lastDecision.executionDetails.amount} +
+
+ Side: + + {status.lastDecision.executionDetails.side} + +
+
+
+
+ Stop Loss: + + ${status.lastDecision.executionDetails.stopLoss?.toFixed(4)} + +
+
+ Take Profit: + + ${status.lastDecision.executionDetails.takeProfit?.toFixed(4)} + +
+ {status.lastDecision.executionDetails.txId && ( +
+ TX ID: + + {status.lastDecision.executionDetails.txId.substring(0, 8)}... + +
+ )} +
+
+
+ + {/* AI Leverage Reasoning */} +
+

+ โšก + AI Leverage Calculation +

+
+
+ {status.lastDecision.executionDetails.aiReasoning} +
+
+
+
+ )} +
+
+ ) : ( +
+
๐Ÿค–
+

AI Analysis Standby

+

+ The AI will analyze market conditions and provide detailed reasoning for all trading decisions. +

+
+
What you'll see when analysis starts:
+
+
    +
  • โ€ขEntry Strategy: Why AI chose this entry point
  • +
  • โ€ขStop Loss Logic: Risk management reasoning
  • +
  • โ€ขTake Profit Target: Profit-taking strategy
  • +
+
    +
  • โ€ขLeverage Calculation: AI's risk assessment
  • +
  • โ€ขConfidence Analysis: Probability scoring
  • +
  • โ€ขExecution Status: Trade confirmation
  • +
+
+
+
+ )} +
+ + {/* Legacy Last Decision Panel - Hidden when new panel is active */} + {status?.lastDecision && false && ( +
+

๐Ÿง  Last Decision

+ +
+ {/* Decision Header */} +
+
+ + + {status.lastDecision.executed ? 'โœ… EXECUTED' : 'โŒ NOT EXECUTED'} + +
+ + {new Date(status.lastDecision.timestamp).toLocaleTimeString()} + +
+ + {/* Analysis Details */} +
+
+ Recommendation: + + {status.lastDecision.recommendation || 'HOLD'} + +
+ +
+ Confidence: +
+ = 80 ? 'text-green-400' : + status.lastDecision.confidence >= 70 ? 'text-yellow-400' : + 'text-red-400' + }`}> + {status.lastDecision.confidence}% + + + (min: {status.lastDecision.minConfidenceRequired}%) + +
+
+ +
+ Reasoning: + {status.lastDecision.reasoning} +
+
+ + {/* Execution Details (if executed) */} + {status.lastDecision.executed && status.lastDecision.executionDetails && ( +
+

๐Ÿ’ฐ Execution Details

+ +
+
+ Side: + + {status.lastDecision.executionDetails.side} + +
+ +
+ Amount: + ${status.lastDecision.executionDetails.amount} +
+ +
+ Entry: + ${status.lastDecision.executionDetails.currentPrice?.toFixed(2)} +
+ +
+ Leverage: + {status.lastDecision.executionDetails.leverage}x +
+
+ + {/* SL/TP Details */} + {(status.lastDecision.executionDetails.stopLoss || status.lastDecision.executionDetails.takeProfit) && ( +
+
๐Ÿ›ก๏ธ Risk Management
+
+ {status.lastDecision.executionDetails.stopLoss && ( +
+ Stop Loss: + + ${status.lastDecision.executionDetails.stopLoss.toFixed(2)} + {status.lastDecision.executionDetails.aiStopLossPercent && ( + ({status.lastDecision.executionDetails.aiStopLossPercent}) + )} + +
+ )} + + {status.lastDecision.executionDetails.takeProfit && ( +
+ Take Profit: + + ${status.lastDecision.executionDetails.takeProfit.toFixed(2)} + +
+ )} +
+ + {status.lastDecision.executionDetails.stopLoss && status.lastDecision.executionDetails.takeProfit && ( +
+ Risk/Reward: 1:2 ratio +
+ )} +
+ )} + + {/* AI Leverage Reasoning */} + {status.lastDecision.executionDetails.aiReasoning && ( +
+
๐Ÿง  AI Leverage Decision
+
+ {status.lastDecision.executionDetails.aiReasoning} +
+
+ )} + + {/* Transaction ID */} + {status.lastDecision.executionDetails.txId && ( +
+ TX ID: + + {status.lastDecision.executionDetails.txId.substring(0, 20)}... + +
+ )} +
+ )} + + {/* Execution Error (if failed) */} + {!status.lastDecision.executed && status.lastDecision.executionError && ( +
+

โŒ Execution Failed

+ {status.lastDecision.executionError} +
+ )} +
+
+ )} + + {/* Position Monitor */} + {monitorData && ( +
+

๐Ÿ“Š Position Monitor

+ +
+
+ Has Position: + + {monitorData.hasPosition ? 'โœ… YES' : 'โŒ NO'} + +
+ +
+ Risk Level: + + {monitorData.riskLevel} + +
+ +
+ Next Action: {monitorData.nextAction} +
+ + {monitorData.orphanedOrderCleanup && ( +
+
+ {monitorData.orphanedOrderCleanup.success ? 'โœ… Cleanup Success' : 'โŒ Cleanup Failed'} +
+
+ {monitorData.orphanedOrderCleanup.message} +
+
+ )} +
+
+ )} + + {/* Balance */} + {balance && ( +
+

๏ฟฝ Account Balance

+ +
+
+ Available: + ${parseFloat(balance.availableBalance).toFixed(2)} +
+ +
+ Total: + ${parseFloat(balance.totalCollateral).toFixed(2)} +
+ +
+ Positions: + {balance.positions || 0} +
+
+
+ )} + + {/* Positions */} + {positions.length > 0 && ( +
+

๐Ÿ“ˆ Open Positions

+ +
+ {positions.map((position, index) => ( +
+
+ {position.symbol} + + {position.side} + +
+ +
+
+ Size: + ${position.size} +
+ + {position.entryPrice && ( +
+ Entry: + ${position.entryPrice} +
+ )} + + {position.markPrice && ( +
+ Mark: + ${position.markPrice} +
+ )} + + {position.pnl !== undefined && ( +
+ PnL: + = 0 ? 'text-green-400' : 'text-red-400' + }`}> + ${position.pnl >= 0 ? '+' : ''}${position.pnl} + +
+ )} +
+
+ ))} +
+
+ )} +
+
+
+ ) +} diff --git a/app/automation-v2/page.js.working b/app/automation-v2/page.js.working new file mode 100644 index 0000000..8a15d81 --- /dev/null +++ b/app/automation-v2/page.js.working @@ -0,0 +1,1064 @@ +'use client' +import React, { useState, useEffect } from 'react' + +// Available timeframes for automation (matching analysis page format) +const timeframes = [ + { label: '5m', value: '5' }, + { label: '15m', value: '15' }, + { label: '30m', value: '30' }, + { label: '1h', value: '60' }, + { label: '2h', value: '120' }, + { label: '4h', value: '240' }, + { label: '1d', value: 'D' }, +] + +export default function AutomationPageV2() { + const [config, setConfig] = useState({ + mode: 'SIMULATION', + dexProvider: 'DRIFT', + symbol: 'SOLUSD', + selectedTimeframes: ['60'], // Multi-timeframe support + tradingAmount: 100, + balancePercentage: 50, // Default to 50% of available balance + }) + + const [status, setStatus] = useState(null) + const [balance, setBalance] = useState(null) + const [positions, setPositions] = useState([]) + const [loading, setLoading] = useState(false) + const [monitorData, setMonitorData] = useState(null) + + useEffect(() => { + fetchStatus() + fetchBalance() + fetchPositions() + fetchMonitorData() + fetchMonitorData() + + const interval = setInterval(() => { + fetchStatus() + fetchBalance() + fetchPositions() + fetchMonitorData() + }, 300000) // 5 minutes instead of 30 seconds + return () => clearInterval(interval) + }, []) + + const toggleTimeframe = (timeframe) => { + setConfig(prev => ({ + ...prev, + selectedTimeframes: prev.selectedTimeframes.includes(timeframe) + ? prev.selectedTimeframes.filter(tf => tf !== timeframe) + : [...prev.selectedTimeframes, timeframe] + })) + } + + const fetchStatus = async () => { + try { + const response = await fetch('/api/automation/status') + const data = await response.json() + console.log('Status response:', data) // Debug log + + if (response.ok && !data.error) { + setStatus(data) // Status data is returned directly, not wrapped in 'success' + } else { + console.error('Status API error:', data.error || 'Unknown error') + } + } catch (error) { + console.error('Failed to fetch status:', error) + } + } + + const fetchBalance = async () => { + try { + const response = await fetch('/api/drift/balance') + const data = await response.json() + if (data.success) { + setBalance(data) + } + } catch (error) { + console.error('Failed to fetch balance:', error) + } + } + + const fetchMonitorData = async () => { + try { + const response = await fetch('/api/automation/position-monitor') + const data = await response.json() + if (data.success) { + setMonitorData(data.monitor) + } + } catch (error) { + console.error('Failed to fetch monitor data:', error) + } + } + + const fetchPositions = async () => { + try { + const response = await fetch('/api/drift/positions') + const data = await response.json() + if (data.success) { + setPositions(data.positions || []) + } + } catch (error) { + console.error('Failed to fetch positions:', error) + } + } + + const handleStart = async () => { + console.log('๐Ÿš€ Starting automation...') + setLoading(true) + try { + if (config.selectedTimeframes.length === 0) { + console.error('No timeframes selected') + setLoading(false) + return + } + + const automationConfig = { + symbol: config.symbol, + selectedTimeframes: config.selectedTimeframes, + mode: config.mode, + tradingAmount: config.tradingAmount, + leverage: config.leverage, + stopLoss: config.stopLoss, + takeProfit: config.takeProfit + } + + const response = await fetch('/api/automation/start', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(automationConfig) + }) + + const data = await response.json() + + if (data.success) { + console.log('โœ… Automation started successfully') + fetchStatus() + } else { + console.error('Failed to start automation:', data.error) + } + } catch (error) { + console.error('Failed to start automation:', error) + } finally { + setLoading(false) + } + } + + const handleStop = async () => { + console.log('๐Ÿ›‘ Stopping automation...') + setLoading(true) + try { + const response = await fetch('/api/automation/stop', { + method: 'POST', + headers: { 'Content-Type': 'application/json' } + }) + + const data = await response.json() + + if (data.success) { + console.log('โœ… Automation stopped successfully') + fetchStatus() + } else { + console.error('Failed to stop automation:', data.error) + } + } catch (error) { + console.error('Failed to stop automation:', error) + } finally { + setLoading(false) + } + } + + const handleEmergencyStop = async () => { + console.log('๐Ÿšจ Emergency stop triggered!') + setLoading(true) + try { + const response = await fetch('/api/automation/emergency-stop', { + method: 'POST', + headers: { 'Content-Type': 'application/json' } + }) + + const data = await response.json() + + if (data.success) { + console.log('โœ… Emergency stop completed successfully') + fetchStatus() + fetchPositions() + fetchMonitorData() + fetchMonitorData() + } else { + console.error('Emergency stop failed:', data.error) + } + } catch (error) { + console.error('Emergency stop error:', error) + } finally { + setLoading(false) + } + } + + const generateTestDecision = async () => { + console.log('๐Ÿงช Generating test AI decision...') + setLoading(true) + try { + const response = await fetch('/api/automation/test-decision', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + action: 'generate_test_decision', + analysis: { + recommendation: 'STRONG BUY', + confidence: 89, + reasoning: `๐ŸŽฏ BULLISH CONVERGENCE DETECTED: + +๐Ÿ“ˆ Technical Analysis: +โ€ข RSI bounced from oversold (28โ†’54) showing strong recovery momentum +โ€ข MACD histogram turning positive with bullish crossover confirmed +โ€ข Price broke above key resistance at $185.40 with 3x normal volume +โ€ข 20 EMA (184.92) providing strong support, price trending above all major EMAs + +๐Ÿ“Š Market Structure: +โ€ข Higher lows pattern intact since yesterday's session +โ€ข Volume profile shows accumulation at current levels +โ€ข Order book depth favoring buyers (67% buy-side liquidity) + +โšก Entry Trigger: +โ€ข Breakout candle closed above $186.00 resistance with conviction +โ€ข Next resistance target: $189.75 (2.1% upside potential) +โ€ข Risk/Reward ratio: 1:2.3 (excellent risk management setup) + +๐Ÿ›ก๏ธ Risk Management: +โ€ข Stop loss at $184.20 (1.0% below entry) protects against false breakout +โ€ข Position sizing optimized for 2% account risk tolerance`, + stopLoss: 184.20, + takeProfit: 189.75, + currentPrice: 186.12, + stopLossPercent: '1.0% protective stop' + }, + config: { + selectedTimeframes: config.selectedTimeframes, + symbol: config.symbol, + mode: config.mode, + enableTrading: config.enableTrading, + tradingAmount: 62 + } + }) + }) + + const data = await response.json() + + if (data.success) { + console.log('โœ… Test decision generated successfully') + fetchStatus() // Refresh to show the decision + } else { + console.error('Failed to generate test decision:', data.error) + } + } catch (error) { + console.error('Test decision error:', error) + } finally { + setLoading(false) + } + } + + const analyzeExistingPosition = async () => { + console.log('๐Ÿ” Analyzing existing position...') + setLoading(true) + try { + // First get the current position data + const positionResponse = await fetch('/api/automation/position-monitor') + const positionData = await positionResponse.json() + + if (positionData.success && positionData.monitor.hasPosition) { + // Analyze the existing position + const response = await fetch('/api/automation/analyze-position', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + action: 'analyze_existing_position', + positionData: positionData.monitor.position + }) + }) + + const data = await response.json() + + if (data.success) { + console.log('โœ… Position analysis generated successfully') + fetchStatus() // Refresh to show the analysis + } else { + console.error('Failed to analyze position:', data.error) + } + } else { + console.log('โ„น๏ธ No position found to analyze') + alert('No active position found to analyze') + } + } catch (error) { + console.error('Position analysis error:', error) + } finally { + setLoading(false) + } + } + + return ( +
+ {/* Top Grid: Automation Control (3 cols) + Bot Status (1 col) */} +
+ {/* ๐Ÿค– Automation Control Panel */} +
+
+ {/* Header with Start/Stop Button */} + {/* Header with Start/Stop Button */} +
+

๐Ÿค– Automation Control

+
+ {status?.isActive ? ( + <> + + + + ) : ( + + )} + + {/* Always available buttons */} + + +
+
+ + {/* Trading Mode - Side by Side Radio Buttons with Logos */} +
+ +
+ + +
+
+ + {/* Symbol and Position Size */} +
+
+ + +
+ +
+ + { + const percentage = parseFloat(e.target.value); + const newAmount = balance ? (parseFloat(balance.availableBalance) * percentage / 100) : 100; + setConfig({ + ...config, + balancePercentage: percentage, + tradingAmount: Math.round(newAmount) + }); + }} + disabled={status?.isActive} + /> +
+ 10% + 50% + 100% +
+
+
+ + {/* MULTI-TIMEFRAME SELECTION */} +
+ + + {/* Timeframe Checkboxes */} +
+ {timeframes.map(tf => ( + + ))} +
+ + {/* Selected Timeframes Display */} + {config.selectedTimeframes.length > 0 && ( +
+
+ Selected: + {config.selectedTimeframes.map(tf => timeframes.find(t => t.value === tf)?.label || tf).filter(Boolean).join(', ')} + +
+
+ ๐Ÿ’ก Multiple timeframes provide more robust analysis +
+
+ )} + + {/* Quick Selection Buttons - Made Bigger */} +
+ + + +
+
+
+
+ + {/* Status and Info Panel */} +
+ {/* Status */} +
+

๐Ÿค– Bot Status

+ +
+
+ Status: + + {status?.isActive ? 'RUNNING' : 'STOPPED'} + +
+ + {status?.isActive && ( + <> +
+ Symbol: + {status.symbol} +
+ +
+ Mode: + + {status.mode} + +
+ +
+ Timeframes: + + {status.timeframes?.map(tf => timeframes.find(t => t.value === tf)?.label || tf).join(', ')} + +
+ + )} + + {/* Rate Limit Notification */} + {status?.rateLimitHit && ( +
+
+ โš ๏ธ Rate Limit Reached +
+ {status.rateLimitMessage && ( +

{status.rateLimitMessage}

+ )} +

+ Automation stopped automatically. Please recharge your OpenAI account to continue. +

+
+ )} +
+
+ + {/* AI Reasoning & Decision Analysis Panel - Improved Layout */} +
+
+

+ ๐Ÿง  AI Trading Analysis +

+
+
+ + {status?.lastDecision ? 'Analysis Available' : 'Waiting for Analysis'} + +
+
+ + {status?.lastDecision ? ( +
+ {/* Quick Summary Row */} +
+
+
Recommendation
+
+ {status.lastDecision.recommendation || 'HOLD'} +
+ {status.lastDecision.isRetrospective && ( +
๐Ÿ“Š Retroactive
+ )} +
+ +
+
Confidence
+
= 80 ? 'text-green-300' : + status.lastDecision.confidence >= 70 ? 'text-yellow-300' : + 'text-red-300' + }`}> + {status.lastDecision.confidence}% +
+
Min: {status.lastDecision.minConfidenceRequired}%
+
+ +
+
Status
+
+ {status.lastDecision.executed ? 'โœ… EXECUTED' : 'โŒ NOT EXECUTED'} +
+
+ {new Date(status.lastDecision.timestamp).toLocaleTimeString()} +
+
+ + {status.lastDecision.executed && status.lastDecision.executionDetails && ( +
+
Leverage
+
+ {status.lastDecision.executionDetails.leverage}x +
+
AI Calculated
+
+ )} +
+ + {/* Main Content Grid */} +
+ {/* AI Reasoning - Left Column */} +
+
+

+ ๐ŸŽฏ + Why This Decision? +

+
+
+ {status.lastDecision.reasoning} +
+
+
+ + {/* Execution Error (if any) */} + {!status.lastDecision.executed && status.lastDecision.executionError && ( +
+

โŒ Execution Failed

+ {status.lastDecision.executionError} +
+ )} +
+ + {/* Trade Details - Right Column */} + {status.lastDecision.executed && status.lastDecision.executionDetails && ( +
+ {/* Entry & Exit Strategy */} +
+

+ ๐Ÿ“ˆ + Entry & Exit Strategy +

+
+
+
+ Entry Price: + + ${status.lastDecision.executionDetails.currentPrice?.toFixed(4)} + +
+
+ Position Size: + ${status.lastDecision.executionDetails.amount} +
+
+ Side: + + {status.lastDecision.executionDetails.side} + +
+
+
+
+ Stop Loss: + + ${status.lastDecision.executionDetails.stopLoss?.toFixed(4)} + +
+
+ Take Profit: + + ${status.lastDecision.executionDetails.takeProfit?.toFixed(4)} + +
+ {status.lastDecision.executionDetails.txId && ( +
+ TX ID: + + {status.lastDecision.executionDetails.txId.substring(0, 8)}... + +
+ )} +
+
+
+ + {/* AI Leverage Reasoning */} +
+

+ โšก + AI Leverage Calculation +

+
+
+ {status.lastDecision.executionDetails.aiReasoning} +
+
+
+
+ )} +
+
+ ) : ( +
+
๐Ÿค–
+

AI Analysis Standby

+

+ The AI will analyze market conditions and provide detailed reasoning for all trading decisions. +

+
+
What you'll see when analysis starts:
+
+
    +
  • โ€ขEntry Strategy: Why AI chose this entry point
  • +
  • โ€ขStop Loss Logic: Risk management reasoning
  • +
  • โ€ขTake Profit Target: Profit-taking strategy
  • +
+
    +
  • โ€ขLeverage Calculation: AI's risk assessment
  • +
  • โ€ขConfidence Analysis: Probability scoring
  • +
  • โ€ขExecution Status: Trade confirmation
  • +
+
+
+
+ )} +
+ + {/* Legacy Last Decision Panel - Hidden when new panel is active */} + {status?.lastDecision && false && ( +
+

๐Ÿง  Last Decision

+ +
+ {/* Decision Header */} +
+
+ + + {status.lastDecision.executed ? 'โœ… EXECUTED' : 'โŒ NOT EXECUTED'} + +
+ + {new Date(status.lastDecision.timestamp).toLocaleTimeString()} + +
+ + {/* Analysis Details */} +
+
+ Recommendation: + + {status.lastDecision.recommendation || 'HOLD'} + +
+ +
+ Confidence: +
+ = 80 ? 'text-green-400' : + status.lastDecision.confidence >= 70 ? 'text-yellow-400' : + 'text-red-400' + }`}> + {status.lastDecision.confidence}% + + + (min: {status.lastDecision.minConfidenceRequired}%) + +
+
+ +
+ Reasoning: + {status.lastDecision.reasoning} +
+
+ + {/* Execution Details (if executed) */} + {status.lastDecision.executed && status.lastDecision.executionDetails && ( +
+

๐Ÿ’ฐ Execution Details

+ +
+
+ Side: + + {status.lastDecision.executionDetails.side} + +
+ +
+ Amount: + ${status.lastDecision.executionDetails.amount} +
+ +
+ Entry: + ${status.lastDecision.executionDetails.currentPrice?.toFixed(2)} +
+ +
+ Leverage: + {status.lastDecision.executionDetails.leverage}x +
+
+ + {/* SL/TP Details */} + {(status.lastDecision.executionDetails.stopLoss || status.lastDecision.executionDetails.takeProfit) && ( +
+
๐Ÿ›ก๏ธ Risk Management
+
+ {status.lastDecision.executionDetails.stopLoss && ( +
+ Stop Loss: + + ${status.lastDecision.executionDetails.stopLoss.toFixed(2)} + {status.lastDecision.executionDetails.aiStopLossPercent && ( + ({status.lastDecision.executionDetails.aiStopLossPercent}) + )} + +
+ )} + + {status.lastDecision.executionDetails.takeProfit && ( +
+ Take Profit: + + ${status.lastDecision.executionDetails.takeProfit.toFixed(2)} + +
+ )} +
+ + {status.lastDecision.executionDetails.stopLoss && status.lastDecision.executionDetails.takeProfit && ( +
+ Risk/Reward: 1:2 ratio +
+ )} +
+ )} + + {/* AI Leverage Reasoning */} + {status.lastDecision.executionDetails.aiReasoning && ( +
+
๐Ÿง  AI Leverage Decision
+
+ {status.lastDecision.executionDetails.aiReasoning} +
+
+ )} + + {/* Transaction ID */} + {status.lastDecision.executionDetails.txId && ( +
+ TX ID: + + {status.lastDecision.executionDetails.txId.substring(0, 20)}... + +
+ )} +
+ )} + + {/* Execution Error (if failed) */} + {!status.lastDecision.executed && status.lastDecision.executionError && ( +
+

โŒ Execution Failed

+ {status.lastDecision.executionError} +
+ )} +
+
+ )} + + {/* Position Monitor */} + {monitorData && ( +
+

๐Ÿ“Š Position Monitor

+ +
+
+ Has Position: + + {monitorData.hasPosition ? 'โœ… YES' : 'โŒ NO'} + +
+ +
+ Risk Level: + + {monitorData.riskLevel} + +
+ +
+ Next Action: {monitorData.nextAction} +
+ + {monitorData.orphanedOrderCleanup && ( +
+
+ {monitorData.orphanedOrderCleanup.success ? 'โœ… Cleanup Success' : 'โŒ Cleanup Failed'} +
+
+ {monitorData.orphanedOrderCleanup.message} +
+
+ )} +
+
+ )} + + {/* Balance */} + {balance && ( +
+

๏ฟฝ Account Balance

+ +
+
+ Available: + ${parseFloat(balance.availableBalance).toFixed(2)} +
+ +
+ Total: + ${parseFloat(balance.totalCollateral).toFixed(2)} +
+ +
+ Positions: + {balance.positions || 0} +
+
+
+ )} + + {/* Positions */} + {positions.length > 0 && ( +
+

๐Ÿ“ˆ Open Positions

+ +
+ {positions.map((position, index) => ( +
+
+ {position.symbol} + + {position.side} + +
+ +
+
+ Size: + ${position.size} +
+ + {position.entryPrice && ( +
+ Entry: + ${position.entryPrice} +
+ )} + + {position.markPrice && ( +
+ Mark: + ${position.markPrice} +
+ )} + + {position.pnl !== undefined && ( +
+ PnL: + = 0 ? 'text-green-400' : 'text-red-400' + }`}> + ${position.pnl >= 0 ? '+' : ''}${position.pnl} + +
+ )} +
+
+ ))} +
+
+ )} +
+
+
+ ) +} diff --git a/merge-positions-script.js b/merge-positions-script.js new file mode 100644 index 0000000..3b47b0c --- /dev/null +++ b/merge-positions-script.js @@ -0,0 +1,141 @@ +const fs = require('fs'); + +console.log('๐Ÿ”ง Merging position sections...'); + +// Read the current file +let content = fs.readFileSync('/app/app/automation-v2/page.js', 'utf8'); + +// Remove the separate positions section +const positionsStart = content.indexOf(' {/* Positions */}'); +const positionsEnd = content.indexOf(' )}', positionsStart + 1) + 12; // Include the closing + +if (positionsStart > -1 && positionsEnd > positionsStart) { + content = content.slice(0, positionsStart) + content.slice(positionsEnd); + console.log('โœ… Removed separate positions section'); +} + +// Find and update the position monitor section +const monitorStart = content.indexOf(' {/* Position Monitor */}'); +const monitorEnd = content.indexOf(' )}', monitorStart + 1) + 12; + +if (monitorStart > -1 && monitorEnd > monitorStart) { + const newMonitorSection = ` {/* Merged Position Status & Monitor */} +
+

๐Ÿ“Š Position Status & Monitor

+ + {/* Monitor Status Section */} + {monitorData && ( +
+

๐Ÿ“ˆ Monitor Overview

+
+
+ Has Position: + + {monitorData.hasPosition ? 'โœ… YES' : 'โŒ NO'} + +
+ +
+ Risk Level: + + {monitorData.riskLevel} + +
+
+ + {monitorData.details && ( +
+

{monitorData.details}

+
+ )} + + {monitorData.orphanedOrderCleanup && ( +
+
+ {monitorData.orphanedOrderCleanup.success ? 'โœ… Cleanup Success' : 'โŒ Cleanup Failed'} +
+
+ {monitorData.orphanedOrderCleanup.message} +
+
+ )} +
+ )} + + {/* Open Positions Section */} + {positions.length > 0 ? ( +
+

๐Ÿ“ˆ Active Positions ({positions.length})

+
+ {positions.map((position, index) => ( +
+
+ {position.symbol} + + {position.side} + +
+ +
+
+ Size: + \${position.size} +
+ + {position.entryPrice && ( +
+ Entry: + \${position.entryPrice} +
+ )} + + {position.markPrice && ( +
+ Mark: + \${position.markPrice} +
+ )} + + {position.pnl !== undefined && ( +
+ PnL: + = 0 ? 'text-green-400' : 'text-red-400' + }\`}> + \${position.pnl >= 0 ? '+' : ''}\${position.pnl} + +
+ )} +
+
+ ))} +
+
+ ) : ( +
+
๐Ÿ“Š
+

No Active Positions

+

Start automation to begin trading

+
+ )} +
+ +`; + + content = content.slice(0, monitorStart) + newMonitorSection + content.slice(monitorEnd); + console.log('โœ… Updated position monitor section with merged content'); +} + +// Write the updated content back +fs.writeFileSync('/app/app/automation-v2/page.js', content); +console.log('๐ŸŽ‰ Positions merged successfully!'); diff --git a/merge-positions-v2.js b/merge-positions-v2.js new file mode 100644 index 0000000..c0c4a1e --- /dev/null +++ b/merge-positions-v2.js @@ -0,0 +1,147 @@ +const fs = require('fs'); + +console.log('๐Ÿ”ง Merging position sections (attempt 2)...'); + +let content = fs.readFileSync('/app/app/automation-v2/page.js', 'utf8'); + +// First, remove the separate "Open Positions" section completely +const openPosStart = content.indexOf(' {/* Positions */}'); +const openPosEnd = content.indexOf(' )}', openPosStart + 50) + 12; // Get the closing + +if (openPosStart > -1 && openPosEnd > openPosStart) { + const before = content.slice(0, openPosStart); + const after = content.slice(openPosEnd); + content = before + after; + console.log('โœ… Removed separate Open Positions section'); +} else { + console.log('โŒ Could not find Open Positions section to remove'); +} + +// Now find and replace the Position Monitor section +const monitorStart = content.indexOf(' {/* Position Monitor */}'); +if (monitorStart > -1) { + const monitorEnd = content.indexOf(' )}', monitorStart + 50) + 12; + + if (monitorEnd > monitorStart) { + const newSection = ` {/* Merged Position Status & Monitor */} +
+

๐Ÿ“Š Position Status & Monitor

+ + {/* Monitor Status Section */} + {monitorData && ( +
+

๐Ÿ“ˆ Monitor Overview

+
+
+ Has Position: + + {monitorData.hasPosition ? 'โœ… YES' : 'โŒ NO'} + +
+ +
+ Risk Level: + + {monitorData.riskLevel} + +
+
+ +
+ Next Action: {monitorData.nextAction} +
+ + {monitorData.orphanedOrderCleanup && ( +
+
+ {monitorData.orphanedOrderCleanup.success ? 'โœ… Cleanup Success' : 'โŒ Cleanup Failed'} +
+
+ {monitorData.orphanedOrderCleanup.message} +
+
+ )} +
+ )} + + {/* Open Positions Section */} + {positions.length > 0 ? ( +
+

๐Ÿ“ˆ Active Positions ({positions.length})

+
+ {positions.map((position, index) => ( +
+
+ {position.symbol} + + {position.side} + +
+ +
+
+ Size: + \${position.size} +
+ + {position.entryPrice && ( +
+ Entry: + \${position.entryPrice} +
+ )} + + {position.markPrice && ( +
+ Mark: + \${position.markPrice} +
+ )} + + {position.pnl !== undefined && ( +
+ PnL: + = 0 ? 'text-green-400' : 'text-red-400' + }\`}> + \${position.pnl >= 0 ? '+' : ''}\${position.pnl} + +
+ )} +
+
+ ))} +
+
+ ) : ( +
+
๐Ÿ“Š
+

No Active Positions

+

Start automation to begin trading

+
+ )} +
+ +`; + + content = content.slice(0, monitorStart) + newSection + content.slice(monitorEnd); + console.log('โœ… Updated Position Monitor with merged content'); + } else { + console.log('โŒ Could not find Position Monitor end'); + } +} else { + console.log('โŒ Could not find Position Monitor section'); +} + +fs.writeFileSync('/app/app/automation-v2/page.js', content); +console.log('๐ŸŽ‰ Positions merged successfully!'); diff --git a/show-position-analysis.js b/show-position-analysis.js new file mode 100644 index 0000000..b356011 --- /dev/null +++ b/show-position-analysis.js @@ -0,0 +1,70 @@ +// Quick script to inject AI analysis for your current position into the UI + +async function showPositionAnalysis() { + console.log('๐Ÿ” Generating AI Analysis for Current Position...\n'); + + try { + // Step 1: Get position data + console.log('๐Ÿ“Š Fetching position data...'); + const positionResponse = await fetch('http://localhost:9001/api/automation/position-monitor'); + const positionData = await positionResponse.json(); + + if (!positionData.success || !positionData.monitor.hasPosition) { + console.log('โŒ No position found to analyze'); + return; + } + + console.log('โœ… Position found:', positionData.monitor.position.symbol, positionData.monitor.position.side); + + // Step 2: Generate analysis + console.log('๐Ÿง  Generating AI analysis...'); + const analysisResponse = await fetch('http://localhost:9001/api/automation/analyze-position', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + action: 'analyze_existing_position', + positionData: positionData.monitor.position + }) + }); + + const analysisData = await analysisResponse.json(); + + if (analysisData.success) { + console.log('\n๐ŸŽฏ AI ANALYSIS GENERATED:'); + console.log('โ•'.repeat(60)); + console.log(`๐Ÿ“Š Recommendation: ${analysisData.decision.recommendation}`); + console.log(`๐ŸŽฏ Confidence: ${analysisData.decision.confidence}%`); + console.log(`โฐ Analysis Type: ${analysisData.decision.isRetrospective ? 'RETROACTIVE' : 'LIVE'}`); + + console.log('\n๐Ÿ’ญ AI REASONING:'); + console.log('โ”€'.repeat(60)); + console.log(analysisData.decision.reasoning); + + console.log('\n๐Ÿ’ฐ EXECUTION DETAILS:'); + console.log('โ”€'.repeat(60)); + const exec = analysisData.decision.executionDetails; + console.log(`Side: ${exec.side}`); + console.log(`Amount: $${exec.amount}`); + console.log(`Leverage: ${exec.leverage}x`); + console.log(`Entry: $${exec.currentPrice.toFixed(4)}`); + console.log(`Stop Loss: $${exec.stopLoss.toFixed(4)}`); + console.log(`Take Profit: $${exec.takeProfit.toFixed(2)}`); + + console.log('\n๐Ÿง  AI LEVERAGE REASONING:'); + console.log('โ”€'.repeat(60)); + console.log(exec.aiReasoning); + + console.log('\nโœ… ANALYSIS COMPLETE!'); + console.log('๐Ÿ“ฑ This analysis should now appear in the automation-v2 page'); + console.log('๐Ÿ”„ Refresh the page if needed to see the AI Trading Analysis panel populated'); + + } else { + console.error('โŒ Analysis failed:', analysisData.error); + } + + } catch (error) { + console.error('โŒ Error:', error.message); + } +} + +showPositionAnalysis().catch(console.error);