import { useState, useEffect } from 'react';
/**
* Phase E: for a set of whale addresses, fetch "what if we copied them under our
* config" backtest (GET /api/signals/whale/{addr}/backtest) and index by address.
* Lets the Leaders list show an expected PF/PnL badge so a whale's *copy-fit* —
* not its raw lifetime PnL — drives the choice (raw stats lie, e.g. Sociable-Rooster
* $85K lifetime but PF 0.24 under our config).
*/
export interface Backtest {
copies: number;
winRate: number;
pnl: number;
pf: number | null; // null = no losing copies
roi: number;
perDay: number;
}
export function useBacktests(addresses: string[], days = 30, size = 2) {
// Stable key so the effect only re-runs when the actual set changes.
const key = addresses.map(a => a.toLowerCase()).sort().join(',');
const [map, setMap] = useState<Record<string, Backtest>>({});
useEffect(() => {
if (!key) return;
const addrs = key.split(',');
// AbortController: leaving the page (e.g. opening a whale profile) cancels all
// in-flight backtest fetches, freeing the browser's ~6 connections so the profile
// GET isn't starved. CONCURRENCY caps how many run at once (defense + lighter API).
const ctrl = new AbortController();
const CONCURRENCY = 8;
(async () => {
const next: Record<string, Backtest> = {};
for (let i = 0; i < addrs.length; i += CONCURRENCY) {
if (ctrl.signal.aborted) return;
const batch = addrs.slice(i, i + CONCURRENCY);
const results = await Promise.allSettled(batch.map(a =>
fetch(`/api/signals/whale/${a}/backtest?days=${days}&size=${size}&pmin=5&pmax=95`, { signal: ctrl.signal })
.then(r => r.ok ? r.json() : null)
.then(d => ({ a, d }))
));
for (const res of results) {
if (res.status !== 'fulfilled' || !res.value?.d?.data) continue;
const { a, d } = res.value;
const v = d.data;
next[a] = {
copies: Number(v.copies) || 0,
winRate: Number(v.winRate) || 0,
pnl: Number(v.pnl) || 0,
pf: v.pf == null ? null : Number(v.pf),
roi: Number(v.roi) || 0,
perDay: Number(v.perDay) || 0,
};
}
}
if (!ctrl.signal.aborted) setMap(next);
})();
return () => ctrl.abort();
}, [key, days, size]);
return key ? map : {};
}
📜 Git History
4714fa6fix(leaders): whale profile stuck loading — useBacktests starved connections12 days ago
6c47fa4chore: local Polikopi project home + Phase 1 redesign artifacts12 days ago