706 lines
35 KiB
TypeScript
706 lines
35 KiB
TypeScript
'use client';
|
|
|
|
import React, { useState, useMemo, useEffect } from 'react';
|
|
import { useSandboxStore, InsiderTrade, CongressTrade, WhaleTrade } from '@/lib/store';
|
|
import { calculateRollingZScore, detectInsiderClusters, coupleBayesianRebound } from '@/lib/math/statistics';
|
|
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
|
|
import 'katex/dist/katex.min.css';
|
|
import { BlockMath, InlineMath } from 'react-katex';
|
|
import InsiderMathModal from './InsiderMathModal';
|
|
import {
|
|
Shield, User, Landmark, ChevronDown, ChevronUp, Radio, Building2, AlertTriangle, Percent,
|
|
BookOpen
|
|
} from 'lucide-react';
|
|
|
|
function estimateCongressShares(valueRange: string): number {
|
|
const clean = valueRange.replace(/[$,]/g, '');
|
|
const parts = clean.split('-').map(p => parseFloat(p.trim()));
|
|
if (parts.length === 2) {
|
|
const mid = (parts[0] + parts[1]) / 2;
|
|
return Math.round(mid / 150); // assuming $150 average share price
|
|
}
|
|
if (parts.length === 1 && !isNaN(parts[0])) {
|
|
return Math.round(parts[0] / 150);
|
|
}
|
|
return 1000; // default fallback
|
|
}
|
|
|
|
function calculateRowMetrics(
|
|
ticker: string,
|
|
volume: number,
|
|
insiderVolumes: Record<string, number[]>,
|
|
priorProbability: number
|
|
) {
|
|
const baseline = insiderVolumes[ticker];
|
|
let volumesToUse: number[];
|
|
if (baseline && baseline.length > 0) {
|
|
volumesToUse = [...baseline, volume];
|
|
} else {
|
|
// Generate a dynamic seed if ticker is unrepresented
|
|
const seedBase = volume > 0 ? volume : 10000;
|
|
volumesToUse = [];
|
|
for (let i = 0; i < 11; i++) {
|
|
const factor = 0.5 + ((i * 7 + ticker.charCodeAt(0)) % 10) * 0.1;
|
|
volumesToUse.push(Math.round(seedBase * factor));
|
|
}
|
|
volumesToUse.push(volume > 0 ? volume : 10000);
|
|
}
|
|
|
|
const zResult = calculateRollingZScore(volumesToUse);
|
|
const zScore = parseFloat(zResult.latest.toFixed(2));
|
|
const coupled = coupleBayesianRebound(priorProbability, zScore);
|
|
|
|
return {
|
|
zScore,
|
|
coupledRebound: coupled
|
|
};
|
|
}
|
|
|
|
export default function InsiderDemo() {
|
|
const {
|
|
insiderTrades,
|
|
congressTrades,
|
|
whaleTrades,
|
|
insiderVolumes,
|
|
priorProbability
|
|
} = useSandboxStore();
|
|
|
|
// Component local UI states
|
|
const [activeSegment, setActiveSegment] = useState<'executives' | 'congress' | 'whales'>('executives');
|
|
const [searchQuery, setSearchQuery] = useState('');
|
|
const [selectedTicker, setSelectedTicker] = useState<string | null>(null);
|
|
const [scanning, setScanning] = useState(false);
|
|
const [scanResults, setScanResults] = useState<{
|
|
ticker: string;
|
|
zScore: number;
|
|
clusterCount: number;
|
|
multiplier: number;
|
|
isAnomaly: boolean;
|
|
coupledRebound: number;
|
|
}[] | null>(null);
|
|
const [showMathAccordion, setShowMathAccordion] = useState(false);
|
|
const [isMathModalOpen, setIsMathModalOpen] = useState(false);
|
|
const [isShieldActive, setIsShieldActive] = useState(false);
|
|
const [loading, setLoading] = useState(false);
|
|
const [errorMsg, setErrorMsg] = useState<string | null>(null);
|
|
|
|
// Load live data from the server-side proxy
|
|
useEffect(() => {
|
|
let active = true;
|
|
async function loadData() {
|
|
setLoading(true);
|
|
setErrorMsg(null);
|
|
try {
|
|
const [execRes, congRes, whaleRes] = await Promise.all([
|
|
fetch('/api/insider?type=executives').then(async r => {
|
|
if (!r.ok) throw new Error(`Executives API HTTP ${r.status}`);
|
|
return r.json();
|
|
}),
|
|
fetch('/api/insider?type=congress').then(async r => {
|
|
if (!r.ok) throw new Error(`Congress API HTTP ${r.status}`);
|
|
return r.json();
|
|
}),
|
|
fetch('/api/insider?type=whales').then(async r => {
|
|
if (!r.ok) throw new Error(`Whales API HTTP ${r.status}`);
|
|
return r.json();
|
|
})
|
|
]);
|
|
if (active) {
|
|
setIsShieldActive(!!execRes.isShieldActive || !!congRes.isShieldActive || !!whaleRes.isShieldActive);
|
|
const unavailable: string[] = [];
|
|
if (execRes.liveDataAvailable === false) unavailable.push('Executives (Form 4)');
|
|
if (congRes.liveDataAvailable === false) unavailable.push('Congress (Stock Act)');
|
|
if (whaleRes.liveDataAvailable === false) unavailable.push('Whales (13F Filings)');
|
|
|
|
if (unavailable.length > 0) {
|
|
setErrorMsg(`Real-time data source temporarily busy for: ${unavailable.join(', ')}. Please try again later.`);
|
|
}
|
|
|
|
useSandboxStore.setState({
|
|
insiderTrades: execRes.results || [],
|
|
congressTrades: congRes.results || [],
|
|
whaleTrades: whaleRes.results || []
|
|
});
|
|
}
|
|
} catch (err: any) {
|
|
console.error('Failed to load live insider data:', err.message);
|
|
if (active) {
|
|
setErrorMsg(err.message || 'Real-time data source temporarily unreachable.');
|
|
}
|
|
} finally {
|
|
if (active) setLoading(false);
|
|
}
|
|
}
|
|
loadData();
|
|
return () => {
|
|
active = false;
|
|
};
|
|
}, []);
|
|
|
|
// Run Global Flow Scan
|
|
const handleGlobalFlowScan = () => {
|
|
setScanning(true);
|
|
setTimeout(() => {
|
|
// Get all tickers present in the current live feed
|
|
const activeTickers = Array.from(new Set([
|
|
...insiderTrades.map(t => t.ticker),
|
|
...congressTrades.map(c => c.ticker),
|
|
...whaleTrades.map(w => w.ticker)
|
|
])).filter(ticker => ticker && ticker !== 'UNKNOWN' && ticker !== '--');
|
|
|
|
const results = activeTickers.map((ticker) => {
|
|
// Calculate the trade volume for this ticker in the current active feed to use for calculation
|
|
// For Executives, we sum shares from insiderTrades. For Congress, we sum estimated shares. For Whales, we sum sharesTraded.
|
|
const execVolume = insiderTrades.filter(t => t.ticker === ticker).reduce((sum, t) => sum + t.shares, 0);
|
|
const congVolume = congressTrades.filter(t => t.ticker === ticker).reduce((sum, t) => sum + estimateCongressShares(t.valueRange), 0);
|
|
const whaleVolume = whaleTrades.filter(t => t.ticker === ticker).reduce((sum, t) => sum + t.sharesTraded, 0);
|
|
const currentVolume = execVolume + congVolume + whaleVolume;
|
|
|
|
const baseline = insiderVolumes[ticker];
|
|
let volumesToUse: number[];
|
|
if (baseline && baseline.length > 0) {
|
|
volumesToUse = [...baseline, currentVolume];
|
|
} else {
|
|
// Generate a dynamic seed if ticker is unrepresented
|
|
const seedBase = currentVolume > 0 ? currentVolume : 10000;
|
|
volumesToUse = [];
|
|
for (let i = 0; i < 11; i++) {
|
|
const factor = 0.5 + ((i * 7 + ticker.charCodeAt(0)) % 10) * 0.1;
|
|
volumesToUse.push(Math.round(seedBase * factor));
|
|
}
|
|
volumesToUse.push(currentVolume > 0 ? currentVolume : 10000);
|
|
}
|
|
|
|
const zResult = calculateRollingZScore(volumesToUse);
|
|
const zScore = parseFloat(zResult.latest.toFixed(2));
|
|
|
|
// Filter trades for this ticker to detect clusters
|
|
const tickerTrades = insiderTrades.filter(t => t.ticker === ticker);
|
|
const clusterResult = detectInsiderClusters(tickerTrades);
|
|
|
|
// Bayesian coupling
|
|
const coupled = coupleBayesianRebound(priorProbability, zScore);
|
|
|
|
return {
|
|
ticker,
|
|
zScore,
|
|
clusterCount: clusterResult.count,
|
|
multiplier: parseFloat(clusterResult.multiplier.toFixed(2)),
|
|
isAnomaly: zScore > 2.0 || clusterResult.isCluster,
|
|
coupledRebound: coupled,
|
|
};
|
|
}).filter(res => res.zScore > 2.0); // Only render cards for tickers with volumetric Z-Score > 2.0
|
|
|
|
// Sort anomalies to the top
|
|
results.sort((a, b) => b.zScore - a.zScore);
|
|
|
|
setScanResults(results);
|
|
setScanning(false);
|
|
}, 1000);
|
|
};
|
|
|
|
// Perform Ticker Lookup
|
|
const handleTickerLookup = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
const query = searchQuery.trim().toUpperCase();
|
|
if (query) {
|
|
setSelectedTicker(query);
|
|
} else {
|
|
setSelectedTicker(null);
|
|
}
|
|
};
|
|
|
|
// Compute stats for selected ticker
|
|
const tickerStats = useMemo(() => {
|
|
if (!selectedTicker) return null;
|
|
|
|
const volumes = insiderVolumes[selectedTicker] || [5000, 4800, 5200, 6000, 4500, 5000]; // fallback
|
|
const zResult = calculateRollingZScore(volumes);
|
|
|
|
const tickerTrades = insiderTrades.filter(t => t.ticker === selectedTicker);
|
|
const clusterResult = detectInsiderClusters(tickerTrades);
|
|
const coupled = coupleBayesianRebound(priorProbability, zResult.latest);
|
|
|
|
return {
|
|
zScore: zResult.latest,
|
|
isAnomaly: zResult.isAnomaly,
|
|
clusterCount: clusterResult.count,
|
|
isCluster: clusterResult.isCluster,
|
|
multiplier: clusterResult.multiplier,
|
|
coupledRebound: coupled,
|
|
volumes,
|
|
};
|
|
}, [selectedTicker, insiderVolumes, insiderTrades, priorProbability]);
|
|
|
|
// Volumes chart data for selected ticker
|
|
const volumeChartData = useMemo(() => {
|
|
if (!tickerStats || !selectedTicker) return [];
|
|
return tickerStats.volumes.map((vol, idx) => ({
|
|
month: `M-${tickerStats.volumes.length - idx - 1}`,
|
|
'Volume (Shares)': vol,
|
|
}));
|
|
}, [tickerStats, selectedTicker]);
|
|
|
|
return (
|
|
<div className="bg-slate-900/60 backdrop-blur-md border border-slate-800 rounded-2xl p-6 text-slate-100 shadow-xl relative overflow-hidden">
|
|
<div className="absolute top-0 right-0 w-32 h-32 bg-purple-500/10 rounded-full blur-3xl -z-10" />
|
|
|
|
{/* Header */}
|
|
<div className="flex flex-col lg:flex-row justify-between items-start lg:items-center gap-4 border-b border-slate-800 pb-4 mb-6">
|
|
<div>
|
|
<span className="text-purple-400 text-xs font-semibold uppercase tracking-wider">Element 3</span>
|
|
<h2 className="text-2xl font-bold bg-gradient-to-r from-purple-400 to-indigo-200 bg-clip-text text-transparent flex flex-wrap items-center gap-2">
|
|
<span>Institutional & Insider Flow Tracker</span>
|
|
{isShieldActive ? (
|
|
<span className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-[10px] font-bold bg-amber-500/10 text-amber-400 border border-amber-500/20 shadow-[0_0_10px_rgba(245,158,11,0.15)] animate-pulse">
|
|
<span className="w-1.5 h-1.5 rounded-full bg-amber-500" />
|
|
DEV ARCHIVE ACTIVE (0 CALLS)
|
|
</span>
|
|
) : (
|
|
<span className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-[10px] font-bold bg-emerald-500/10 text-emerald-400 border border-emerald-500/20 shadow-[0_0_10px_rgba(16,185,129,0.15)]">
|
|
<span className="w-1.5 h-1.5 rounded-full bg-emerald-500 animate-ping" />
|
|
LIVE API ENDPOINT (FMP CORPO)
|
|
</span>
|
|
)}
|
|
</h2>
|
|
</div>
|
|
<div className="flex flex-wrap items-center gap-3">
|
|
<button
|
|
onClick={() => setIsMathModalOpen(true)}
|
|
className="flex items-center gap-1.5 px-4 py-2 rounded-xl bg-slate-950/80 hover:bg-slate-900 border border-slate-800 hover:border-slate-700 transition-all font-semibold text-xs tracking-wider text-purple-400 justify-center h-11"
|
|
>
|
|
<BookOpen className="w-3.5 h-3.5" />
|
|
<span>📖 Quantitative Handbook</span>
|
|
</button>
|
|
|
|
<div className="bg-slate-900 border border-slate-800 rounded-xl px-4 py-2 flex items-center gap-3 h-11">
|
|
<Shield className="text-purple-400 w-5 h-5" />
|
|
<div>
|
|
<p className="text-slate-400 text-xs">Bayesian Coupling</p>
|
|
<p className="font-mono text-sm font-bold text-purple-400">
|
|
Prior Rebound: {(priorProbability * 100).toFixed(0)}%
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* SECTION 1: Dual-Query Actions */}
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
|
|
<div className="p-4 rounded-xl border border-slate-800 bg-slate-950/30 flex flex-col justify-between gap-4">
|
|
<div className="space-y-1">
|
|
<h4 className="text-sm font-semibold text-slate-300">Global Flow Outlier Scan</h4>
|
|
<p className="text-xs text-slate-400">Filters the market for statistically significant purchase volumes (Z-Score > 2.0) and concerted buying (insider clusters).</p>
|
|
</div>
|
|
<button
|
|
onClick={handleGlobalFlowScan}
|
|
disabled={scanning}
|
|
className="w-full bg-gradient-to-r from-purple-500 to-indigo-500 hover:from-purple-600 hover:to-indigo-600 disabled:from-purple-900 text-white font-bold py-2.5 px-4 rounded-xl transition-all shadow-lg shadow-purple-500/10 flex items-center justify-center gap-2"
|
|
>
|
|
<Radio className={`w-4 h-4 ${scanning ? 'animate-pulse' : ''}`} />
|
|
<span>{scanning ? 'Scanning Transactions...' : 'Start Global Flow Scan'}</span>
|
|
</button>
|
|
</div>
|
|
|
|
<div className="p-4 rounded-xl border border-slate-800 bg-slate-950/30 flex flex-col justify-between gap-4">
|
|
<div className="space-y-1">
|
|
<h4 className="text-sm font-semibold text-slate-300">Ticker Lookup (Single Evaluation)</h4>
|
|
<p className="text-xs text-slate-400">Targeted query of insider Z-scores and Bayesian coupling updates (e.g., PLTR, RACE, AMZN, AAPL).</p>
|
|
</div>
|
|
<form onSubmit={handleTickerLookup} className="flex gap-2">
|
|
<input
|
|
type="text"
|
|
required
|
|
placeholder="e.g. PLTR"
|
|
className="bg-slate-950 border border-slate-800 rounded-lg p-2.5 flex-1 text-slate-100 font-mono text-sm uppercase focus:outline-none focus:border-purple-500"
|
|
value={searchQuery}
|
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
/>
|
|
<button
|
|
type="submit"
|
|
className="bg-slate-800 hover:bg-slate-700 text-purple-400 hover:text-purple-300 font-bold px-4 py-2 border border-slate-700 rounded-lg transition-colors text-sm"
|
|
>
|
|
Lookup
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Ticker Stats / Lookup Section */}
|
|
{selectedTicker && tickerStats && (
|
|
<div className="p-5 rounded-2xl border border-purple-500/30 bg-purple-500/5 grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6 animate-fade-in">
|
|
<div className="space-y-3">
|
|
<div className="flex justify-between items-center border-b border-slate-800 pb-2">
|
|
<h3 className="font-mono font-bold text-lg text-slate-100">{selectedTicker} Single Evaluation</h3>
|
|
{tickerStats.isAnomaly && <span className="px-2 py-0.5 rounded bg-purple-500/20 text-purple-300 text-[10px] font-bold border border-purple-500/30 animate-pulse">FLOW OUTLIER</span>}
|
|
</div>
|
|
|
|
<div className="space-y-2 text-xs">
|
|
<div className="flex justify-between font-mono">
|
|
<span className="text-slate-400">Volumetric Z-Score:</span>
|
|
<span className={`font-bold ${tickerStats.isAnomaly ? 'text-purple-400' : 'text-slate-300'}`}>
|
|
Z = {tickerStats.zScore.toFixed(2)}
|
|
</span>
|
|
</div>
|
|
<div className="flex justify-between font-mono">
|
|
<span className="text-slate-400">Concerted Cluster (14 Days):</span>
|
|
<span className={`font-bold ${tickerStats.isCluster ? 'text-emerald-400' : 'text-slate-400'}`}>
|
|
{tickerStats.isCluster ? `YES (${tickerStats.clusterCount} Insiders)` : `NO (${tickerStats.clusterCount})`}
|
|
</span>
|
|
</div>
|
|
{tickerStats.isCluster && (
|
|
<div className="flex justify-between font-mono text-emerald-400">
|
|
<span>Cluster Exponent Multiplier:</span>
|
|
<span>x{tickerStats.multiplier.toFixed(2)}</span>
|
|
</div>
|
|
)}
|
|
<div className="flex justify-between font-mono border-t border-slate-800/80 pt-2 text-sm">
|
|
<span className="text-slate-300">Coupled Rebound Prob.:</span>
|
|
<span className="text-purple-400 font-bold flex items-center gap-1">
|
|
<Percent className="w-3.5 h-3.5" />
|
|
{(tickerStats.coupledRebound * 100).toFixed(0)}%
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Volume chart */}
|
|
<div className="lg:col-span-2 h-44 w-full">
|
|
<div className="text-[10px] text-slate-400 mb-1 text-center font-mono font-semibold">Insider Trading Volume (24-Month Baseline)</div>
|
|
<ResponsiveContainer width="100%" height="90%">
|
|
<BarChart data={volumeChartData}>
|
|
<CartesianGrid strokeDasharray="3 3" stroke="#1e293b" />
|
|
<XAxis dataKey="month" stroke="#64748b" fontSize={9} />
|
|
<YAxis stroke="#64748b" fontSize={9} />
|
|
<Tooltip contentStyle={{ backgroundColor: '#0f172a', borderColor: '#334155', borderRadius: '8px' }} />
|
|
<Bar dataKey="Volume (Shares)" fill="#8b5cf6" radius={[3, 3, 0, 0]} />
|
|
</BarChart>
|
|
</ResponsiveContainer>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Global Scan Outlier List */}
|
|
{scanResults && (
|
|
<div className="p-5 rounded-2xl border border-slate-800 bg-slate-950/20 mb-6 space-y-3 animate-fade-in">
|
|
<h3 className="text-sm font-bold text-slate-200 uppercase tracking-wider">Global Flow Scan Results</h3>
|
|
{scanResults.length === 0 ? (
|
|
<div className="p-6 text-center border border-dashed border-slate-800 rounded-xl text-slate-400 bg-slate-900/10">
|
|
<Radio className="w-8 h-8 text-purple-500/50 mx-auto mb-2 animate-pulse" />
|
|
<p className="text-xs font-semibold text-slate-300">No Significant Volume Anomalies Found</p>
|
|
<p className="text-[10px] text-slate-500 mt-1">No transactions with a calculated volumetric Z-Score > 2.0 were identified in the active live feeds.</p>
|
|
</div>
|
|
) : (
|
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
{scanResults.map((res) => (
|
|
<div
|
|
key={res.ticker}
|
|
onClick={() => setSelectedTicker(res.ticker)}
|
|
className="p-3 rounded-xl border cursor-pointer hover:bg-slate-900/60 transition-colors border-purple-500/40 bg-purple-500/5"
|
|
>
|
|
<div className="flex justify-between items-center">
|
|
<span className="font-mono font-bold text-slate-200">{res.ticker}</span>
|
|
<span className="w-2.5 h-2.5 rounded-full bg-purple-500 animate-pulse" />
|
|
</div>
|
|
<div className="text-[10px] text-slate-400 mt-2 space-y-1">
|
|
<div>Z-Score: <span className="font-mono text-slate-300 font-bold">{res.zScore}</span></div>
|
|
<div>Cluster: <span className="font-mono text-slate-300">{res.clusterCount} Traders</span></div>
|
|
<div>Rebound: <span className="font-mono text-purple-400 font-bold">{Math.round(res.coupledRebound * 100)}%</span></div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{/* SECTION 2: Smart Money Segment Tabs */}
|
|
<div className="space-y-4">
|
|
<div className="flex bg-slate-950/80 p-1 rounded-xl border border-slate-800/80 max-w-md">
|
|
<button
|
|
onClick={() => setActiveSegment('executives')}
|
|
className={`flex-1 py-2 rounded-lg text-xs font-semibold font-sans transition-all flex items-center justify-center gap-1.5 ${activeSegment === 'executives' ? 'bg-purple-500 text-white font-bold' : 'text-slate-400 hover:text-slate-200'}`}
|
|
>
|
|
<User className="w-3.5 h-3.5" /> Executives (Form 4)
|
|
</button>
|
|
<button
|
|
onClick={() => setActiveSegment('congress')}
|
|
className={`flex-1 py-2 rounded-lg text-xs font-semibold font-sans transition-all flex items-center justify-center gap-1.5 ${activeSegment === 'congress' ? 'bg-purple-500 text-white font-bold' : 'text-slate-400 hover:text-slate-200'}`}
|
|
>
|
|
<Landmark className="w-3.5 h-3.5" /> Congress (Stock Act)
|
|
</button>
|
|
<button
|
|
onClick={() => setActiveSegment('whales')}
|
|
className={`flex-1 py-2 rounded-lg text-xs font-semibold font-sans transition-all flex items-center justify-center gap-1.5 ${activeSegment === 'whales' ? 'bg-purple-500 text-white font-bold' : 'text-slate-400 hover:text-slate-200'}`}
|
|
>
|
|
<Building2 className="w-3.5 h-3.5" /> Whales (13F Filings)
|
|
</button>
|
|
</div>
|
|
|
|
{errorMsg && (
|
|
<div className="p-4 rounded-xl border border-red-550/30 bg-red-550/10 text-red-400 text-xs flex items-center gap-3 mb-4 animate-fade-in">
|
|
<AlertTriangle className="w-5 h-5 shrink-0 animate-pulse" />
|
|
<div>
|
|
<span className="font-bold">Data Load Error:</span> {errorMsg}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Ledger displays */}
|
|
<div className="border border-slate-800 rounded-xl overflow-hidden bg-slate-950/40">
|
|
{activeSegment === 'executives' && (
|
|
<table className="w-full border-collapse text-left text-xs">
|
|
<thead>
|
|
<tr className="border-b border-slate-800 text-slate-400 font-semibold bg-slate-900/40">
|
|
<th className="p-3">Ticker</th>
|
|
<th className="p-3">Insider Name</th>
|
|
<th className="p-3">Position</th>
|
|
<th className="p-3">Transaction</th>
|
|
<th className="p-3 font-mono">Shares</th>
|
|
<th className="p-3 text-right">Value ($)</th>
|
|
<th className="p-3 font-mono text-center">Volumetric Z-Score</th>
|
|
<th className="p-3 font-mono text-center">P(R|Z)</th>
|
|
<th className="p-3">Strategic Assessment</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{loading && (
|
|
<tr>
|
|
<td colSpan={9} className="p-8 text-center text-slate-400">
|
|
<div className="flex items-center justify-center gap-2">
|
|
<div className="w-4 h-4 border-2 border-purple-500 border-t-transparent rounded-full animate-spin" />
|
|
<span>Loading live insider transactions (Form 4)...</span>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
)}
|
|
{!loading && insiderTrades.length === 0 && (
|
|
<tr>
|
|
<td colSpan={9} className="p-8 text-center text-slate-500">
|
|
No insider transactions loaded.
|
|
</td>
|
|
</tr>
|
|
)}
|
|
{!loading && insiderTrades.map((t) => {
|
|
const isBuy = t.type === 'BUY';
|
|
const { zScore, coupledRebound } = calculateRowMetrics(
|
|
t.ticker,
|
|
t.shares,
|
|
insiderVolumes,
|
|
priorProbability
|
|
);
|
|
return (
|
|
<tr key={t.id} className="border-b border-slate-850/50 hover:bg-slate-850/10 transition-colors">
|
|
<td className="p-3 font-bold font-mono text-purple-400">{t.ticker}</td>
|
|
<td className="p-3 text-slate-200 font-semibold">{t.insiderName}</td>
|
|
<td className="p-3 text-slate-400">{t.relation}</td>
|
|
<td className="p-3">
|
|
<span className={`px-1.5 py-0.5 rounded text-[9px] font-bold ${isBuy ? 'bg-emerald-500/10 text-emerald-400 border border-emerald-500/20' : 'bg-rose-500/10 text-rose-400 border border-rose-500/20'}`}>
|
|
{isBuy ? 'BUY' : 'SELL'}
|
|
</span>
|
|
</td>
|
|
<td className="p-3 font-mono text-slate-300">{t.shares.toLocaleString()}</td>
|
|
<td className={`p-3 font-mono text-right font-bold ${isBuy ? 'text-emerald-400' : 'text-rose-400'}`}>
|
|
${t.value.toLocaleString()}
|
|
</td>
|
|
<td className={`p-3 font-mono text-center font-bold ${zScore >= 2.0 ? 'text-purple-400' : 'text-slate-350'}`}>{zScore}</td>
|
|
<td className="p-3 font-mono text-center text-purple-400 font-bold">{(coupledRebound * 100).toFixed(0)}%</td>
|
|
<td className="p-3 text-slate-350 whitespace-normal break-words">{t.insight || 'Opportunistic Diversification'}</td>
|
|
</tr>
|
|
);
|
|
})}
|
|
</tbody>
|
|
</table>
|
|
)}
|
|
|
|
{activeSegment === 'congress' && (
|
|
<div className="space-y-4">
|
|
<div className="p-3 bg-amber-500/10 border-b border-slate-800 text-xs text-amber-400 flex items-start gap-2.5">
|
|
<AlertTriangle className="w-5 h-5 shrink-0" />
|
|
<div>
|
|
<span className="font-bold">U.S. Congress Stock Act Disclosure Lags:</span> Representatives must disclose transactions within 30 to 45 days. The Alpha Lag in the table visualizes this reporting delay. Transactions may be priced in with a delay.
|
|
</div>
|
|
</div>
|
|
<table className="w-full border-collapse text-left text-xs">
|
|
<thead>
|
|
<tr className="border-b border-slate-800 text-slate-400 font-semibold bg-slate-900/40">
|
|
<th className="p-3">Ticker</th>
|
|
<th className="p-3">Representative</th>
|
|
<th className="p-3">Chamber</th>
|
|
<th className="p-3">Transaction</th>
|
|
<th className="p-3">Volume Range</th>
|
|
<th className="p-3 font-mono">Transaction Date</th>
|
|
<th className="p-3 font-mono">Filing Date</th>
|
|
<th className="p-3 text-right">Alpha Lag</th>
|
|
<th className="p-3 font-mono text-center">Volumetric Z-Score</th>
|
|
<th className="p-3 font-mono text-center">P(R|Z)</th>
|
|
<th className="p-3">Strategic Assessment</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{loading && (
|
|
<tr>
|
|
<td colSpan={11} className="p-8 text-center text-slate-400">
|
|
<div className="flex items-center justify-center gap-2">
|
|
<div className="w-4 h-4 border-2 border-purple-500 border-t-transparent rounded-full animate-spin" />
|
|
<span>Loading US Congress transactions...</span>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
)}
|
|
{!loading && congressTrades.length === 0 && (
|
|
<tr>
|
|
<td colSpan={11} className="p-8 text-center text-slate-500">
|
|
No Congress transactions loaded.
|
|
</td>
|
|
</tr>
|
|
)}
|
|
{!loading && congressTrades.map((c) => {
|
|
const isBuy = c.type === 'BUY';
|
|
const estShares = estimateCongressShares(c.valueRange);
|
|
const { zScore, coupledRebound } = calculateRowMetrics(
|
|
c.ticker,
|
|
estShares,
|
|
insiderVolumes,
|
|
priorProbability
|
|
);
|
|
return (
|
|
<tr key={c.id} className="border-b border-slate-850/50 hover:bg-slate-850/10 transition-colors">
|
|
<td className="p-3 font-bold font-mono text-purple-400">{c.ticker}</td>
|
|
<td className="p-3 text-slate-200 font-semibold">{c.representative}</td>
|
|
<td className="p-3 text-slate-400">{c.chamber}</td>
|
|
<td className="p-3">
|
|
<span className={`px-1.5 py-0.5 rounded text-[9px] font-bold ${isBuy ? 'bg-emerald-500/10 text-emerald-400 border border-emerald-500/20' : 'bg-rose-500/10 text-rose-400 border border-rose-500/20'}`}>
|
|
{isBuy ? 'BUY' : 'SELL'}
|
|
</span>
|
|
</td>
|
|
<td className="p-3 text-slate-300 font-mono">{c.valueRange}</td>
|
|
<td className="p-3 font-mono text-slate-400">{c.transactionDate}</td>
|
|
<td className="p-3 font-mono text-slate-400">{c.filingDate}</td>
|
|
<td className="p-3 font-mono text-right text-amber-400 font-bold">{c.lagDays} Days</td>
|
|
<td className={`p-3 font-mono text-center font-bold ${zScore >= 2.0 ? 'text-purple-400' : 'text-slate-350'}`}>{zScore}</td>
|
|
<td className="p-3 font-mono text-center text-purple-400 font-bold">{(coupledRebound * 100).toFixed(0)}%</td>
|
|
<td className="p-3 text-slate-350 whitespace-normal break-words">{c.insight || 'Opportunistic Diversification'}</td>
|
|
</tr>
|
|
);
|
|
})}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
)}
|
|
|
|
{activeSegment === 'whales' && (
|
|
<table className="w-full border-collapse text-left text-xs">
|
|
<thead>
|
|
<tr className="border-b border-slate-800 text-slate-400 font-semibold bg-slate-900/40">
|
|
<th className="p-3">Ticker</th>
|
|
<th className="p-3">Institution (13F Filers)</th>
|
|
<th className="p-3">Type</th>
|
|
<th className="p-3 font-mono">Shares Traded</th>
|
|
<th className="p-3 font-mono">Shares Held</th>
|
|
<th className="p-3 font-mono">Filing Date</th>
|
|
<th className="p-3 text-right">Estimated Value ($)</th>
|
|
<th className="p-3 font-mono text-center">Volumetric Z-Score</th>
|
|
<th className="p-3 font-mono text-center">P(R|Z)</th>
|
|
<th className="p-3">Strategic Assessment</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{loading && (
|
|
<tr>
|
|
<td colSpan={10} className="p-8 text-center text-slate-400">
|
|
<div className="flex items-center justify-center gap-2">
|
|
<div className="w-4 h-4 border-2 border-purple-500 border-t-transparent rounded-full animate-spin" />
|
|
<span>Loading 13F whale transactions...</span>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
)}
|
|
{!loading && whaleTrades.length === 0 && (
|
|
<tr>
|
|
<td colSpan={10} className="p-8 text-center text-slate-500">
|
|
No institutional transactions loaded.
|
|
</td>
|
|
</tr>
|
|
)}
|
|
{!loading && whaleTrades.map((w) => {
|
|
const isBuy = w.type === 'BUY' || w.type === 'NEW';
|
|
const { zScore, coupledRebound } = calculateRowMetrics(
|
|
w.ticker,
|
|
w.sharesTraded,
|
|
insiderVolumes,
|
|
priorProbability
|
|
);
|
|
return (
|
|
<tr key={w.id} className="border-b border-slate-850/50 hover:bg-slate-850/10 transition-colors">
|
|
<td className="p-3 font-bold font-mono text-purple-400">{w.ticker}</td>
|
|
<td className="p-3 text-slate-200 font-semibold">{w.institution}</td>
|
|
<td className="p-3">
|
|
<span className={`px-1.5 py-0.5 rounded text-[9px] font-bold ${isBuy ? 'bg-emerald-500/10 text-emerald-400 border border-emerald-500/20' : 'bg-rose-500/10 text-rose-400 border border-rose-500/20'}`}>
|
|
{w.type}
|
|
</span>
|
|
</td>
|
|
<td className="p-3 font-mono text-slate-300">{w.sharesTraded.toLocaleString()}</td>
|
|
<td className="p-3 font-mono text-slate-400">{w.sharesHeld.toLocaleString()}</td>
|
|
<td className="p-3 font-mono text-slate-400">{w.filingDate}</td>
|
|
<td className={`p-3 font-mono text-right font-bold ${isBuy ? 'text-emerald-400' : 'text-rose-400'}`}>
|
|
${w.estimatedValue.toLocaleString()}
|
|
</td>
|
|
<td className={`p-3 font-mono text-center font-bold ${zScore >= 2.0 ? 'text-purple-400' : 'text-slate-350'}`}>{zScore}</td>
|
|
<td className="p-3 font-mono text-center text-purple-400 font-bold">{(coupledRebound * 100).toFixed(0)}%</td>
|
|
<td className="p-3 text-slate-350 whitespace-normal break-words">{w.insight || 'Opportunistic Rebalancing'}</td>
|
|
</tr>
|
|
);
|
|
})}
|
|
</tbody>
|
|
</table>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* SECTION 3: Mathematical LaTeX Accordion */}
|
|
<div className="border-t border-slate-850 pt-4 mt-6">
|
|
<button
|
|
onClick={() => setShowMathAccordion(!showMathAccordion)}
|
|
className="flex items-center gap-1.5 text-xs text-slate-400 hover:text-purple-400 transition-colors focus:outline-none"
|
|
>
|
|
<span>{showMathAccordion ? <ChevronUp className="w-4 h-4" /> : <ChevronDown className="w-4 h-4" />}</span>
|
|
<span className="font-semibold uppercase tracking-wider">Mathematical Formulation (Z-Score & Bayesian Coupling)</span>
|
|
</button>
|
|
|
|
{showMathAccordion && (
|
|
<div className="mt-4 p-4 rounded-xl border border-slate-850 bg-slate-950/40 text-xs text-slate-300 space-y-4">
|
|
<div>
|
|
<h4 className="font-bold text-purple-400 mb-1">1. Volumetric Z-Score (Statistical Significance)</h4>
|
|
<p className="mb-2">
|
|
The Z-Score indicates how many standard deviations the current transaction volume <InlineMath math="X_t" /> deviates from the historical average <InlineMath math="\\mu" />:
|
|
</p>
|
|
<div className="py-2 overflow-x-auto text-slate-200">
|
|
<BlockMath math="Z = \\frac{X_t - \\mu}{\\sigma}" />
|
|
</div>
|
|
<p className="text-slate-400">
|
|
A Z-Score > 2.0 is classified as an outlier (anomaly trigger), corresponding to a probability of less than 2.27% for a random spike (one-sided test under a normal distribution).
|
|
</p>
|
|
</div>
|
|
|
|
<div className="border-t border-slate-900 pt-3">
|
|
<h4 className="font-bold text-purple-400 mb-1">2. Bayesian Coupling (Rebound Probability)</h4>
|
|
<p className="mb-2">
|
|
We couple price drop sentiment (Element 2) with insider Z-Scores (Element 3) to determine the posterior probability of a true rebound:
|
|
</p>
|
|
<div className="py-2 overflow-x-auto text-slate-200">
|
|
<BlockMath math="P(R|Z) = \\frac{P(Z|R) \\cdot P(R)}{P(Z|R) \\cdot P(R) + P(Z|\\neg R) \\cdot (1 - P(R))}" />
|
|
</div>
|
|
<p className="text-slate-400">
|
|
where <InlineMath math="P(R)" /> is the prior probability. When the Z-Score is high (buying), the likelihood <InlineMath math="P(Z|R)" /> increases significantly, maximizing the rebound expectation.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<InsiderMathModal isOpen={isMathModalOpen} onClose={() => setIsMathModalOpen(false)} />
|
|
</div>
|
|
);
|
|
}
|