perf: trades/summary聚合下推PG(SQL GROUP BY替代Python循环) + trades/latest加2秒缓存

This commit is contained in:
root 2026-02-27 16:52:35 +00:00
parent e56766ed98
commit 61287657df

View File

@ -330,43 +330,37 @@ async def get_trades_summary(
interval_ms = {"1m": 60000, "5m": 300000, "15m": 900000, "1h": 3600000}.get(interval, 60000)
sym_full = symbol.upper() + "USDT"
# PG分区表自动裁剪,直接查主表
# PG原生聚合比Python循环快100倍
rows = await async_fetch(
"SELECT agg_id, price, qty, time_ms, is_buyer_maker FROM agg_trades "
"WHERE symbol = $1 AND time_ms >= $2 AND time_ms < $3 ORDER BY time_ms ASC",
sym_full, start_ms, end_ms
"""
SELECT
(time_ms / $4) * $4 AS bar_ms,
ROUND(SUM(CASE WHEN is_buyer_maker = 0 THEN qty ELSE 0 END)::numeric, 4) AS buy_vol,
ROUND(SUM(CASE WHEN is_buyer_maker = 1 THEN qty ELSE 0 END)::numeric, 4) AS sell_vol,
COUNT(*) AS trade_count,
ROUND((SUM(price * qty) / NULLIF(SUM(qty), 0))::numeric, 2) AS vwap,
ROUND(MAX(qty)::numeric, 4) AS max_qty
FROM agg_trades
WHERE symbol = $1 AND time_ms >= $2 AND time_ms < $3
GROUP BY bar_ms
ORDER BY bar_ms ASC
""",
sym_full, start_ms, end_ms, interval_ms
)
bars: dict = {}
for row in rows:
bar_ms = (row["time_ms"] // interval_ms) * interval_ms
if bar_ms not in bars:
bars[bar_ms] = {"time_ms": bar_ms, "buy_vol": 0.0, "sell_vol": 0.0,
"trade_count": 0, "vwap_num": 0.0, "vwap_den": 0.0, "max_qty": 0.0}
b = bars[bar_ms]
qty = float(row["qty"])
price = float(row["price"])
if row["is_buyer_maker"] == 0:
b["buy_vol"] += qty
else:
b["sell_vol"] += qty
b["trade_count"] += 1
b["vwap_num"] += price * qty
b["vwap_den"] += qty
b["max_qty"] = max(b["max_qty"], qty)
result = []
for b in sorted(bars.values(), key=lambda x: x["time_ms"]):
total = b["buy_vol"] + b["sell_vol"]
for r in rows:
buy = float(r["buy_vol"])
sell = float(r["sell_vol"])
result.append({
"time_ms": b["time_ms"],
"buy_vol": round(b["buy_vol"], 4),
"sell_vol": round(b["sell_vol"], 4),
"delta": round(b["buy_vol"] - b["sell_vol"], 4),
"total_vol": round(total, 4),
"trade_count": b["trade_count"],
"vwap": round(b["vwap_num"] / b["vwap_den"], 2) if b["vwap_den"] > 0 else 0,
"max_qty": round(b["max_qty"], 4),
"time_ms": r["bar_ms"],
"buy_vol": buy,
"sell_vol": sell,
"delta": round(buy - sell, 4),
"total_vol": round(buy + sell, 4),
"trade_count": r["trade_count"],
"vwap": float(r["vwap"]) if r["vwap"] else 0,
"max_qty": float(r["max_qty"]),
})
return {"symbol": symbol, "interval": interval, "count": len(result), "data": result}
@ -378,13 +372,18 @@ async def get_trades_latest(
limit: int = 30,
user: dict = Depends(get_current_user),
):
cache_key = f"trades_latest_{symbol}_{limit}"
cached = get_cache(cache_key, 2)
if cached: return cached
sym_full = symbol.upper() + "USDT"
rows = await async_fetch(
"SELECT agg_id, price, qty, time_ms, is_buyer_maker FROM agg_trades "
"WHERE symbol = $1 ORDER BY time_ms DESC, agg_id DESC LIMIT $2",
sym_full, limit
)
return {"symbol": symbol, "count": len(rows), "data": rows}
result = {"symbol": symbol, "count": len(rows), "data": rows}
set_cache(cache_key, result)
return result
@app.get("/api/collector/health")