找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2687

积分

0

好友

377

主题
发表于 9 小时前 | 查看: 0| 回复: 0

本文基于外网文章翻译,并结合腾讯元宝根据A股市场实践整理,旨在分享量化投资从研究到实盘的实战心得。

关于策略

量化研究的核心并非寻找“回测曲线很漂亮”的策略,而是发掘那些有经济逻辑支撑、能被检验、可复现的Alpha。如果试图在此“偷懒”,后续所有工作都可能沦为一种“数据挖掘的自我感动”。

因此,我们构建的根基应是:一个可证伪的交易假设

普通人的思路:

“我有个赚钱的好点子!我要去验证它是对的!”

量化研究员的思路:

“我有个可能赚钱的点子。但我先要给自己设置各种‘坑’,看看这个点子会不会掉进这些坑里。”

一个优秀的交易假设是策略的基石,必须拒绝笼统表述,满足具体性、可证伪性、经济合理性、有时限性四大核心要求。模糊的假设只会让后续研究迷失方向。

  • 具体性:拒绝“动量策略有效”这类空泛表述,必须明确标的、周期、收益阈值。例如:“沪深300成分股中,过去12个月(剔除最后1个月)收益为正的资产,其未来1年收益率比收益为负的资产高出5%-10%”。
  • 可证伪性:检验前提前定义明确的拒绝标准,避免“事后找理由”。例如:“若策略扣除交易成本后夏普比率小于0.5,或t统计量小于2.0,则直接拒绝该假设”。
  • 金融逻辑合理性:回答核心问题“超额收益从何而来”,这是区分“真正Alpha”和“数据噪音”的关键。需从四大维度寻找逻辑支撑:行为偏差(投资者过度反应、损失厌恶、追涨杀跌)、结构性因素(指数再平衡、监管限制)、风险溢价(套利风险、流动性风险)、信息优势(信息扩散速度差异)。
  • 有时限性:明确策略有效性的边界,以及优势消失的场景。例如:“该策略的超额收益来自散户对盈利意外的过度反应,若市场中算法交易占比提升、散户参与度下降,这一优势将逐步消失”。

在将任何因子或信号策略落地前,建议先思考以下4个问题,从根源上过滤无逻辑的策略:

  1. 这笔交易的另一方是谁? 若对手是消息灵通的机构,你很可能成为“接盘方”;若对手是资金受限的被动投资者(如指数基金),则超额收益更具真实性。
  2. 为什么这种套利行为尚未被消除? 是市场存在产能限制、实施成本过高,还是成熟投资者不愿承担的尾部风险?这是Alpha能持续的核心原因。
  3. 持仓周期和换手率对交易有何影响? 频率越高,执行风险(滑点、市场冲击)越大;周期越短,基本面风险(业绩变脸、政策调整)越大,需找到平衡点。
  4. 这种策略优势何时会停止? 需预判失效场景:市场风格切换、策略拥挤度上升、监管政策调整、市场结构发生根本性变化。

区分虚假信号与真正Alpha

在量化投资中,区分数据挖掘的虚假信号与真正的Alpha至关重要。虚假信号通常有八大特征:

  1. 策略没有合理的经济逻辑支撑,如同不知道为什么赢牌;
  2. 参数过度优化,像碰巧打开一个密码锁就宣称掌握了通用密码;
  3. 设计过度复杂,违背了“简单策略更稳健”的原则;
  4. 只通过样本内测试,如同学生只会做过的题目;
  5. 只在特定有利时期有效,无法穿越牛熊;
  6. 存在幸存者偏差,只统计存活样本;
  7. 依赖无法实现的精确择时
  8. 假设不切实际的理想交易条件,忽略了真实的摩擦成本。

与之相对,真正的Alpha策略具备六大核心特征:

  1. 具备跨市场的普适性,如同优秀厨师精通多种菜系;
  2. 对参数扰动具有鲁棒性,不依赖精确的数字设定;
  3. 能适应不同的市场环境,在牛熊市中均能生存;
  4. 具有可扩展的容量,不会因规模扩大而失效;
  5. 拥有清晰且未被充分定价的经济逻辑,而非依赖数据巧合;
  6. 在充分考虑所有交易成本后仍能持续盈利

简言之,真正的Alpha源于对市场规律的深刻理解,而非对历史数据的过度拟合。

一个“回测很好”的策略往往是在历史数据里“刻舟求剑”,但一上实盘就“见光死”。而一个真正能实盘赚钱的策略一定是逻辑清晰,简单稳健,经得起各种考验。因此在构建新策略前,可以先自问几个问题:

  1. “这策略为什么能赚钱?”
  2. “改几个参数还行吗?”
  3. “熊市那年能扛住吗?”
  4. “真有1个亿还能这样操作吗?”
  5. “扣掉所有费用还剩多少?”

同时,也要对自己的策略进行“淘汰更新”。当策略表现不佳时,首先要看是否触及“五个必须砍”的红线:

  • 实盘跑一年还亏钱(夏普为负);
  • 实际收益不到回测一半(前向效率<0.5);
  • 买卖信号完全反了(相关性反转);
  • 钱一多就赚不到差价(冲击成本吃光利润);
  • 赚钱的逻辑基础已经不存在了(如市场规则变化)。

只要踩中任何一条,必须立即清仓止损。如果以上都没问题,但存在以下情况,则需要权衡是否放弃:

  • 策略和现有持仓高度同质(无法分散风险);
  • 运维成本远超收益(性价比过低);
  • 存在极端尾部风险(平时小赚,危机时巨亏)。

记住,淘汰坏策略和发掘好策略同样重要。

关于数据

量化研究中,数据的质量直接决定策略的成败。前瞻性偏差、幸存者偏差、异常值处理不当,都会让回测结果成为“空中楼阁”。对于一些数据源有限的初创团队而言,掌握科学的数据管理方法尤为重要。

1. 异常值处理

金融数据(尤其是收益数据)天然具有厚尾分布,极端值不是“噪音”,而是真实的市场行为(如闪崩、盈利意外)。盲目剔除异常值会扭曲数据的真实性,正确的做法是“标记而非删除,调查而非忽略”。

def detect_and_handle_outliers(df, columns, method='iqr', threshold=3.0):
    """
    检测并处理金融数据中的异常值,核心原则:标记不删除,保留真实市场特征
    重要提示:切勿盲目删除金融数据中的异常值,极端值是真实市场行为的体现
    """
    import numpy as np
    import pandas as pd
    df_clean = df.copy()
    outlier_report = {}  # 生成异常值报告,便于后续调查
    for col in columns:
        if col not in df.columns:
            continue
        series = df[col].dropna()
        if method == 'iqr':
            # 四分位距法,适合厚尾分布的金融数据
            Q1 = series.quantile(0.25)
            Q3 = series.quantile(0.75)
            IQR = Q3 - Q1
            lower = Q1 - threshold * IQR
            upper = Q3 + threshold * IQR
            outliers = (series < lower) | (series > upper)
        else:
            # z分数法,适用于近似正态分布的因子数据
            mean = series.mean()
            std = series.std()
            z = np.abs((series - mean) / std)
            outliers = z > threshold
        # 标记异常值,不删除
        df_clean[f'{col}_outlier'] = False
        df_clean.loc[outliers.index[outliers], f'{col}_outlier'] = True
        # 生成异常值报告,记录数量、占比、关键日期,便于后续事件调查
        outlier_report[col] = {
            'count': outliers.sum(),
            'pct': outliers.mean() * 100,
            'dates': series.index[outliers].tolist()[:10]
        }
    # 异常值后续处理:结合市场事件调查(如闪崩、财报发布、政策调整)
    return df_clean, outlier_report

2. 缺失数据处理

数据缺失往往暗藏市场信号(如公司停牌、业绩披露延迟)。处理的核心是拒绝会引入前瞻偏差的方法,同时重视缺失数据背后的信息

核心规则:

  • 切勿使用前向填充(bfill)——这是引入前瞻性偏差的重灾区;
  • 反向填充(ffill)仅适用于参考数据(如行业分类),绝不适用于价格、收益率等核心交易数据;
  • 对金融数据进行插值(线性、多项式)极其危险。

可接受的方法:

  • 直接删除缺少关键数据的行;
  • 参考数据使用最后一个已知值(shift(1).ffill()),且需明确标注;
  • 将缺失值标记为NaN,在策略逻辑中单独处理;
  • 利用多个数据源交叉验证。

3. 股票池处理(幸存者偏差)

仅对当前存续的证券进行测试,会忽略已退市或被剔除的标的,这些标的往往具有低收益、高风险特征,导致策略年收益率被虚高1-2%。预防措施包括:

  • 使用时点成分列表,还原研究区间内的真实标的池;
  • 将退市、被剔除的证券纳入测试;
  • 优先使用无幸存者偏差的专业数据库;
  • 对免费数据源保持怀疑,手动补充缺失数据。

4. 时间点数据管理

上市公司财报常被重述,若使用重述后的数据进行回测(如用5月修订的数据做4月的交易决策),就构成了严重的“用未来数据决策”。解决方案包括:使用时间点数据库获取原始版本数据、以实际公布日期作为纳入策略的时点、并对基本面数据施加保守的滞后(如财报后45天再使用)。

5. 特征归一化

必须杜绝前瞻性偏差。若使用整个样本期的统计量对因子进行标准化,就等于让策略在回测中“预知”了未来的整体分布。正确做法是严格使用滚动窗口或扩展窗口进行归一化,仅基于历史数据计算统计量。

def proper_feature_normalization(df, feature_cols, method='zscore', window=252):
    """
    滚动归一化,从根源防止前瞻性偏差
    关键原则:始终使用扩展窗口或滚动窗口,切勿使用整个样本进行归一化
    """
    import numpy as np
    import pandas as pd
    df_norm = df.copy()
    for col in feature_cols:
        if col not in df.columns:
            continue
        series = df[col]
        if method == 'zscore':
            # 滚动z分数归一化,消除量纲影响
            rolling_mean = series.rolling(window, min_periods=window//2).mean()
            rolling_std = series.rolling(window, min_periods=window//2).std()
            df_norm[f'{col}_norm'] = (series - rolling_mean) / (rolling_std + 1e-8)  # 加小值避免除零
        elif method == 'rank':
            # 滚动秩归一化,适用于非正态分布的因子
            df_norm[f'{col}_norm'] = series.rolling(window).apply(
                lambda x: (x.iloc[-1] > x[:-1]).mean() if len(x) > 1 else 0.5,
                raw=False
            )
        elif method == 'minmax':
            # 滚动最大最小值归一化,将因子映射到[0,1]区间
            rolling_min = series.rolling(window).min()
            rolling_max = series.rolling(window).max()
            df_norm[f'{col}_norm'] = (series - rolling_min) / (rolling_max - rolling_min + 1e-8)
    return df_norm

6. 数据平稳性

绝大多数金融时间序列(如价格、交易量)都具有非平稳特性,直接建模易导致虚假回归。建模前必须通过变换转换为平稳序列,常用变换包括:收益率变换、差分变换、比率变换、Z分数变换。铁律:对于任何机器学习模型,原始价格绝不能直接作为输入,必须转换为收益率或其他平稳序列。

7. 回溯窗口选择

回溯窗口的长度选择,本质是在策略响应速度信号稳健性之间寻求平衡:

  • 波动周期策略:20-60天窗口(月度至季度);
  • 趋势与动量策略:60-252天窗口(季度至年度);
  • 均值回归策略:5-20天窗口(每周到每月);
  • 相关性计算:60-126天窗口。

一个实用原则是:根据市场波动状态灵活调整——高波动时期缩短窗口,低波动时期延长窗口。

关于回测

回测的核心目的不是“证明策略有效”,而是尽可能还原实盘场景,发现策略的潜在问题。回测的严格性直接决定策略实盘的存活率。

1. 前瞻性偏差

前瞻性偏差是指使用未来信息做出本应仅基于过去信息的决策,是导致“回测暴利、实盘亏损”的最常见且隐蔽的原因。其主要来源包括六大方面,前文在数据部分已提及。此外,可通过统计方法检测:

def check_lookahead_bias(df, signal_col, return_col, horizon=1):
    """
    前瞻偏差的统计检验,核心逻辑:存在前瞻偏差的信号,与过去收益相关性异常高
    """
    import numpy as np
    from scipy.stats import spearmanr
    signal = df[signal_col].dropna()
    results = {}
    # 检测信号与过去收益的相关性(正常情况下应极低)
    for lag in [1, 2, 5, 10]:
        past_ret = df[return_col].shift(lag)
        valid = signal.notna() & past_ret.notna()
        if valid.sum() > 30:  # 保证足够的观测数据
            corr, pval = spearmanr(signal[valid], past_ret[valid])
            results[f'corr_lag_{lag}'] = {'corr': corr, 'pval': pval}
            # 预警:相关性绝对值>0.1且p值<0.05,大概率存在前瞻偏差
            if abs(corr) > 0.1 and pval < 0.05:
                print(f"警告:信号与{lag}天的过去收益存在显著相关性!")
                print(f"相关性:{corr:.4f},p值:{pval:.4f}")
    # 检测信号与未来收益的相关性(策略有效时应显著为正/负)
    fwd_ret = df[return_col].shift(-horizon)
    valid = signal.notna() & fwd_ret.notna()
    if valid.sum() > 30:
        corr, pval = spearmanr(signal[valid], fwd_ret[valid])
        results['corr_forward'] = {'corr': corr, 'pval': pval}
    return results

2. 过拟合

过拟合是指策略过度拟合历史数据中的噪声,而非捕捉真实的市场规律。识别过拟合有六大核心信号:样本内外夏普比率差距悬殊;前向行走效率低于0.5;参数微调导致收益断崖下跌;随着测试次数增加策略越发复杂;完美契合历史噪声却在模拟数据中表现差;可复现性差。缓解措施包括:采用正则化技术、使用严格的时间序列交叉验证、限制模型复杂度、优先选用简单清晰的信号。

3. 交易成本模型

回测中常见的“零成本假设”会严重高估策略表现。一个贴合实际的交易成本模型应包含佣金、买卖价差、市场冲击成本、滑点乃至空头借贷费用。

class TransactionCostModel:
    """
    用于回测的实际交易成本模型,全面考虑佣金、点差、市场冲击、滑点、借贷成本
    适配大多数对冲基金的实盘交易场景,参数可根据券商费率、市场情况调整
    """
    def __init__(self,
                 commission_pct=0.0005,     # 佣金比例,默认万5
                 spread_bps=5,               # 买卖价差成本,单位:基点,默认5个基点
                 market_impact_bps=2,        # 市场冲击成本,每100万美元交易的基点数
                 borrow_cost_annual=0.005,   # 空头头寸的年化借贷成本,默认50个基点
                 min_commission=1.0):        # 最低佣金,默认1元
        self.commission_pct = commission_pct
        self.spread_bps = spread_bps / 10000  # 转换为比例
        self.market_impact_bps = market_impact_bps / 10000
        self.borrow_cost_annual = borrow_cost_annual
        self.min_commission = min_commission

    def compute_cost(self, trade_value, is_short=False, adv=None):
        """
        计算单笔交易的总成本
        参数:
        -----------
        trade_value : float
            交易的绝对美元/人民币价值
        is_short : bool
            是否为空头头寸,空头需计算借贷成本
        adv : float, optional
            标的平均每日交易量(美元/人民币),用于计算市场冲击
        返回值:
        --------
        total_cost : float
            总交易成本(美元/人民币)
        cost_breakdown : dict
            交易成本按组成部分分解,便于分析各成本占比
        """
        import numpy as np
        # 佣金成本,取比例佣金和最低佣金的最大值
        commission = max(trade_value * self.commission_pct, self.min_commission)
        # 买卖价差成本
        spread_cost = trade_value * self.spread_bps
        # 市场冲击成本,遵循平方根定律(交易规模越大,冲击成本越高)
        if adv and adv > 0:
            participation = trade_value / adv  # 交易规模占日均交易量的比例
            market_impact = trade_value * self.market_impact_bps * np.sqrt(participation)
        else:
            market_impact = trade_value * self.market_impact_bps
        # 空头借贷成本,按20个交易日(月均)计算
        borrow_cost = 0
        if is_short:
            borrow_cost = trade_value * self.borrow_cost_annual * (20 / 252)
        # 总交易成本
        total_cost = commission + spread_cost + market_impact + borrow_cost
        # 成本分解
        cost_breakdown = {
            'commission': commission,
            'spread': spread_cost,
            'market_impact': market_impact,
            'borrow_cost': borrow_cost
        }
        return total_cost, cost_breakdown

4. 前向分析

前向分析(Walk Forward Analysis)是最贴近实盘的验证方法,核心逻辑是“用历史数据逐步训练模型,用后续数据逐步测试模型”,能有效检测策略的样本外稳健性。

def walk_forward_analysis(df, feature_cols, target_col,
                          min_train_days=252,
                          refit_frequency=20,
                          embargo_days=10,
                          model_class=None):
    """
    具有适当时间结构的前向分析,时间序列验证的黄金标准
    核心逻辑:训练集→禁运期→测试集,避免信息泄露,还原实盘迭代场景
    """
    import numpy as np
    import pandas as pd
    from sklearn.preprocessing import StandardScaler
    results = []
    fold_stats = []
    # 过滤缺失值,保证数据有效性
    valid_mask = df[feature_cols + [target_col]].notna().all(axis=1)
    valid_data = df[valid_mask].copy()
    train_end_idx = min_train_days  # 最小训练天数,保证模型有足够的训练数据
    fold_num = 0
    while train_end_idx < len(valid_data) - embargo_days:
        fold_num += 1
        # 划分训练集:起始到train_end - 禁运期,避免信息泄露
        train_data = valid_data.iloc[:train_end_idx - embargo_days]
        # 划分测试集:train_end到train_end + 重拟合频率
        test_start = train_end_idx
        test_end = min(test_start + refit_frequency, len(valid_data))
        test_data = valid_data.iloc[test_start:test_end]
        if len(test_data) == 0:
            break
        # 提取特征和标签
        X_train = train_data[feature_cols].values
        y_train = train_data[target_col].values
        X_test = test_data[feature_cols].values
        y_test = test_data[target_col].values
        # 特征标准化:仅用训练集数据拟合,避免前瞻偏差
        scaler = StandardScaler()
        X_train_scaled = scaler.fit_transform(X_train)
        X_test_scaled = scaler.transform(X_test)
        # 模型训练与预测
        if model_class:
            model = model_class()
            model.fit(X_train_scaled, y_train)
            predictions = model.predict(X_test_scaled)
        else:
            predictions = np.zeros(len(y_test))  # 无模型时返回空预测
        # 记录每一次预测的结果
        for i, (idx, pred, actual) in enumerate(zip(test_data.index, predictions, y_test)):
            results.append({
                'date': idx,
                'prediction': pred,
                'actual': actual,
                'fold': fold_num
            })
        # 记录每一轮的折数统计,便于后续分析
        fold_stats.append({
            'fold': fold_num,
            'train_start': train_data.index[0],
            'train_end': train_data.index[-1],
            'test_start': test_data.index[0],
            'test_end': test_data.index[-1],
            'train_samples': len(train_data),
            'test_samples': len(test_data)
        })
        # 移动训练集终点,进行下一轮前向分析
        train_end_idx += refit_frequency
    return pd.DataFrame(results), fold_stats

5. 时间序列中的未来数据泄露

当预测目标为多期收益时,标准的随机划分会导致标签期的重叠而产生信息泄露。解决方案是引入 “清除(Purge)”“禁运(Embargo)” 技术。“清除”是在训练集末端移除一定数量的样本(通常为标签跨越周期H-1天)。“禁运”是在清除之后,于训练集与测试集之间再设置一段与标签周期H相当的空白间隔期。

6. 综合绩效评价

单一的夏普比率无法全面评估策略,实盘需要关注回撤、风险调整后收益、极端风险等。因此回测中需要计算综合绩效指标。

def compute_robust_metrics(returns, benchmark_returns=None, rf_rate=0.0):
    """
    计算量化策略的综合性能指标,全面评估收益、风险、回撤、统计显著性等
    """
    import numpy as np
    import pandas as pd
    from scipy.stats import skew, kurtosis, t
    r = returns.dropna()
    n = len(r)
    if n < 30:  # 保证足够的观测数据,避免指标失真
        return {'error': '数据不足(需要30个以上观测值)'}
    # 基础收益指标
    annual_return = r.mean() * 252  # 年化收益,按252个交易日计算
    annual_vol = r.std() * np.sqrt(252)  # 年化波动率
    sharpe = annual_return / annual_vol if annual_vol > 0 else 0  # 夏普比率
    # 回撤相关指标(实盘最关注的风险指标)
    cum_returns = (1 + r).cumprod()  # 累计收益
    running_max = cum_returns.expanding().max()  # 滚动最大值
    drawdowns = (cum_returns - running_max) / running_max  # 回撤序列
    max_dd = drawdowns.min()  # 最大回撤
    calmar = annual_return / abs(max_dd) if max_dd != 0 else np.nan  # 卡玛比率
    # 高阶矩:偏度和峰度,评估收益分布的极端风险
    ret_skew = skew(r)  # 偏度:<0为左偏,存在极端下跌风险;>0为右偏
    ret_kurt = kurtosis(r)  # 峰度:>0为厚尾,极端行情发生概率更高
    # 下行风险指标:索提诺比率(仅考虑下行波动率)
    downside_returns = r[r < 0]
    downside_vol = downside_returns.std() * np.sqrt(252) if len(downside_returns) > 0 else annual_vol
    sortino = annual_return / downside_vol if downside_vol > 0 else 0
    # 统计显著性:t统计量和p值,评估收益的统计可靠性
    t_stat = sharpe * np.sqrt(n / 252)
    p_value = 2 * (1 - t.cdf(abs(t_stat), df=n-1))
    # 盈亏比指标:欧米茄比率
    gains = r[r > 0].sum()  # 总盈利
    loss = abs(r[r < 0].sum())  # 总亏损
    omega = gains / loss if loss > 0 else np.nan
    # 基础绩效指标字典
    metrics = {
        'annual_return': annual_return,
        'cumulative_return': cum_returns.iloc[-1] - 1,
        'annual_volatility': annual_vol,
        'max_drawdown': max_dd,
        'avg_drawdown': drawdowns.mean(),
        'sharpe_ratio': sharpe,
        'sortino_ratio': sortino,
        'calmar_ratio': calmar,
        'omega_ratio': omega,
        't_statistic': t_stat,
        'p_value': p_value,
        'significant_5pct': p_value < 0.05,
        'skewness': ret_skew,
        'kurtosis': ret_kurt,
        'n_observations': n,
        'hit_rate': (r > 0).mean(),
        'profit_factor': gains / loss if loss > 0 else np.nan
    }
    # 相对基准的绩效指标(如超额收益、阿尔法、贝塔)
    if benchmark_returns is not None:
        bm = benchmark_returns.reindex(r.index).dropna()
        if len(bm) > 30:
            excess = r.loc[bm.index] - bm  # 超额收益
            tracking_error = excess.std() * np.sqrt(252)  # 跟踪误差
            info_ratio = excess.mean() * 252 / tracking_error if tracking_error > 0 else 0  # 信息比率
            # 计算阿尔法和贝塔
            cov = r.loc[bm.index].cov(bm)
            bm_var = bm.var()
            beta = cov / bm_var if bm_var > 0 else 1
            alpha = (r.loc[bm.index].mean() - rf_rate / 252 - beta * (bm.mean() - rf_rate / 252)) * 252
            # 更新相对指标
            metrics.update({
                'alpha': alpha,
                'beta': beta,
                'information_ratio': info_ratio,
                'tracking_error': tracking_error,
                'annual_excess_return': excess.mean() * 252
            })
    return metrics

7. 参数敏感性分析

稳健的量化策略不应因参数的微小扰动而急剧退化。参数敏感性分析的核心是测试策略在参数合理范围内的表现,筛选出参数稳健的策略。

def parameter_sensitivity_analysis(strategy_func, base_params, param_ranges, df):
    """
    分析策略性能对参数变化的敏感度,评估策略的稳健性
    核心逻辑:在参数合理范围内微调,观察策略夏普比率的变化
    """
    import numpy as np
    results = {}
    sensitivity_metrics = {}
    # 遍历每个待测试的参数
    for param_name, values in param_ranges.items():
        results[param_name] = []
        # 遍历参数的每个取值
        for val in values:
            test_params = base_params.copy()
            test_params[param_name] = val  # 微调当前参数
            try:
                # 运行策略,获取核心绩效指标(夏普比率)
                sharpe = strategy_func(df, **test_params)
                results[param_name].append({'value': val, 'sharpe': sharpe})
            except Exception as e:
                # 捕获策略运行错误,标记为NaN
                results[param_name].append({'value': val, 'sharpe': np.nan, 'error': str(e)})
        # 计算参数敏感性指标,评估策略对该参数的敏感程度
        sharpes = [r['sharpe'] for r in results[param_name] if not np.isnan(r['sharpe'])]
        if len(sharpes) > 1:
            sensitivity_metrics[param_name] = {
                'mean_sharpe': np.mean(sharpes),
                'std_sharpe': np.std(sharpes),
                'cv': np.std(sharpes) / abs(np.mean(sharpes)) if np.mean(sharpes) != 0 else np.nan,  # 变异系数
                'min_sharpe': np.min(sharpes),
                'max_sharpe': np.max(sharpes)
            }
    return results, sensitivity_metrics

关于实盘

在初创对冲基金,研究出有效策略只是第一步,将策略平稳落地到实盘,实现持续收益输出,才是核心价值。这一步需要解决研究代码与生产代码的分离、仓位管理、风险控制、Alpha衰变检测等问题。

研究代码和生产代码的核心目标不同,必须严格分离。

  • 研究代码:核心是快速迭代、验证假设,允许探索性混乱,可使用Jupyter Notebook,注重速度。
  • 生产代码:核心是稳健、可靠、可维护,必须是清洁、有注释、经过严格测试的Python模块,采用配置驱动,注重稳定性。

从研究到生产的建议流程:

  1. 冻结研究代码版本,固定策略核心逻辑和参数。
  2. 提取核心逻辑,封装为独立Python模块,去除探索性代码。
  3. 添加综合测试(单元测试、集成测试)。
  4. 采用配置驱动,将所有可调参数放入配置文件。
  5. 进行代码审查。
  6. 先部署到模拟交易中运行验证,再投入实盘。

仓位管理

仓位规模调整的核心是在策略收益和交易风险之间找平衡。初创基金资金规模有限,更需要科学的仓位调整方法。

class PositionSizer:
    """
    生产就绪的仓位规模调整方法,支持凯利准则、波动率目标、风险平价三种核心方法
    适配对冲基金实盘交易场景,可根据策略类型和资金规模灵活选择
    """
    @staticmethod
    def kelly_criterion(win_rate, avg_win, avg_loss, fraction=0.25):
        """
        分数凯利准则仓位调整,适用于趋势类、择时类策略
        核心:完全凯利准则最优但波动率过大,生产中使用1/4-1/2分数凯利,平衡收益和风险
        参数:
        win_rate: 策略胜率
        avg_win: 平均盈利
        avg_loss: 平均亏损
        fraction: 分数凯利系数,默认0.25(最稳健)
        """
        if avg_loss == 0:
            return 0
        b = avg_win / avg_loss  # 盈亏比
        p = win_rate
        q = 1 - win_rate
        kelly = (p * b - q) / b  # 标准凯利公式
        return max(0, min(kelly * fraction, 1.0))  # 仓位限制在0-1之间

    @staticmethod
    def volatility_targeting(returns, target_vol, current_position=1.0,
                             lookback=20, vol_cap=2.0):
        """
        波动率目标仓位调整,适用于多因子、指数增强策略
        核心:调整仓位规模,使策略的实际波动率始终贴近目标波动率
        参数:
        returns: 策略历史收益序列
        target_vol: 目标年化波动率
        current_position: 当前仓位
        lookback: 回溯窗口,默认20天
        vol_cap: 仓位缩放上限,默认2.0(避免仓位过高)
        """
        import numpy as np
        recent_returns = returns.iloc[-lookback:]
        implemented_vol = recent_returns.std() * np.sqrt(252)  # 实际年化波动率
        if implemented_vol <= 0:
            return current_position
        scale = target_vol / implemented_vol  # 仓位缩放比例
        scale = np.clip(scale, 1/vol_cap, vol_cap)  # 限制缩放比例
        return current_position * scale

    @staticmethod
    def risk_parity_weights(covariance_matrix, target_risk=None):
        """
        风险平价仓位分配,适用于多资产、多策略组合
        核心:让每个资产/策略对组合的风险贡献相等,实现风险分散
        参数:
        covariance_matrix: 资产/策略的协方差矩阵
        target_risk: 组合的目标年化波动率
        """
        import numpy as np
        import pandas as pd
        from scipy.optimize import minimize
        cov = covariance_matrix.values
        n = len(cov)
        # 风险平价目标函数:最小化各资产风险贡献的方差
        def risk_budget_objective(weights):
            port_vol = np.sqrt(weights @ cov @ weights)
            marginal_contrib = cov @ weights / port_vol
            risk_contrib = weights * marginal_contrib
            return np.sum((risk_contrib - port_vol/n)**2)
        # 约束条件:权重和为1,单权重在0-1之间(不做空)
        constraints = [{'type': 'eq', 'fun': lambda w: np.sum(w) - 1}]
        bounds = [(0, 1) for _ in range(n)]
        # 优化求解风险平价权重
        result = minimize(
            risk_budget_objective,
            x0=np.ones(n)/n,  # 初始权重:等权重
            method='SLSQP',
            bounds=bounds,
            constraints=constraints
        )
        weights = pd.Series(result.x, index=covariance_matrix.columns)
        # 若设置目标风险,调整权重使组合风险贴近目标风险
        if target_risk:
            current_risk = np.sqrt(weights.values @ cov @ weights.values) * np.sqrt(252)
            weights = weights * (target_risk / current_risk)
        return weights

Alpha 衰变探测

量化策略的Alpha不是永恒的。实盘中必须建立Alpha衰变探测机制,实时监控策略表现。

核心探测方法:

  • 滚动绩效指标监控:实时计算滚动夏普比率等,与回测指标对比,若持续低于历史水平的50%,则预警。
  • 收益分布监控:监控实盘收益的偏度、峰度、胜率,若发生根本性变化(如胜率大幅下降),则说明核心逻辑可能失效。
  • 因子有效性监控:对于多因子策略,实时监控每个因子的IC值、IC_IR,若核心因子有效性持续下降,需及时替换。
  • 策略拥挤度监控:通过市场成交量、资金流向等指标监控策略拥挤度,若持续上升,则Alpha可持续性下降。

Alpha衰变的应对策略:

  • 短期:降低仓位,减少交易频率。
  • 中期:调整参数、替换失效因子、优化策略逻辑。
  • 长期:若核心经济逻辑已失效,果断停止策略,转移资源。

总结

在初创对冲基金做量化研究,最大的收获是学会了用“实盘思维”做研究——懂得经济逻辑比回测收益更重要,稳健性比高收益更重要,风险控制比盈利更重要。

量化研究的本质,是在市场的非有效性中寻找可复现的Alpha,并通过严格的风险控制将其转化为实盘收益。初创基金的优势是“船小好调头”,劣势是资源有限、抗风险能力弱。因此,必须坚持“简单、稳健、可落地”的原则,守住“研究有逻辑、回测贴实盘、实盘能盈利、风险可控制”的核心底线。

作为量化研究员,最核心的能力不是编写复杂的代码,而是独立的思考能力、严谨的研究方法、敏锐的市场洞察力,以及果断的止损意识。懂得如何提出有价值的假设,如何严谨验证,如何发现问题,如何在实盘中控制风险,如何在Alpha衰变时及时止损——这些能力,才是最宝贵的财富。

欢迎在云栈社区继续交流探讨量化投资与人工智能智能&数据&云领域的应用实践。




上一篇:Python Flask 构建热点新闻RESTful API:查询、分页与状态管理实战
下一篇:实战指南:基于GitHub Actions的Docusaurus站点自动化部署
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-28 15:32 , Processed in 0.262056 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表