Hallmark redesign + QuantReport fix
Header/Tabs: Hallmark Cobalt aesthetic - Hairlines, cool paper bg, JetBrains Mono + Inter - Electric cobalt accent on active tab - No branding, no purple badges, no gradients QuantReport: inline in strategy detail view - Renders below trade history on every tab - API maps strategy name -> file prefix - Proper backtestId from historical data Server: strategy-name-to-prefix lookup ofi, avellaneda, iceberg, momentum, mean_rev, funding_arb, kalman_pairs, pairs
This commit is contained in:
+24
-18
@@ -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:
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user