Results
The tabbed pane over a backtest result — equity, trades, performance, the returns distribution, and a validation tab that combines the probabilistic Sharpe of one run with the deflated Sharpe, PBO and walk-forward consistency a sweep and a walk-forward add.
crates/vike-studio/src/results.rs's header describes the pane: tabbed rendering over a
BacktestResult, with the table-building helpers pure and tested and the tabs drawn as plot lines
and bars plus grids. It sits in the centre panel whenever the last thing asked for was a backtest —
a Run, a Sweep or a Walk-Forward (the research pane claims the
panel for a study instead). With a single run present it renders that; with only a sweep, it
renders the sweep's best entry, so Equity and Trades have data even when you never ran a single
backtest. The ★ on the ranked table marks that link.
The five tabs
Equity plots the equity curve by bar index. Trades is a striped grid — #, side, entry,
exit, size, pnl, fees — with long/short coloured by direction and pnl by sign.
Performance is perf_cells: Total return, Final equity, Sharpe, Sortino, Max drawdown, CAGR,
Calmar, Win rate, Profit factor, SQN, Trades. Annualised figures use PPY = 252 periods per year,
the convention the pane's comment shares with the tearsheet. Rows where the sign means something at
a glance — the returns and the Sharpe family — read green or red; drawdown and counts stay neutral,
because a drawdown is always a decline. The metric definitions are
analytics'.
Distribution bins the per-bar returns (metrics::returns over the equity curve) into 21 bins
between the minimum and maximum.
An empty distribution is a real case, and it once took the frame down
A curve shorter than two points yields no returns, and the fold that sizes the bins seeds with
(f64::MAX, f64::MIN), so every bar centre landed near 1.7e308 — infinite once taken as f32.
That never reached the frame-sanity check: the plot library's own bounds check panicked first,
inside the render, measured on egui_plot 0.37 by removing the guard. A run that stops on its first
bar, or a strategy that never trades, produces exactly that curve. The tab now paints the empty plot
frame — axes and grid, no bars — and leaves the populated path unchanged; the kill proof lives
beside the guard in the module's tests.
Validation is two grids, then whatever a sweep or walk-forward adds.
What validation shows
The first grid, validation_rows, is what one run can say: the annualised Sharpe, then the inputs
to the probabilistic Sharpe ratio — observations, skew, kurtosis — and Prob. Sharpe > 0 (PSR) as
a percentage. The inputs come from the shared overfit::sharpe_moments derivation, which owns the
per-period-Sharpe and non-excess-kurtosis conventions; the pane's doc flags both as footguns and is
explicit that the displayed Sharpe is the annualised value, a different quantity that must never
be fed back into the overfitting statistics.
The second grid, validation_verdict_rows, needs more than one run:
- Deflated Sharpe — from a sweep's trial set, else
—. - PBO — the probability of backtest overfitting, computed by CSCV across the sweep's per-trial
returns matrix;
NaNrenders—andoverfit_verdicttreats it as "not assessed", its own reason row rather than a vote toward low risk.StudioSweep::pbo's doc names theNaNcases: fewer than two trials, a too-short slice, or non-finite returns. - Overfit risk —
Low,MediumorHigh, fromoverfit_verdict, which combines PBO, the deflated Sharpe and, when present, walk-forward consistency into a label with the reasons behind it; each reason becomes aNoterow. - Walk-forward consistency — when a walk-forward report exists.
Under the grids: the ranked Parameter sweep table (each grid point's overrides as k=v, its
annualised Sharpe coloured by sign, total return, final equity), and the stitched Walk-forward
OOS equity curve. The statistics themselves are overfitting's;
the procedures are sweeps and
walk-forward.
How a sweep is ranked
crates/vike-studio-core/src/run.rs builds a StudioSweep: entries ranked best-first by
annualised Sharpe through the harness's one ranking comparator, cmp_scores_desc, so a NaN
Sharpe — an equity curve poisoned by a blown-up strategy — sorts last and can never sit at
best_index 0 and feed the deflated Sharpe. rank_entries_by_sharpe's doc records the bug this
replaced: a hand-rolled comparison treated NaN as equal to everything, so a degenerate point
placed first by grid order stayed ranked first. Equal Sharpes keep grid order. PBO uses sixteen
CSCV splits, the canonical even count, computed off the UI thread inside the sweep. The Studio's
walk-forward is four anchored splits over one bar series — a tick or multi-symbol slice is refused
with a readable error rather than silently falling back to the first symbol.
When there is nothing to show
A failure replaces the tabs with a header naming which action failed — "Run failed", "Sweep failed" or "Walk-forward failed" — and the message in a framed monospace block; the shell's comment records that a fixed "Run failed" header used to misattribute sweep failures, and that a failed walk-forward used to be entirely invisible. The Sweep & Validate pane repeats a sweep or walk-forward failure inline too, for the case where the centre panel is busy showing an older successful result. A worker thread that dies surfaces the same way, as "run failed (worker terminated)".
The research pane
The studies a user has written, the one button that runs one, and the runs that came out — one list for the interpreted and compiled tiers, and a results surface that keeps a study's numbers out of any column a backtest's could share.
Saved strategies and workspace
Two files with two owners — a workspace snapshot of restorable UI state under the project's state directory, and the legacy saved-strategies JSON blob beside the store that the file-tree migration moves off, since a user's strategies are files now.