feat: trade log table, strategy params panel, B+W color scheme

Dashboard:
- Trade log table: all trades with time, side, size, entry/exit price, PnL, duration
  in scrollable panel below charts
- Strategy params panel: displays all coefficients (z_entry, gamma, obi_entry,
  grid_levels, etc.) for the selected strategy
- Color scheme: professional black/white
  • positive: #03A9F4 (light blue)
  • negative: #FF5252 (red)
  • neutral: #777 (gray)
  • backgrounds: #0a0a0a / #111 / #181818
  • borders: #222 / #333

VBT runner:
- _extract_metrics now captures trades from pf.trades.records_readable
  (Avg Entry Price, Avg Exit Price, PnL, Return, Duration, Direction)
- _strategy_params() returns key coefficients per strategy type
- _empty_result includes empty trades/params

New vbt_server.py: minimal standalone dashboard (no live trading machinery,
no memory guard, no broadcast loop) — avoids crashing issues
This commit is contained in:
ramseshk
2026-08-07 12:53:52 +08:00
parent 121c67ae5f
commit 887a33f278
3 changed files with 399 additions and 212 deletions
+38
View File
@@ -442,6 +442,24 @@ class VBTBacktestRunner:
return coin_map.get(strategy, ["BTC"])
def _extract_metrics(self, pf, stats, strategy, interval, n_bars) -> dict:
# Extract trade records from VectorBT portfolio
trades = []
try:
trade_records = pf.trades.records_readable
for _, t in trade_records.iterrows():
trades.append({
"time": str(t.get("Exit Timestamp", t.get("Entry Timestamp", "")))[:19],
"side": "BUY" if str(t.get("Direction", "")) == "Long" else "SELL",
"size": round(float(t.get("Size", 0)), 6),
"entry_px": round(float(t.get("Avg Entry Price", 0)), 2),
"exit_px": round(float(t.get("Avg Exit Price", 0)), 2),
"pnl": round(float(t.get("PnL", 0)), 4),
"return_pct": round(float(t.get("Return", 0)) * 100, 3),
"duration": str(t.get("Duration", "")),
})
except Exception:
pass
return {
"strategy": strategy,
"interval": interval,
@@ -456,6 +474,8 @@ class VBTBacktestRunner:
"win_rate": round(float(stats.get("Win Rate [%]", 0)) / 100, 3),
"profit_factor": round(float(stats.get("Profit Factor", 0)), 3),
"expectancy": round(float(stats.get("Expectancy", 0)), 3),
"trades": trades,
"params": _strategy_params(strategy),
}
def _empty_result(self, strategy: str, interval: str) -> dict:
@@ -472,10 +492,28 @@ class VBTBacktestRunner:
"max_drawdown_pct": 0.0,
"win_rate": 0.0,
"total_trades": 0,
"trades": [],
"params": _strategy_params(strategy),
"generated_at": datetime.now(timezone.utc).isoformat(),
}
def _strategy_params(strategy: str) -> dict:
"""Return the key parameters/coefficients for a strategy."""
params = {
"pairs": {"z_entry": 1.5, "z_exit": 0.5, "lookback": 20, "type": "Stat Arb"},
"hurst_vpin": {"hurst_entry": 0.55, "hurst_exit": 0.45, "vpin_threshold": 0.25, "vpin_window": 50, "hurst_window": 64, "type": "Directional"},
"as_mm": {"gamma": 0.1, "sigma_dynamic": True, "inventory_skew": True, "type": "Market Making"},
"obi": {"obi_lookback": 20, "obi_entry": 0.35, "obi_exit": 0.10, "type": "Reversal"},
"grid_mm": {"grid_levels": 10, "grid_spacing_pct": 0.1, "rebalance_every": 20, "type": "Market Making"},
"composite_mm": {"obi_weight": 0.30, "as_weight": 0.40, "hurst_weight": 0.30, "entry_score": 0.50, "type": "Ensemble"},
"iceberg": {"vol_mult": 1.8, "min_consec": 3, "max_hold": 8, "type": "Momentum"},
"momentum": {"bollinger_window": 20, "bollinger_std": 2.0, "type": "Momentum"},
"mean_rev": {"vwap_window": 20, "deviation": 1.0, "type": "Reversal"},
}
return params.get(strategy, {"type": "Unknown"})
def _generate_signals_sweep(
strategy: str,
data: dict[str, pd.DataFrame],