Memory guard: 512MB hard cap, GC at 256MB, +swap
This commit is contained in:
@@ -29,6 +29,35 @@ import sys
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
from config.fee_tiers import get_perp_fees, PERPS_TIERS, STAKING_TIERS, STRATEGY_FEE_MODELS
|
||||
from common.risk import risk_summary
|
||||
from strategies.quant_report import compute_quant_report
|
||||
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
# Memory guard: cap RSS at 512MB, GC-aggressive at 256MB
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
import resource, gc, signal
|
||||
|
||||
MEM_SOFT_LIMIT = 256 * 1024 * 1024 # 256 MB — force GC
|
||||
MEM_HARD_LIMIT = 512 * 1024 * 1024 # 512 MB — terminate
|
||||
|
||||
resource.setrlimit(resource.RLIMIT_AS, (MEM_HARD_LIMIT, MEM_HARD_LIMIT))
|
||||
|
||||
def check_memory():
|
||||
"""Check RSS, force GC if over soft limit, raise if over hard limit."""
|
||||
try:
|
||||
with open("/proc/self/status") as f:
|
||||
for line in f:
|
||||
if line.startswith("VmRSS:"):
|
||||
rss_kb = int(line.split()[1])
|
||||
rss = rss_kb * 1024
|
||||
if rss > MEM_HARD_LIMIT:
|
||||
print(f"[CRIT] RSS {rss_kb // 1024}MB > 512MB — exiting", flush=True)
|
||||
os._exit(1)
|
||||
if rss > MEM_SOFT_LIMIT:
|
||||
gc.collect()
|
||||
gc.collect()
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
import uvicorn
|
||||
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
@@ -109,6 +138,7 @@ def broadcast_loop():
|
||||
"""Continuously read metrics and broadcast to all clients."""
|
||||
while True:
|
||||
time.sleep(1)
|
||||
check_memory()
|
||||
data = read_metrics()
|
||||
payload = json.dumps(data, default=str)
|
||||
for ws in list(connected_clients):
|
||||
@@ -437,6 +467,63 @@ app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
|
||||
# Main
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
|
||||
@app.get("/api/quant-report/{name}")
|
||||
async def get_quant_report(name: str):
|
||||
"""Compute full QF-Lib quant report from a backtest file.
|
||||
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
|
||||
for path in candidates:
|
||||
if os.path.exists(path):
|
||||
backtest_path = path
|
||||
break
|
||||
else:
|
||||
# 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):
|
||||
f_clean = f.lower()
|
||||
# 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:
|
||||
with open(backtest_path) as f:
|
||||
data = json.load(f)
|
||||
trades = data.get("trades", data.get("trade_history", []))
|
||||
strategy_name = data.get("name", data.get("strategy", name))
|
||||
strategy_id = data.get("id", name)
|
||||
report = compute_quant_report(strategy_name, strategy_id, trades, 100.0)
|
||||
return JSONResponse(report)
|
||||
except Exception as e:
|
||||
return JSONResponse({"error": str(e)}, status_code=500)
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
Reference in New Issue
Block a user