季末粉饰橱窗效应:基金排名压力如何扭曲最后三天的价格
基金季末要披露持仓,于是有动机在最后几天买入本季赢家、卖出输家——让报表看起来英明。12 年 48 个季度、300 只股票的模拟把这个效应量化:季末最后 3 天赢家减输家价差 53.16bp(t=6.02,81.3% 的季度为正),新季度前 3 天反转 -55.90bp(t=-7.17),反转覆盖比例 105.2%——涨出来的全吐回去了,这是「价格压力」而非「信息」的判定性证据。但真正决定这项研究成立与否的是三个对照:非季末月末同一套算法只有 5.92bp(t=0.95,不显著),排除了月度再平衡流的解释;随机日期安慰剂 14.29bp(t=1.94)刚好卡在临界值,说明短期动量本身贡献了约四分之一,必须扣除;无粉饰压力的纯净市场跑出 -7.22bp(t=-0.85),确认引擎不造假信号。横截面识别更硬:同为赢家,高机构持仓组 +5.21bp、低机构持仓组 -8.20bp。可交易性则很骨感——单边 15bp 成本下 Sharpe 从 1.74 掉到 0.76,盈亏平衡成本约 26bp;容量墙在 5 亿元(净收益 -1.45bp),30 亿元时成交完成率只剩 60.4%。附一个隐蔽 bug 复盘:分季度写入收益时,下一季的赋值会覆盖上一季注入的反转压力
基金每季度要披露持仓。这意味着在季末最后几天,基金经理面对一个不对称的激励:买入本季表现好的股票,让持仓报表看起来像是早就抓住了它们;卖出表现差的,让失误从名单上消失。
这就是 window dressing(粉饰橱窗)。它有个很干净的可检验预测——如果这是真的价格压力而非信息,那么季末被推高的部分应该在新季度开盘后吐回去。
12 年 48 个季度、300 只股票的模拟给出的答案:季末最后 3 天赢家减输家 53.16bp(t=6.02),新季度前 3 天反转 -55.90bp(t=-7.17),反转覆盖 105.2%。

左图直接画多空价差(已剔除市场漂移),反转看得很清楚:涨到峰值 0.47%,然后几乎全部吐回。右图是分组原始路径——两条线在事件窗口里同向上行,那是市场收益,如果只看右图会误以为”输家也在涨、哪来的压力”。这是分组事件研究最常见的读图错误:必须看价差,不能看分组绝对水平。
一、机制设定:把压力写成可审查的公式#
模拟的核心是让粉饰压力同时依赖三个变量,这样后面才能做横截面识别:
# 季内表现排名(只用粉饰窗口之前的数据,避免用未来信息选样)
qtd = ret[s:e - DRESS_DAYS].sum(axis=0)
rank = pd.Series(qtd).rank(pct=True).values
tilt = 2 * (rank - 0.5) # -1(最差) ~ +1(最好)
# 流动性越差,同样资金推动幅度越大
liq_amp = (adv.mean() / adv) ** 0.35
liq_amp = liq_amp / liq_amp.mean()
# 压力 ∝ 季内表现 × 机构持股比例 × 流动性放大
push = DRESS_STRENGTH * tilt * inst_own / inst_own.max() * liq_amp
push *= (1 + 0.25 * rng.standard_normal(N_STOCK)) # 每季强度有噪音python压力按 [0.2, 0.3, 0.5] 的权重分配到最后 3 天(越接近季末越猛),然后在新季度前 3 天按 [0.5, 0.3, 0.2] 释放 85%。
DRESS_STRENGTH = 0.007,即顶格机构持仓的赢家被推高 0.7%。这个量级参考了 Carhart 等人对季末价格模式的实证区间——不是拟合出来的,是先设定后检验。
一个会静默毁掉整份研究的 bug#
第一版代码我把”生成常规收益”和”注入粉饰压力”写在同一个季度循环里:
for q in range(QUARTERS):
s, e = q*DAYS_Q, (q+1)*DAYS_Q
ret[s:e] = 生成常规收益(...) # ← 这一行
...
ret[e:e+3] += 反转压力 # ← 写到了下一季的区间python问题在于:第 q 季写入 ret[e:e+3] 的反转压力,会被第 q+1 季的 ret[s:e] = ... 整段覆盖掉。注意这是赋值 = 不是累加 +=。
后果是反转消失。第一版跑出来 H2 是 +13.04bp(t=1.49)——方向反了,还不显著。如果我当时没有交叉验证”反转覆盖比例应该接近 1”这个先验,很可能就写成”粉饰效应存在但不反转,说明它包含真实信息”这种完全错误的结论。
修复方法是拆成两遍:
# 第一遍:先生成全部常规收益
for q in range(QUARTERS):
ret[s:e] = 生成常规收益(...)
# 第二遍:再叠加压力与反转(用 += )
for q in range(QUARTERS):
ret[e-3:e] += push * w_d
ret[e:e+3] += -push * 0.85 * w_rpython修复后 H2 变成 -55.90bp(t=-7.17),反转覆盖比例 105.2%。
这类 bug 的可怕之处在于它不报错、不产生 NaN、结果看起来还挺”有意思”。唯一能抓住它的是先验:如果这是价格压力,它必须反转。当数据说”不反转”时,第一反应应该是查代码,而不是编故事。
二、三个对照:这到底是不是季末特有的?#
单看 53.16bp 和 t=6.02 说明不了什么。至少有三个替代解释需要排除。
对照一:会不会是月度再平衡流?#
很多机构每月末调仓。如果非季末的月末也有同样效应,那抓到的就不是”季末披露压力”,而是”月度资金流”。
用完全相同的算法跑季内第 1、2 个月末(96 个样本):
| 窗口 | 赢家−输家 3 日价差 | t 值 |
|---|---|---|
| 季末 | 53.16bp | 6.02 |
| 非季末月末 | 5.92bp | 0.95 |
非季末月末不显著。 效应确实集中在季末,与”披露驱动”的假说一致。

对照二:会不会只是短期动量?#
“季内涨得多的股票在接下来 3 天继续涨”——这可能只是动量,跟季末没关系。用随机日期安慰剂检验:在季内随机挑一个日期,用同样的方法排名并测算之后 3 天的价差。
结果:14.29bp(t=1.94)。
这个数字很关键,它没有归零。t=1.94 刚好卡在 5% 显著性的边缘。诚实的读法是:53.16bp 里大约有 14bp 来自短期动量本身,真正归因于季末的增量约 39bp,而不是全部 53bp。
如果只做对照一就收工,会把动量的贡献算进粉饰效应,高估约 27%。
对照三:引擎会不会自己造信号?#
生成一个完全没有粉饰压力的纯净市场,跑同一套策略:
-7.22bp(t=-0.85),不显著。引擎不造假信号。
三、横截面识别:压力应该随机构持仓放大#
时序对照能排除日历解释,但最有力的证据来自横截面。如果这真是基金行为驱动的,那么同样是赢家,机构持股比例高的应该被推得更猛。
把赢家组按机构持股比例再切两半:
| 分组 | 季末 3 日收益 |
|---|---|
| 高机构持仓赢家 | +5.21bp |
| 低机构持仓赢家 | -8.20bp |
方向和单调性都对上了,而且低机构持仓组是负的——它们没有被粉饰,只表现出正常的短期反转。
这个检验比时序对照更难被替代解释攻破:任何”市场整体在季末上涨”的说法都无法解释为什么涨幅系统性地依赖机构持股比例。
四、稳定性:81% 的季度为正,但离散度很大#

48 个季度里 39 个为正(81.3%),但柱子高低差异极大——最好的季度接近 200bp,最差的接近 -120bp。
这是日历效应策略的典型形态:统计上稳健,单季度上不可靠。t=6.02 是 48 个季度累积出来的,任何单一季度都可能大幅偏离。用它做实盘意味着必须容忍连续几个季度的回撤,而每年只有 4 次机会——样本累积速度极慢,10 年才 40 个观测。这也意味着,即使效应真实存在,你也很难在有生之年用实盘业绩把它和运气区分开。
五、可交易性:成本容忍度很低#
前面都是毛收益。加上成本:
| 单边成本 | 全市场 Sharpe | 高机构持仓过滤 Sharpe |
|---|---|---|
| 0bp | 1.74 | 1.69 |
| 5bp | 1.41 | 1.45 |
| 10bp | 1.08 | 1.20 |
| 15bp | 0.76 | 0.96 |
| 20bp | 0.43 | 0.71 |
| 30bp | -0.22 | 0.22 |
| 40bp | -0.88 | -0.28 |

盈亏平衡成本约 26bp(全市场)。 A 股卖出印花税 5bp + 双边佣金 + 冲击,机构口径下 15-25bp 是现实区间——也就是说这个策略在扣完成本后剩不下多少。
注意一个反直觉的现象:在低成本区,高机构持仓过滤反而略差(0bp 时 1.69 vs 1.74)。因为过滤后股票池从 300 缩到约 150,每边只能选 30 只,分散度下降、波动上升。但成本一旦超过 10bp,过滤组就全面胜出(30bp 时 0.22 vs -0.22)——信号密度的价值只在成本足够高时才体现出来。
六、容量墙#
加上平方根冲击成本和成交量约束(单只不超过窗口成交额的 8%):
| 策略规模 | 成交完成率 | 冲击成本 | 每季净收益 | Sharpe |
|---|---|---|---|---|
| 0.1 亿 | 100% | 3.48bp | +19.68bp | 0.64 |
| 0.5 亿 | 100% | 7.78bp | +15.38bp | 0.50 |
| 1 亿 | 100% | 11.01bp | +12.15bp | 0.40 |
| 5 亿 | 100% | 24.61bp | -1.45bp | -0.05 |
| 10 亿 | 99.1% | 34.60bp | -11.93bp | -0.39 |
| 30 亿 | 60.4% | 42.90bp | -40.91bp | -2.20 |
5 亿元是临界规模。 更值得注意的是失效的顺序:在 5 亿时成交完成率还是 100%,杀死策略的是冲击成本;要到 30 亿成交完成率才掉到 60.4%。
“买得进”和”买得划算”是两个不同的约束,前者远比后者宽松。只检查持仓上限而不建模冲击,会把容量高估一个数量级。
还有一层现实约束模型没有覆盖:这个策略要在季末最后 3 天集中调仓,而这正是粉饰资金自己在抢的时间窗口。你的对手盘是一群有强制披露约束、对价格不敏感的买家——听起来像好事,但同时意味着流动性在这 3 天被大量消耗,实际冲击很可能高于平方根律的估计。
七、总结:真实、可识别、但基本不可交易#
三层结论分开说:
统计层面成立。 53.16bp(t=6.02),81.3% 的季度为正,反转覆盖 105.2%——涨出来的全吐回去,这是价格压力而非信息的判定性特征。
因果识别成立。 非季末月末不显著(t=0.95)排除月度再平衡;横截面上压力随机构持股比例单调放大(+5.21bp vs -8.20bp)排除市场整体解释;纯净市场对照(t=-0.85)排除引擎伪影。
交易层面基本不成立。 盈亏平衡成本约 26bp,容量约 5 亿元,且随机日期安慰剂(14.29bp,t=1.94)说明其中约四分之一其实是普通短期动量。扣掉这部分、扣掉成本,剩下的空间对绝大多数资金规模都不值得占用一条策略线。
它更适合的用途是风险管理而非 alpha:如果你的组合恰好重仓了一批本季大涨、机构持仓集中的股票,那么季末最后 3 天的账面浮盈里有一部分是借来的,新季度开盘会还回去。知道这一点,比试图去交易它更有价值。
局限与偏差#
这是结构模拟不是历史回测。 压力强度、反转比例、衰减权重全部外生设定。它能验证”如果机制成立,能观测到什么、能否交易”,但不能证明真实市场中该效应的具体幅度。真实检验需要基金持仓披露数据 + 逐笔成交数据。
监管环境在变。 各主要市场近年都加强了对季末异常交易的监控,部分司法辖区将刻意抬价列为操纵。历史上存在的效应强度不能外推到当下,且效应本身可能因为被广泛研究而衰减。
冲击成本模型偏乐观。 平方根律的系数取自常态市场,而季末最后 3 天流动性被粉饰资金大量消耗,真实冲击很可能更高。5 亿容量应视为上界。
A 股的实施约束未建模。 空头端需要融券,而季末赢家往往券源紧张、费率高企;T+1 制度也限制了调仓灵活性。本文的多空框架在 A 股更接近理论构造,实盘更可行的形态是单边规避(季末减持高机构持仓的本季赢家)而非双边套利。