← back to Ken

kalshi-dash/src/pages/Positions.jsx

970 lines

import React, { useState, useEffect, useCallback } from 'react';
import { api, post } from '../api';
import MiniCandlestick, { genCandles } from '../components/MiniCandlestick';
import {
  BarChart, Bar, Cell, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer,
} from 'recharts';

// ── Helpers ──
const fmt = (cents) => `$${(cents / 100).toFixed(2)}`;
const fmtPnl = (cents) => {
  const val = cents / 100;
  const prefix = val >= 0 ? '+' : '';
  return `${prefix}$${val.toFixed(2)}`;
};
const pnlColor = (cents) => cents >= 0 ? 'text-green' : 'text-red';
const sideBadge = (dir) => dir === 'BUY_YES' ? 'bg-green' : 'bg-red';
const sideLabel = (dir) => dir === 'BUY_YES' ? 'YES' : 'NO';
const fmtCents = (cents) => `${cents}¢`;
const calcExitPrice = (entryPriceCents, pnlCents, contracts) => {
  if (!contracts || contracts === 0) return null;
  return Math.round(entryPriceCents + (pnlCents / contracts));
};
const kalshiLink = (ticker) =>
  `https://kalshi.com/search?query=${encodeURIComponent((ticker || '').split('-')[0])}`;
const timeAgo = (d) => {
  if (!d) return '';
  const ms = Date.now() - new Date(d).getTime();
  if (ms < 60000) return 'just now';
  if (ms < 3600000) return `${Math.floor(ms / 60000)}m ago`;
  if (ms < 86400000) return `${Math.floor(ms / 3600000)}h ago`;
  return `${Math.floor(ms / 86400000)}d ago`;
};

/* ── Chart 6: P&L Distribution Histogram ── */
function PnlDistributionChart({ positions }) {
  if (!positions || !positions.length) return null;

  /* Define buckets */
  const BUCKETS = [
    { label: '<-$50',    min: -Infinity,  max: -5000, color: '#FF5C5C' },
    { label: '-$20-50',  min: -5000,      max: -2000, color: '#FF7A7A' },
    { label: '-$1-20',   min: -2000,      max: -100,  color: '#FF9999' },
    { label: '$0',       min: -100,       max: 100,   color: '#6B7280' },
    { label: '$1-20',    min: 100,        max: 2000,  color: '#88DDBB' },
    { label: '$20-50',   min: 2000,       max: 5000,  color: '#44CC99' },
    { label: '>$50',     min: 5000,       max: Infinity, color: '#00C389' },
  ];

  const bucketed = BUCKETS.map(b => {
    const count = positions.filter(p => {
      const v = p.realized_pnl_cents || 0;
      return v > b.min && v <= b.max;
    }).length;
    return { ...b, count };
  }).filter(b => b.count > 0);

  if (!bucketed.length) return null;

  const CustomTooltip = ({ active, payload, label }) => {
    if (!active || !payload?.length) return null;
    const d = payload[0]?.payload || {};
    return (
      <div style={{
        background: '#1C1C24', border: '1px solid #2A2A35', borderRadius: 8,
        padding: '8px 12px', fontSize: 12, fontFamily: "'Inter', sans-serif",
      }}>
        <p style={{ color: '#E0E0E0', fontWeight: 700, marginBottom: 4 }}>{label}</p>
        <p style={{ color: d.color, fontFamily: "'JetBrains Mono', monospace", fontWeight: 700 }}>
          {d.count} position{d.count !== 1 ? 's' : ''}
        </p>
      </div>
    );
  };

  return (
    <div className="card">
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 14 }}>
        <h2 className="section-title" style={{ fontSize: '1.05rem' }}>P&L Distribution</h2>
        <span style={{ fontSize: 11, color: '#6B7280' }}>{positions.length} positions total</span>
      </div>
      <ResponsiveContainer width="100%" height={180}>
        <BarChart data={bucketed} margin={{ top: 4, right: 12, left: 0, bottom: 4 }}>
          <CartesianGrid
            strokeDasharray="3 3"
            stroke="#2A2A35"
            strokeOpacity={0.6}
            vertical={false}
          />
          <XAxis
            dataKey="label"
            stroke="#2A2A35"
            tick={{ fill: '#6B7280', fontSize: 10, fontFamily: "'Inter', sans-serif" }}
            tickLine={false}
            axisLine={{ stroke: '#2A2A35' }}
          />
          <YAxis
            stroke="#2A2A35"
            tick={{ fill: '#6B7280', fontSize: 11, fontFamily: "'Inter', sans-serif" }}
            tickLine={false}
            axisLine={false}
            allowDecimals={false}
            width={30}
          />
          <Tooltip content={<CustomTooltip />} cursor={{ fill: 'rgba(0,240,255,0.04)' }} />
          <Bar dataKey="count" radius={[4, 4, 0, 0]} maxBarSize={48}>
            {bucketed.map((entry, i) => (
              <Cell key={i} fill={entry.color} fillOpacity={0.85} />
            ))}
          </Bar>
        </BarChart>
      </ResponsiveContainer>
    </div>
  );
}

// ── Loading Spinner ──
function Spinner() {
  return (
    <div className="flex items-center justify-center h-64">
      <div
        className="w-9 h-9 rounded-full border-2 animate-spin"
        style={{ borderColor: 'var(--border)', borderTopColor: 'var(--cyan)' }}
      />
    </div>
  );
}

// ── Trade Sub-table ──
function TradeSubTable({ p, trades, loading }) {
  return (
    <div style={{ background: 'var(--bg)', borderTop: '1px solid var(--border)', borderBottom: '1px solid var(--border)' }} className="px-4 py-3">
      <div className="flex items-center justify-between mb-3">
        <span className="mono text-xs font-bold" style={{ color: 'var(--cyan)' }}>
          Trade History — {p.ticker}
        </span>
        <div className="flex gap-4 text-xs" style={{ color: 'var(--text-muted)' }}>
          <span>Open: <b style={{ color: 'var(--blue)' }}>{p.open_trades}</b></span>
          <span>Closed: <b style={{ color: 'var(--text-dim)' }}>{p.closed_trades}</b></span>
          <span>
            Entry Range:{' '}
            <b style={{ color: 'var(--orange)' }}>
              {p.min_entry}¢ – {p.max_entry}¢
            </b>
          </span>
          <span>First: <b>{new Date(p.first_trade_at).toLocaleDateString()}</b></span>
        </div>
      </div>
      {loading ? (
        <div className="flex justify-center py-4">
          <div className="w-5 h-5 rounded-full border-2 animate-spin" style={{ borderColor: 'var(--border)', borderTopColor: 'var(--cyan)' }} />
        </div>
      ) : trades.length > 0 ? (
        <div className="overflow-x-auto" style={{ maxHeight: 200, overflowY: 'auto' }}>
          <table className="text-xs">
            <thead>
              <tr>
                <th>Time</th>
                <th>Direction</th>
                <th>Contracts</th>
                <th>Entry</th>
                <th>Exit</th>
                <th>Cost</th>
                <th>Profit</th>
                <th>Status</th>
              </tr>
            </thead>
            <tbody>
              {trades.map((t, i) => {
                const entryC = parseInt(t.entry_price_cents) || 0;
                const pnlC = parseInt(t.pnl_cents) || 0;
                const ctrs = parseInt(t.contracts) || 0;
                const isClosed = t.status === 'closed' || t.status === 'expired';
                const exitPrice = isClosed && ctrs > 0 ? calcExitPrice(entryC, pnlC, ctrs) : null;
                return (
                  <tr key={i}>
                    <td className="mono" style={{ color: 'var(--text-muted)' }}>
                      {new Date(t.created_at).toLocaleString()}
                    </td>
                    <td>
                      <span className={`badge ${sideBadge(t.direction)}`}>{sideLabel(t.direction)}</span>
                    </td>
                    <td className="mono">{t.contracts}</td>
                    <td className="mono" style={{ color: 'var(--orange)', fontFamily: "'JetBrains Mono', monospace" }}>
                      {entryC}¢
                    </td>
                    <td className="mono" style={{ fontFamily: "'JetBrains Mono', monospace", color: exitPrice != null ? 'var(--text-dim)' : 'var(--text-muted)' }}>
                      {exitPrice != null ? `${exitPrice}¢` : '—'}
                    </td>
                    <td className="mono" style={{ color: 'var(--text-dim)' }}>{fmt(t.cost_cents || 0)}</td>
                    <td className="mono" style={{
                      fontFamily: "'JetBrains Mono', monospace",
                      fontWeight: 700,
                      color: pnlC > 0 ? 'var(--green)' : pnlC < 0 ? 'var(--red)' : 'var(--text-muted)',
                    }}>
                      {t.pnl_cents != null && isClosed ? fmtPnl(pnlC) : '—'}
                    </td>
                    <td>
                      <span className={`badge ${t.status === 'open' ? 'bg-blue' : t.status === 'closed' ? 'bg-green' : 'bg-orange'}`}>
                        {t.status}
                      </span>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      ) : (
        <p className="text-center py-3 text-xs" style={{ color: 'var(--text-muted)' }}>
          No trade details available
        </p>
      )}
    </div>
  );
}

export default function Positions() {
  const [positions, setPositions] = useState([]);
  const [grouped, setGrouped] = useState([]);
  const [summary, setSummary] = useState({});
  const [loading, setLoading] = useState(true);
  const [expandedGroup, setExpandedGroup] = useState(null);
  const [expandedPos, setExpandedPos] = useState(null);
  const [trades, setTrades] = useState({});
  const [tradesLoading, setTradesLoading] = useState(null);
  const [viewMode, setViewMode] = useState('grouped'); // grouped | flat | pnl
  const [sortBy, setSortBy] = useState('cost'); // cost | pnl | contracts

  const load = useCallback(async () => {
    try {
      const r = await api('/positions');
      if (r.success) {
        setPositions(r.positions || []);
        setGrouped(r.grouped || []);
        setSummary(r.summary || {});
      }
    } catch {}
    setLoading(false);
  }, []);

  useEffect(() => {
    load();
    const i = setInterval(load, 30000);
    return () => clearInterval(i);
  }, [load]);

  async function loadTrades(ticker, direction) {
    const key = `${ticker}_${direction}`;
    if (trades[key]) return;
    setTradesLoading(key);
    try {
      const r = await post('/positions/trades', { ticker, direction, limit: 50 });
      if (r.success) {
        setTrades(prev => ({ ...prev, [key]: r.trades || [] }));
      }
    } catch {}
    setTradesLoading(null);
  }

  function toggleGroup(event) {
    setExpandedGroup(prev => (prev === event ? null : event));
    setExpandedPos(null);
  }

  function togglePos(ticker, direction) {
    const key = `${ticker}_${direction}`;
    if (expandedPos === key) {
      setExpandedPos(null);
    } else {
      setExpandedPos(key);
      loadTrades(ticker, direction);
    }
  }

  const sortedPositions = [...positions].sort((a, b) => {
    if (sortBy === 'pnl') return b.realized_pnl_cents - a.realized_pnl_cents;
    if (sortBy === 'contracts') return b.open_contracts - a.open_contracts;
    return b.open_cost_cents - a.open_cost_cents;
  });

  const winners = positions
    .filter(p => p.realized_pnl_cents > 0)
    .sort((a, b) => b.realized_pnl_cents - a.realized_pnl_cents);
  const losers = positions
    .filter(p => p.realized_pnl_cents < 0)
    .sort((a, b) => a.realized_pnl_cents - b.realized_pnl_cents);
  const flat = positions.filter(p => p.realized_pnl_cents === 0);

  const winRate =
    positions.length > 0 && winners.length + losers.length > 0
      ? `${Math.round((winners.length / (winners.length + losers.length)) * 100)}%`
      : '—';

  if (loading) return <Spinner />;

  return (
    <div className="space-y-6 fade-in">

      {/* ── Page Header ── */}
      <div className="flex items-center justify-between">
        <div>
          <h1 className="heading text-3xl" style={{ color: 'var(--text)' }}>
            Positions
            <span className="badge bg-cyan ml-3 align-middle text-base">
              {summary.positionCount || 0}
            </span>
          </h1>
          <p className="section-subtitle mt-1">
            {summary.positionCount || 0} active positions &bull;{' '}
            {summary.totalTrades?.toLocaleString() || 0} total trades
          </p>
        </div>
        <div className="flex items-center gap-2">
          {/* View mode toggle */}
          <div
            className="flex rounded-lg p-0.5 gap-0.5"
            style={{ background: 'var(--surface-el)', border: '1px solid var(--border)' }}
          >
            {[['grouped', 'Grouped'], ['flat', 'All'], ['pnl', 'P&L']].map(([v, label]) => (
              <button
                key={v}
                onClick={() => setViewMode(v)}
                className="px-3 py-1 text-xs font-semibold rounded-md transition"
                style={
                  viewMode === v
                    ? { background: 'var(--cyan)', color: 'var(--bg)', fontFamily: 'var(--font-body)' }
                    : { color: 'var(--text-muted)', fontFamily: 'var(--font-body)' }
                }
              >
                {label}
              </button>
            ))}
          </div>
          <button onClick={load} className="btn btn-o btn-sm">Refresh</button>
        </div>
      </div>

      {/* ── Summary Stat Cards ── */}
      <div className="grid grid-cols-2 md:grid-cols-5 gap-3">
        <div className="card card-accent-cyan">
          <p className="stat-label">Positions</p>
          <p className="stat-v mono mt-2" style={{ color: 'var(--cyan)' }}>{summary.positionCount || 0}</p>
        </div>
        <div className="card card-accent-orange">
          <p className="stat-label">Total Cost</p>
          <p className="stat-v mono mt-2" style={{ color: 'var(--text)' }}>{fmt(summary.totalCost || 0)}</p>
        </div>
        <div className={`card ${(summary.totalPnl || 0) >= 0 ? 'card-accent-green' : 'card-accent-red'}`}>
          <p className="stat-label">Realized P&L</p>
          <p className={`stat-v mono mt-2 ${(summary.totalPnl || 0) >= 0 ? 'pnl-positive' : 'pnl-negative'}`}>
            {fmtPnl(summary.totalPnl || 0)}
          </p>
        </div>
        <div className="card card-accent-blue">
          <p className="stat-label">Contracts</p>
          <p className="stat-v mono mt-2" style={{ color: 'var(--blue)' }}>
            {(summary.totalContracts || 0).toLocaleString()}
          </p>
        </div>
        <div className="card card-accent-green">
          <p className="stat-label">Win Rate</p>
          <p className="stat-v mono mt-2 text-green">{winRate}</p>
        </div>
      </div>

      {/* ── P&L Breakdown Bar ── */}
      {summary.totalPnl !== 0 && (
        <div className="card py-3">
          <div className="flex items-center justify-between text-xs mb-2">
            <span className="font-semibold text-green">{winners.length} winners</span>
            <span style={{ color: 'var(--text-muted)' }}>{flat.length} flat</span>
            <span className="font-semibold text-red">{losers.length} losers</span>
          </div>
          <div className="progress-bar" style={{ height: 8 }}>
            {(() => {
              const totalAbs =
                winners.reduce((s, p) => s + p.realized_pnl_cents, 0) +
                Math.abs(losers.reduce((s, p) => s + p.realized_pnl_cents, 0));
              if (!totalAbs) return null;
              const winPct = (winners.reduce((s, p) => s + p.realized_pnl_cents, 0) / totalAbs) * 100;
              return (
                <div className="flex h-full w-full">
                  <div className="bar-win-glow" style={{ width: `${winPct}%`, background: 'var(--green)', borderRadius: '2px 0 0 2px' }} />
                  <div className="bar-loss-glow" style={{ width: `${100 - winPct}%`, background: 'var(--red)', borderRadius: '0 2px 2px 0' }} />
                </div>
              );
            })()}
          </div>
        </div>
      )}

      {/* ── Chart 6: P&L Distribution Histogram ── */}
      <PnlDistributionChart positions={positions} />

      {/* ══════════════════════════════
          GROUPED VIEW
          ══════════════════════════════ */}
      {viewMode === 'grouped' && (
        <div className="space-y-3">
          {grouped.map((group) => {
            const isExpanded = expandedGroup === group.event;
            return (
              <div
                key={group.event}
                className="card p-0 overflow-hidden"
                style={{ padding: 0 }}
              >
                {/* Group Header */}
                <button
                  onClick={() => toggleGroup(group.event)}
                  className="w-full flex items-center gap-3 p-4 text-left transition"
                  style={{ background: 'transparent' }}
                  onMouseEnter={e => (e.currentTarget.style.background = 'var(--cyan-glow)')}
                  onMouseLeave={e => (e.currentTarget.style.background = 'transparent')}
                >
                  <span
                    className="text-xs inline-block transition-transform"
                    style={{
                      color: 'var(--cyan)',
                      transform: isExpanded ? 'rotate(90deg)' : 'rotate(0deg)',
                    }}
                  >
                    ▶
                  </span>
                  <div className="flex-1 min-w-0">
                    <div className="flex items-center gap-2">
                      <span className="mono text-sm font-bold" style={{ color: 'var(--orange)' }}>
                        {group.event}
                      </span>
                      <span className="badge bg-cyan">{group.positions.length}</span>
                    </div>
                    <p className="text-xs truncate mt-0.5" style={{ color: 'var(--text-muted)' }}>
                      {group.title}
                    </p>
                  </div>
                  <div className="flex items-center gap-6 text-right flex-shrink-0">
                    <div>
                      <p className="text-xs" style={{ color: 'var(--text-muted)' }}>Cost</p>
                      <p className="mono text-sm" style={{ color: 'var(--text)' }}>{fmt(group.totalCost)}</p>
                    </div>
                    <div>
                      <p className="text-xs" style={{ color: 'var(--text-muted)' }}>Contracts</p>
                      <p className="mono text-sm" style={{ color: 'var(--blue)' }}>
                        {group.totalContracts.toLocaleString()}
                      </p>
                    </div>
                    <div>
                      <p className="text-xs" style={{ color: 'var(--text-muted)' }}>Realized P&L</p>
                      <p className={`mono text-sm font-bold ${pnlColor(group.totalPnl)}`}>
                        {fmtPnl(group.totalPnl)}
                      </p>
                    </div>
                  </div>
                </button>

                {/* Expanded Positions Table */}
                {isExpanded && (
                  <div style={{ borderTop: '1px solid var(--border)' }}>
                    <table>
                      <thead>
                        <tr>
                          <th></th>
                          <th>Ticker</th>
                          <th>Side</th>
                          <th>Contracts</th>
                          <th>Bought At</th>
                          <th>Sold At</th>
                          <th>Cost</th>
                          <th>Profit</th>
                          <th>Trades</th>
                          <th>Last Trade</th>
                        </tr>
                      </thead>
                      <tbody>
                        {group.positions.map((p) => {
                          const posKey = `${p.ticker}_${p.direction}`;
                          const isPosExpanded = expandedPos === posKey;
                          const posTrades = trades[posKey] || [];
                          const isLoadingTrades = tradesLoading === posKey;
                          const isWinning = (p.realized_pnl_cents || 0) > 0;
                          const isLosing  = (p.realized_pnl_cents || 0) < 0;
                          // Deterministic seed from ticker string
                          const tickerSeed = p.ticker.split('').reduce((acc, c) => acc + c.charCodeAt(0), 0);
                          const candleTrend = isWinning ? 'up' : isLosing ? 'down' : 'flat';
                          const candleData = genCandles(tickerSeed, 9, candleTrend);
                          const rowClass = isWinning ? 'cursor-pointer position-winning' : isLosing ? 'cursor-pointer position-losing' : 'cursor-pointer';
                          return (
                            <React.Fragment key={posKey}>
                              <tr
                                className={rowClass}
                                onClick={() => togglePos(p.ticker, p.direction)}
                                style={{ position: 'relative' }}
                              >
                                <td style={{ width: 24 }}>
                                  <span
                                    className="text-xs inline-block transition-transform"
                                    style={{
                                      color: 'var(--cyan)',
                                      transform: isPosExpanded ? 'rotate(90deg)' : 'rotate(0deg)',
                                    }}
                                  >
                                    ▶
                                  </span>
                                </td>
                                <td>
                                  <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                                    <a
                                      href={kalshiLink(p.ticker)}
                                      target="_blank"
                                      rel="noopener noreferrer"
                                      className="mono text-sm hover:underline"
                                      style={{ color: 'var(--orange)' }}
                                      onClick={e => e.stopPropagation()}
                                    >
                                      {p.ticker}
                                    </a>
                                    <MiniCandlestick data={candleData} width={72} height={28} />
                                  </div>
                                </td>
                                <td>
                                  <span className={`badge ${sideBadge(p.direction)}`}>
                                    {sideLabel(p.direction)}
                                  </span>
                                </td>
                                <td className="mono">{p.open_contracts?.toLocaleString()}</td>
                                <td className="mono" style={{ color: 'var(--orange)', fontFamily: "'JetBrains Mono', monospace" }}>
                                  {p.avg_entry_price}¢
                                </td>
                                <td className="mono" style={{ fontFamily: "'JetBrains Mono', monospace" }}>
                                  {(() => {
                                    const closedCount = parseInt(p.closed_trades) || 0;
                                    const openCount = parseInt(p.open_trades) || 0;
                                    if (closedCount > 0 && openCount === 0) {
                                      const exit = calcExitPrice(p.avg_entry_price, p.realized_pnl_cents, parseInt(p.open_contracts) || parseInt(p.total_trades) || 1);
                                      return <span style={{ color: 'var(--text-dim)' }}>{exit}¢</span>;
                                    }
                                    return <span className="badge bg-blue" style={{ fontSize: '0.65rem' }}>Open</span>;
                                  })()}
                                </td>
                                <td className="mono" style={{ color: 'var(--text)' }}>
                                  {fmt(p.open_cost_cents)}
                                </td>
                                <td className="mono" style={{
                                  fontFamily: "'JetBrains Mono', monospace",
                                  fontWeight: 700,
                                  color: p.realized_pnl_cents > 0 ? 'var(--green)' : p.realized_pnl_cents < 0 ? 'var(--red)' : 'var(--text-muted)',
                                }}>
                                  {p.realized_pnl_cents !== 0 ? fmtPnl(p.realized_pnl_cents) : '—'}
                                </td>
                                <td className="mono" style={{ color: 'var(--text-muted)' }}>
                                  {p.total_trades?.toLocaleString()}
                                </td>
                                <td className="text-xs" style={{ color: 'var(--text-muted)' }}>
                                  {timeAgo(p.last_trade_at)}
                                </td>
                              </tr>
                              {isPosExpanded && (
                                <tr>
                                  <td colSpan={10} style={{ padding: 0 }}>
                                    <TradeSubTable
                                      p={p}
                                      trades={posTrades}
                                      loading={isLoadingTrades}
                                    />
                                  </td>
                                </tr>
                              )}
                            </React.Fragment>
                          );
                        })}
                      </tbody>
                    </table>
                  </div>
                )}
              </div>
            );
          })}
        </div>
      )}

      {/* ══════════════════════════════
          FLAT VIEW
          ══════════════════════════════ */}
      {viewMode === 'flat' && (
        <div className="card overflow-x-auto" style={{ padding: 0 }}>
          {/* Sort controls */}
          <div
            className="flex items-center gap-3 px-4 py-3"
            style={{ borderBottom: '1px solid var(--border)' }}
          >
            <span className="text-xs" style={{ color: 'var(--text-muted)' }}>Sort by:</span>
            {[['cost', 'Cost'], ['pnl', 'P&L'], ['contracts', 'Contracts']].map(([v, label]) => (
              <button
                key={v}
                onClick={() => setSortBy(v)}
                className="text-xs font-semibold px-2.5 py-1 rounded-md transition"
                style={
                  sortBy === v
                    ? { background: 'var(--cyan-dim)', color: 'var(--cyan)' }
                    : { color: 'var(--text-muted)' }
                }
              >
                {label}
              </button>
            ))}
          </div>
          <table>
            <thead>
              <tr>
                <th>#</th>
                <th>Ticker</th>
                <th>Market</th>
                <th>Side</th>
                <th>Contracts</th>
                <th>Bought At</th>
                <th>Sold At</th>
                <th>Cost</th>
                <th>Profit</th>
                <th>Trades</th>
                <th>Last Trade</th>
              </tr>
            </thead>
            <tbody>
              {sortedPositions.length === 0 ? (
                <tr>
                  <td colSpan={11} className="text-center py-12" style={{ color: 'var(--text-muted)' }}>
                    No open positions
                  </td>
                </tr>
              ) : (
                sortedPositions.map((p, i) => {
                  const posKey = `${p.ticker}_${p.direction}`;
                  const isPosExpanded = expandedPos === posKey;
                  const posTrades = trades[posKey] || [];
                  const isLoadingTrades = tradesLoading === posKey;
                  const isWinningFlat = (p.realized_pnl_cents || 0) > 0;
                  const isLosingFlat  = (p.realized_pnl_cents || 0) < 0;
                  const tickerSeedFlat = p.ticker.split('').reduce((acc, c) => acc + c.charCodeAt(0), 0);
                  const candleTrendFlat = isWinningFlat ? 'up' : isLosingFlat ? 'down' : 'flat';
                  const candleDataFlat = genCandles(tickerSeedFlat + 100, 9, candleTrendFlat);
                  const flatRowClass = isWinningFlat ? 'cursor-pointer position-winning' : isLosingFlat ? 'cursor-pointer position-losing' : 'cursor-pointer';
                  return (
                    <React.Fragment key={posKey}>
                      <tr
                        className={flatRowClass}
                        onClick={() => togglePos(p.ticker, p.direction)}
                        style={{ position: 'relative' }}
                      >
                        <td style={{ color: 'var(--text-muted)' }}>{i + 1}</td>
                        <td>
                          <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                            <a
                              href={kalshiLink(p.ticker)}
                              target="_blank"
                              rel="noopener noreferrer"
                              className="mono text-sm hover:underline"
                              style={{ color: 'var(--orange)' }}
                              onClick={e => e.stopPropagation()}
                            >
                              {p.ticker}
                            </a>
                            <MiniCandlestick data={candleDataFlat} width={72} height={28} />
                          </div>
                        </td>
                        <td
                          className="text-xs max-w-[192px] truncate"
                          style={{ color: 'var(--text-dim)' }}
                        >
                          {p.title}
                        </td>
                        <td>
                          <span className={`badge ${sideBadge(p.direction)}`}>
                            {sideLabel(p.direction)}
                          </span>
                        </td>
                        <td className="mono">{p.open_contracts?.toLocaleString()}</td>
                        <td className="mono" style={{ color: 'var(--orange)', fontFamily: "'JetBrains Mono', monospace" }}>
                          {p.avg_entry_price}¢
                        </td>
                        <td className="mono" style={{ fontFamily: "'JetBrains Mono', monospace" }}>
                          {(() => {
                            const closedCount = parseInt(p.closed_trades) || 0;
                            const openCount = parseInt(p.open_trades) || 0;
                            if (closedCount > 0 && openCount === 0) {
                              const exit = calcExitPrice(p.avg_entry_price, p.realized_pnl_cents, parseInt(p.open_contracts) || parseInt(p.total_trades) || 1);
                              return <span style={{ color: 'var(--text-dim)' }}>{exit}¢</span>;
                            }
                            return <span className="badge bg-blue" style={{ fontSize: '0.65rem' }}>Open</span>;
                          })()}
                        </td>
                        <td className="mono" style={{ color: 'var(--text)' }}>
                          {fmt(p.open_cost_cents)}
                        </td>
                        <td className="mono" style={{
                          fontFamily: "'JetBrains Mono', monospace",
                          fontWeight: 700,
                          color: p.realized_pnl_cents > 0 ? 'var(--green)' : p.realized_pnl_cents < 0 ? 'var(--red)' : 'var(--text-muted)',
                        }}>
                          {p.realized_pnl_cents !== 0 ? fmtPnl(p.realized_pnl_cents) : '—'}
                        </td>
                        <td className="mono" style={{ color: 'var(--text-muted)' }}>
                          {p.total_trades?.toLocaleString()}
                        </td>
                        <td className="text-xs" style={{ color: 'var(--text-muted)' }}>
                          {timeAgo(p.last_trade_at)}
                        </td>
                      </tr>
                      {isPosExpanded && (
                        <tr>
                          <td colSpan={11} style={{ padding: 0 }}>
                            <TradeSubTable p={p} trades={posTrades} loading={isLoadingTrades} />
                          </td>
                        </tr>
                      )}
                    </React.Fragment>
                  );
                })
              )}
            </tbody>
          </table>
        </div>
      )}

      {/* ══════════════════════════════
          P&L VIEW
          ══════════════════════════════ */}
      {viewMode === 'pnl' && (
        <div className="space-y-4">

          {/* Winners */}
          <div className="card overflow-hidden" style={{ padding: 0 }}>
            <div
              className="flex items-center gap-3 px-4 py-3"
              style={{
                background: 'rgba(0,195,137,0.06)',
                borderBottom: '1px solid rgba(0,195,137,0.2)',
                borderLeft: '4px solid var(--green)',
              }}
            >
              <span className="text-sm font-bold text-green" style={{ textShadow: '0 0 10px rgba(0,195,137,0.4)' }}>Winners</span>
              <span className="badge bg-green">{winners.length}</span>
              <div className="flex-1" />
              <span className="mono font-bold pnl-positive">
                {fmtPnl(winners.reduce((s, p) => s + p.realized_pnl_cents, 0))}
              </span>
            </div>
            <table>
              <thead>
                <tr>
                  <th>Ticker</th>
                  <th>Market</th>
                  <th>Side</th>
                  <th>Contracts</th>
                  <th>Bought At</th>
                  <th>Sold At</th>
                  <th>Cost</th>
                  <th>Profit</th>
                  <th>ROI</th>
                </tr>
              </thead>
              <tbody>
                {winners.map(p => {
                  const roi =
                    p.open_cost_cents > 0
                      ? `${((p.realized_pnl_cents / p.open_cost_cents) * 100).toFixed(0)}%`
                      : '∞';
                  const wTickerSeed = p.ticker.split('').reduce((acc, c) => acc + c.charCodeAt(0), 0);
                  const wCandles = genCandles(wTickerSeed + 200, 9, 'up');
                  const closedCount = parseInt(p.closed_trades) || 0;
                  const openCount = parseInt(p.open_trades) || 0;
                  const exitPrice = (closedCount > 0 && openCount === 0)
                    ? calcExitPrice(p.avg_entry_price, p.realized_pnl_cents, parseInt(p.open_contracts) || parseInt(p.total_trades) || 1)
                    : null;
                  return (
                    <tr key={`${p.ticker}_${p.direction}`} className="position-winning" style={{ position: 'relative' }}>
                      <td>
                        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                          <a
                            href={kalshiLink(p.ticker)}
                            target="_blank"
                            rel="noopener"
                            className="mono text-sm hover:underline"
                            style={{ color: 'var(--orange)' }}
                          >
                            {p.ticker}
                          </a>
                          <MiniCandlestick data={wCandles} width={64} height={26} />
                        </div>
                      </td>
                      <td className="text-xs max-w-[192px] truncate" style={{ color: 'var(--text-dim)' }}>
                        {p.title}
                      </td>
                      <td>
                        <span className={`badge ${sideBadge(p.direction)}`}>
                          {sideLabel(p.direction)}
                        </span>
                      </td>
                      <td className="mono">{p.open_contracts?.toLocaleString()}</td>
                      <td className="mono" style={{ color: 'var(--orange)', fontFamily: "'JetBrains Mono', monospace" }}>
                        {p.avg_entry_price}¢
                      </td>
                      <td className="mono" style={{ fontFamily: "'JetBrains Mono', monospace", color: exitPrice != null ? 'var(--text-dim)' : 'var(--text-muted)' }}>
                        {exitPrice != null ? `${exitPrice}¢` : <span className="badge bg-blue" style={{ fontSize: '0.65rem' }}>Open</span>}
                      </td>
                      <td className="mono" style={{ color: 'var(--text-dim)' }}>
                        {fmt(p.open_cost_cents)}
                      </td>
                      <td className="mono font-bold pnl-positive" style={{ fontFamily: "'JetBrains Mono', monospace" }}>
                        {fmtPnl(p.realized_pnl_cents)}
                      </td>
                      <td className="mono pnl-positive">{roi}</td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>

          {/* Losers */}
          <div className="card overflow-hidden" style={{ padding: 0 }}>
            <div
              className="flex items-center gap-3 px-4 py-3"
              style={{
                background: 'rgba(255,92,92,0.06)',
                borderBottom: '1px solid rgba(255,92,92,0.2)',
                borderLeft: '4px solid var(--red)',
              }}
            >
              <span className="text-sm font-bold text-red" style={{ textShadow: '0 0 10px rgba(255,92,92,0.4)' }}>Losers</span>
              <span className="badge bg-red">{losers.length}</span>
              <div className="flex-1" />
              <span className="mono font-bold pnl-negative">
                {fmtPnl(losers.reduce((s, p) => s + p.realized_pnl_cents, 0))}
              </span>
            </div>
            <table>
              <thead>
                <tr>
                  <th>Ticker</th>
                  <th>Market</th>
                  <th>Side</th>
                  <th>Contracts</th>
                  <th>Bought At</th>
                  <th>Sold At</th>
                  <th>Cost</th>
                  <th>Profit</th>
                  <th>ROI</th>
                </tr>
              </thead>
              <tbody>
                {losers.map(p => {
                  const roi =
                    p.open_cost_cents > 0
                      ? `${((p.realized_pnl_cents / p.open_cost_cents) * 100).toFixed(0)}%`
                      : '—';
                  const lTickerSeed = p.ticker.split('').reduce((acc, c) => acc + c.charCodeAt(0), 0);
                  const lCandles = genCandles(lTickerSeed + 300, 9, 'down');
                  const closedCount = parseInt(p.closed_trades) || 0;
                  const openCount = parseInt(p.open_trades) || 0;
                  const exitPrice = (closedCount > 0 && openCount === 0)
                    ? calcExitPrice(p.avg_entry_price, p.realized_pnl_cents, parseInt(p.open_contracts) || parseInt(p.total_trades) || 1)
                    : null;
                  return (
                    <tr key={`${p.ticker}_${p.direction}`} className="position-losing" style={{ position: 'relative' }}>
                      <td>
                        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                          <a
                            href={kalshiLink(p.ticker)}
                            target="_blank"
                            rel="noopener"
                            className="mono text-sm hover:underline"
                            style={{ color: 'var(--orange)' }}
                          >
                            {p.ticker}
                          </a>
                          <MiniCandlestick data={lCandles} width={64} height={26} />
                        </div>
                      </td>
                      <td className="text-xs max-w-[192px] truncate" style={{ color: 'var(--text-dim)' }}>
                        {p.title}
                      </td>
                      <td>
                        <span className={`badge ${sideBadge(p.direction)}`}>
                          {sideLabel(p.direction)}
                        </span>
                      </td>
                      <td className="mono">{p.open_contracts?.toLocaleString()}</td>
                      <td className="mono" style={{ color: 'var(--orange)', fontFamily: "'JetBrains Mono', monospace" }}>
                        {p.avg_entry_price}¢
                      </td>
                      <td className="mono" style={{ fontFamily: "'JetBrains Mono', monospace", color: exitPrice != null ? 'var(--text-dim)' : 'var(--text-muted)' }}>
                        {exitPrice != null ? `${exitPrice}¢` : <span className="badge bg-blue" style={{ fontSize: '0.65rem' }}>Open</span>}
                      </td>
                      <td className="mono" style={{ color: 'var(--text-dim)' }}>
                        {fmt(p.open_cost_cents)}
                      </td>
                      <td className="mono font-bold pnl-negative" style={{ fontFamily: "'JetBrains Mono', monospace" }}>
                        {fmtPnl(p.realized_pnl_cents)}
                      </td>
                      <td className="mono pnl-negative">{roi}</td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>

          {/* Flat */}
          {flat.length > 0 && (
            <div className="card overflow-hidden" style={{ padding: 0 }}>
              <div
                className="flex items-center gap-3 px-4 py-3"
                style={{
                  background: 'var(--surface-el)',
                  borderBottom: '1px solid var(--border)',
                }}
              >
                <span className="text-sm font-bold" style={{ color: 'var(--text-dim)' }}>
                  Flat (No Realized P&L)
                </span>
                <span className="badge bg-orange">{flat.length}</span>
              </div>
              <table>
                <thead>
                  <tr>
                    <th>Ticker</th>
                    <th>Market</th>
                    <th>Side</th>
                    <th>Contracts</th>
                    <th>Cost</th>
                  </tr>
                </thead>
                <tbody>
                  {flat.map(p => (
                    <tr key={`${p.ticker}_${p.direction}`}>
                      <td>
                        <a
                          href={kalshiLink(p.ticker)}
                          target="_blank"
                          rel="noopener"
                          className="mono text-sm hover:underline"
                          style={{ color: 'var(--orange)' }}
                        >
                          {p.ticker}
                        </a>
                      </td>
                      <td className="text-xs max-w-[192px] truncate" style={{ color: 'var(--text-dim)' }}>
                        {p.title}
                      </td>
                      <td>
                        <span className={`badge ${sideBadge(p.direction)}`}>
                          {sideLabel(p.direction)}
                        </span>
                      </td>
                      <td className="mono">{p.open_contracts?.toLocaleString()}</td>
                      <td className="mono" style={{ color: 'var(--text-dim)' }}>
                        {fmt(p.open_cost_cents)}
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          )}
        </div>
      )}
    </div>
  );
}