File size: 2,760 Bytes
9e89154 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | "use client";
import { useCallback, useState } from "react";
import { useLedgerSession } from "@/context/LedgerSessionContext";
import { optimizePortfolio } from "@/lib/api";
import { DEFAULT_TICKERS, DEFAULT_WEIGHT_MAX, DEFAULT_WEIGHT_MIN } from "@/lib/defaultUniverse";
export interface ScenarioResult {
name: string;
objective: string;
sharpe: number;
ret: number;
vol: number;
nActive: number;
}
const OBJECTIVES = [
"hybrid",
"markowitz",
"hrp",
"qubo_sa",
"min_variance",
"equal_weight",
] as const;
function objectiveLabel(obj: string): string {
return obj
.replace(/_/g, " ")
.replace(/\b\w/g, (c) => c.toUpperCase());
}
function mapOptimizeRow(obj: string, resp: Record<string, unknown>): ScenarioResult {
const inner = (resp.qsw_result as Record<string, unknown> | undefined) || resp;
const sharpe = Number(inner.sharpe_ratio ?? 0);
const ret = Number(inner.expected_return ?? 0) * 100;
const vol = Number(inner.volatility ?? 0) * 100;
const nActive = Number(inner.n_active ?? 0);
return {
name: objectiveLabel(obj),
objective: obj,
sharpe,
ret,
vol,
nActive,
};
}
export function useSimulationComparison() {
const { session } = useLedgerSession();
const [scenarios, setScenarios] = useState<ScenarioResult[]>([]);
const [running, setRunning] = useState(false);
const [error, setError] = useState<string | null>(null);
const runComparison = useCallback(async () => {
setRunning(true);
setError(null);
const results: ScenarioResult[] = [];
let failures = 0;
const tickers =
session.tickers.length > 0 ? session.tickers : [...DEFAULT_TICKERS];
const wMin = session.constraints.weightMin ?? DEFAULT_WEIGHT_MIN;
const wMax = session.constraints.weightMax ?? DEFAULT_WEIGHT_MAX;
for (const obj of OBJECTIVES) {
try {
const resp = (await optimizePortfolio({
tickers,
objective: obj,
weight_min: wMin,
maxWeight: wMax,
})) as Record<string, unknown>;
results.push(mapOptimizeRow(obj, resp));
} catch {
failures += 1;
results.push({
name: objectiveLabel(obj),
objective: obj,
sharpe: 0,
ret: 0,
vol: 0,
nActive: 0,
});
}
}
setScenarios(results);
if (failures === OBJECTIVES.length) {
setError(
"All optimization calls failed. Is the API running and reachable?"
);
} else if (failures > 0) {
setError(
`${failures} of ${OBJECTIVES.length} objectives failed (see zeros in table).`
);
}
setRunning(false);
}, [session]);
return {
scenarios,
running,
error,
runComparison,
};
}
|