vike
Desktop appStudio

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; NaN renders — and overfit_verdict treats it as "not assessed", its own reason row rather than a vote toward low risk. StudioSweep::pbo's doc names the NaN cases: fewer than two trials, a too-short slice, or non-finite returns.
  • Overfit risk — Low, Medium or High, from overfit_verdict, which combines PBO, the deflated Sharpe and, when present, walk-forward consistency into a label with the reasons behind it; each reason becomes a Note row.
  • 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)".

On this page