#!/usr/bin/env python3 """Benchmark dashboard API latency and write JSON/HTML reports.""" from __future__ import annotations import argparse import html import json import time from datetime import datetime, timezone from pathlib import Path from typing import Any from urllib.error import HTTPError, URLError from urllib.parse import urlencode from urllib.request import Request, urlopen DEFAULT_ENDPOINTS = [ ("health", "/health", {}), ("stock_history_2330_6m", "/api/stock/2330/history", {"months": 6}), ("stock_predict_2330", "/api/stock/2330/predict", {}), ("stock_backtest_2330_6m", "/api/stock/2330/backtest", {"months": 6}), ("recommend_all_top20", "/api/stock/recommend", {"limit": 20, "signal_filter": "ALL"}), ( "hotspots_top20", "/api/stock/hotspots", {"limit": 20, "watchlist_limit": 20, "candidate_limit": 20}, ), ( "paper_fast_summary", "/api/paper-trades", {"mark_prices": "false", "price_timeout_seconds": 1.0}, ), ( "paper_mark_prices", "/api/paper-trades", {"mark_prices": "true", "price_timeout_seconds": 1.0}, ), ("oil_brent_history", "/api/oil/BZ=F/history", {"months": 6, "timeout_seconds": 4}), ("oil_brent_predict", "/api/oil/BZ=F/predict", {"timeout_seconds": 4}), ("oil_brent_backtest", "/api/oil/BZ=F/backtest", {"months": 12, "timeout_seconds": 4}), ] def _url(base_url: str, path: str, params: dict[str, Any]) -> str: query = urlencode(params) return f"{base_url.rstrip('/')}{path}{'?' + query if query else ''}" def _request(url: str, timeout: float) -> dict[str, Any]: started = time.perf_counter() status = None size = 0 error = None try: with urlopen(Request(url, headers={"Accept": "application/json"}), timeout=timeout) as resp: body = resp.read() status = resp.status size = len(body) except HTTPError as exc: status = exc.code try: size = len(exc.read()) except Exception: size = 0 error = str(exc) except (TimeoutError, URLError, OSError) as exc: error = str(exc) elapsed_ms = round((time.perf_counter() - started) * 1000, 2) return { "status": status, "elapsed_ms": elapsed_ms, "size_bytes": size, "ok": bool(status and 200 <= int(status) < 400), "error": error, } def _render_html(report: dict[str, Any]) -> str: rows = [] for row in report["results"]: cls = "ok" if row["ok"] else "fail" rows.append( "
Generated: {html.escape(report['generated_at'])} · Base URL: {html.escape(report['base_url'])}
OK: {report['summary']['ok_count']} / {report['summary']['count']} · p50: {report['summary']['p50_ms']:.2f} ms · p95: {report['summary']['p95_ms']:.2f} ms
| Name | Path | Status | Latency ms | Bytes | Error |
|---|