quant

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 18, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package quant provides quantitative-finance tools for evaluating trading strategies and portfolios: performance metrics (Sharpe ratio, maximum drawdown, annualized return), backtest-overfitting diagnostics (CSCV PBO, Deflated Sharpe Ratio), and time-series walk-forward out-of-sample validation.

Unlike the finance package (which uses high-precision decimals for TVM, NPV/IRR, and bond pricing), quant operates on plain float64 series — the industry convention for return/equity analytics, where statistical noise dwarfs floating-point error. Inputs are []float64 (a return series or an equity/NAV curve); exported functions follow an error-first convention, returning an error for invalid input rather than logging or panicking.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AnnualizedReturn

func AnnualizedReturn(equity insyra.IDataList, days int) (float64, error)

AnnualizedReturn returns the annualized (CAGR-style) return implied by an equity curve spanning days calendar days:

(equity[last] / equity[0]) ^ (365 / days) - 1

equity is a value/NAV curve (only its first and last points matter); days is the calendar-day span the curve covers.

Returns an error if fewer than 2 points are given, days is non-positive, or the first/last equity value is non-positive (the growth ratio would be undefined).

func DeflatedSharpeRatio

func DeflatedSharpeRatio(observedSR float64, n int, skew, kurt float64, trialSharpes insyra.IDataList) (float64, error)

DeflatedSharpeRatio returns the Deflated Sharpe Ratio (DSR): the PSR of the selected strategy measured against the deflation benchmark SR₀ derived from the whole set of trial Sharpe ratios. It corrects the observed Sharpe for selection bias from multiple testing, non-normality, and sample length in one number — DSR ≈ 1 means the result survives deflation; DSR near 0 means it is likely a false discovery.

observedSR is the selected strategy's per-period (non-annualized) Sharpe (typically the maximum of trialSharpes). n is its number of return observations; skew and kurt are that strategy's skewness and non-excess kurtosis. trialSharpes holds the per-period Sharpe ratios of ALL trials considered during the search; their count and (population) variance feed SR₀.

Returns an error if trialSharpes is empty or any downstream computation fails.

func ExpectedMaxSharpe

func ExpectedMaxSharpe(sharpeVariance float64, nTrials int) (float64, error)

ExpectedMaxSharpe returns SR₀, the expected maximum (per-period, non-annualized) Sharpe ratio obtained by chance after nTrials independent backtests whose Sharpe ratios have variance sharpeVariance. This is the deflation benchmark used by DeflatedSharpeRatio:

SR₀ = √V · [ (1-γ)·Z⁻¹(1 - 1/N) + γ·Z⁻¹(1 - 1/(N·e)) ]

where V = sharpeVariance, N = nTrials, γ is the Euler-Mascheroni constant, e is Euler's number, and Z⁻¹ is the standard-normal quantile.

With nTrials ≤ 1 there is no selection bias, so SR₀ = 0. Returns an error if sharpeVariance is negative.

func MaxDrawdown

func MaxDrawdown(equity insyra.IDataList) (float64, error)

MaxDrawdown returns the maximum drawdown of an equity (cumulative value / NAV) curve as a non-negative fraction: 0.2 means the curve fell 20% below a prior running peak at its worst point. A monotonically non-decreasing curve has a drawdown of 0.

equity should be a positive value series; points where the running peak is non-positive are skipped (drawdown is undefined there).

Returns an error if equity is empty.

func PBO

func PBO(perf insyra.IDataTable, nSplits int) (float64, error)

PBO estimates the Probability of Backtest Overfitting via Combinatorially Symmetric Cross-Validation (CSCV), per Bailey, Borwein, López de Prado & Zhu.

perf is a T×N performance DataTable: column j is candidate strategy j and row i is period i, so perf[i][j] is strategy j's period-i return (T rows, N ≥ 2 columns). nSplits (S) is the number of equal, contiguous time blocks the rows are cut into; it must be even. CSCV enumerates every way to split the S blocks into an in-sample half (IS) and an out-of-sample half (OOS). For each split it picks the IS-best strategy (by Sharpe ratio) and records its OOS rank; PBO is the fraction of splits where that strategy's OOS performance lands in the bottom half (logit ω ≤ 0). A high PBO means in-sample winners tend to be out-of-sample losers — the signature of overfitting.

If T is not a multiple of nSplits, the trailing T mod nSplits rows are dropped (each block has T/nSplits rows). Per-block Sharpe uses the sample standard deviation; a zero-volatility series contributes a Sharpe of 0.

Returns an error for an empty matrix, columns of unequal length, fewer than 2 strategies, an odd or non-positive nSplits, or nSplits greater than T.

func ProbabilisticSharpeRatio

func ProbabilisticSharpeRatio(observedSR, benchmarkSR float64, n int, skew, kurt float64) (float64, error)

ProbabilisticSharpeRatio returns the Probabilistic Sharpe Ratio (PSR) of Bailey & López de Prado: the probability that the true Sharpe ratio exceeds a benchmark, given the estimate's standard error under non-normal returns.

PSR = Φ[ (SR̂ - SR*)·√(n-1) / √(1 - γ₃·SR̂ + ((γ₄-1)/4)·SR̂²) ]

observedSR (SR̂) and benchmarkSR (SR*) are per-period, NON-annualized Sharpe ratios. n is the number of return observations. skew (γ₃) and kurt (γ₄) are the skewness and the NON-excess kurtosis of the returns (a normal distribution has skew 0 and kurt 3).

Returns an error if n < 2 or the variance term in the denominator is non-positive (which can happen under extreme skew/kurtosis combinations).

func SharpeRatio

func SharpeRatio(returns insyra.IDataList, riskFreeRate, periodsPerYear float64) (float64, error)

SharpeRatio returns the annualized Sharpe ratio of a periodic return series:

Sharpe = mean(returns - riskFreeRate) / stddev(returns) · √periodsPerYear

returns are per-period simple returns (e.g. daily returns as 0.012 for +1.2%). riskFreeRate is the risk-free rate expressed in the SAME period as returns (pass 0 for an excess-return series or to ignore it). periodsPerYear is the annualization factor — 252 for daily Taiwan-stock returns, 12 for monthly, 52 for weekly. Pass periodsPerYear = 1 to obtain the per-period (non-annualized) Sharpe used by the overfitting diagnostics.

The standard deviation is the sample (n-1) standard deviation, matching the common convention used by most backtesting tools and by gonum.

Returns an error if fewer than 2 returns are given, periodsPerYear is non-positive, or the return series has zero volatility (Sharpe undefined).

Types

type WalkForwardConfig

type WalkForwardConfig struct {
	// TrainSize is the number of in-sample periods used to pick parameters
	// for each fold.
	TrainSize int
	// TestSize is the number of out-of-sample periods evaluated per fold.
	// Folds advance by TestSize, so out-of-sample windows never overlap.
	TestSize int
	// Anchored controls the training window: false (default) uses a fixed
	// TrainSize rolling window; true uses an expanding window anchored at
	// period 0 (training always starts at 0 and grows each fold).
	Anchored bool
}

WalkForwardConfig configures the sliding train/test windows of a walk-forward analysis.

type WalkForwardFold

type WalkForwardFold struct {
	TrainStart int
	TrainEnd   int
	TestStart  int
	TestEnd    int
	// OOSReturns is the per-period out-of-sample return series returned by
	// evaluate for this fold.
	OOSReturns []float64
}

WalkForwardFold records the index ranges and out-of-sample result of one fold. All ranges are half-open [Start, End).

type WalkForwardResult

type WalkForwardResult struct {
	Folds []WalkForwardFold
	// OOSReturns concatenates the out-of-sample returns of every fold in
	// chronological order — the stitched out-of-sample track record.
	OOSReturns []float64
	// Equity is the compounded out-of-sample equity curve starting at 1.0,
	// so len(Equity) == len(OOSReturns)+1.
	Equity []float64
}

WalkForwardResult aggregates every fold of a walk-forward run.

func WalkForward

func WalkForward[P any](
	n int,
	cfg WalkForwardConfig,
	optimize func(trainStart, trainEnd int) P,
	evaluate func(p P, testStart, testEnd int) []float64,
) (*WalkForwardResult, error)

WalkForward runs a time-series walk-forward (out-of-sample) validation over n periods. For each fold it calls optimize on the in-sample window to pick parameters of type P, then evaluate on the out-of-sample window to obtain that fold's per-period returns; the out-of-sample returns are stitched together and compounded into a single equity curve. This is the standard guard against optimizing and evaluating on the same data (Pardo).

Both callbacks receive half-open [start, end) index ranges into the caller's own data (the caller closes over the actual series), so any data layout works. evaluate should return one return per out-of-sample period (typically TestEnd-TestStart values).

Windows advance by TestSize starting at TrainSize. With a rolling window fold k is train [TestStart-TrainSize, TestStart), test [TestStart, TestStart+TestSize); Anchored fixes the training start at 0 (expanding). If n-TrainSize is not a multiple of TestSize, the final out-of-sample window is shorter than TestSize rather than dropped, so all data is used.

Returns an error if n, TrainSize, or TestSize is non-positive, TrainSize leaves no room for testing (TrainSize >= n), or either callback is nil.

func (*WalkForwardResult) AnnualizedReturn

func (r *WalkForwardResult) AnnualizedReturn(days int) (float64, error)

AnnualizedReturn returns the annualized (CAGR-style) return of the out-of-sample equity curve over the given calendar-day span. See AnnualizedReturn for details.

func (*WalkForwardResult) MaxDrawdown

func (r *WalkForwardResult) MaxDrawdown() (float64, error)

MaxDrawdown returns the maximum drawdown of the out-of-sample equity curve. See MaxDrawdown for details.

func (*WalkForwardResult) Sharpe

func (r *WalkForwardResult) Sharpe(riskFreeRate, periodsPerYear float64) (float64, error)

Sharpe returns the annualized Sharpe ratio of the stitched out-of-sample returns. See SharpeRatio for the parameters and conventions.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL