What is this? Monte Carlo simulation assumes you know the data-generating distribution. Bootstrap methods don’t - they resample your actual data to estimate confidence intervals, standard errors, and p-values. Use bootstrap when you want to know if your edge is statistically real without making distributional assumptions.
Bootstrap Methods
Horizon provides 11 bootstrap variants and jackknife bias estimation, all implemented in Rust for performance. These are non-parametric alternatives to Monte Carlo that work directly from observed data. The bootstrap is especially valuable in prediction markets where returns are non-normal and sample sizes are small.
IID Bootstrap bootstrap_mean(), bootstrap_sharpe(), bootstrap_var(), bootstrap_edge() - classic resampling with replacement.
Time-Series Bootstrap block_bootstrap(), circular_block_bootstrap(), stationary_bootstrap() - preserve autocorrelation structure.
Hypothesis Testing bootstrap_hypothesis_test() - permutation test to compare two strategies without distributional assumptions.
Jackknife jackknife_bias() - leave-one-out bias and variance estimation with pseudovalues.
IID Bootstrap Functions
hz.bootstrap_mean
Confidence interval for the mean via IID resampling.
import horizon as hz
returns = [ 0.02 , - 0.01 , 0.03 , 0.01 , - 0.005 , 0.015 , 0.025 , - 0.008 ]
r = hz.bootstrap_mean(returns, n_resamples = 10000 , confidence = 0.95 , seed = 42 )
print ( f "Mean: { r.point_estimate :.4f} " )
print ( f "95% CI: [ { r.ci_lower :.4f} , { r.ci_upper :.4f} ]" )
print ( f "Std error: { r.std_error :.4f} " )
hz.bootstrap_sharpe
Confidence interval for the Sharpe ratio. Particularly useful because the Sharpe ratio’s sampling distribution is highly non-normal for small samples.
r = hz.bootstrap_sharpe(returns, n_resamples = 10000 , confidence = 0.95 , seed = 42 )
print ( f "Sharpe: { r.point_estimate :.4f} " )
print ( f "95% CI: [ { r.ci_lower :.4f} , { r.ci_upper :.4f} ]" )
# If CI includes zero, your Sharpe isn't significantly different from zero
hz.bootstrap_var
Confidence interval for Value-at-Risk.
r = hz.bootstrap_var(returns, var_level = 0.05 , n_resamples = 10000 , seed = 42 )
print ( f "5% VaR: { r.point_estimate :.4f} " )
print ( f "95% CI: [ { r.ci_lower :.4f} , { r.ci_upper :.4f} ]" )
hz.bootstrap_correlation
Confidence interval for Pearson correlation (paired resampling).
x = [ 0.5 , 0.6 , 0.7 , 0.55 , 0.65 ]
y = [ 0.48 , 0.62 , 0.71 , 0.52 , 0.68 ]
r = hz.bootstrap_correlation(x, y, n_resamples = 10000 , seed = 42 )
print ( f "Correlation: { r.point_estimate :.4f} " )
print ( f "95% CI: [ { r.ci_lower :.4f} , { r.ci_upper :.4f} ]" )
hz.bootstrap_edge
Confidence interval for prediction edge mean(outcome - prediction). This is the core bootstrap for prediction market edge estimation.
predictions = [ 0.55 , 0.60 , 0.45 , 0.70 , 0.50 ]
outcomes = [ 1.0 , 1.0 , 0.0 , 1.0 , 0.0 ]
r = hz.bootstrap_edge(predictions, outcomes, n_resamples = 10000 , seed = 42 )
print ( f "Edge: { r.point_estimate :.4f} " )
print ( f "95% CI: [ { r.ci_lower :.4f} , { r.ci_upper :.4f} ]" )
if r.ci_lower > 0 :
print ( "Statistically significant positive edge!" )
Time-Series Bootstrap
For autocorrelated data (e.g., cumulative returns, price series), IID resampling destroys the dependence structure. These variants preserve it.
hz.block_bootstrap
Moving block bootstrap (Kunsch 1989). Resamples contiguous blocks of data.
prices = [ 0.50 , 0.51 , 0.52 , 0.51 , 0.53 , 0.54 , 0.52 , 0.55 ]
r = hz.block_bootstrap(prices, block_size = 3 , n_resamples = 5000 , seed = 42 )
print ( f "Mean: { r.point_estimate :.4f} , CI: [ { r.ci_lower :.4f} , { r.ci_upper :.4f} ]" )
Parameter Type Description datalist[float]Time series data block_sizeintSize of each contiguous block n_resamplesintNumber of bootstrap resamples (default 10000) confidencefloatConfidence level (default 0.95) seedintRNG seed (default 42)
hz.circular_block_bootstrap
Politis-Romano circular variant. Wraps the data circularly so blocks can span the boundary. Reduces bias from edge effects.
r = hz.circular_block_bootstrap(prices, block_size = 3 , n_resamples = 5000 , seed = 42 )
hz.stationary_bootstrap
Politis-Romano stationary bootstrap. Block lengths are geometric random variables, making the resampled series stationary.
r = hz.stationary_bootstrap(prices, expected_block_size = 5.0 , n_resamples = 5000 , seed = 42 )
Parameter Type Description expected_block_sizefloatExpected block length (geometric distribution)
Hypothesis Testing
hz.bootstrap_hypothesis_test
Two-sample permutation test. Tests whether two samples come from distributions with the same mean.
strategy_a = [ 0.02 , 0.01 , 0.03 , - 0.01 , 0.015 ]
strategy_b = [ 0.005 , - 0.01 , 0.01 , - 0.005 , 0.002 ]
r = hz.bootstrap_hypothesis_test(strategy_a, strategy_b, n_resamples = 10000 , seed = 42 )
print ( f "Test statistic: { r.test_statistic :.4f} " )
print ( f "P-value: { r.p_value :.4f} " )
print ( f "Reject H0: { r.reject } " ) # True if p < 0.05
Prediction Intervals
hz.bootstrap_prediction_interval
Build prediction intervals from residual resampling. Useful for forecasting.
residuals = [ - 0.05 , - 0.02 , 0.01 , 0.03 , - 0.01 , 0.04 , - 0.03 ]
forecast = 0.65
r = hz.bootstrap_prediction_interval(residuals, forecast, n_resamples = 10000 , seed = 42 )
print ( f "Forecast: { r.point_estimate :.4f} " )
print ( f "95% PI: [ { r.ci_lower :.4f} , { r.ci_upper :.4f} ]" )
Jackknife
hz.jackknife_bias
Leave-one-out jackknife for bias and variance estimation.
data = [ 0.02 , 0.01 , 0.03 , - 0.01 , 0.015 , 0.025 ]
r = hz.jackknife_bias(data, statistic = "mean" )
print ( f "Estimate: { r.estimate :.4f} " )
print ( f "Bias: { r.bias :.6f} " )
print ( f "Std error: { r.std_error :.4f} " )
print ( f "Pseudovalues: { len (r.pseudovalues) } " )
Parameter Type Description datalist[float]Input data statisticstrOne of "mean", "sharpe", "var" (default "mean")
Result Types
BootstrapResult
Field Type Description point_estimatefloatOriginal sample statistic ci_lowerfloatLower confidence bound ci_upperfloatUpper confidence bound std_errorfloatBootstrap standard error n_resamplesintNumber of resamples used biasfloatBootstrap bias estimate
BootstrapHypothesisResult
Field Type Description test_statisticfloatObserved difference in means p_valuefloatTwo-sided p-value ci_lowerfloatLower CI of permutation distribution ci_upperfloatUpper CI of permutation distribution rejectboolTrue if p-value < 0.05
JackknifeResult
Field Type Description estimatefloatFull-sample statistic biasfloatJackknife bias estimate std_errorfloatJackknife standard error pseudovalueslist[float]Leave-one-out pseudovalues
Pipeline Integration
hz.bootstrap_edge_tracker
Tracks bootstrap CI for prediction edge in a live pipeline. Accumulates (prediction, outcome) pairs per market and computes CIs when the window fills.
hz.run(
pipeline = [
my_model,
my_quoter,
hz.bootstrap_edge_tracker( window = 100 , n_resamples = 5000 , confidence = 0.95 ),
],
...
)
# Injects into ctx.params: "bootstrap_edge" = {edge, ci_lower, ci_upper, is_significant}
hz.bootstrap_strategy_test
Compares two strategies’ returns using a permutation test.
hz.run(
pipeline = [
hz.bootstrap_strategy_test( "returns_a" , "returns_b" ),
],
...
)
# Reads "returns_a" and "returns_b" from ctx.params
# Injects: "strategy_test" = {test_statistic, p_value, reject}