🚀 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() {
|
||||
const [symbol, setSymbol] = useState('BTCUSD')
|
||||
const [selectedLayouts, setSelectedLayouts] = useState<string[]>([layouts[0]])
|
||||
const [timeframe, setTimeframe] = useState('60')
|
||||
const [selectedLayouts, setSelectedLayouts] = useState<string[]>(['ai', 'diy']) // Default to both AI and DIY
|
||||
const [selectedTimeframes, setSelectedTimeframes] = useState<string[]>(['60']) // Support multiple timeframes
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [result, setResult] = useState<any>(null)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
@@ -51,32 +51,79 @@ export default function AIAnalysisPanel() {
|
||||
)
|
||||
}
|
||||
|
||||
const performAnalysis = async (analysisSymbol = symbol, analysisTimeframe = timeframe) => {
|
||||
if (loading || selectedLayouts.length === 0) return
|
||||
const toggleTimeframe = (timeframe: string) => {
|
||||
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)
|
||||
setError(null)
|
||||
setResult(null)
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/enhanced-screenshot', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
symbol: analysisSymbol,
|
||||
timeframe: analysisTimeframe,
|
||||
layouts: selectedLayouts,
|
||||
analyze: true // Request AI analysis of captured screenshots
|
||||
if (analysisTimeframes.length === 1) {
|
||||
// Single timeframe analysis
|
||||
const response = await fetch('/api/enhanced-screenshot', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
symbol: analysisSymbol,
|
||||
timeframe: analysisTimeframes[0],
|
||||
layouts: selectedLayouts,
|
||||
analyze: true
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || 'Analysis failed')
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || 'Analysis failed')
|
||||
}
|
||||
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
setResult(data)
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to perform analysis')
|
||||
} finally {
|
||||
@@ -84,59 +131,34 @@ export default function AIAnalysisPanel() {
|
||||
}
|
||||
}
|
||||
|
||||
const quickAnalyze = async (coinSymbol: string) => {
|
||||
const quickAnalyze = async (coinSymbol: string, quickTimeframes = selectedTimeframes) => {
|
||||
setSymbol(coinSymbol)
|
||||
if (!loading) {
|
||||
await performAnalysis(coinSymbol)
|
||||
await performAnalysis(coinSymbol, quickTimeframes)
|
||||
}
|
||||
}
|
||||
|
||||
const quickTimeframeTest = async (testTimeframe: string) => {
|
||||
setTimeframe(testTimeframe)
|
||||
if (!loading && symbol) {
|
||||
await performAnalysis(symbol, testTimeframe)
|
||||
// Toggle the timeframe in selection instead of replacing
|
||||
const newTimeframes = selectedTimeframes.includes(testTimeframe)
|
||||
? selectedTimeframes.filter(tf => tf !== testTimeframe)
|
||||
: [...selectedTimeframes, testTimeframe]
|
||||
|
||||
setSelectedTimeframes(newTimeframes)
|
||||
|
||||
if (!loading && symbol && newTimeframes.length > 0) {
|
||||
await performAnalysis(symbol, newTimeframes)
|
||||
}
|
||||
}
|
||||
|
||||
const testAllTimeframes = async () => {
|
||||
if (loading) return
|
||||
|
||||
setLoading(true)
|
||||
setError(null)
|
||||
const results = []
|
||||
const allTimeframeValues = timeframes.map(tf => tf.value)
|
||||
setSelectedTimeframes(allTimeframeValues)
|
||||
|
||||
try {
|
||||
for (const tf of timeframes) {
|
||||
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)
|
||||
if (!loading && symbol) {
|
||||
await performAnalysis(symbol, allTimeframeValues)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,32 +181,70 @@ export default function AIAnalysisPanel() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quick Coin Selection */}
|
||||
{/* Quick Coin & Timeframe Analysis */}
|
||||
<div className="mb-8">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<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>
|
||||
Quick Analysis
|
||||
</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>
|
||||
|
||||
{/* 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">
|
||||
{popularCoins.map(coin => (
|
||||
<button
|
||||
key={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 ${
|
||||
symbol === coin.symbol
|
||||
? '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'
|
||||
} ${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`}>
|
||||
{coin.icon}
|
||||
</div>
|
||||
<div className="text-xs font-semibold text-white">{coin.name}</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 && (
|
||||
<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
|
||||
</h3>
|
||||
|
||||
{/* Symbol and Timeframe */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
|
||||
<div className="md:col-span-2">
|
||||
{/* Symbol Input */}
|
||||
<div className="grid grid-cols-1 gap-4 mb-6">
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-gray-400 mb-2">Trading Pair</label>
|
||||
<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"
|
||||
@@ -211,20 +271,48 @@ export default function AIAnalysisPanel() {
|
||||
placeholder="e.g., BTCUSD, ETHUSD"
|
||||
/>
|
||||
</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>
|
||||
|
||||
{/* 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>
|
||||
|
||||
{/* Layout Selection */}
|
||||
@@ -269,9 +357,9 @@ export default function AIAnalysisPanel() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Quick Timeframe Testing */}
|
||||
{/* Quick Timeframe Actions */}
|
||||
<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">
|
||||
{timeframes.map(tf => (
|
||||
<button
|
||||
@@ -279,7 +367,7 @@ export default function AIAnalysisPanel() {
|
||||
onClick={() => quickTimeframeTest(tf.value)}
|
||||
disabled={loading || selectedLayouts.length === 0}
|
||||
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'
|
||||
: loading
|
||||
? 'bg-gray-700 text-gray-500 cursor-not-allowed'
|
||||
@@ -290,17 +378,30 @@ export default function AIAnalysisPanel() {
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<button
|
||||
onClick={testAllTimeframes}
|
||||
disabled={loading || selectedLayouts.length === 0 || !symbol}
|
||||
className={`w-full py-2 px-4 rounded-lg text-sm font-medium transition-all ${
|
||||
loading
|
||||
? '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'
|
||||
}`}
|
||||
>
|
||||
{loading ? '🔄 Testing...' : '🧪 Test All Timeframes'}
|
||||
</button>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||
<button
|
||||
onClick={testAllTimeframes}
|
||||
disabled={loading || selectedLayouts.length === 0 || !symbol}
|
||||
className={`py-2 px-4 rounded-lg text-sm font-medium transition-all ${
|
||||
loading
|
||||
? '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'
|
||||
}`}
|
||||
>
|
||||
🔍 Analyze All Timeframes
|
||||
</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>
|
||||
|
||||
{/* Analyze Button */}
|
||||
@@ -347,13 +448,106 @@ export default function AIAnalysisPanel() {
|
||||
<div>
|
||||
<h4 className="text-cyan-400 font-medium text-sm">AI Processing</h4>
|
||||
<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>
|
||||
</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 && (
|
||||
<div className="mt-6 space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
|
||||
Reference in New Issue
Block a user