← back to Bertha
kalshi-dash/src/pages/Orbit.jsx
699 lines
import React, { useState, useEffect, useMemo, useRef } from 'react';
import { api } from '../api';
// ── Orbit Ring Configuration ──
const ORBIT_RINGS = [
{ radius: 80, label: 'Core', color: '#00F0FF', opacity: 0.5, description: 'Highest confidence' },
{ radius: 160, label: 'Inner', color: '#00C389', opacity: 0.35, description: 'Strong signals' },
{ radius: 240, label: 'Mid', color: '#3B82F6', opacity: 0.25, description: 'Moderate signals' },
{ radius: 320, label: 'Outer', color: '#A855F7', opacity: 0.18, description: 'Weak signals' },
{ radius: 400, label: 'Edge', color: '#FF5C5C', opacity: 0.12, description: 'Speculative' },
];
const DIRECTION_COLORS = {
BUY_YES: { fill: '#00C389', stroke: '#009E6F', glow: 'rgba(0,195,137,0.5)' },
BUY_NO: { fill: '#FF5C5C', stroke: '#CC4A4A', glow: 'rgba(255,92,92,0.5)' },
YES: { fill: '#00C389', stroke: '#009E6F', glow: 'rgba(0,195,137,0.5)' },
NO: { fill: '#FF5C5C', stroke: '#CC4A4A', glow: 'rgba(255,92,92,0.5)' },
};
function getSignalColor(confidence) {
if (confidence >= 0.9) return '#00C389';
if (confidence >= 0.7) return '#F7931A';
if (confidence >= 0.5) return '#3B82F6';
if (confidence >= 0.3) return '#A855F7';
return '#FF5C5C';
}
function getOrbitRing(confidence) {
if (confidence >= 0.9) return 0;
if (confidence >= 0.75) return 1;
if (confidence >= 0.6) return 2;
if (confidence >= 0.4) return 3;
return 4;
}
// ── Animated Star Field Background ──
function StarField({ width, height }) {
const stars = useMemo(() => {
const s = [];
for (let i = 0; i < 80; i++) {
s.push({
x: Math.random() * width,
y: Math.random() * height,
r: Math.random() * 1.2 + 0.3,
opacity: Math.random() * 0.4 + 0.05,
delay: Math.random() * 3,
});
}
return s;
}, [width, height]);
return (
<>
{stars.map((star, i) => (
<circle
key={i}
cx={star.x}
cy={star.y}
r={star.r}
fill="white"
opacity={star.opacity}
className="animate-pulse"
style={{ animationDelay: `${star.delay}s`, animationDuration: `${2 + Math.random() * 3}s` }}
/>
))}
</>
);
}
// ── Planet/Market Node ──
function MarketNode({ market, cx, cy, size, isSelected, onSelect, animate }) {
const dirColors = DIRECTION_COLORS[market.direction] || DIRECTION_COLORS.BUY_YES;
const confidence = parseFloat(market.confidence) || 0;
const signalColor = getSignalColor(confidence);
const pulseSize = size + 4;
const gradId = `gradient-${market.ticker?.replace(/[^a-zA-Z0-9]/g, '')}`;
return (
<g
onClick={() => onSelect(market)}
className="cursor-pointer"
style={{ transition: 'all 0.5s ease-out' }}
>
{/* Glow pulse ring */}
<circle
cx={cx}
cy={cy}
r={pulseSize}
fill="none"
stroke={dirColors.glow}
strokeWidth={1.5}
opacity={isSelected ? 0.9 : 0.3}
className={animate ? 'animate-ping' : ''}
style={{ animationDuration: '2s', transformOrigin: `${cx}px ${cy}px` }}
/>
{/* Signal strength dashed ring */}
<circle
cx={cx}
cy={cy}
r={size + 2}
fill="none"
stroke={signalColor}
strokeWidth={2}
opacity={0.65}
strokeDasharray={`${confidence * 20} ${(1 - confidence) * 20}`}
/>
{/* Main planet */}
<circle
cx={cx}
cy={cy}
r={size}
fill={`url(#${gradId})`}
stroke={isSelected ? '#00F0FF' : dirColors.stroke}
strokeWidth={isSelected ? 2.5 : 1}
className="transition-all duration-300"
/>
{/* Gradient definition */}
<defs>
<radialGradient id={gradId}>
<stop offset="0%" stopColor={dirColors.fill} stopOpacity="0.9" />
<stop offset="70%" stopColor={dirColors.fill} stopOpacity="0.6" />
<stop offset="100%" stopColor={dirColors.stroke} stopOpacity="0.4" />
</radialGradient>
</defs>
{/* Edge % label */}
{size >= 8 && (
<text
x={cx}
y={cy + 1}
textAnchor="middle"
dominantBaseline="middle"
fill="white"
fontSize={Math.max(7, size * 0.6)}
fontWeight="bold"
fontFamily="'JetBrains Mono', monospace"
style={{ pointerEvents: 'none' }}
>
{market.edge_display || ''}
</text>
)}
{/* Ticker label */}
{size >= 10 && (
<text
x={cx}
y={cy + size + 12}
textAnchor="middle"
fill="#A8A8A8"
fontSize={8}
fontFamily="'JetBrains Mono', monospace"
style={{ pointerEvents: 'none' }}
>
{(market.shortTitle || '').substring(0, 18)}
</text>
)}
</g>
);
}
// ── Orbit Detail Panel ──
function OrbitDetail({ market, onClose }) {
if (!market) return null;
const conf = parseFloat(market.confidence) || 0;
const edge = parseFloat(market.expected_edge) || 0;
const dirColors = DIRECTION_COLORS[market.direction] || DIRECTION_COLORS.BUY_YES;
const ringLabel = ORBIT_RINGS[getOrbitRing(conf)]?.label || 'Edge';
return (
<div className="card-glow fade-in">
<div className="flex items-start justify-between mb-4">
<div className="flex-1 min-w-0 mr-4">
<div className="flex items-center gap-2 mb-2">
<span className="inline-block w-3 h-3 rounded-full" style={{ backgroundColor: dirColors.fill }} />
<span
className="badge"
style={
market.direction === 'BUY_YES' || market.direction === 'YES'
? { background: 'var(--green-dim)', color: 'var(--green)' }
: { background: 'var(--red-dim)', color: 'var(--red)' }
}
>
{market.direction}
</span>
{market.risk_approved ? (
<span className="badge bg-green">APPROVED</span>
) : (
<span className="badge bg-red">BLOCKED</span>
)}
</div>
<h3 className="heading text-sm font-bold" style={{ color: 'var(--text)' }}>
{market.title || market.ticker}
</h3>
<p className="mono text-xs mt-1" style={{ color: 'var(--text-muted)' }}>{market.ticker}</p>
</div>
<button onClick={onClose} className="btn btn-ghost text-lg leading-none" style={{ padding: '0 6px' }}>
×
</button>
</div>
<div className="grid grid-cols-3 gap-3 mb-4">
{[
{ label: 'Edge', value: `${(edge * 100).toFixed(1)}%`, color: 'var(--orange)' },
{ label: 'Confidence', value: `${(conf * 100).toFixed(0)}%`, color: getSignalColor(conf) },
{ label: 'Orbit Ring', value: ringLabel, color: 'var(--purple)' },
].map(s => (
<div
key={s.label}
className="rounded-lg p-2.5 text-center"
style={{ background: 'var(--surface-el)', border: '1px solid var(--border)' }}
>
<p className="stat-label mb-1">{s.label}</p>
<p className="stat-v text-lg" style={{ color: s.color }}>{s.value}</p>
</div>
))}
</div>
{market.reasoning && (
<div
className="text-[11px] rounded-lg px-3 py-2 mb-3 leading-relaxed"
style={{
color: 'var(--text-dim)',
background: 'var(--surface-el)',
border: '1px solid var(--border)',
}}
>
{market.reasoning}
</div>
)}
{market.sources && market.sources.length > 0 && (
<div className="space-y-1.5">
<p className="stat-label mb-1">Signal Sources</p>
{market.sources.map((src, i) => (
<div
key={i}
className="flex items-center gap-2 text-xs rounded-lg px-3 py-2"
style={{ background: 'var(--surface-el)', border: '1px solid var(--border)' }}
>
<span className="badge bg-blue">{src.source}</span>
<span className="truncate flex-1" style={{ color: 'var(--text-dim)' }}>{src.headline}</span>
<span
className="mono text-[10px]"
style={{ color: src.sentiment > 0 ? 'var(--green)' : src.sentiment < 0 ? 'var(--red)' : 'var(--text-muted)' }}
>
{src.sentiment > 0 ? '+' : ''}{src.sentiment?.toFixed(2)}
</span>
</div>
))}
</div>
)}
<div className="mt-3 text-[10px]" style={{ color: 'var(--text-muted)' }}>
Signal created:{' '}
{market.created_at
? new Date(market.created_at).toLocaleString('en-US', {
timeZone: 'America/Los_Angeles',
hour: '2-digit', minute: '2-digit', month: 'short', day: 'numeric',
})
: 'Unknown'}
</div>
</div>
);
}
// ── Main Orbit Visualization ──
export default function Orbit() {
const [data, setData] = useState(null);
const [signals, setSignals] = useState(null);
const [loading, setLoading] = useState(true);
const [selectedTicker, setSelectedTicker] = useState(null);
const [filter, setFilter] = useState('all'); // 'all' | 'approved' | 'yes' | 'no'
const [animating, setAnimating] = useState(true);
const svgRef = useRef(null);
useEffect(() => {
load();
const i = setInterval(load, 30000);
return () => clearInterval(i);
}, []);
async function load() {
try {
const [d, s, sd] = await Promise.all([
api('/dashboard'),
api('/signals'),
api('/signals/dashboard'),
]);
setData(d);
setSignals({ signals: s?.signals || [], dashboard: sd });
} catch {}
setLoading(false);
}
const { orbitalMarkets, stats } = useMemo(() => {
if (!signals?.signals) return { orbitalMarkets: [], stats: {} };
let items = signals.signals.map(s => {
const conf = parseFloat(s.confidence) || 0;
const edge = parseFloat(s.expected_edge) || 0;
const ring = getOrbitRing(conf);
const parts = (s.ticker || '').split('-');
const shortTitle = parts[0]?.replace('KX', '').substring(0, 12) || s.ticker;
return {
...s,
shortTitle,
title: s.reasoning?.split('"')[1] || s.ticker,
ring,
conf,
edge,
edge_display: `${(edge * 100).toFixed(0)}%`,
size: Math.max(6, Math.min(22, edge * 120 + 6)),
};
});
// Apply filter
if (filter === 'approved') items = items.filter(i => i.risk_approved);
if (filter === 'yes') items = items.filter(i => i.direction === 'BUY_YES');
if (filter === 'no') items = items.filter(i => i.direction === 'BUY_NO');
// Limit to top 60 by edge
items.sort((a, b) => b.edge - a.edge);
items = items.slice(0, 60);
const approved = items.filter(i => i.risk_approved).length;
const buyYes = items.filter(i => i.direction === 'BUY_YES').length;
const buyNo = items.filter(i => i.direction === 'BUY_NO').length;
const avgConf = items.length > 0 ? items.reduce((s, i) => s + i.conf, 0) / items.length : 0;
const avgEdge = items.length > 0 ? items.reduce((s, i) => s + i.edge, 0) / items.length : 0;
return {
orbitalMarkets: items,
stats: { total: items.length, approved, buyYes, buyNo, avgConf, avgEdge },
};
}, [signals, filter]);
const selectedMarket = orbitalMarkets.find(m => m.ticker === selectedTicker);
if (loading) return (
<div className="flex items-center justify-center h-64">
<div
className="w-8 h-8 rounded-full border-2 border-t-transparent animate-spin"
style={{ borderColor: 'var(--cyan)', borderTopColor: 'transparent' }}
/>
</div>
);
const CX = 450;
const CY = 430;
const SVG_W = 900;
const SVG_H = 860;
// Position markets on orbit rings with angular distribution
const ringCounts = {};
orbitalMarkets.forEach(m => {
ringCounts[m.ring] = (ringCounts[m.ring] || 0) + 1;
});
const ringIndices = {};
const positionedMarkets = orbitalMarkets.map(m => {
const ring = ORBIT_RINGS[m.ring];
if (!ringIndices[m.ring]) ringIndices[m.ring] = 0;
const idx = ringIndices[m.ring]++;
const count = ringCounts[m.ring];
const angleStep = (2 * Math.PI) / count;
const angle = angleStep * idx + (m.ring * 0.4);
const jitter = (Math.sin(idx * 7.3) * 0.15);
const effectiveAngle = angle + jitter;
const radiusJitter = ring.radius + (Math.cos(idx * 3.7) * 15);
return {
...m,
cx: CX + radiusJitter * Math.cos(effectiveAngle),
cy: CY + radiusJitter * Math.sin(effectiveAngle),
};
});
const filterOptions = [
{ key: 'all', label: 'All' },
{ key: 'approved', label: 'Approved' },
{ key: 'yes', label: 'BUY YES' },
{ key: 'no', label: 'BUY NO' },
];
const summaryStats = [
{ label: 'Active Signals', value: stats.total || 0, color: 'var(--cyan)' },
{ label: 'Risk Approved', value: stats.approved || 0, color: 'var(--green)' },
{ label: 'BUY YES', value: stats.buyYes || 0, color: 'var(--green)' },
{ label: 'BUY NO', value: stats.buyNo || 0, color: 'var(--red)' },
{ label: 'Avg Confidence', value: `${((stats.avgConf || 0) * 100).toFixed(0)}%`, color: 'var(--blue)' },
{ label: 'Avg Edge', value: `${((stats.avgEdge || 0) * 100).toFixed(1)}%`, color: 'var(--purple)' },
];
return (
<div className="space-y-6">
{/* Page Header */}
<div className="flex items-center justify-between">
<div>
<h1 className="heading text-2xl font-bold flex items-center gap-2">
<span style={{ color: 'var(--cyan)' }}>◐</span>
<span className="gradient-cyan">Signal Orbit</span>
</h1>
<p className="section-subtitle mt-1">
Radial visualization — distance from center = confidence, size = edge strength
</p>
</div>
<div className="flex items-center gap-3">
{/* Filter toggle group */}
<div
className="flex items-center gap-0.5 rounded-lg p-0.5"
style={{ background: 'var(--surface-el)', border: '1px solid var(--border)' }}
>
{filterOptions.map(f => (
<button
key={f.key}
onClick={() => setFilter(f.key)}
className="btn btn-sm"
style={filter === f.key
? { background: 'var(--cyan)', color: 'var(--bg)', fontWeight: 700 }
: { background: 'transparent', color: 'var(--text-muted)' }
}
>
{f.label}
</button>
))}
</div>
<label className="flex items-center gap-2 text-xs cursor-pointer" style={{ color: 'var(--text-dim)' }}>
<input
type="checkbox"
checked={animating}
onChange={e => setAnimating(e.target.checked)}
className="rounded"
style={{ accentColor: 'var(--cyan)' }}
/>
Animate
</label>
<button onClick={load} className="btn btn-o btn-sm">Refresh</button>
</div>
</div>
{/* Stats Row */}
<div className="grid grid-cols-6 gap-3">
{summaryStats.map(s => (
<div key={s.label} className="card">
<p className="stat-label mb-1">{s.label}</p>
<p className="stat-v" style={{ color: s.color }}>{s.value}</p>
</div>
))}
</div>
{/* Orbit SVG Visualization */}
<div
className="card p-0 overflow-hidden relative"
style={{ background: 'radial-gradient(ellipse at center, #0D1B2E 0%, #080D14 60%, var(--bg) 100%)' }}
>
<svg
ref={svgRef}
viewBox={`0 0 ${SVG_W} ${SVG_H}`}
className="w-full"
style={{ maxHeight: '75vh' }}
>
{/* Star field */}
<StarField width={SVG_W} height={SVG_H} />
{/* Orbit rings */}
{ORBIT_RINGS.map((ring, i) => (
<g key={i}>
<circle
cx={CX}
cy={CY}
r={ring.radius}
fill="none"
stroke={ring.color}
strokeWidth={0.8}
opacity={ring.opacity}
strokeDasharray="4 4"
/>
<text
x={CX + ring.radius + 6}
y={CY - 4}
fill={ring.color}
fontSize={9}
opacity={ring.opacity + 0.2}
fontFamily="'JetBrains Mono', monospace"
>
{ring.label}
</text>
</g>
))}
{/* Center sun — cyan glow */}
<defs>
<radialGradient id="sun-gradient">
<stop offset="0%" stopColor="#00F0FF" stopOpacity="1" />
<stop offset="50%" stopColor="#00B8C8" stopOpacity="0.8" />
<stop offset="100%" stopColor="#006070" stopOpacity="0.4" />
</radialGradient>
<filter id="sun-glow">
<feGaussianBlur stdDeviation="10" result="blur" />
<feMerge>
<feMergeNode in="blur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
<circle cx={CX} cy={CY} r={40} fill="url(#sun-gradient)" filter="url(#sun-glow)" opacity={0.55} />
<circle cx={CX} cy={CY} r={25} fill="url(#sun-gradient)" />
{/* KEN label in center */}
<text
x={CX} y={CY - 4}
textAnchor="middle"
fill="#0E0E10"
fontSize={12}
fontWeight="bold"
fontFamily="'JetBrains Mono', monospace"
>
KEN
</text>
<text
x={CX} y={CY + 10}
textAnchor="middle"
fill="#0E0E10"
fontSize={7}
fontFamily="'JetBrains Mono', monospace"
opacity={0.7}
>
TRADER
</text>
{/* Connection lines from markets to center */}
{positionedMarkets.map(m => {
const dirColors = DIRECTION_COLORS[m.direction] || DIRECTION_COLORS.BUY_YES;
return (
<line
key={`line-${m.ticker}`}
x1={CX}
y1={CY}
x2={m.cx}
y2={m.cy}
stroke={dirColors.fill}
strokeWidth={0.4}
opacity={selectedTicker === m.ticker ? 0.6 : 0.1}
/>
);
})}
{/* Market nodes */}
{positionedMarkets.map(m => (
<MarketNode
key={m.ticker}
market={m}
cx={m.cx}
cy={m.cy}
size={m.size}
isSelected={selectedTicker === m.ticker}
onSelect={market => setSelectedTicker(market.ticker === selectedTicker ? null : market.ticker)}
animate={animating && m.risk_approved}
/>
))}
</svg>
{/* Bottom legend overlay */}
<div
className="absolute bottom-3 left-3 rounded-lg p-3"
style={{
background: 'rgba(14,14,16,0.85)',
backdropFilter: 'blur(8px)',
border: '1px solid var(--border)',
}}
>
<div className="flex items-center gap-4 text-[10px]">
<div className="flex items-center gap-1.5">
<div className="w-3 h-3 rounded-full" style={{ backgroundColor: 'var(--green)' }} />
<span style={{ color: 'var(--text-dim)' }}>BUY YES</span>
</div>
<div className="flex items-center gap-1.5">
<div className="w-3 h-3 rounded-full" style={{ backgroundColor: 'var(--red)' }} />
<span style={{ color: 'var(--text-dim)' }}>BUY NO</span>
</div>
<span style={{ color: 'var(--border-light)' }}>|</span>
<span style={{ color: 'var(--text-muted)' }}>Closer to center = higher confidence</span>
<span style={{ color: 'var(--text-muted)' }}>Larger = bigger edge</span>
</div>
</div>
{/* Top-right ring legend */}
<div
className="absolute top-3 right-3 rounded-lg p-2.5"
style={{
background: 'rgba(14,14,16,0.85)',
backdropFilter: 'blur(8px)',
border: '1px solid var(--border)',
}}
>
<p className="stat-label mb-1.5">Orbit Rings</p>
<div className="space-y-1">
{ORBIT_RINGS.map((ring, i) => (
<div key={i} className="flex items-center gap-2 text-[10px]">
<div className="w-2 h-2 rounded-full" style={{ backgroundColor: ring.color, opacity: ring.opacity + 0.3 }} />
<span style={{ color: ring.color }}>{ring.label}</span>
<span style={{ color: 'var(--text-muted)' }}>{ring.description}</span>
</div>
))}
</div>
</div>
</div>
{/* Selected Market Detail */}
{selectedMarket && (
<OrbitDetail market={selectedMarket} onClose={() => setSelectedTicker(null)} />
)}
{/* Top Signals Table */}
<div className="card">
<div className="flex items-center gap-2 mb-4">
<h2 className="section-title">Orbital Signal Registry</h2>
<span className="text-xs" style={{ color: 'var(--text-muted)' }}>
{orbitalMarkets.length} signals in orbit
</span>
</div>
<div className="overflow-x-auto">
<table>
<thead>
<tr>
<th>Ring</th>
<th>Ticker</th>
<th>Direction</th>
<th>Edge</th>
<th>Confidence</th>
<th>Risk</th>
<th>Sources</th>
</tr>
</thead>
<tbody>
{orbitalMarkets.slice(0, 20).map(m => {
const ring = ORBIT_RINGS[m.ring];
return (
<tr
key={m.ticker}
onClick={() => setSelectedTicker(m.ticker === selectedTicker ? null : m.ticker)}
className="cursor-pointer"
style={selectedTicker === m.ticker ? { background: 'var(--cyan-glow)' } : {}}
>
<td>
<span className="text-xs font-bold heading" style={{ color: ring.color }}>
{ring.label}
</span>
</td>
<td className="mono text-xs" style={{ color: 'var(--text-dim)' }}>{m.ticker}</td>
<td>
<span
className="badge"
style={
m.direction === 'BUY_YES'
? { background: 'var(--green-dim)', color: 'var(--green)' }
: { background: 'var(--red-dim)', color: 'var(--red)' }
}
>
{m.direction}
</span>
</td>
<td className="mono font-bold text-sm" style={{ color: 'var(--orange)' }}>
{(m.edge * 100).toFixed(1)}%
</td>
<td className="mono text-sm" style={{ color: getSignalColor(m.conf) }}>
{(m.conf * 100).toFixed(0)}%
</td>
<td>
{m.risk_approved ? (
<span className="text-xs font-bold" style={{ color: 'var(--green)' }}>OK</span>
) : (
<span className="text-xs font-bold" style={{ color: 'var(--red)' }}>BLOCKED</span>
)}
</td>
<td className="text-xs" style={{ color: 'var(--text-muted)' }}>
{m.sources?.length || 0}
</td>
</tr>
);
})}
</tbody>
</table>
</div>
</div>
</div>
);
}