🚀 Enhanced Multi-Timeframe Analysis with Smart Defaults
✨ Major UI/UX Improvements: - AI + DIY layouts now default selections for comprehensive analysis - Multi-timeframe selection support for broader market outlook - Quick timeframe presets: Scalping, Day Trading, Swing, Position - Combined coin + timeframe selection workflow 📊 Enhanced Analysis Features: - Multiple timeframe analysis in single request - Smart timeframe grouping (5m+15m+1h for scalping, etc.) - Cross-timeframe consensus display - Individual timeframe result breakdown - Visual timeframe selection with active indicators 🎨 Improved User Experience: - Quick action buttons for instant multi-timeframe analysis - Clear selection indicators and counters - Preset combinations for different trading styles - Streamlined workflow: Select presets → Pick coin → Analyze - Beautiful multi-timeframe result visualization 🔧 Technical Enhancements: - Support for analyzing 1-8 timeframes simultaneously - Automatic delay between requests to prevent rate limiting - Error handling for individual timeframe failures - Responsive grid layouts for different screen sizes
This commit is contained in:
@@ -26,8 +26,8 @@ const popularCoins = [
|
|||||||
|
|
||||||
export default function AIAnalysisPanel() {
|
export default function AIAnalysisPanel() {
|
||||||
const [symbol, setSymbol] = useState('BTCUSD')
|
const [symbol, setSymbol] = useState('BTCUSD')
|
||||||
const [selectedLayouts, setSelectedLayouts] = useState<string[]>([layouts[0]])
|
const [selectedLayouts, setSelectedLayouts] = useState<string[]>(['ai', 'diy']) // Default to both AI and DIY
|
||||||
const [timeframe, setTimeframe] = useState('60')
|
const [selectedTimeframes, setSelectedTimeframes] = useState<string[]>(['60']) // Support multiple timeframes
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [result, setResult] = useState<any>(null)
|
const [result, setResult] = useState<any>(null)
|
||||||
const [error, setError] = useState<string | null>(null)
|
const [error, setError] = useState<string | null>(null)
|
||||||
@@ -51,22 +51,32 @@ export default function AIAnalysisPanel() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const performAnalysis = async (analysisSymbol = symbol, analysisTimeframe = timeframe) => {
|
const toggleTimeframe = (timeframe: string) => {
|
||||||
if (loading || selectedLayouts.length === 0) return
|
setSelectedTimeframes(prev =>
|
||||||
|
prev.includes(timeframe)
|
||||||
|
? prev.filter(tf => tf !== timeframe)
|
||||||
|
: [...prev, timeframe]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const performAnalysis = async (analysisSymbol = symbol, analysisTimeframes = selectedTimeframes) => {
|
||||||
|
if (loading || selectedLayouts.length === 0 || analysisTimeframes.length === 0) return
|
||||||
|
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
setError(null)
|
setError(null)
|
||||||
setResult(null)
|
setResult(null)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (analysisTimeframes.length === 1) {
|
||||||
|
// Single timeframe analysis
|
||||||
const response = await fetch('/api/enhanced-screenshot', {
|
const response = await fetch('/api/enhanced-screenshot', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
symbol: analysisSymbol,
|
symbol: analysisSymbol,
|
||||||
timeframe: analysisTimeframe,
|
timeframe: analysisTimeframes[0],
|
||||||
layouts: selectedLayouts,
|
layouts: selectedLayouts,
|
||||||
analyze: true // Request AI analysis of captured screenshots
|
analyze: true
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -77,6 +87,43 @@ export default function AIAnalysisPanel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setResult(data)
|
setResult(data)
|
||||||
|
} else {
|
||||||
|
// Multiple timeframe analysis
|
||||||
|
const results = []
|
||||||
|
|
||||||
|
for (const tf of analysisTimeframes) {
|
||||||
|
console.log(`🧪 Analyzing timeframe: ${timeframes.find(t => t.value === tf)?.label || tf}`)
|
||||||
|
|
||||||
|
const response = await fetch('/api/enhanced-screenshot', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
symbol: analysisSymbol,
|
||||||
|
timeframe: tf,
|
||||||
|
layouts: selectedLayouts,
|
||||||
|
analyze: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await response.json()
|
||||||
|
results.push({
|
||||||
|
timeframe: tf,
|
||||||
|
timeframeLabel: timeframes.find(t => t.value === tf)?.label || tf,
|
||||||
|
success: response.ok,
|
||||||
|
result
|
||||||
|
})
|
||||||
|
|
||||||
|
// Small delay between requests
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 2000))
|
||||||
|
}
|
||||||
|
|
||||||
|
setResult({
|
||||||
|
type: 'multi_timeframe',
|
||||||
|
symbol: analysisSymbol,
|
||||||
|
summary: `Analyzed ${results.length} timeframes for ${analysisSymbol}`,
|
||||||
|
results
|
||||||
|
})
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : 'Failed to perform analysis')
|
setError(err instanceof Error ? err.message : 'Failed to perform analysis')
|
||||||
} finally {
|
} finally {
|
||||||
@@ -84,59 +131,34 @@ export default function AIAnalysisPanel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const quickAnalyze = async (coinSymbol: string) => {
|
const quickAnalyze = async (coinSymbol: string, quickTimeframes = selectedTimeframes) => {
|
||||||
setSymbol(coinSymbol)
|
setSymbol(coinSymbol)
|
||||||
if (!loading) {
|
if (!loading) {
|
||||||
await performAnalysis(coinSymbol)
|
await performAnalysis(coinSymbol, quickTimeframes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const quickTimeframeTest = async (testTimeframe: string) => {
|
const quickTimeframeTest = async (testTimeframe: string) => {
|
||||||
setTimeframe(testTimeframe)
|
// Toggle the timeframe in selection instead of replacing
|
||||||
if (!loading && symbol) {
|
const newTimeframes = selectedTimeframes.includes(testTimeframe)
|
||||||
await performAnalysis(symbol, testTimeframe)
|
? selectedTimeframes.filter(tf => tf !== testTimeframe)
|
||||||
|
: [...selectedTimeframes, testTimeframe]
|
||||||
|
|
||||||
|
setSelectedTimeframes(newTimeframes)
|
||||||
|
|
||||||
|
if (!loading && symbol && newTimeframes.length > 0) {
|
||||||
|
await performAnalysis(symbol, newTimeframes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const testAllTimeframes = async () => {
|
const testAllTimeframes = async () => {
|
||||||
if (loading) return
|
if (loading) return
|
||||||
|
|
||||||
setLoading(true)
|
const allTimeframeValues = timeframes.map(tf => tf.value)
|
||||||
setError(null)
|
setSelectedTimeframes(allTimeframeValues)
|
||||||
const results = []
|
|
||||||
|
|
||||||
try {
|
if (!loading && symbol) {
|
||||||
for (const tf of timeframes) {
|
await performAnalysis(symbol, allTimeframeValues)
|
||||||
console.log(`🧪 Testing timeframe: ${tf.label}`)
|
|
||||||
setTimeframe(tf.value)
|
|
||||||
|
|
||||||
const response = await fetch('/api/enhanced-screenshot', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({
|
|
||||||
symbol,
|
|
||||||
timeframe: tf.value,
|
|
||||||
layouts: selectedLayouts,
|
|
||||||
analyze: true // Request AI analysis for timeframe testing too
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const result = await response.json()
|
|
||||||
results.push({ timeframe: tf.label, success: response.ok, result })
|
|
||||||
|
|
||||||
// Small delay between tests
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 2000))
|
|
||||||
}
|
|
||||||
|
|
||||||
setResult({
|
|
||||||
type: 'timeframe_test',
|
|
||||||
summary: `Tested ${results.length} timeframes`,
|
|
||||||
results
|
|
||||||
})
|
|
||||||
} catch (err) {
|
|
||||||
setError(err instanceof Error ? err.message : 'Timeframe testing failed')
|
|
||||||
} finally {
|
|
||||||
setLoading(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,32 +181,70 @@ export default function AIAnalysisPanel() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Quick Coin Selection */}
|
{/* Quick Coin & Timeframe Analysis */}
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<h3 className="text-sm font-semibold text-gray-300 flex items-center">
|
<h3 className="text-sm font-semibold text-gray-300 flex items-center">
|
||||||
<span className="w-4 h-4 bg-yellow-500 rounded-full mr-2"></span>
|
<span className="w-4 h-4 bg-yellow-500 rounded-full mr-2"></span>
|
||||||
Quick Analysis
|
Quick Analysis
|
||||||
</h3>
|
</h3>
|
||||||
<span className="text-xs text-gray-500">Click any coin for instant analysis</span>
|
<span className="text-xs text-gray-500">Select coin + timeframe combo for instant analysis</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Quick Timeframe Presets */}
|
||||||
|
<div className="mb-4 p-3 bg-gray-800/30 rounded-lg">
|
||||||
|
<label className="block text-xs font-medium text-gray-400 mb-2">Quick Timeframe Presets</label>
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-2">
|
||||||
|
<button
|
||||||
|
onClick={() => setSelectedTimeframes(['5', '15', '60'])}
|
||||||
|
className="py-2 px-3 rounded-lg text-xs font-medium bg-purple-600/20 text-purple-300 hover:bg-purple-600/30 transition-all"
|
||||||
|
>
|
||||||
|
🕒 Scalping (5m, 15m, 1h)
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setSelectedTimeframes(['60', '240', 'D'])}
|
||||||
|
className="py-2 px-3 rounded-lg text-xs font-medium bg-blue-600/20 text-blue-300 hover:bg-blue-600/30 transition-all"
|
||||||
|
>
|
||||||
|
📊 Day Trading (1h, 4h, 1d)
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setSelectedTimeframes(['240', 'D', 'W'])}
|
||||||
|
className="py-2 px-3 rounded-lg text-xs font-medium bg-green-600/20 text-green-300 hover:bg-green-600/30 transition-all"
|
||||||
|
>
|
||||||
|
📈 Swing (4h, 1d, 1w)
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setSelectedTimeframes(['D', 'W', 'M'])}
|
||||||
|
className="py-2 px-3 rounded-lg text-xs font-medium bg-orange-600/20 text-orange-300 hover:bg-orange-600/30 transition-all"
|
||||||
|
>
|
||||||
|
🎯 Position (1d, 1w, 1m)
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Coin Selection */}
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||||
{popularCoins.map(coin => (
|
{popularCoins.map(coin => (
|
||||||
<button
|
<button
|
||||||
key={coin.symbol}
|
key={coin.symbol}
|
||||||
onClick={() => quickAnalyze(coin.symbol)}
|
onClick={() => quickAnalyze(coin.symbol)}
|
||||||
disabled={loading}
|
disabled={loading || selectedTimeframes.length === 0}
|
||||||
className={`group relative p-4 rounded-xl border transition-all duration-300 ${
|
className={`group relative p-4 rounded-xl border transition-all duration-300 ${
|
||||||
symbol === coin.symbol
|
symbol === coin.symbol
|
||||||
? 'border-cyan-500 bg-cyan-500/10 shadow-lg shadow-cyan-500/20'
|
? 'border-cyan-500 bg-cyan-500/10 shadow-lg shadow-cyan-500/20'
|
||||||
: 'border-gray-700 bg-gray-800/50 hover:border-gray-600 hover:bg-gray-800'
|
: 'border-gray-700 bg-gray-800/50 hover:border-gray-600 hover:bg-gray-800'
|
||||||
} ${loading ? 'opacity-50 cursor-not-allowed' : 'hover:scale-105 hover:shadow-lg'}`}
|
} ${loading || selectedTimeframes.length === 0 ? 'opacity-50 cursor-not-allowed' : 'hover:scale-105 hover:shadow-lg'}`}
|
||||||
>
|
>
|
||||||
<div className={`w-10 h-10 bg-gradient-to-br ${coin.color} rounded-lg flex items-center justify-center mb-3 mx-auto text-white font-bold`}>
|
<div className={`w-10 h-10 bg-gradient-to-br ${coin.color} rounded-lg flex items-center justify-center mb-3 mx-auto text-white font-bold`}>
|
||||||
{coin.icon}
|
{coin.icon}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs font-semibold text-white">{coin.name}</div>
|
<div className="text-xs font-semibold text-white">{coin.name}</div>
|
||||||
<div className="text-xs text-gray-400 mt-1">{coin.symbol}</div>
|
<div className="text-xs text-gray-400 mt-1">{coin.symbol}</div>
|
||||||
|
{selectedTimeframes.length > 0 && (
|
||||||
|
<div className="text-xs text-cyan-400 mt-1 font-medium">
|
||||||
|
{selectedTimeframes.length} TF{selectedTimeframes.length > 1 ? 's' : ''}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{symbol === coin.symbol && (
|
{symbol === coin.symbol && (
|
||||||
<div className="absolute top-2 right-2 w-2 h-2 bg-cyan-400 rounded-full animate-pulse"></div>
|
<div className="absolute top-2 right-2 w-2 h-2 bg-cyan-400 rounded-full animate-pulse"></div>
|
||||||
)}
|
)}
|
||||||
@@ -200,9 +260,9 @@ export default function AIAnalysisPanel() {
|
|||||||
Advanced Analysis
|
Advanced Analysis
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
{/* Symbol and Timeframe */}
|
{/* Symbol Input */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
|
<div className="grid grid-cols-1 gap-4 mb-6">
|
||||||
<div className="md:col-span-2">
|
<div>
|
||||||
<label className="block text-xs font-medium text-gray-400 mb-2">Trading Pair</label>
|
<label className="block text-xs font-medium text-gray-400 mb-2">Trading Pair</label>
|
||||||
<input
|
<input
|
||||||
className="w-full px-4 py-3 bg-gray-800/50 border border-gray-700 rounded-lg text-white placeholder-gray-500 focus:border-cyan-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 transition-all"
|
className="w-full px-4 py-3 bg-gray-800/50 border border-gray-700 rounded-lg text-white placeholder-gray-500 focus:border-cyan-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 transition-all"
|
||||||
@@ -211,20 +271,48 @@ export default function AIAnalysisPanel() {
|
|||||||
placeholder="e.g., BTCUSD, ETHUSD"
|
placeholder="e.g., BTCUSD, ETHUSD"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<label className="block text-xs font-medium text-gray-400 mb-2">Timeframe</label>
|
|
||||||
<select
|
|
||||||
className="w-full px-4 py-3 bg-gray-800/50 border border-gray-700 rounded-lg text-white focus:border-cyan-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 transition-all"
|
|
||||||
value={timeframe}
|
|
||||||
onChange={e => setTimeframe(e.target.value)}
|
|
||||||
>
|
|
||||||
{timeframes.map(tf => (
|
|
||||||
<option key={tf.value} value={tf.value} className="bg-gray-800">
|
|
||||||
{tf.label}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Timeframe Selection */}
|
||||||
|
<div className="mb-6">
|
||||||
|
<label className="block text-xs font-medium text-gray-400 mb-3">
|
||||||
|
Analysis Timeframes
|
||||||
|
<span className="text-xs text-cyan-400 ml-2">({selectedTimeframes.length} selected)</span>
|
||||||
|
</label>
|
||||||
|
<div className="grid grid-cols-4 gap-2">
|
||||||
|
{timeframes.map(tf => (
|
||||||
|
<label key={tf.value} className="group relative cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={selectedTimeframes.includes(tf.value)}
|
||||||
|
onChange={() => toggleTimeframe(tf.value)}
|
||||||
|
className="sr-only"
|
||||||
|
/>
|
||||||
|
<div className={`flex items-center justify-center p-3 rounded-lg border transition-all ${
|
||||||
|
selectedTimeframes.includes(tf.value)
|
||||||
|
? 'border-cyan-500 bg-cyan-500/10 text-cyan-300 shadow-lg shadow-cyan-500/20'
|
||||||
|
: 'border-gray-700 bg-gray-800/30 text-gray-400 hover:border-gray-600 hover:bg-gray-800/50 hover:text-gray-300'
|
||||||
|
}`}>
|
||||||
|
<span className="text-sm font-medium">{tf.label}</span>
|
||||||
|
{selectedTimeframes.includes(tf.value) && (
|
||||||
|
<div className="absolute top-1 right-1 w-2 h-2 bg-cyan-400 rounded-full animate-pulse"></div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{selectedTimeframes.length > 0 && (
|
||||||
|
<div className="mt-3 p-3 bg-gray-800/30 rounded-lg">
|
||||||
|
<div className="text-xs text-gray-400">
|
||||||
|
Selected timeframes: <span className="text-cyan-400">
|
||||||
|
{selectedTimeframes.map(tf => timeframes.find(t => t.value === tf)?.label).join(', ')}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-gray-500 mt-1">
|
||||||
|
💡 Multiple timeframes provide broader market outlook
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Layout Selection */}
|
{/* Layout Selection */}
|
||||||
@@ -269,9 +357,9 @@ export default function AIAnalysisPanel() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Quick Timeframe Testing */}
|
{/* Quick Timeframe Actions */}
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<label className="block text-xs font-medium text-gray-400 mb-3">Quick Timeframe Tests</label>
|
<label className="block text-xs font-medium text-gray-400 mb-3">Quick Actions</label>
|
||||||
<div className="grid grid-cols-4 gap-2 mb-3">
|
<div className="grid grid-cols-4 gap-2 mb-3">
|
||||||
{timeframes.map(tf => (
|
{timeframes.map(tf => (
|
||||||
<button
|
<button
|
||||||
@@ -279,7 +367,7 @@ export default function AIAnalysisPanel() {
|
|||||||
onClick={() => quickTimeframeTest(tf.value)}
|
onClick={() => quickTimeframeTest(tf.value)}
|
||||||
disabled={loading || selectedLayouts.length === 0}
|
disabled={loading || selectedLayouts.length === 0}
|
||||||
className={`py-2 px-3 rounded-lg text-xs font-medium transition-all ${
|
className={`py-2 px-3 rounded-lg text-xs font-medium transition-all ${
|
||||||
timeframe === tf.value
|
selectedTimeframes.includes(tf.value)
|
||||||
? 'bg-cyan-500 text-white shadow-lg'
|
? 'bg-cyan-500 text-white shadow-lg'
|
||||||
: loading
|
: loading
|
||||||
? 'bg-gray-700 text-gray-500 cursor-not-allowed'
|
? 'bg-gray-700 text-gray-500 cursor-not-allowed'
|
||||||
@@ -290,17 +378,30 @@ export default function AIAnalysisPanel() {
|
|||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={testAllTimeframes}
|
onClick={testAllTimeframes}
|
||||||
disabled={loading || selectedLayouts.length === 0 || !symbol}
|
disabled={loading || selectedLayouts.length === 0 || !symbol}
|
||||||
className={`w-full py-2 px-4 rounded-lg text-sm font-medium transition-all ${
|
className={`py-2 px-4 rounded-lg text-sm font-medium transition-all ${
|
||||||
loading
|
loading
|
||||||
? 'bg-gray-700 text-gray-500 cursor-not-allowed'
|
? 'bg-gray-700 text-gray-500 cursor-not-allowed'
|
||||||
: 'bg-gradient-to-r from-purple-500 to-pink-500 text-white hover:from-purple-600 hover:to-pink-600 transform hover:scale-[1.02] active:scale-[0.98] shadow-lg'
|
: 'bg-gradient-to-r from-purple-500 to-pink-500 text-white hover:from-purple-600 hover:to-pink-600 transform hover:scale-[1.02] active:scale-[0.98] shadow-lg'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{loading ? '🔄 Testing...' : '🧪 Test All Timeframes'}
|
🔍 Analyze All Timeframes
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setSelectedTimeframes([])}
|
||||||
|
disabled={loading || selectedTimeframes.length === 0}
|
||||||
|
className={`py-2 px-4 rounded-lg text-sm font-medium transition-all ${
|
||||||
|
loading || selectedTimeframes.length === 0
|
||||||
|
? 'bg-gray-700 text-gray-500 cursor-not-allowed'
|
||||||
|
: 'bg-gray-700 text-gray-300 hover:bg-gray-600 hover:text-white transform hover:scale-[1.02] active:scale-[0.98]'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
🗑️ Clear Selection
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Analyze Button */}
|
{/* Analyze Button */}
|
||||||
@@ -347,13 +448,106 @@ export default function AIAnalysisPanel() {
|
|||||||
<div>
|
<div>
|
||||||
<h4 className="text-cyan-400 font-medium text-sm">AI Processing</h4>
|
<h4 className="text-cyan-400 font-medium text-sm">AI Processing</h4>
|
||||||
<p className="text-cyan-300 text-xs mt-1 opacity-90">
|
<p className="text-cyan-300 text-xs mt-1 opacity-90">
|
||||||
Analyzing {symbol} on {timeframe} timeframe...
|
Analyzing {symbol} on {selectedTimeframes.length === 1
|
||||||
|
? `${timeframes.find(tf => tf.value === selectedTimeframes[0])?.label} timeframe`
|
||||||
|
: `${selectedTimeframes.length} timeframes`
|
||||||
|
}...
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{result && result.type === 'multi_timeframe' && (
|
||||||
|
<div className="mt-6 space-y-4">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<h3 className="text-lg font-bold text-white flex items-center">
|
||||||
|
<span className="w-6 h-6 bg-gradient-to-br from-purple-400 to-purple-600 rounded-lg flex items-center justify-center mr-2 text-sm">
|
||||||
|
📊
|
||||||
|
</span>
|
||||||
|
Multi-Timeframe Analysis
|
||||||
|
</h3>
|
||||||
|
<div className="text-xs text-gray-400">
|
||||||
|
{result.symbol} • {result.results.length} timeframes
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="p-4 bg-gradient-to-r from-purple-800/30 to-purple-700/30 rounded-lg border border-purple-500/30">
|
||||||
|
<h4 className="text-sm font-semibold text-purple-300 mb-2">Analysis Summary</h4>
|
||||||
|
<p className="text-white text-sm">{result.summary}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid gap-4">
|
||||||
|
{result.results.map((timeframeResult: any, index: number) => (
|
||||||
|
<div key={index} className={`p-4 rounded-lg border ${
|
||||||
|
timeframeResult.success
|
||||||
|
? 'bg-green-500/5 border-green-500/30'
|
||||||
|
: 'bg-red-500/5 border-red-500/30'
|
||||||
|
}`}>
|
||||||
|
<div className="flex items-center justify-between mb-3">
|
||||||
|
<h5 className={`font-semibold ${
|
||||||
|
timeframeResult.success ? 'text-green-400' : 'text-red-400'
|
||||||
|
}`}>
|
||||||
|
{timeframeResult.success ? '✅' : '❌'} {timeframeResult.timeframeLabel} Timeframe
|
||||||
|
</h5>
|
||||||
|
<span className="text-xs text-gray-400">
|
||||||
|
{timeframeResult.success ? 'Analysis Complete' : 'Failed'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{timeframeResult.success && timeframeResult.result.analysis && (
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
||||||
|
<div className="text-center p-3 bg-gray-800/30 rounded">
|
||||||
|
<div className="text-xs text-gray-400">Sentiment</div>
|
||||||
|
<div className="text-sm font-medium text-white">
|
||||||
|
{safeRender(timeframeResult.result.analysis.marketSentiment)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-center p-3 bg-gray-800/30 rounded">
|
||||||
|
<div className="text-xs text-gray-400">Recommendation</div>
|
||||||
|
<div className="text-sm font-medium text-white">
|
||||||
|
{safeRender(timeframeResult.result.analysis.recommendation)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-center p-3 bg-gray-800/30 rounded">
|
||||||
|
<div className="text-xs text-gray-400">Confidence</div>
|
||||||
|
<div className="text-sm font-medium text-white">
|
||||||
|
{safeRender(timeframeResult.result.analysis.confidence)}%
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{timeframeResult.success && timeframeResult.result.analysis?.entry && (
|
||||||
|
<div className="mt-3 p-3 bg-yellow-500/5 border border-yellow-500/20 rounded">
|
||||||
|
<div className="text-xs text-yellow-400 font-semibold mb-1">Entry Setup</div>
|
||||||
|
<div className="text-sm text-white">
|
||||||
|
📍 ${safeRender(timeframeResult.result.analysis.entry.price)}
|
||||||
|
{timeframeResult.result.analysis.entry.buffer && (
|
||||||
|
<span className="text-yellow-400 ml-1">
|
||||||
|
{safeRender(timeframeResult.result.analysis.entry.buffer)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{timeframeResult.result.analysis.stopLoss && (
|
||||||
|
<div className="text-sm text-red-300 mt-1">
|
||||||
|
🛑 SL: ${safeRender(timeframeResult.result.analysis.stopLoss.price)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!timeframeResult.success && (
|
||||||
|
<div className="text-red-300 text-sm">
|
||||||
|
Analysis failed for this timeframe
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{result && result.analysis && (
|
{result && result.analysis && (
|
||||||
<div className="mt-6 space-y-4">
|
<div className="mt-6 space-y-4">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
|
|||||||
Reference in New Issue
Block a user