UI Honesty Pivot: Remove predictive claims, add methodology, descriptive flow
Build Institutional Trader / build-and-deploy (push) Successful in 2m8s Details

This commit is contained in:
Deep Koluguri 2026-06-25 20:32:15 -04:00
parent 2e2efaf530
commit cd78058072
4 changed files with 171 additions and 29 deletions

View File

@ -3,7 +3,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import OptionsFlowPanel from '@/components/dashboard/OptionsFlowPanel';
import ScannerPanel from '@/components/dashboard/ScannerPanel';
import BacktestPanel from '@/components/dashboard/BacktestPanel';
import PerformanceTrackingPanel from '@/components/dashboard/PerformanceTrackingPanel';
import MethodologyModal from '@/components/modals/MethodologyModal';
import ReversalAlerts from '@/components/alerts/ReversalAlerts';
import ConvergenceAlerts from '@/components/alerts/ConvergenceAlerts';
import MarketScreenerPanel from '@/components/dashboard/MarketScreenerPanel';
@ -11,11 +11,13 @@ import StockDetailPanel from '@/components/dashboard/StockDetailPanel';
export default function App() {
const [selectedSymbol, setSelectedSymbol] = useState(null);
const [isMethodologyOpen, setIsMethodologyOpen] = useState(false);
return (
<div className="min-h-screen bg-slate-950 text-slate-100">
<ConvergenceAlerts />
<ReversalAlerts />
<MethodologyModal isOpen={isMethodologyOpen} onClose={() => setIsMethodologyOpen(false)} />
{/* Header */}
<header className="border-b border-slate-800 bg-slate-900/50 backdrop-blur sticky top-0 z-40">
@ -23,14 +25,20 @@ export default function App() {
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold bg-gradient-to-r from-blue-400 to-purple-400 bg-clip-text text-transparent">
🚀 Institutional Flow Platform
FactorLab: Transparent Market Research
</h1>
<p className="text-xs text-slate-400 mt-0.5">
Real-time options flow · Tape analysis · Pro-grade signals
We show you the facts fast and we tell you what we've tested and what we haven't.
</p>
</div>
<div className="flex items-center gap-4">
<div className="text-right">
<button
onClick={() => setIsMethodologyOpen(true)}
className="text-xs font-semibold text-blue-400 hover:text-blue-300 transition-colors border border-blue-400/30 px-3 py-1.5 rounded bg-blue-500/10"
>
Methodology & Disclaimers
</button>
<div className="text-right border-l border-slate-700 pl-4 ml-2">
<div className="text-xs text-slate-400">Market Status</div>
<div className="text-sm font-semibold text-green-400">RTH LIVE</div>
</div>
@ -79,11 +87,6 @@ export default function App() {
<BacktestPanel />
</TabsContent>
</Tabs>
{/* Bottom: Performance Tracking */}
<div className="mt-6">
<PerformanceTrackingPanel />
</div>
</div>
</div>
);

View File

@ -56,33 +56,47 @@ function ScoreRing({ score, color }) {
// Factor Profile Component
function pctBar(pct) {
if (pct == null) return <span className="text-slate-600">No data</span>;
const filled = Math.round(pct / 10);
const empty = 10 - filled;
if (pct == null) return <span className="text-slate-600 text-xs italic">Missing</span>;
let colorClass = 'bg-yellow-500'; // middle 50%
if (pct >= 75) colorClass = 'bg-emerald-500'; // top 25%
else if (pct < 25) colorClass = 'bg-red-500'; // bottom 25%
return (
<span className="font-mono tracking-tighter">
<span className="text-blue-500">{'█'.repeat(filled)}</span>
<span className="text-slate-700">{'░'.repeat(empty)}</span>
</span>
<div className="flex-1 mx-3 h-2 bg-slate-800 rounded-full overflow-hidden">
<div
className={`h-full rounded-full ${colorClass} transition-all duration-500`}
style={{ width: `${pct}%` }}
/>
</div>
);
}
function FactorProfileCard({ profile, onSelect }) {
// Check for incomplete data
const isIncomplete = profile.value == null || profile.quality == null || profile.lowVol == null || profile.momentum == null;
return (
<div
onClick={() => onSelect?.(profile.ticker)}
className="group bg-slate-900 border border-slate-700/50 rounded-xl p-4 cursor-pointer
hover:bg-slate-800 hover:border-blue-500/50 transition-all duration-150
hover:shadow-lg active:scale-[0.99] relative"
title={profile._disclaimer}
className={`group bg-slate-900 border rounded-xl p-4 cursor-pointer transition-all duration-150 relative ${
isIncomplete ? 'border-orange-900/50 hover:border-orange-500/50' : 'border-slate-700/50 hover:border-blue-500/50 hover:shadow-lg'
}`}
>
<div className="flex items-center justify-between mb-3 border-b border-slate-800 pb-2">
<span className="font-bold text-white text-lg group-hover:text-blue-400 transition-colors">
{profile.ticker}
</span>
<span className="text-[10px] text-slate-500 uppercase tracking-wider font-semibold bg-slate-800 px-2 py-1 rounded">
Factor Profile (vs. Universe)
</span>
{isIncomplete ? (
<span className="text-[10px] text-orange-400 font-semibold bg-orange-950/50 px-2 py-1 rounded border border-orange-800/50" title="Rank computed from incomplete data. Low confidence.">
INCOMPLETE DATA
</span>
) : (
<span className="text-[10px] text-slate-500 uppercase tracking-wider font-semibold bg-slate-800 px-2 py-1 rounded">
Factor Profile
</span>
)}
</div>
<div className="space-y-2 text-sm">
@ -193,6 +207,7 @@ export default function MarketScreenerPanel({ onSelectSymbol }) {
const [leaderboard, setLeaderboard] = useState(null);
const [loading, setLoading] = useState(true);
const [lastUpdated, setLastUpdated] = useState(null);
const [sortConfig, setSortConfig] = useState({ key: 'composite', direction: 'desc' });
const fetchData = useCallback(async () => {
try {
@ -221,9 +236,13 @@ export default function MarketScreenerPanel({ onSelectSymbol }) {
return () => clearInterval(interval);
}, [fetchData]);
// Sort profiles by composite percentile desc
// Sort profiles based on config
const profiles = screenerData?.profiles || [];
const sortedProfiles = [...profiles].sort((a, b) => (b.composite || 0) - (a.composite || 0));
const sortedProfiles = [...profiles].sort((a, b) => {
const aVal = a[sortConfig.key] || 0;
const bVal = b[sortConfig.key] || 0;
return sortConfig.direction === 'desc' ? bVal - aVal : aVal - bVal;
});
return (
<div className="space-y-5">
@ -254,8 +273,23 @@ export default function MarketScreenerPanel({ onSelectSymbol }) {
</div>
</div>
{/* Search */}
<div className="flex items-center gap-3">
{/* Search and Sort */}
<div className="flex items-center gap-3 mt-3 md:mt-0">
<select
className="bg-slate-800 border border-slate-700 text-slate-200 text-sm rounded-xl px-3 py-2 outline-none focus:border-blue-500 transition-colors cursor-pointer"
value={`${sortConfig.key}-${sortConfig.direction}`}
onChange={(e) => {
const [key, direction] = e.target.value.split('-');
setSortConfig({ key, direction });
}}
>
<option value="composite-desc">Sort: Composite Top</option>
<option value="value-desc">Sort: Value (Cheap)</option>
<option value="quality-desc">Sort: Quality (High)</option>
<option value="lowVol-desc">Sort: Low-Vol (Safe)</option>
<option value="momentum-desc">Sort: Momentum (Strong)</option>
</select>
<SearchBar
onResult={(sym) => onSelectSymbol?.(sym)}
onClear={() => {}}
@ -272,8 +306,13 @@ export default function MarketScreenerPanel({ onSelectSymbol }) {
{/* ─── Factor Grid ─────────────────────────────────────────────────── */}
<div className="p-5 bg-slate-950/50">
<div className="mb-4 text-sm text-slate-400 max-w-3xl">
<p><strong>Descriptive only.</strong> Shows where each stock ranks against its peers across four foundational factors based on current data. We have not validated that these ranks predict forward returns. We will only upgrade these to "predictive" signals after they pass rigorous out-of-sample holdout validation.</p>
<div className="mb-6 bg-blue-950/20 border border-blue-900/50 p-4 rounded-lg flex items-start gap-3 text-sm text-slate-300 max-w-4xl shadow-inner">
<span className="text-xl leading-none">💡</span>
<p>
<strong>This is a research starting point, not a buy list.</strong> High rank = "worth a closer look," not "will go up."
Think Zillow for stocks: it shows you the facts fast you still do the homework and decide.
We will only upgrade these to "predictive" signals after they pass rigorous out-of-sample holdout validation.
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">

View File

@ -1471,6 +1471,15 @@ export default function OptionsFlowPanel() {
return (
<div className="space-y-4">
{/* Descriptive Disclaimer */}
<div className="bg-yellow-950/20 border border-yellow-900/50 p-4 rounded-lg flex items-start gap-3 text-sm text-yellow-200/80 shadow-inner">
<span className="text-xl leading-none"></span>
<p>
<strong>Descriptive Only.</strong> Options flow imbalance is shown strictly for market context and sentiment analysis.
The "Smart Money" thesis has not been validated out-of-sample as a predictive signal by our engine. Do not treat flow as a buy/sell signal.
</p>
</div>
{/* Header with Date and Quick Actions */}
<div className="flex items-center justify-between bg-slate-900/50 backdrop-blur-sm rounded-lg border border-slate-800/50 p-3">
<div className="flex items-center gap-4">

View File

@ -0,0 +1,91 @@
import { Info, X } from 'lucide-react';
import { Button } from '@/components/ui/button';
export default function MethodologyModal({ isOpen, onClose }) {
if (!isOpen) return null;
return (
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50 p-4" onClick={onClose}>
<div
className="bg-slate-900 border border-slate-700 rounded-lg max-w-2xl w-full mx-auto shadow-2xl flex flex-col max-h-[90vh]"
onClick={(e) => e.stopPropagation()}
>
<div className="flex items-center justify-between border-b border-slate-800 p-4">
<div className="flex items-center gap-3">
<Info className="w-5 h-5 text-blue-400" />
<h2 className="text-xl font-bold text-slate-100">
Our Methodology: Why We Don't Sell Predictions
</h2>
</div>
<button onClick={onClose} className="text-slate-400 hover:text-white transition-colors">
<X className="w-5 h-5" />
</button>
</div>
<div className="overflow-y-auto p-6 space-y-6 text-sm text-slate-300">
<section>
<h3 className="text-lg font-semibold text-slate-100 mb-2">The Out-of-Sample Reality</h3>
<p className="mb-4">
Most platforms show you backtests that work perfectly. We're showing you ours that didn't because that's how you know our descriptive tools are honest.
</p>
<p className="mb-4">
We built a highly sophisticated predictive scoring engine based on technical indicators and options flow. We optimized it, tuned the hyperparameters, and achieved a 63% win rate in-sample.
<strong> Then, we tested it honestly.</strong> We ran a strict out-of-sample forward test on unseen data.
</p>
<div className="bg-slate-950 border border-slate-800 rounded-md overflow-hidden mb-4">
<table className="w-full text-left border-collapse">
<thead>
<tr className="bg-slate-800/50 text-slate-200">
<th className="p-3 border-b border-slate-700 font-medium">Test Group</th>
<th className="p-3 border-b border-slate-700 font-medium text-right">Expectancy (R-Multiple)</th>
<th className="p-3 border-b border-slate-700 font-medium text-right">Hit Rate</th>
</tr>
</thead>
<tbody>
<tr>
<td className="p-3 border-b border-slate-800 text-slate-300">Random Control (Coin Flip)</td>
<td className="p-3 border-b border-slate-800 text-right font-mono">+0.027R</td>
<td className="p-3 border-b border-slate-800 text-right font-mono">49.2%</td>
</tr>
<tr className="bg-red-950/20">
<td className="p-3 text-red-200">Top-Graded "A" Signals</td>
<td className="p-3 text-right font-mono text-red-300">+0.019R</td>
<td className="p-3 text-right font-mono text-red-300">48.5%</td>
</tr>
</tbody>
</table>
</div>
<p className="text-slate-400 italic">
<strong>The Result:</strong> The edge completely vanished. The highly-engineered signals failed to beat a random coin flip. So we stopped selling them.
</p>
</section>
<section>
<h3 className="text-lg font-semibold text-slate-100 mb-2">The FactorLab Approach</h3>
<p className="mb-3">
Instead of black-box predictions, we built the <strong>FactorLab</strong>. This is a purely descriptive lens.
</p>
<p className="mb-3">
We take the Top 50 liquid stocks and ETFs, ingest their raw fundamentals and technicals, and mathematically rank them across four academic factors:
<span className="text-blue-300 ml-1">Value</span>,
<span className="text-emerald-300 mx-1">Quality</span>,
<span className="text-purple-300 mx-1">Low-Volatility</span>, and
<span className="text-orange-300 ml-1">Momentum</span>.
</p>
<p>
When you see a stock is in the 95th percentile for Quality, it is a mathematical fact about its balance sheet relative to its peers. <strong>It is not a buy recommendation.</strong> Think of this tool like Zillow for stocks: we show you the facts fast, but you still do the homework and make the decision.
</p>
</section>
</div>
<div className="p-4 border-t border-slate-800 bg-slate-900 rounded-b-lg flex justify-end">
<Button onClick={onClose} className="px-6 py-2 bg-blue-600 hover:bg-blue-500 text-white rounded">
Got it, I'll do my own homework
</Button>
</div>
</div>
</div>
);
}