diff --git a/app/automation-v2/page.js b/app/automation-v2/page.js index 895e1dd..3ff0b91 100644 --- a/app/automation-v2/page.js +++ b/app/automation-v2/page.js @@ -20,16 +20,17 @@ export default function AutomationPageV2() { timeframe: '1h', // Primary timeframe for backwards compatibility selectedTimeframes: ['60'], // Multi-timeframe support tradingAmount: 100, + balancePercentage: 50, // Default to 50% of available balance maxLeverage: 5, stopLossPercent: 2, - takeProfitPercent: 6, - riskPercentage: 2 + takeProfitPercent: 6 }) const [status, setStatus] = useState(null) const [balance, setBalance] = useState(null) const [positions, setPositions] = useState([]) const [loading, setLoading] = useState(false) + const [nextAnalysisCountdown, setNextAnalysisCountdown] = useState(0) useEffect(() => { fetchStatus() @@ -44,6 +45,51 @@ export default function AutomationPageV2() { return () => clearInterval(interval) }, []) + // Timer effect for countdown + useEffect(() => { + let countdownInterval = null + + if (status?.isActive && status?.nextAnalysisIn > 0) { + setNextAnalysisCountdown(status.nextAnalysisIn) + + countdownInterval = setInterval(() => { + setNextAnalysisCountdown(prev => { + if (prev <= 1) { + // Refresh status when timer reaches 0 + fetchStatus() + return 0 + } + return prev - 1 + }) + }, 1000) + } else { + setNextAnalysisCountdown(0) + } + + return () => { + if (countdownInterval) { + clearInterval(countdownInterval) + } + } + }, [status?.nextAnalysisIn, status?.isActive]) + + // Helper function to format countdown time + const formatCountdown = (seconds) => { + if (seconds <= 0) return 'Analyzing now...' + + const hours = Math.floor(seconds / 3600) + const minutes = Math.floor((seconds % 3600) / 60) + const secs = seconds % 60 + + if (hours > 0) { + return `${hours}h ${minutes}m ${secs}s` + } else if (minutes > 0) { + return `${minutes}m ${secs}s` + } else { + return `${secs}s` + } + } + const toggleTimeframe = (timeframe) => { setConfig(prev => ({ ...prev, @@ -242,7 +288,7 @@ export default function AutomationPageV2() { {/* Symbol and Position Size */} -
+
setConfig({...config, tradingAmount: parseFloat(e.target.value)})} + max="100" + step="5" + value={config.balancePercentage} + onChange={(e) => { + 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} /> - {balance && ( -

- Available: ${parseFloat(balance.availableBalance).toFixed(2)} • Using {((config.tradingAmount / balance.availableBalance) * 100).toFixed(1)}% of balance -

- )} +
+ 10% + 50% + 100% +
{balance && config.maxLeverage > 1 && (

- With {config.maxLeverage}x leverage: ${(config.tradingAmount * config.maxLeverage).toFixed(2)} position size -

- )} -
- -
- - - {balance && ( -

- Quick calculation based on ${parseFloat(balance.availableBalance).toFixed(2)} balance + With {config.maxLeverage}x leverage: ${(config.tradingAmount * config.maxLeverage).toFixed(2)} position exposure

)}
@@ -420,20 +454,6 @@ export default function AutomationPageV2() { disabled={status?.isActive} />
- -
- - setConfig({...config, riskPercentage: parseFloat(e.target.value)})} - disabled={status?.isActive} - /> -
@@ -524,6 +544,172 @@ export default function AutomationPageV2() { )} + {/* Analysis Progress */} + {status?.analysisProgress && ( +
+
+

Analysis Progress

+
+ Session: {status.analysisProgress.sessionId.split('-').pop()} +
+
+
+ {/* Overall Progress */} +
+ Step {status.analysisProgress.currentStep} of {status.analysisProgress.totalSteps} + + {Math.round((status.analysisProgress.currentStep / status.analysisProgress.totalSteps) * 100)}% + +
+
+
+
+ + {/* Timeframe Progress */} + {status.analysisProgress.timeframeProgress && ( +
+
+ + Analyzing {status.analysisProgress.timeframeProgress.currentTimeframe || 'timeframes'} + + + {status.analysisProgress.timeframeProgress.current}/{status.analysisProgress.timeframeProgress.total} + +
+
+ )} + + {/* Detailed Steps */} +
+ {status.analysisProgress.steps.map((step, index) => ( +
+ {/* Status Icon */} +
+ {step.status === 'active' ? '⏳' : + step.status === 'completed' ? '✓' : + step.status === 'error' ? '✗' : + index + 1} +
+ + {/* Step Info */} +
+
+ {step.title} +
+
+ {step.details || step.description} +
+
+ + {/* Duration */} + {step.duration && ( +
+ {(step.duration / 1000).toFixed(1)}s +
+ )} +
+ ))} +
+
+
+ )} + + {/* Analysis Timer */} + {status?.isActive && !status?.analysisProgress && ( +
+
+

Analysis Timer

+
+ Cycle #{status.currentCycle || 0} +
+
+
+
+
+ {formatCountdown(nextAnalysisCountdown)} +
+
+ {nextAnalysisCountdown > 0 ? 'Next Analysis In' : 'Analysis Starting Soon'} +
+
+
+
0 ? + `${Math.max(0, 100 - (nextAnalysisCountdown / status.analysisInterval) * 100)}%` : + '0%' + }} + >
+
+
+ Analysis Interval: {Math.floor((status.analysisInterval || 0) / 60)}m +
+
+
+ )} + + {/* Individual Timeframe Results */} + {status?.individualTimeframeResults && status.individualTimeframeResults.length > 0 && ( +
+

Timeframe Analysis

+
+ {status.individualTimeframeResults.map((result, index) => ( +
+
+ + {timeframes.find(tf => tf.value === result.timeframe)?.label || result.timeframe} + + + {result.recommendation} + +
+
+
+ {result.confidence}% +
+
+ confidence +
+
+
+ ))} +
+
+
+ ✅ Last Updated: {status.individualTimeframeResults[0]?.timestamp ? + new Date(status.individualTimeframeResults[0].timestamp).toLocaleTimeString() : + 'N/A' + } +
+
+
+ )} + {/* Trading Metrics */}

Trading Metrics