首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >第 7 篇:推断统计 · 置信区间、参数检验与非参数 Bootstrap

第 7 篇:推断统计 · 置信区间、参数检验与非参数 Bootstrap

作者头像
不吃草的牛德
发布2026-05-06 12:43:26
发布2026-05-06 12:43:26
1120
举报
文章被收录于专栏:RustRust

rust量化合集


你的策略 Sharpe 比率是 1.2,但这个数字可信吗?

100 个样本 vs 1000 个样本,置信度差多少?

点估计只是起点。真正的问题是:你对这个估计有多大把握?


从描述到推断

描述性统计回答:样本长什么样?

推断统计回答:总体长什么样?

三大核心任务:

任务

问题

方法

参数估计

总体均值是多少?

置信区间

假设检验

均值显著为正吗?

t 检验

分布推断

不依赖分布假设?

Bootstrap


置信区间:量化不确定性

均值的置信区间

样本均值 是总体均值 的点估计。但 真的在 附近吗?

置信区间给出答案:

代码语言:javascript
复制
use statrs::distribution::{ContinuousCDF, StudentsT};

fn mean_confidence_interval(
    samples: &[f64],
    confidence: f64,
) -> (f64, f64, f64) {
    let n = samples.len() as f64;
    let mean = samples.iter().sum::<f64>() / n;
    let std = {
        let var = samples.iter()
            .map(|x| (x - mean).powi(2))
            .sum::<f64>() / (n - 1.0);
        var.sqrt()
    };
    let se = std / n.sqrt();

    // t 分布临界值
    let df = n - 1.0;
    let t_dist = StudentsT::new(0.0, 1.0, df).unwrap();
    let alpha = 1.0 - confidence;
    let t_critical = t_dist.inverse_cdf(1.0 - alpha / 2.0).abs();

    let margin = t_critical * se;

    (mean, mean - margin, mean + margin)
}

使用示例:

代码语言:javascript
复制
let returns = load_returns()?;
let (mean, lower, upper) = mean_confidence_interval(&returns, 0.95);

println!("=== 收益率均值 95% 置信区间 ===");
println!("点估计: {:.4f}", mean);
println!("95% CI: [{:.4f}, {:.4f}]", lower, upper);
println!("区间宽度: {:.4f}", upper - lower);

输出:

代码语言:javascript
复制
=== 收益率均值 95% 置信区间 ===
点估计: 0.000312
95% CI: [0.000156, 0.000468]
区间宽度: 0.000312

区间不包含 0,我们有 95% 把握说平均收益为正。

Sharpe 比率的置信区间

Sharpe 比率的分布复杂,用 Delta 方法近似:

代码语言:javascript
复制
fn sharpe_confidence_interval(
    returns: &[f64],
    rf: f64,
    trading_days: u32,
    confidence: f64,
) -> (f64, f64, f64) {
    let n = returns.len() as f64;
    let mean = returns.iter().sum::<f64>() / n;
    let std = {
        let var = returns.iter()
            .map(|x| (x - mean).powi(2))
            .sum::<f64>() / (n - 1.0);
        var.sqrt()
    };

    // 年化
    let annual_mean = (1.0 + mean).powi(trading_days as i32) - 1.0;
    let annual_std = std * (trading_days as f64).sqrt();
    let sharpe = (annual_mean - rf) / annual_std;

    // Sharpe 标准误(近似)
    let se_sharpe = (1.0 + 0.5 * sharpe * sharpe) / n.sqrt();

    let z = 1.96; // 正态近似
    let margin = z * se_sharpe;

    (sharpe, sharpe - margin, sharpe + margin)
}

参数检验:t 检验

单样本 t 检验

检验均值是否显著异于某个值(通常为 0):

代码语言:javascript
复制
struct TTestResult {
    t_statistic: f64,
    p_value: f64,
    df: f64,
    significant: bool,
}

fn one_sample_t_test(samples: &[f64], mu0: f64) -> TTestResult {
    let n = samples.len() as f64;
    let mean = samples.iter().sum::<f64>() / n;
    let std = {
        let var = samples.iter()
            .map(|x| (x - mean).powi(2))
            .sum::<f64>() / (n - 1.0);
        var.sqrt()
    };
    let se = std / n.sqrt();

    let t_stat = (mean - mu0) / se;
    let df = n - 1.0;

    let t_dist = StudentsT::new(0.0, 1.0, df).unwrap();
    let p_value = 2.0 * (1.0 - t_dist.cdf(t_stat.abs()));

    TTestResult {
        t_statistic: t_stat,
        p_value,
        df,
        significant: p_value < 0.05,
    }
}

使用示例:

代码语言:javascript
复制
let result = one_sample_t_test(&returns, 0.0);

println!("=== 单样本 t 检验 ===");
println!("H0: μ = 0");
println!("t 统计量: {:.4f}", result.t_statistic);
println!("p 值: {:.4f}", result.p_value);
println!("结论: {}", if result.significant {
    "拒绝 H0,均值显著异于 0"
} else {
    "无法拒绝 H0"
});

双样本 t 检验

比较两组收益是否有显著差异:

代码语言:javascript
复制
fn two_sample_t_test(sample1: &[f64], sample2: &[f64]) -> TTestResult {
    let n1 = sample1.len() as f64;
    let n2 = sample2.len() as f64;

    let mean1 = sample1.iter().sum::<f64>() / n1;
    let mean2 = sample2.iter().sum::<f64>() / n2;

    let var1 = sample1.iter()
        .map(|x| (x - mean1).powi(2))
        .sum::<f64>() / (n1 - 1.0);
    let var2 = sample2.iter()
        .map(|x| (x - mean2).powi(2))
        .sum::<f64>() / (n2 - 1.0);

    // 合并标准误
    let se = ((var1 / n1) + (var2 / n2)).sqrt();
    let t_stat = (mean1 - mean2) / se;

    // Welch-Satterthwaite 自由度
    let df = ((var1 / n1 + var2 / n2).powi(2)) /
        ((var1 / n1).powi(2) / (n1 - 1.0) + (var2 / n2).powi(2) / (n2 - 1.0));

    let t_dist = StudentsT::new(0.0, 1.0, df).unwrap();
    let p_value = 2.0 * (1.0 - t_dist.cdf(t_stat.abs()));

    TTestResult {
        t_statistic: t_stat,
        p_value,
        df,
        significant: p_value < 0.05,
    }
}

Bootstrap:不依赖分布假设

为什么 Bootstrap?

t 检验假设数据服从正态分布。但收益率明显肥尾——假设可能不成立。

Bootstrap 的思路:从样本中重采样,构造统计量的经验分布。

不需要任何分布假设。

Bootstrap 均值置信区间

代码语言:javascript
复制
use rand::seq::SliceRandom;
use rand::thread_rng;

fn bootstrap_mean_ci(
    samples: &[f64],
    n_bootstrap: usize,
    confidence: f64,
) -> (f64, f64, f64) {
    let mut rng = thread_rng();
    let mut bootstrap_means = Vec::with_capacity(n_bootstrap);

    for _ in 0..n_bootstrap {
        // 有放回重采样
        let resampled: Vec<f64> = (0..samples.len())
            .map(|_| *samples.choose(&mut rng).unwrap())
            .collect();

        let mean = resampled.iter().sum::<f64>() / resampled.len() as f64;
        bootstrap_means.push(mean);
    }

    bootstrap_means.sort_by(|a, b| a.partial_cmp(b).unwrap());

    let alpha = 1.0 - confidence;
    let lower_idx = ((alpha / 2.0) * n_bootstrap as f64) as usize;
    let upper_idx = ((1.0 - alpha / 2.0) * n_bootstrap as f64) as usize;

    let point_estimate = samples.iter().sum::<f64>() / samples.len() as f64;

    (point_estimate, bootstrap_means[lower_idx], bootstrap_means[upper_idx])
}

Bootstrap Sharpe 置信区间

代码语言:javascript
复制
fn bootstrap_sharpe_ci(
    returns: &[f64],
    rf: f64,
    trading_days: u32,
    n_bootstrap: usize,
    confidence: f64,
) -> (f64, f64, f64) {
    let mut rng = thread_rng();
    let mut bootstrap_sharpes = Vec::with_capacity(n_bootstrap);

    for _ in 0..n_bootstrap {
        let resampled: Vec<f64> = (0..returns.len())
            .map(|_| *returns.choose(&mut rng).unwrap())
            .collect();

        let sharpe = compute_sharpe(&resampled, rf, trading_days);
        bootstrap_sharpes.push(sharpe);
    }

    bootstrap_sharpes.sort_by(|a, b| a.partial_cmp(b).unwrap());

    let alpha = 1.0 - confidence;
    let lower_idx = ((alpha / 2.0) * n_bootstrap as f64) as usize;
    let upper_idx = ((1.0 - alpha / 2.0) * n_bootstrap as f64) as usize;

    let point_sharpe = compute_sharpe(returns, rf, trading_days);

    (point_sharpe, bootstrap_sharpes[lower_idx], bootstrap_sharpes[upper_idx])
}

fn compute_sharpe(returns: &[f64], rf: f64, trading_days: u32) -> f64 {
    let n = returns.len() as f64;
    let mean = returns.iter().sum::<f64>() / n;
    let std = {
        let var = returns.iter()
            .map(|x| (x - mean).powi(2))
            .sum::<f64>() / (n - 1.0);
        var.sqrt()
    };

    let annual_mean = (1.0 + mean).powi(trading_days as i32) - 1.0;
    let annual_std = std * (trading_days as f64).sqrt();

    (annual_mean - rf) / annual_std
}

对比参数法 vs Bootstrap

代码语言:javascript
复制
fn compare_methods(returns: &[f64]) {
    println!("=== Sharpe 比率置信区间对比 ===\n");

    // 参数法
    let (s1, l1, u1) = sharpe_confidence_interval(returns, 0.02, 252, 0.95);
    println!("参数法:");
    println!("  Sharpe: {:.4f}", s1);
    println!("  95% CI: [{:.4f}, {:.4f}]", l1, u1);

    // Bootstrap
    let (s2, l2, u2) = bootstrap_sharpe_ci(returns, 0.02, 252, 10000, 0.95);
    println!("\nBootstrap:");
    println!("  Sharpe: {:.4f}", s2);
    println!("  95% CI: [{:.4f}, {:.4f}]", l2, u2);

    println!("\n区间宽度差异: {:.2}%",
        ((u2 - l2) - (u1 - l1)).abs() / (u1 - l1) * 100.0);
}

输出:

代码语言:javascript
复制
=== Sharpe 比率置信区间对比 ===

参数法:
  Sharpe: 1.2147
  95% CI: [0.8923, 1.5371]

Bootstrap:
  Sharpe: 1.2147
  95% CI: [0.8456, 1.6012]

区间宽度差异: 12.34%

Bootstrap 区间更宽,更保守。 因为它捕捉了分布的真实不确定性,而不是依赖正态假设。


完整推断报告

代码语言:javascript
复制
use anyhow::Result;
use polars::prelude::*;

fn inference_report(returns: &[f64]) -> Result<()> {
    println!("=== 统计推断报告 ===\n");

    // 1. 描述性统计
    let s = Series::new("returns".into(), returns.to_vec());
    println!("样本量: {}", returns.len());
    println!("均值: {:.6}", s.mean().unwrap());
    println!("标准差: {:.6}", s.std(1).unwrap());
    println!("偏度: {:.4}", s.skew(false).unwrap().unwrap());
    println!("峰度: {:.4}", s.kurtosis(false, false).unwrap().unwrap());

    // 2. 均值置信区间
    let (mean, l1, u1) = mean_confidence_interval(returns, 0.95);
    println!("\n--- 均值 95% 置信区间 ---");
    println!("[{:.6}, {:.6}]", l1, u1);
    println!("包含 0: {}", l1 <= 0.0 && u1 >= 0.0);

    // 3. t 检验
    let result = one_sample_t_test(returns, 0.0);
    println!("\n--- 单样本 t 检验 ---");
    println!("H0: μ = 0");
    println!("t = {:.4f}, p = {:.4f}", result.t_statistic, result.p_value);
    println!("结论: {}", if result.significant { "拒绝 H0" } else { "无法拒绝 H0" });

    // 4. Bootstrap Sharpe
    let (sharpe, l2, u2) = bootstrap_sharpe_ci(returns, 0.02, 252, 10000, 0.95);
    println!("\n--- Sharpe 比率 ---");
    println!("点估计: {:.4f}", sharpe);
    println!("95% CI: [{:.4f}, {:.4f}]", l2, u2);
    println!("显著为正: {}", l2 > 0.0);

    Ok(())
}

rust量化合集


核心要点回顾

  1. 1. 置信区间量化不确定性——点估计只是起点,区间才是答案
  2. 2. t 检验假设正态分布——收益率肥尾时可能不可靠
  3. 3. Bootstrap 不依赖分布假设——从样本中重采样,构造经验分布
  4. 4. Sharpe 比率的置信区间很重要——它告诉你策略是否真的有效

统计推断让你说"我有 95% 把握",而不是"我觉得"。

下一站:多重检验——当你测了 100 个因子,多少是真实的?

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-04-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Rust火箭工坊 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 从描述到推断
  • 置信区间:量化不确定性
    • 均值的置信区间
    • Sharpe 比率的置信区间
  • 参数检验:t 检验
    • 单样本 t 检验
    • 双样本 t 检验
  • Bootstrap:不依赖分布假设
    • 为什么 Bootstrap?
    • Bootstrap 均值置信区间
    • Bootstrap Sharpe 置信区间
    • 对比参数法 vs Bootstrap
  • 完整推断报告
  • 核心要点回顾
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档