diff --git a/backend/src/routes/marketAnalysis.js b/backend/src/routes/marketAnalysis.js
index a01f47f..1062383 100644
--- a/backend/src/routes/marketAnalysis.js
+++ b/backend/src/routes/marketAnalysis.js
@@ -49,6 +49,8 @@ router.get('/universe', async (req, res) => {
change_pct: quote?.change_pct ?? null,
market_cap: quote?.market_cap ?? null,
volume: quote?.volume ?? null,
+ day_high: quote?.high ?? null,
+ day_low: quote?.low ?? null,
high_52w: null, // populated by quoteSummary if needed
low_52w: null,
market_state: quote?.market_state ?? null,
diff --git a/backend/src/services/aiAnalysisService.js b/backend/src/services/aiAnalysisService.js
index 21ca959..2759604 100644
--- a/backend/src/services/aiAnalysisService.js
+++ b/backend/src/services/aiAnalysisService.js
@@ -303,20 +303,26 @@ ${yahooData ? `STOCK DATA (Yahoo Finance):
- Volume: ${yahooData.volume?.toLocaleString() || 'N/A'}
` : ''}
-Provide a concise analysis (2-3 sentences max) with:
-1. Overall assessment (Bullish/Bearish/Neutral)
-2. Key risk factors or opportunities
-3. Clear action recommendation (Enter/Wait/Avoid)
+Provide a detailed analysis categorized into three distinct trader profiles based on the data.
Format as JSON:
{
- "assessment": "BULLISH|BEARISH|NEUTRAL",
- "summary": "Brief 1-2 sentence summary",
- "keyFactors": ["factor1", "factor2", "factor3"],
- "recommendation": "ENTER|WAIT|AVOID",
- "reasoning": "Brief explanation",
- "riskLevel": "LOW|MEDIUM|HIGH",
- "timeHorizon": "INTRADAY|SWING|POSITION"
+ "longTerm": {
+ "assessment": "BULLISH|BEARISH|NEUTRAL",
+ "moatAndFundamentals": "Brief analysis of economic moat, EPS growth, and fundamental ratios (P/E, P/B)",
+ "recommendation": "ENTER|WAIT|AVOID"
+ },
+ "swing": {
+ "assessment": "BULLISH|BEARISH|NEUTRAL",
+ "technicalSetup": "Analysis of technical setups, consolidation, moving averages, and RSI momentum",
+ "recommendation": "ENTER|WAIT|AVOID"
+ },
+ "dayTrade": {
+ "assessment": "BULLISH|BEARISH|NEUTRAL",
+ "volatilityAndLiquidity": "Analysis of immediate catalysts, intraday liquidity/volume, and volatility risks",
+ "recommendation": "ENTER|WAIT|AVOID"
+ },
+ "summary": "Overall 1-2 sentence conclusion covering the most viable approach"
}`;
// Use model from env or default to stable version
@@ -346,13 +352,22 @@ Format as JSON:
// Fallback: create structured response from text
console.warn('Failed to parse AI response as JSON, using fallback');
analysis = {
- assessment: direction,
- summary: responseText.substring(0, 200),
- keyFactors: [],
- recommendation: score >= 2.0 ? 'ENTER' : 'WAIT',
- reasoning: responseText,
- riskLevel: score < 2.0 ? 'HIGH' : score < 5.0 ? 'MEDIUM' : 'LOW',
- timeHorizon: 'INTRADAY'
+ longTerm: {
+ assessment: direction,
+ moatAndFundamentals: 'Unable to perform deep fundamental analysis. Please review ratios manually.',
+ recommendation: score >= 5.0 ? 'ENTER' : 'WAIT'
+ },
+ swing: {
+ assessment: direction,
+ technicalSetup: 'Technical analysis currently unavailable.',
+ recommendation: score >= 3.0 ? 'ENTER' : 'WAIT'
+ },
+ dayTrade: {
+ assessment: direction,
+ volatilityAndLiquidity: 'Analysis of intraday volume and catalysts unavailable.',
+ recommendation: score >= 2.0 ? 'ENTER' : 'WAIT'
+ },
+ summary: responseText.substring(0, 200) || 'Analysis unavailable'
};
}
@@ -369,13 +384,10 @@ Format as JSON:
success: false,
error: error.message,
analysis: {
- assessment: 'NEUTRAL',
- summary: 'Analysis unavailable',
- keyFactors: [],
- recommendation: 'WAIT',
- reasoning: 'Unable to generate analysis',
- riskLevel: 'MEDIUM',
- timeHorizon: 'INTRADAY'
+ longTerm: { assessment: 'NEUTRAL', moatAndFundamentals: 'Analysis unavailable', recommendation: 'WAIT' },
+ swing: { assessment: 'NEUTRAL', technicalSetup: 'Analysis unavailable', recommendation: 'WAIT' },
+ dayTrade: { assessment: 'NEUTRAL', volatilityAndLiquidity: 'Analysis unavailable', recommendation: 'WAIT' },
+ summary: 'Analysis unavailable'
}
};
}
diff --git a/frontend/src/components/dashboard/MarketScreenerPanel.jsx b/frontend/src/components/dashboard/MarketScreenerPanel.jsx
index 29ea36d..e32ba44 100644
--- a/frontend/src/components/dashboard/MarketScreenerPanel.jsx
+++ b/frontend/src/components/dashboard/MarketScreenerPanel.jsx
@@ -57,6 +57,7 @@ function CapBadge({ tier }) {
export default function MarketScreenerPanel({ onSelectSymbol }) {
const [activeTab, setActiveTab] = useState('all');
+ const [setupFilter, setSetupFilter] = useState('all');
const [sortBy, setSortBy] = useState('market_cap');
const [sortDir, setSortDir] = useState('desc');
const [search, setSearch] = useState('');
@@ -98,11 +99,30 @@ export default function MarketScreenerPanel({ onSelectSymbol }) {
}, [fetchData]);
const processed = [...data]
+ .map(r => {
+ // Calculate intraday volatility if we have high/low
+ const volPct = (r.day_high && r.day_low && r.day_low > 0)
+ ? ((r.day_high - r.day_low) / r.day_low) * 100
+ : null;
+ return { ...r, volatility_pct: volPct };
+ })
.filter(r => {
if (!search) return true;
const q = search.toUpperCase();
return r.symbol.includes(q) || r.name.toUpperCase().includes(q) || r.sector?.toUpperCase().includes(q);
})
+ .filter(r => {
+ if (setupFilter === 'all') return true;
+ if (setupFilter === 'day') {
+ // Day Trade: high volume (>1M), high volatility (>3%)
+ return (r.volume >= 1000000) && (r.volatility_pct > 3 || Math.abs(r.change_pct || 0) > 3);
+ }
+ if (setupFilter === 'swing') {
+ // Swing Trade: potential reversal (massive drop > 5% or massive gain > 5% on high volume)
+ return (r.volume >= 500000) && (Math.abs(r.change_pct || 0) > 5);
+ }
+ return true;
+ })
.sort((a, b) => {
const av = a[sortBy] ?? -Infinity;
const bv = b[sortBy] ?? -Infinity;
@@ -161,20 +181,38 @@ export default function MarketScreenerPanel({ onSelectSymbol }) {
{/* Cap tier tabs */}
-
- {CAP_TABS.map(tab => (
-
- ))}
+
+
+ {CAP_TABS.map(tab => (
+
+ ))}
+
+
+ Setups:
+ {['all', 'day', 'swing'].map(f => (
+
+ ))}
+
{/* Table */}
@@ -207,6 +245,10 @@ export default function MarketScreenerPanel({ onSelectSymbol }) {
className="text-right px-4 py-3 font-medium cursor-pointer hover:text-white transition-colors"
onClick={() => toggleSort('volume')}
>Volume
+
toggleSort('volatility_pct')}
+ >Volatility |
Cap Tier |
@@ -214,7 +256,7 @@ export default function MarketScreenerPanel({ onSelectSymbol }) {
{loading && data.length === 0 ? (
Array.from({ length: 10 }).map((_, i) => (
- {Array.from({ length: 8 }).map((_, j) => (
+ {Array.from({ length: 9 }).map((_, j) => (
|
|
@@ -222,7 +264,7 @@ export default function MarketScreenerPanel({ onSelectSymbol }) {
))
) : processed.length === 0 ? (
-
| No results found |
+
| No results found |
) : (
processed.map(row => (
{fmtVolume(row.volume)}
+ |
+ {row.volatility_pct != null ? (
+ 3 ? 'text-emerald-400 font-medium' : 'text-slate-400'}`}>
+ {row.volatility_pct.toFixed(2)}%
+
+ ) : '—'}
+ |
|
diff --git a/frontend/src/components/dashboard/StockDetailPanel.jsx b/frontend/src/components/dashboard/StockDetailPanel.jsx
index 4f04c03..7c1eabd 100644
--- a/frontend/src/components/dashboard/StockDetailPanel.jsx
+++ b/frontend/src/components/dashboard/StockDetailPanel.jsx
@@ -241,9 +241,9 @@ function FundamentalsTab({ f }) {
{
title: '💰 Valuation',
metrics: [
- { label: 'P/E Ratio (TTM)', value: fmt(f.pe_ttm, 1) },
- { label: 'P/E Ratio (Fwd)', value: fmt(f.pe_forward, 1) },
- { label: 'Price / Book', value: fmt(f.price_to_book, 2) },
+ { label: 'P/E Ratio (TTM)', value: fmt(f.pe_ttm, 1), h: (f.pe_ttm >= 15 && f.pe_ttm <= 25) ? 'positive' : (f.pe_ttm < 0 || f.pe_ttm > 50) ? 'negative' : undefined },
+ { label: 'P/E Ratio (Fwd)', value: fmt(f.pe_forward, 1), h: (f.pe_forward >= 15 && f.pe_forward <= 25) ? 'positive' : (f.pe_forward < 0 || f.pe_forward > 50) ? 'negative' : undefined },
+ { label: 'Price / Book', value: fmt(f.price_to_book, 2), h: (f.price_to_book >= 1 && f.price_to_book <= 3) ? 'positive' : (f.price_to_book > 10) ? 'negative' : undefined },
{ label: 'Price / Sales', value: fmt(f.price_to_sales, 2) },
{ label: 'EV / EBITDA', value: fmt(f.ev_ebitda, 1) },
{ label: 'Market Cap', value: fmtLarge(f.market_cap) },
@@ -267,7 +267,7 @@ function FundamentalsTab({ f }) {
{ label: 'EBITDA Margin', value: fmtPct(f.ebitda_margins) },
{ label: 'Operating Margin', value: fmtPct(f.operating_margins) },
{ label: 'Profit Margin', value: fmtPct(f.profit_margins) },
- { label: 'Return on Equity', value: fmtPct(f.return_on_equity), h: f.return_on_equity > 0.15 ? 'positive' : f.return_on_equity < 0 ? 'negative' : undefined },
+ { label: 'Return on Equity', value: fmtPct(f.return_on_equity), h: (f.return_on_equity >= 0.1 && f.return_on_equity <= 0.2) ? 'positive' : f.return_on_equity < 0 ? 'negative' : undefined },
{ label: 'Return on Assets', value: fmtPct(f.return_on_assets) },
]
},
@@ -303,7 +303,7 @@ function FundamentalsTab({ f }) {
{ label: 'Inst. Ownership', value: fmtPct(f.held_pct_institutions) },
{ label: 'Insider Ownership', value: fmtPct(f.held_pct_insiders) },
{ label: 'Dividend Yield', value: fmtPct(f.dividend_yield), h: f.dividend_yield > 0 ? 'positive' : undefined },
- { label: 'Payout Ratio', value: fmtPct(f.payout_ratio) },
+ { label: 'Payout Ratio', value: fmtPct(f.payout_ratio), h: f.payout_ratio < 0.6 ? 'positive' : f.payout_ratio > 0.7 ? 'negative' : undefined },
]
},
];
diff --git a/frontend/src/components/tables/TradeAnalysisModal.jsx b/frontend/src/components/tables/TradeAnalysisModal.jsx
index a181b72..e83e633 100644
--- a/frontend/src/components/tables/TradeAnalysisModal.jsx
+++ b/frontend/src/components/tables/TradeAnalysisModal.jsx
@@ -495,49 +495,60 @@ export function TradeAnalysisModal({ row, isOpen, onClose }) {
)}
{aiAnalysis && aiAnalysis.analysis && (
-
-
-
- {aiAnalysis.analysis.assessment}
-
-
- {aiAnalysis.analysis.recommendation}
-
-
- {aiAnalysis.analysis.riskLevel} Risk
-
+
+
+ Summary: {aiAnalysis.analysis.summary}
+
+
+
+ {/* Long Term */}
+ {aiAnalysis.analysis.longTerm && (
+
+
Long-Term Investor
+
+
+ {aiAnalysis.analysis.longTerm.assessment}
+
+
+ {aiAnalysis.analysis.longTerm.recommendation}
+
+
+
{aiAnalysis.analysis.longTerm.moatAndFundamentals}
+
+ )}
+
+ {/* Swing Trade */}
+ {aiAnalysis.analysis.swing && (
+
+
Swing Trader
+
+
+ {aiAnalysis.analysis.swing.assessment}
+
+
+ {aiAnalysis.analysis.swing.recommendation}
+
+
+
{aiAnalysis.analysis.swing.technicalSetup}
+
+ )}
+
+ {/* Day Trade */}
+ {aiAnalysis.analysis.dayTrade && (
+
+
Day Trader
+
+
+ {aiAnalysis.analysis.dayTrade.assessment}
+
+
+ {aiAnalysis.analysis.dayTrade.recommendation}
+
+
+
{aiAnalysis.analysis.dayTrade.volatilityAndLiquidity}
+
+ )}
-
{aiAnalysis.analysis.summary}
-
- {aiAnalysis.analysis.keyFactors && aiAnalysis.analysis.keyFactors.length > 0 && (
-
-
Key Factors:
-
- {aiAnalysis.analysis.keyFactors.map((factor, idx) => (
- - {factor}
- ))}
-
-
- )}
-
- {aiAnalysis.analysis.reasoning && (
-
-
Reasoning:
-
{aiAnalysis.analysis.reasoning}
-
- )}
)}
diff --git a/frontend/src/components/tables/expandedRowDetails/AnalysisSection.jsx b/frontend/src/components/tables/expandedRowDetails/AnalysisSection.jsx
index bea697f..4bbfde4 100644
--- a/frontend/src/components/tables/expandedRowDetails/AnalysisSection.jsx
+++ b/frontend/src/components/tables/expandedRowDetails/AnalysisSection.jsx
@@ -60,56 +60,64 @@ export function AnalysisSection({ row, hasPhase1Data, onPhase1Click }) {
)}
{aiAnalysis && aiAnalysis.analysis && (
-
-
-
-
- {aiAnalysis.analysis.assessment}
-
-
- {aiAnalysis.analysis.recommendation}
-
-
- {aiAnalysis.analysis.riskLevel} Risk
-
-
-
{aiAnalysis.analysis.summary}
+
+
+ Summary: {aiAnalysis.analysis.summary}
+
+
+
+ {/* Long Term */}
+ {aiAnalysis.analysis.longTerm && (
+
+
Long-Term Investor
+
+
+ {aiAnalysis.analysis.longTerm.assessment}
+
+
+ {aiAnalysis.analysis.longTerm.recommendation}
+
+
+
{aiAnalysis.analysis.longTerm.moatAndFundamentals}
+
+ )}
+
+ {/* Swing Trade */}
+ {aiAnalysis.analysis.swing && (
+
+
Swing Trader
+
+
+ {aiAnalysis.analysis.swing.assessment}
+
+
+ {aiAnalysis.analysis.swing.recommendation}
+
+
+
{aiAnalysis.analysis.swing.technicalSetup}
+
+ )}
+
+ {/* Day Trade */}
+ {aiAnalysis.analysis.dayTrade && (
+
+
Day Trader
+
+
+ {aiAnalysis.analysis.dayTrade.assessment}
+
+
+ {aiAnalysis.analysis.dayTrade.recommendation}
+
+
+
{aiAnalysis.analysis.dayTrade.volatilityAndLiquidity}
+
+ )}
- {aiAnalysis.analysis.keyFactors && aiAnalysis.analysis.keyFactors.length > 0 && (
-
-
Key Factors:
-
- {aiAnalysis.analysis.keyFactors.map((factor, idx) => (
- - • {factor}
- ))}
-
-
- )}
-
- {aiAnalysis.analysis.reasoning && (
-
-
Reasoning:
-
{aiAnalysis.analysis.reasoning}
-
- )}
-
-
-
Time Horizon: {aiAnalysis.analysis.timeHorizon}
+
{aiAnalysis.timestamp && (
- • {new Date(aiAnalysis.timestamp).toLocaleTimeString()}
+ Generated: {new Date(aiAnalysis.timestamp).toLocaleTimeString()}
)}