Pandas实现首行独立、后续行递推的权重再平衡日度回测计算
可落地实现方案
实现思路
- 带状态递推、连续计数的逻辑,纯向量化的
np.where/lambda写法几乎必然会引入边界bug,逐行循环是最稳定、最易校验的方案:5年日度数据也就1200多行,循环开销可以忽略,比反复调试向量化逻辑的成本低得多。 - 全程维护3个独立的全局状态,完全不依赖行索引位置:
- 上一个交易日各标的的持仓价值
- 每个标的连续超出权重区间的天数计数器
- 待生效的再平衡基准值(触发再平衡当日的组合总价值,供下一个交易日换算初始持仓)
预定义静态参数
把所有配置抽离,避免硬编码在循环逻辑里:
import pandas as pd import numpy as np # 初始权重(百分比口径) init_weights = pd.Series({ "SPY": 50, "IWM": 25, "TLT": 25 }) # 各标的的权重波动区间:{标的: (下限, 上限)} weight_bands = { "SPY": (40, 60), "IWM": (20, 30), "TLT": (20, 30) } # 触发再平衡所需的连续超带天数 REBAL_TRIGGER_DAYS = 5
核心递推逻辑
逐行遍历收益率数据,维护状态完成全流程计算:
# 初始化结果容器 holding = pd.DataFrame(index=ret_df.index, columns=ret_df.columns, dtype=np.float64) weight = pd.DataFrame(index=ret_df.index, columns=ret_df.columns, dtype=np.float64) rebal_record = pd.Series(False, index=ret_df.index) # 记录当日是否触发再平衡 # 初始化循环状态 prev_holding = None # 前一交易日的持仓价值 overband_days = {ticker: 0 for ticker in ret_df.columns} # 单标的连续超带计数 rebal_base = None # 待生效的再平衡换算基准 for curr_date, daily_ret in ret_df.iterrows(): # 1. 计算当日持仓价值 if prev_holding is None: # 首个交易日:初始权重直接乘当日收益率 curr_holding = init_weights * daily_ret elif rebal_base is not None: # 再平衡后的第一个交易日:按规则用初始权重/前一日总价值换算基数,再乘当日收益率 curr_holding = (init_weights / rebal_base) * daily_ret rebal_base = None # 基准生效后清空 else: # 普通交易日:前一日持仓乘当日收益率 curr_holding = prev_holding * daily_ret # 存储当日持仓、计算实际权重 holding.loc[curr_date] = curr_holding total_value = curr_holding.sum() curr_weight = curr_holding / total_value * 100 # 权重转百分比口径 # 2. 更新超带计数、判断是否触发再平衡 is_trigger = False for ticker in ret_df.columns: low, high = weight_bands[ticker] # 判断当前权重是否超出区间 out_of_band = (curr_weight[ticker] < low) or (curr_weight[ticker] > high) if out_of_band: overband_days[ticker] += 1 # 任意标的连续超带达到阈值即触发再平衡 if overband_days[ticker] >= REBAL_TRIGGER_DAYS: is_trigger = True else: # 权重回到区间内则清零计数 overband_days[ticker] = 0 # 3. 处理再平衡后续动作 if is_trigger: rebal_record.loc[curr_date] = True # 记录当日总价值作为下一个交易日的换算基准 rebal_base = total_value # 清零所有超带计数 overband_days = {ticker: 0 for ticker in ret_df.columns} # 更新存储和状态 weight.loc[curr_date] = curr_weight prev_holding = curr_holding
逻辑对齐校验
- 首行单独分支处理,完全隔离后续逻辑,不会出现布尔判断错位的问题
- 所有递推逻辑只依赖循环内维护的状态,不依赖datetime索引位置,哪怕数据中间有缺失行,只要迭代顺序正确,计算结果就不会出错
- 连续超带计数逐行累加,权重回到区间立即清零,完全匹配“连续5天超出区间”的规则
- 再平衡触发逻辑在当日收盘计算完权重后执行,记录当日总价值作为下一个交易日的换算基数,和给出的计算示例完全对齐
性能说明
- 针对5年约1250个交易日的日度数据,整个循环运行耗时在10ms级别,无性能瓶颈
- 相比反复调试纯向量化逻辑的时间成本,这种实现方式的开发效率和可维护性要高得多
内容的提问来源于stack exchange,提问作者DMike
相关产品推荐
相关产品推荐

