Spaces:
Running on Zero
Running on Zero
| """The design's own markup, rendered from state. | |
| Every visible control here is the element the design uses -- a real `<button>` | |
| with the design's exact padding and type -- not a Gradio component restyled from | |
| outside. Gradio still owns the plots and the transport; it no longer owns the | |
| chrome. | |
| Values are lifted verbatim from `Backtest Lab.dc.html`: 286px left aside, `8px 12px` header padding, `3px 7px` chips, `4px 6px` segments, | |
| `9px 12px` panel headers, `0 12px 12px` section bodies with a 10px gap. The DS | |
| utility classes (`mono-data`, `pixel-text`, `styrene-text`) come from base.css | |
| and are used rather than reinvented. | |
| """ | |
| from __future__ import annotations | |
| from .bridge import emit | |
| from .format import EM, count, esc, money, num, pct, sharpe_tone | |
| # Zone widths, straight from the design. | |
| LEFT_W = "286px" | |
| # The mark shipped with the design (uploads/bit-trading-mark.svg). | |
| MARK = ( | |
| '<svg viewBox="0 0 100 100" width="18" height="18" role="img" ' | |
| 'aria-label="The Bit Trading Company" style="flex:0 0 auto">' | |
| '<path fill="var(--accent-amber)" fill-rule="evenodd" ' | |
| 'd="M0 0H100V100H0Z M50 10H90V90H50Z M22 42H38V58H22Z M62 42H78V58H62Z">' | |
| "</path></svg>" | |
| ) | |
| DISCLAIMER = ( | |
| "Simulated results with modeled costs. Backtests are hypotheses, not " | |
| "promises. Past performance does not predict future results. " | |
| "Not financial advice." | |
| ) | |
| # -------------------------------------------------------------------------- | |
| # Primitives, matching the design's exact treatments | |
| # -------------------------------------------------------------------------- | |
| def chip(label, action=None, *, active=False, title=""): | |
| """A `3px 7px` mono chip -- timeframe, date range, ticker.""" | |
| bg = "var(--accent-amber)" if active else "transparent" | |
| fg = "var(--stone-950)" if active else "var(--text-secondary)" | |
| border = "var(--accent-amber)" if active else "var(--border-default)" | |
| attr = f' data-bit="{esc(action)}"' if action else "" | |
| t = f' title="{esc(title)}"' if title else "" | |
| return ( | |
| f'<button{attr}{t} class="mono-data" style="padding:3px 7px;' | |
| f"background:{bg};border:1px solid {border};color:{fg};cursor:pointer;" | |
| f'font-size:var(--text-xs);transition:background 1s ease,color 1s ease">' | |
| f"{esc(label)}</button>" | |
| ) | |
| def segment(label, action, *, active=False, first=False): | |
| """A full-width segmented control cell (`4px 6px`, styrene, uppercase).""" | |
| bg = "var(--accent-amber)" if active else "transparent" | |
| fg = "var(--stone-950)" if active else "var(--text-secondary)" | |
| return ( | |
| f'<button data-bit="{esc(action)}" style="flex:1 1 auto;padding:4px 6px;' | |
| f"background:{bg};border:1px solid var(--border-default);" | |
| f"border-left-width:{'1px' if first else '0'};color:{fg};cursor:pointer;" | |
| f"font-family:var(--font-styrene);font-size:var(--text-xs);" | |
| f"text-transform:uppercase;letter-spacing:var(--tracking-wide);" | |
| f'cursor:pointer">{esc(label)}</button>' | |
| ) | |
| def field_label(text): | |
| return ( | |
| f'<div class="pixel-text" style="color:var(--text-secondary);' | |
| f'margin-bottom:4px">{esc(text)}</div>' | |
| ) | |
| def row(children, gap="4px"): | |
| return ( | |
| f'<div style="display:flex;flex-wrap:wrap;gap:{gap}">' | |
| f'{"".join(children)}</div>' | |
| ) | |
| def field(label, children, gap="4px"): | |
| return f"<div>{field_label(label)}{row(children, gap)}</div>" | |
| def note(text, danger=False): | |
| color = "var(--fin-down)" if danger else "var(--accent-amber)" | |
| return ( | |
| f'<div class="mono-data" style="border-left:2px solid {color};' | |
| f"background:var(--bg-raised);padding:6px 10px;font-size:var(--text-2xs);" | |
| f'line-height:1.6;color:var(--text-secondary);margin-bottom:6px">{text}</div>' | |
| ) | |
| def micro(text): | |
| return f'<div class="pixel-text" style="color:var(--text-tertiary)">{esc(text)}</div>' | |
| def panel(title, body, meta=""): | |
| m = (f'<span class="pixel-text" style="color:var(--text-tertiary);' | |
| f'margin-left:auto">{esc(meta)}</span>') if meta else "" | |
| return ( | |
| f'<div style="background:var(--bg-panel);border:1px solid ' | |
| f'var(--border-default);margin-bottom:8px">' | |
| f'<div style="display:flex;align-items:center;gap:8px;padding:9px 12px;' | |
| f'border-bottom:1px solid var(--border-default)">' | |
| f'<span style="font-family:var(--font-styrene);font-size:var(--text-sm);' | |
| f"text-transform:uppercase;letter-spacing:var(--tracking-wide)\">" | |
| f"{esc(title)}</span>{m}</div>" | |
| f'<div style="padding:12px">{body}</div></div>' | |
| ) | |
| def section(key, number, title, open_, body): | |
| """A numbered, collapsible left-panel section.""" | |
| glyph = "-" if open_ else "+" | |
| inner = ( | |
| f'<div style="padding:0 12px 12px;display:flex;flex-direction:column;' | |
| f'gap:10px">{body}</div>' | |
| ) if open_ else "" | |
| return ( | |
| f'<div style="border-bottom:1px solid var(--border-default)">' | |
| f'<button data-bit="{esc(emit("acc", key))}" style="width:100%;' | |
| f"display:flex;align-items:center;justify-content:space-between;gap:8px;" | |
| f"padding:8px 12px;background:transparent;border:0;cursor:pointer;" | |
| f'color:var(--text-primary)">' | |
| f'<span style="font-family:var(--font-styrene);font-size:var(--text-sm);' | |
| f"text-transform:uppercase;letter-spacing:var(--tracking-wide)\">" | |
| f"{number} · {esc(title)}</span>" | |
| f'<span class="mono-data" style="font-size:var(--text-2xs);' | |
| f'color:var(--text-tertiary)">{glyph}</span></button>{inner}</div>' | |
| ) | |
| # -------------------------------------------------------------------------- | |
| # Top bar | |
| # -------------------------------------------------------------------------- | |
| def nav_tab(label, key, active): | |
| """A header navigation tab.""" | |
| color = "var(--text-primary)" if active else "var(--text-tertiary)" | |
| border = "var(--accent-amber)" if active else "transparent" | |
| return ( | |
| f'<button data-bit="{esc(emit("tab", key))}" style="padding:6px 10px;' | |
| f"background:transparent;border:0;border-bottom:2px solid {border};" | |
| f"color:{color};cursor:pointer;font-family:var(--font-styrene);" | |
| f"text-transform:uppercase;letter-spacing:var(--tracking-wide);" | |
| f'font-size:var(--text-xs)">{esc(label)}</button>' | |
| ) | |
| def glossary_tooltip(items): | |
| """A `?` in the header that reveals the metrics glossary on hover. | |
| Pure CSS -- no JS, and no state to keep in sync. | |
| """ | |
| rows = "".join( | |
| f'<div style="margin-bottom:6px">' | |
| f'<div class="pixel-text" style="color:var(--accent-amber-strong)">{esc(t)}</div>' | |
| f'<div style="font-size:var(--text-2xs);color:var(--text-secondary);' | |
| f'line-height:1.6">{esc(d)}</div></div>' | |
| for t, d in items) | |
| return ( | |
| f'<span class="bit-help">' | |
| f'<span class="bit-help-dot" role="button" tabindex="0" ' | |
| f'aria-label="Metrics glossary">?</span>' | |
| f'<span class="bit-help-pop">' | |
| f'<span class="pixel-text" style="color:var(--text-tertiary);' | |
| f'display:block;margin-bottom:6px">Metrics glossary</span>{rows}</span></span>' | |
| ) | |
| def login_control(user=None, on_space=True): | |
| """Signed-in handle, or a note when there is no OAuth to reach. | |
| The sign-in *button* is a real `gr.LoginButton` placed beside this markup: | |
| Gradio only mounts `/login/huggingface` when it sees that component in the | |
| app, so hand-rolling an anchor points at a route that returns 404. | |
| """ | |
| if user: | |
| return ( | |
| f'<span class="mono-data" style="font-size:var(--text-2xs);' | |
| f"color:var(--accent-moss-strong);padding:3px 8px;" | |
| f'border:1px solid var(--accent-moss-dim)">@{esc(user)}</span>' | |
| ) | |
| if not on_space: | |
| return ( | |
| f'<span class="mono-data" style="font-size:var(--text-2xs);' | |
| f"color:var(--text-tertiary);padding:3px 8px;" | |
| f'border:1px solid var(--border-default)" ' | |
| f'title="Sign-in needs a Hugging Face Space. Reading and ' | |
| f'backtesting work without an account.">SIGN IN · ON SPACE</span>' | |
| ) | |
| return "" # the real LoginButton renders here | |
| def top_bar(context="", status="NO RUN LOADED", tone="idle", elapsed="", | |
| tab="compare", glossary=(), user=None, on_space=True): | |
| colors = { | |
| "ok": ("var(--accent-moss-strong)", "var(--accent-moss-dim)"), | |
| "run": ("var(--accent-amber-strong)", "var(--accent-amber-dim)"), | |
| "warn": ("var(--fin-down)", "var(--fin-down)"), | |
| "idle": ("var(--text-tertiary)", "var(--border-default)"), | |
| } | |
| color, border = colors.get(tone, colors["idle"]) | |
| ctx = ( | |
| f'<span class="mono-data" style="font-size:var(--text-xs);' | |
| f"color:var(--text-secondary);padding:3px 8px;border:1px solid " | |
| f'var(--border-default);white-space:nowrap">{esc(context)}</span>' | |
| ) if context else "" | |
| el = ( | |
| f'<span class="mono-data" style="font-size:var(--text-2xs);' | |
| f'color:var(--text-tertiary)">{esc(elapsed)}</span>' | |
| ) if elapsed else "" | |
| pulse = "animation:bitPulse 2s ease-in-out infinite" if tone == "run" else "" | |
| return ( | |
| f'<header style="position:sticky;top:0;z-index:30;display:flex;' | |
| f"align-items:center;gap:12px;flex-wrap:wrap;padding:8px 12px;" | |
| f'background:var(--bg-panel);border-bottom:1px solid var(--border-default)">' | |
| f"{MARK}" | |
| f'<span style="color:var(--text-tertiary)">/</span>' | |
| f'<span style="font-family:var(--font-styrene);text-transform:uppercase;' | |
| f"letter-spacing:var(--tracking-wide);font-size:var(--text-md);" | |
| f'white-space:nowrap;margin-top:3px">Backtest Lab</span>' | |
| f'<nav style="display:flex;gap:2px;margin-left:8px">' | |
| f'{nav_tab("Compare", "compare", tab == "compare")}' | |
| f'{nav_tab("Backtest", "backtest", tab == "backtest")}</nav>' | |
| f"{ctx}" | |
| f'<div style="display:flex;align-items:center;gap:8px;margin-left:auto;' | |
| f'flex-wrap:wrap">' | |
| f"{el}" | |
| f'<span style="display:inline-flex;align-items:center;gap:6px;' | |
| f'padding:3px 8px;border:1px solid {border}">' | |
| f'<span style="display:inline-block;width:5px;height:5px;' | |
| f'background:{color};{pulse}"></span>' | |
| f'<span class="mono-data" style="font-size:var(--text-2xs);color:{color};' | |
| f'white-space:nowrap">{esc(status)}</span></span>' | |
| f"{glossary_tooltip(glossary) if glossary else ''}" | |
| f"{login_control(user, on_space)}" | |
| f'<a href="https://huggingface.co/datasets/The-Bit-Trading-Company/' | |
| f'bit-signal-store" target="_blank" rel="noopener" class="mono-data" ' | |
| f'style="font-size:var(--text-2xs);color:var(--text-tertiary);' | |
| f'padding:3px 8px;border:1px solid var(--border-default)">STORE ↗</a>' | |
| f"</div></header>" | |
| ) | |
| def footer(): | |
| return ( | |
| f'<footer style="position:sticky;bottom:0;z-index:30;display:flex;' | |
| f"justify-content:space-between;gap:12px;padding:6px 12px;" | |
| f"background:var(--bg-panel);border-top:1px solid var(--border-default)\">" | |
| f'<span class="mono-data" style="font-size:var(--text-2xs);' | |
| f'color:var(--text-tertiary)">{esc(DISCLAIMER)}</span>' | |
| f'<span class="mono-data" style="font-size:var(--text-2xs);' | |
| f'color:var(--text-tertiary);white-space:nowrap">BITTRADING SDK 1.2.0</span>' | |
| f"</footer>" | |
| ) | |
| # -------------------------------------------------------------------------- | |
| # Inputs | |
| # -------------------------------------------------------------------------- | |
| def number_input(param_key, label, value, *, step="any"): | |
| """Label left, boxed value right -- the design's parameter row.""" | |
| val = "" if value is None else value | |
| return ( | |
| f'<div style="display:flex;align-items:center;gap:8px">' | |
| f'<span class="pixel-text" style="color:var(--text-secondary);flex:1 1 auto">' | |
| f"{esc(label)}</span>" | |
| f'<input type="number" step="{step}" value="{esc(val)}" ' | |
| f'data-bit-input="param:{esc(param_key)}" class="mono-data" ' | |
| f'style="width:74px;padding:3px 6px;background:var(--bg-sunken);' | |
| f"border:1px solid var(--border-default);color:var(--text-primary);" | |
| f'font-size:var(--text-xs);text-align:right"></div>' | |
| ) | |
| def toggle(action, label, on, *, warn_when_off=False): | |
| """A two-state switch rendered as the design's segmented pair.""" | |
| danger = warn_when_off and not on | |
| on_bg = "var(--accent-amber)" if on else "transparent" | |
| off_bg = "var(--fin-down)" if danger else "transparent" | |
| on_fg = "var(--stone-950)" if on else "var(--text-tertiary)" | |
| off_fg = "var(--stone-950)" if danger else "var(--text-tertiary)" | |
| return ( | |
| f'<div style="display:flex;align-items:center;gap:8px">' | |
| f'<span class="pixel-text" style="color:var(--text-secondary);flex:1 1 auto">' | |
| f"{esc(label)}</span>" | |
| f'<div style="display:flex">' | |
| f'<button data-bit="{esc(action)}=1" style="padding:3px 8px;' | |
| f"background:{on_bg};border:1px solid var(--border-default);color:{on_fg};" | |
| f'cursor:pointer;font-family:var(--font-mono);font-size:var(--text-2xs)">ON</button>' | |
| f'<button data-bit="{esc(action)}=0" style="padding:3px 8px;' | |
| f"background:{off_bg};border:1px solid var(--border-default);border-left:0;" | |
| f"color:{off_fg};cursor:pointer;font-family:var(--font-mono);" | |
| f'font-size:var(--text-2xs)">OFF</button></div></div>' | |
| ) | |
| # -------------------------------------------------------------------------- | |
| # Left panel -- Strategy Builder | |
| # -------------------------------------------------------------------------- | |
| def left_panel(st, *, presets, assets, timeframes, models, preset_params): | |
| """The design's 286px aside, rendered from state.""" | |
| open_ = st.get("acc", {}) | |
| preset_chips = row( | |
| [chip(p, emit("strategy", p), active=(p == st["strategy"])) for p in presets]) | |
| params = "".join( | |
| number_input(k, label, st["params"].get(k, default)) | |
| for k, label, default in preset_params) | |
| model_block = "" | |
| if st.get("needs_signals"): | |
| model_block = field( | |
| "Forecast model", | |
| [chip(m, emit("model", m), active=(m == st.get("model"))) for m in models] | |
| or [micro("no models cached")]) | |
| strategy_body = ( | |
| field_label("Preset") + preset_chips | |
| + (f'<div style="display:flex;flex-direction:column;gap:6px">{params}</div>' | |
| if params else "") + model_block) | |
| universe_body = ( | |
| field("Ticker", [chip(a, emit("asset", a), active=(a == st["asset"])) | |
| for a in assets]) | |
| + field("Timeframe", [chip(t, emit("tf", t), active=(t == st["timeframe"])) | |
| for t in timeframes]) | |
| + field("Date range", [chip(r, emit("range", r), active=(r == st["range"])) | |
| for r in ("1Y", "3Y", "5Y", "Max")]) | |
| + (micro(st["coverage"]) if st.get("coverage") else "")) | |
| costs_body = ( | |
| toggle("costs", "Costs", st["costs_on"], warn_when_off=True) | |
| + number_input("commission_bps", "Commission bps / side", st["commission_bps"]) | |
| + number_input("slippage_bps", "Slippage bps", st["slippage_bps"]) | |
| + field("Slippage model", | |
| [segment("Fixed bps", emit("slippage", "fixed"), | |
| active=st["slippage_model"] == "fixed", first=True), | |
| segment("Volume", emit("slippage", "volume_scaled"), | |
| active=st["slippage_model"] == "volume_scaled")], gap="0") | |
| + field("Fill", [chip("Next bar open", None, active=True, | |
| title="Enforced by the engine; not configurable.")]) | |
| + note("Costs on. Turning these off is how strategies lie to you.", | |
| danger=not st["costs_on"])) | |
| sizing_body = ( | |
| field("Sizing", | |
| [segment("Fixed %", emit("sizing", "fixed_pct"), | |
| active=st["sizing_mode"] == "fixed_pct", first=True), | |
| segment("Vol-target", emit("sizing", "vol_target"), | |
| active=st["sizing_mode"] == "vol_target")], gap="0") | |
| + number_input("size_pct", "Position size", st["size_pct"], step="0.05") | |
| + number_input("leverage", "Leverage", st["leverage"], step="0.5") | |
| + number_input("sl_pct", "Stop loss %", st["sl_pct"]) | |
| + number_input("tp_pct", "Take profit %", st["tp_pct"]) | |
| + number_input("trail_pct", "Trailing stop %", st["trail_pct"])) | |
| val_modes = [("Walk-forward", "walk_forward"), ("Split", "split"), | |
| ("Holdout", "holdout"), ("None", "none")] | |
| validation_body = ( | |
| field("Mode", [chip(lbl, emit("validation", v), | |
| active=st["validation_mode"] == v) for lbl, v in val_modes]) | |
| + number_input("train_months", "Train months", st["train_months"]) | |
| + number_input("test_months", "Test months", st["test_months"]) | |
| + number_input("roll_months", "Roll months", st["roll_months"]) | |
| + number_input("holdout_months", "OOS holdout months", st["holdout_months"])) | |
| sections = ( | |
| section("strategy", "1", "Strategy", open_.get("strategy", True), strategy_body) | |
| + section("universe", "2", "Universe & Data", open_.get("universe", True), | |
| universe_body) | |
| + section("costs", "3", "Costs & Execution", open_.get("costs", False), | |
| costs_body) | |
| + section("sizing", "4", "Sizing & Risk", open_.get("sizing", False), | |
| sizing_body) | |
| + section("validation", "5", "Validation", open_.get("validation", False), | |
| validation_body)) | |
| run_row = ( | |
| f'<div style="padding:12px;display:flex;flex-direction:column;gap:6px">' | |
| f'<button data-bit="{esc(emit("run"))}" style="width:100%;padding:8px 12px;' | |
| f"background:var(--accent-amber);border:1px solid var(--accent-amber);" | |
| f"color:var(--stone-950);cursor:pointer;font-family:var(--font-styrene);" | |
| f"text-transform:uppercase;letter-spacing:var(--tracking-wide);" | |
| f'font-size:var(--text-xs)">▶ Run backtest</button>' | |
| f'<button data-bit="{esc(emit("example"))}" style="width:100%;padding:6px 12px;' | |
| f"background:transparent;border:1px solid var(--border-default);" | |
| f"color:var(--text-secondary);cursor:pointer;font-family:var(--font-mono);" | |
| f'font-size:var(--text-2xs)">Load example</button></div>') | |
| return ( | |
| f'<aside style="width:{LEFT_W};flex:0 0 {LEFT_W};' | |
| f"border-right:1px solid var(--border-default);background:var(--bg-panel);" | |
| f'display:flex;flex-direction:column">' | |
| f'<div style="display:flex;align-items:center;justify-content:space-between;' | |
| f'padding:9px 12px;border-bottom:1px solid var(--border-default)">' | |
| f'<span style="font-family:var(--font-styrene);font-size:var(--text-md);' | |
| f"text-transform:uppercase;letter-spacing:var(--tracking-wide)\">" | |
| f"Strategy Builder</span>" | |
| f'<span class="pixel-text" style="color:var(--text-tertiary)">' | |
| f'CFG #{esc(st.get("cfg_id", "0000"))}</span></div>' | |
| f"{sections}{run_row}</aside>") | |
| # -------------------------------------------------------------------------- | |
| # Stat band | |
| # -------------------------------------------------------------------------- | |
| def stat_cell(label, value, sub, *, tone_class="", title=""): | |
| color = {"up": "var(--fin-up-strong)", | |
| "down": "var(--fin-down-strong)"}.get(tone_class, "var(--text-primary)") | |
| t = f' title="{esc(title)}"' if title else "" | |
| return ( | |
| f'<div{t} style="flex:1 1 124px;padding:8px 12px;' | |
| f"border-right:1px solid var(--border-subtle);" | |
| f'border-bottom:1px solid var(--border-subtle)">' | |
| f'<div class="pixel-text" style="color:var(--text-tertiary)">{esc(label)}</div>' | |
| f'<div class="mono-data" style="font-size:var(--text-lg);' | |
| f"line-height:var(--leading-tight);color:{color};margin:2px 0 1px;" | |
| f'letter-spacing:var(--tracking-tight)">{value}</div>' | |
| f'<div class="mono-data" style="font-size:var(--text-2xs);' | |
| f'color:var(--text-tertiary)">{sub}</div></div>' | |
| ) | |
| def stat_band(cells, notes=()): | |
| band = ( | |
| f'<div style="display:flex;flex-wrap:wrap;border:1px solid ' | |
| f'var(--border-default);background:var(--bg-panel);margin-bottom:8px">' | |
| f'{"".join(cells)}</div>' | |
| ) | |
| return band + "".join(notes) | |
| # -------------------------------------------------------------------------- | |
| # Tables -- rendered as markup, not as a Gradio Dataframe | |
| # -------------------------------------------------------------------------- | |
| def table(headers, rows, *, align_right=(), max_height="430px", empty="no rows"): | |
| """The design's table treatment: pixel-text header, mono body, 1px rules.""" | |
| if not rows: | |
| return micro(empty) | |
| def cell_style(i, header=False): | |
| a = "right" if i in align_right else "left" | |
| if header: | |
| return (f"padding:5px 8px;text-align:{a};" | |
| f"background:var(--bg-raised);color:var(--text-tertiary);" | |
| f"border-bottom:1px solid var(--border-default);" | |
| f"position:sticky;top:0;white-space:nowrap") | |
| return (f"padding:4px 8px;text-align:{a};color:var(--text-secondary);" | |
| f"border-bottom:1px solid var(--border-subtle);white-space:nowrap") | |
| head = "".join( | |
| f'<th class="pixel-text" style="{cell_style(i, True)}">{esc(h)}</th>' | |
| for i, h in enumerate(headers)) | |
| body = "".join( | |
| "<tr>" + "".join( | |
| f'<td style="{cell_style(i)}">{c}</td>' for i, c in enumerate(r) | |
| ) + "</tr>" for r in rows) | |
| return ( | |
| f'<div style="overflow:auto;max-height:{max_height};' | |
| f'border:1px solid var(--border-default);background:var(--bg-panel)">' | |
| f'<table class="mono-data bit-selectable" style="width:100%;' | |
| f"border-collapse:collapse;font-size:var(--text-2xs)\">" | |
| f"<thead><tr>{head}</tr></thead><tbody>{body}</tbody></table></div>" | |
| ) | |
| def frame_to_rows(df, limit=200): | |
| """DataFrame -> list of escaped string rows, ready for `table`.""" | |
| if df is None or df.empty: | |
| return [], [] | |
| sub = df.head(limit) | |
| headers = [str(c) for c in sub.columns] | |
| rows = [[esc("" if v is None else v) for v in rec] | |
| for rec in sub.itertuples(index=False, name=None)] | |
| return headers, rows | |
| # -------------------------------------------------------------------------- | |
| # Compare view controls | |
| # -------------------------------------------------------------------------- | |
| def compare_controls(st, *, metrics, assets, timeframes, strategies_, models): | |
| """How the leaderboard is ranked, filtered and plotted. | |
| Rendered as the design's chips rather than dropdowns so the controls read | |
| as part of the board rather than a form sitting above it. Filters are | |
| multi-select: clicking a chip toggles it, and none selected means all. | |
| """ | |
| sel = st.get("filters", {}) | |
| def toggle_row(label, kind, options, active): | |
| chips = [chip(o, emit("filter", f"{kind}={o}"), active=(o in active)) | |
| for o in options] | |
| return field(label, chips) | |
| rank = field("Rank by", [ | |
| chip(m, emit("metric", m), active=(m == st["metric"])) for m in metrics]) | |
| top = field("Show top", [ | |
| chip(str(n), emit("topn", str(n)), active=(int(st["topn"]) == n)) | |
| for n in (10, 15, 25, 50)]) | |
| minimum = field("Min trades", [ | |
| chip(str(n), emit("filter", f"min_trades={n}"), | |
| active=int(sel.get("min_trades", 0)) == n) | |
| for n in (0, 20, 50)]) | |
| flags = field("Show", [ | |
| chip("Out-of-sample only", emit("filter", "require_oos=toggle"), | |
| active=bool(sel.get("require_oos", True))), | |
| chip("Hide baselines", emit("filter", "hide_baselines=toggle"), | |
| active=bool(sel.get("hide_baselines", False))), | |
| chip("Reset", emit("reset"), active=False), | |
| ]) | |
| body = ( | |
| f'<div style="display:flex;flex-wrap:wrap;gap:16px">{rank}{top}{minimum}' | |
| f"{flags}</div>" | |
| + toggle_row("Assets", "asset", assets, sel.get("assets", [])) | |
| + toggle_row("Timeframes", "tf", timeframes, sel.get("timeframes", [])) | |
| + toggle_row("Strategies", "strategy", strategies_, sel.get("strategies", [])) | |
| + toggle_row("Models", "model", models, sel.get("models", [])) | |
| ) | |
| return panel("View", body, meta="CLICK TO FILTER · NONE SELECTED = ALL") | |
| def chart_controls(st): | |
| """Plot options for the Backtest overview.""" | |
| return row([ | |
| chip("Log scale", emit("logscale"), active=bool(st.get("log_scale"))), | |
| chip("Colorblind-safe", emit("cvd"), active=bool(st.get("cvd"))), | |
| ], gap="6px") | |