← Back
import { useState, useEffect, useCallback } from 'react';

export interface TradeActivity {
  id: string;
  conditionId: string;
  title: string;
  slug: string;
  side: string;        // 'BUY' | 'SELL'
  outcome: string;     // 'Yes' | 'No'
  size: number;
  price: number;
  amount: number;
  fee: number;
  realizedPnl: number;
  type: string;        // 'TRADE' | 'REDEEM' | ...
  createdAt: string;
  icon?: string;
}

export interface DailyPnl {
  date: string;
  pnl: number;
  trades: number;
  volume: number;
}

export interface PortfolioSummary {
  totalTrades: number;
  totalBought: number;
  totalSold: number;
  totalRedeemed: number;
  totalRealizedPnl: number;
  currentValue: number;
  totalPnl: number;
  totalFees: number;
  uniqueMarkets: number;
  firstTrade: string;
  lastTrade: string;
}

export interface PortfolioStats {
  summary: PortfolioSummary;
  dailyPnl: DailyPnl[];
  winRate: number;
  closedTrades: number;
  winningTrades: number;
  losingTrades: number;
  avgWin: number;
  avgLoss: number;
  profitFactor: number | null;
}

export function usePortfolioActivity(address: string | undefined, limit = 50) {
  const [trades, setTrades] = useState<TradeActivity[]>([]);
  const [loading, setLoading] = useState(false);
  const [hasMore, setHasMore] = useState(true);
  const [offset, setOffset] = useState(0);

  const fetchPage = useCallback(async (off: number) => {
    if (!address) return;
    setLoading(true);
    try {
      const res = await fetch(
        `/api/portfolio/activity?address=${address}&limit=${limit}&offset=${off}`
      );
      const d = await res.json();
      if (d.success) {
        // Data API /activity uses usdcSize/timestamp and has no id/amount/createdAt —
        // normalize to TradeActivity so rows show real $ amounts and times (raw
        // passthrough rendered $0 everywhere with duplicate React keys).
        const raw: Record<string, unknown>[] = d.data ?? [];
        const num = (v: unknown) => Number(v) || 0;
        const str = (v: unknown) => (typeof v === 'string' ? v : '');
        const items: TradeActivity[] = raw.map((r) => ({
          id: `${str(r.transactionHash)}-${str(r.asset) || str(r.conditionId)}`,
          conditionId: str(r.conditionId),
          title: str(r.title),
          slug: str(r.slug),
          side: str(r.side),
          outcome: str(r.outcome),
          size: num(r.size),
          price: num(r.price),
          amount: num(r.usdcSize),
          fee: num(r.fee),
          realizedPnl: num(r.realizedPnl),
          type: str(r.type) || 'TRADE',
          createdAt: r.timestamp ? new Date(num(r.timestamp) * 1000).toISOString() : '',
          icon: str(r.icon),
        }));
        if (off === 0) {
          setTrades(items);
        } else {
          setTrades(prev => [...prev, ...items]);
        }
        setHasMore(items.length >= limit);
      }
    } catch { /* ignore */ }
    finally { setLoading(false); }
  }, [address, limit]);

  useEffect(() => {
    if (!address) {
      const t = setTimeout(() => { setTrades([]); setOffset(0); }, 0);
      return () => { clearTimeout(t); };
    }
    const t = setTimeout(() => { setOffset(0); fetchPage(0); }, 0);
    return () => { clearTimeout(t); };
  }, [address, fetchPage]);

  const loadMore = useCallback(() => {
    const next = offset + limit;
    setOffset(next);
    fetchPage(next);
  }, [offset, limit, fetchPage]);

  return { trades, loading, hasMore, loadMore, refresh: () => fetchPage(0) };
}

export function usePortfolioStats(address: string | undefined) {
  const [stats, setStats] = useState<PortfolioStats | null>(null);
  const [loading, setLoading] = useState(false);

  const fetch_ = useCallback(async () => {
    if (!address) return;
    setLoading(true);
    try {
      const res = await fetch(`/api/portfolio/summary?address=${address}`);
      const d = await res.json();
      if (d.success) {
        setStats(d.data ?? null);
      }
    } catch { /* ignore */ }
    finally { setLoading(false); }
  }, [address]);

  useEffect(() => {
    if (!address) {
      const t = setTimeout(() => { setStats(null); }, 0);
      return () => { clearTimeout(t); };
    }
    const t = setTimeout(fetch_, 0);
    return () => { clearTimeout(t); };
  }, [address, fetch_]);

  return { stats, loading, refresh: fetch_ };
}

📜 Git History

6c47fa4chore: local Polikopi project home + Phase 1 redesign artifacts12 days ago
Show last diff
Loading...