调仓时机核心逻辑:趋势跟踪与均值回归、动量因子与反转因子、市场状态识别
说实话,调仓时机这事儿,我琢磨了好几年。
刚开始做轮动策略那会儿,我总觉得只要选对股票池,什么时候调仓都差不多。结果呢?回测曲线漂亮得像假的一样,实盘一跑就露馅。后来我才明白——调仓时机,才是轮动策略的命门。
今天咱们就聊聊这个核心逻辑。我会把趋势跟踪、均值回归、动量因子、反转因子,还有市场状态识别,串成一条线来讲。
1. 趋势跟踪 vs 均值回归:两种世界观
先问个问题:你觉得价格会沿着当前方向继续走,还是会掉头回来?
这个问题,决定了你用什么逻辑去判断调仓时机。
趋势跟踪:价格会延续当前方向。涨的还会涨,跌的还会跌。
均值回归:价格会回到历史均值附近。涨多了会跌,跌多了会涨。
我个人习惯把这两种逻辑看作「矛」和「盾」。趋势跟踪是矛,进攻性强;均值回归是盾,防守性好。你不可能同时用矛和盾去攻击,但你可以根据市场状态切换。
举个例子。我在2020年做过一个行业轮动策略,用的是趋势跟踪。那一年新能源涨疯了,趋势跟踪让我一直持有,收益翻倍。但到了2021年春节后,市场风格突变,趋势跟踪开始频繁打脸。我赶紧切到均值回归逻辑,才把回撤控制住。
嗯,这里要注意:没有哪种逻辑永远有效。关键是你得知道什么时候该用哪个。
2. 动量因子与反转因子:量化你的判断
光有逻辑还不够,你得把它量化成因子。
动量因子,说白了就是「过去涨得好,未来还会涨」。我常用的动量因子有:
- 过去N日收益率(比如20日、60日)
- 过去N日收益率除以波动率(夏普比率变种)
- 过去N日相对强度(RSI的一种变形)
反转因子,就是「过去涨太多,该回调了」。常用的反转因子:
- 过去N日收益率(负值越大,反转概率越高)
- 偏离均线的程度(比如偏离200日均线超过20%)
- 布林带位置(触及上轨或下轨)
我曾经犯过一个错误:同时用动量因子和反转因子做信号,结果两个信号打架,策略完全失效。后来我学乖了——要么用动量,要么用反转,别混着来。除非你有一个明确的切换机制。
我的经验:动量因子在趋势市里表现好,反转因子在震荡市里表现好。如果你能识别市场状态,就能决定用哪个因子。
3. 市场状态识别:调仓时机的「总开关」
市场状态识别,是我认为整个调仓时机判断里最重要的一环。没有它,你的因子就是无头苍蝇。
我一般把市场状态分成三种:
| 市场状态 | 特征 | 适合的因子 | 调仓频率 |
|---|---|---|---|
| 趋势市 | 价格沿均线运行,波动率稳定 | 动量因子 | 低频(周度或月度) |
| 震荡市 | 价格在区间内来回摆动 | 反转因子 | 中频(日度或隔日) |
| 无序市 | 价格随机波动,无规律 | 不调仓或减仓 | 不调仓 |
怎么识别?我常用的方法有三个:
- ADX指标:ADX > 25 是趋势市,ADX < 20 是震荡市。这个指标我用了好几年,简单粗暴但有效。
- 波动率聚类:用过去20日波动率的变化率。如果波动率突然放大,可能是无序市的信号。
- 均线排列:短期均线在长期均线上方且发散,是趋势市;均线缠绕在一起,是震荡市。
避坑指南:我曾经只用ADX一个指标做市场状态识别,结果在2022年3月那波急跌中,ADX快速飙升到40以上,我以为是趋势市,继续用动量因子做多,结果被狠狠收割。后来我加了波动率过滤——当波动率超过历史90%分位数时,强制切换到「观望」状态。
4. 核心逻辑框架图
下面这张图,是我自己总结的调仓时机判断框架。你看一眼就能明白整个逻辑链条。
你看,整个逻辑其实不复杂。先识别市场状态,再选对应的因子,最后做调仓决策。每一步都有据可依,而不是拍脑袋。
5. 代码示例:一个简单的市场状态识别器
光说不练假把式。我给你写个简单的市场状态识别函数,用的是ADX加波动率过滤。
import pandas as pd
import numpy as np
def market_state_recognizer(df, lookback=20):
"""
识别市场状态
返回:'trend', 'range', 'chaos'
"""
# 计算ADX
high = df['high']
low = df['low']
close = df['close']
# 简化版ADX计算
tr = np.maximum(high - low,
np.abs(high - close.shift(1)),
np.abs(low - close.shift(1)))
atr = tr.rolling(lookback).mean()
up_move = high - high.shift(1)
down_move = low.shift(1) - low
plus_dm = np.where((up_move > down_move) & (up_move > 0), up_move, 0)
minus_dm = np.where((down_move > up_move) & (down_move > 0), down_move, 0)
plus_di = 100 * pd.Series(plus_dm).rolling(lookback).mean() / atr
minus_di = 100 * pd.Series(minus_dm).rolling(lookback).mean() / atr
dx = 100 * np.abs(plus_di - minus_di) / (plus_di + minus_di)
adx = dx.rolling(lookback).mean()
# 计算波动率
returns = close.pct_change()
volatility = returns.rolling(lookback).std()
vol_percentile = volatility.rank(pct=True).iloc[-1]
# 判断状态
current_adx = adx.iloc[-1]
if vol_percentile > 0.9:
return 'chaos' # 高波动,无序市
elif current_adx > 25:
return 'trend' # 趋势市
elif current_adx < 20:
return 'range' # 震荡市
else:
return 'range' # 默认震荡市
这个函数我实盘用过,效果还行。但你要注意,参数需要根据你的品种和周期去调。比如做日线轮动,lookback设20比较合适;做小时线,可能得设40。
一个小技巧:把市场状态识别做成一个独立的模块,每天跑一次。这样你的调仓逻辑就清晰了——状态变了,因子就变,调仓频率也跟着变。
6. 避坑指南:我踩过的三个坑
最后,分享几个我亲身踩过的坑,希望能帮你省点学费。
- 坑一:过度优化因子参数。我曾经花了一周时间,把动量因子的回溯期从20天优化到23天,回测收益提升了5%。结果实盘一跑,反而亏了。后来我明白了——参数越优化,越容易过拟合。现在我只用整数参数,比如20、60、120。
- 坑二:忽略交易成本。趋势跟踪策略在震荡市里会频繁调仓,交易成本能把收益吃光。我吃过这个亏,后来在策略里加了调仓成本惩罚——每次调仓扣掉0.1%的成本,回测结果瞬间真实了很多。
- 坑三:市场状态切换太慢。ADX指标有滞后性,等它确认趋势时,行情可能已经走了一半。我的解决办法是:用多个时间周期的ADX做综合判断,比如同时看日线、周线、月线的ADX,取多数投票。
好了,关于调仓时机的核心逻辑,就聊到这儿。记住一句话:没有完美的因子,只有合适的时机。把市场状态识别做好了,你的轮动策略就成功了一半。
公众号:蓝海资料掘金营,微信deep3321