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:
@@ -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],
|
||||
|
||||
Reference in New Issue
Block a user