diff --git a/dashboard-next/src/app/page.tsx b/dashboard-next/src/app/page.tsx index c0f345d..a743b07 100644 --- a/dashboard-next/src/app/page.tsx +++ b/dashboard-next/src/app/page.tsx @@ -13,6 +13,7 @@ import { PositionsPanel } from "@/components/positions-panel"; import { OBIDetail } from "@/components/obi-detail"; import OrderBookDepthMap from "@/components/orderbook-depth-map"; import L2Terminal from "@/components/L2Terminal"; +import QuantReport from "@/components/QuantReport"; import { useLiveMetrics, usePaperMetrics, fetchHistorical, fetchBacktestDetail, recalcBacktest } from "@/lib/api"; import type { Strategy, BacktestSummary, BacktestFull, Trade, Position, Order } from "@/lib/types"; @@ -102,7 +103,7 @@ export default function Dashboard() { if (detailOpen) { return ( -
+
@@ -277,7 +278,16 @@ export default function Dashboard() {

No trades recorded yet

)}
- {/* Live L2 Order Book + Trade Tape (all strategies, live tab only) */} + + {/* QF-Lib Quant Report — Hallmark Cobalt inline */} +
+ +
+ + {/* Live L2 Order Book + Trade Tape */} {detailTab === "live" && (
@@ -292,7 +302,7 @@ export default function Dashboard() { } return ( -
+
{/* Header — Hallmark Cobalt */}
diff --git a/dashboard/server.py b/dashboard/server.py index 212a4e4..90532b8 100644 --- a/dashboard/server.py +++ b/dashboard/server.py @@ -441,40 +441,46 @@ app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static") @app.get("/api/quant-report/{name}") async def get_quant_report(name: str): """Compute full QF-Lib quant report from a backtest file. - Accepts either an exact filename or a strategy-name prefix. + Accepts strategy name and auto-maps to filename prefix. """ + # Strategy name → file prefix mapping + NAME_MAP = { + "order book imbalance": "ofi", + "avellaneda-stoikov": "avellaneda", + "funding rate arb": "funding_arb", + "iceberg detection": "iceberg", + "momentum breakout": "momentum", + "mean reversion": "mean_rev", + "kalman pairs": "kalman_pairs", + "pairs trading": "pairs", + } + + name_lower = name.lower() + prefix = NAME_MAP.get(name_lower, name_lower.replace(" ", "_")) + # Build candidate paths candidates = [] exact_path = os.path.join(BACKTEST_DIR, name) hist_exact = os.path.join(HISTORICAL_DIR, name) candidates.extend([exact_path, hist_exact]) - # Try exact match first + # Try exact match for path in candidates: if os.path.exists(path): backtest_path = path break else: - # Fuzzy match: look for files containing the name + # Fuzzy match: find files starting with the mapped prefix + fuzzy = [] for d in [BACKTEST_DIR, HISTORICAL_DIR]: if not os.path.exists(d): continue for f in os.listdir(d): - # Match: name is a substring of filename (case insensitive) - name_clean = name.lower().replace(" ", "_").replace(".json", "") f_clean = f.lower() - if name_clean in f_clean or f_clean.startswith(name_clean): - candidates.append(os.path.join(d, f)) - if not candidates: - # Try partial match on strategy name - for d in [BACKTEST_DIR, HISTORICAL_DIR]: - if not os.path.exists(d): continue - for f in os.listdir(d): - parts = f.lower().replace(".json", "").split("_") - name_parts = name.lower().replace(" ", "_").split("_") - if all(p in parts for p in name_parts): - candidates.append(os.path.join(d, f)) - if candidates: - backtest_path = candidates[0] + # Match by prefix, then prefer BTC/ETH files + if f_clean.startswith(f"{prefix}_"): + fuzzy.append(os.path.join(d, f)) + if fuzzy: + backtest_path = fuzzy[0] else: return JSONResponse({"error": f"Backtest '{name}' not found"}, status_code=404) try: diff --git a/dashboard/static/index.html b/dashboard/static/index.html index f6f8a4a..7c91d9b 100644 --- a/dashboard/static/index.html +++ b/dashboard/static/index.html @@ -1,4 +1,4 @@ -FTDT Quant Lab
Live Testnet
OFFLINE · ···
\ No newline at end of file + text-[#6e7381] hover:text-[#1a1c23]">Historical
\ No newline at end of file