作者:老余捞鱼
原创不易,转载请标明出处及原作者。

写在前面的话:常有人问:没内幕、没资金,普通人怎么在市场里生存?我的答案很简单:借力。别总想着逆市去接那些下坠的飞刀,要学会识别那些已经跑起来的“快车”并跳上去。今天我就把这套基于物理学“惯性”原理的动量策略拆解开,告诉你如何利用市场的情绪惯性,做一个聪明的随风起舞者。
一、别把动量当成了追涨杀跌
刚开始接触动量策略那会儿,我的想法特别简单:价格突破昨天的高点,我就跟进;跌破昨天的低点,我就离场。看起来合情合理,对吧?
但当我把这套逻辑扩展到更多品种、更长时间段时,噩梦来了。我给它起了个名字——”千刀万剐式亏损”。
具体来说,就是小亏不断,偶尔大赚,但总体收益曲线居然跑不赢最简单的”买了放着不动”。问题在哪?我后来发现,我把所有突破都当成了一回事,完全没考虑当时的市场环境是平静还是躁动,就像用开跑车的力度去开拖拉机,能不出事吗?
我意识到自己缺两样东西:一是更靠谱的趋势判断方法,二是更聪明的波动过滤机制。这就把我引向了VWAP这个指标。
图1:VWAP(成交量加权平均价)在图表中的典型表现 | 来源:Alchemy Markets
二、VWAP:找到”真金白银聚集的地方”
VWAP,中文叫成交量加权平均价。听起来高大上,其实逻辑特朴实:
假设一个标的价格在10元成交了100手,在11元成交了300手。那平均成本不是简单的(10+11)/2=10.5元,而是(10×100+11×300)/(100+300)=10.75元。
看出门道了吗?成交越多的地方,对平均价格的影响越大。11元成交了更多,所以平均成本更靠近11元。这就是市场真实的”成本重心”。
我当时的灵光一闪是:如果不用单日的VWAP,而是把很多天的VWAP再做一次移动平均呢?这样既能保留”成交密集区更重要”这个特性,又能平滑短期噪音。
于是我的”SMVWAP”(简单移动VWAP)诞生了:
- 短期SMVWAP:用2-60天的周期,捕捉近期动态;
- 长期SMVWAP:用100-250天的周期,识别大方向。
核心规则就一句话:只有当长期SMVWAP向上倾斜时,才考虑参与多头机会;如果它向下走,那就乖乖看戏。
就这么一条简单的过滤,把那些”在下跌趋势里硬要抢反弹”的糟糕操作干掉了大半。要知道,会躲坑本身就是一门被严重低估的技能。
图2:VWAP在实战中常扮演动态支撑或阻力角色 | 来源:TrueData
三、标准差:给策略加上”减震器”
解决了趋势方向的问题,但还有一个麻烦:即使在上升趋势里,价格也会上蹿下跳,围绕SMVWAP来回折腾。这时候如果每个小波动都跟着动,会被震得七荤八素。
这时候我引入了标准差这个统计工具。说白了,它就是告诉你”这东西平时一般晃悠多大范围”。
举个例子:假设过去20天,收盘价和长期SMVWAP的平均距离是2元,标准差是1元。那我可以设定一个门槛:只有当价格偏离SMVWAP达到0.7个标准差以上时,才考虑行动。
计算示例:
门槛 = SMVWAP + 0.7 × 标准差 = SMVWAP + 0.7 × 1元 = SMVWAP + 0.7元
这个数字看着小,但在流动性好的市场里,足以过滤掉大量无意义的噪音。市场躁动时,价格可能在SMVWAP附近±0.5元来回晃悠,但达不到0.7元的门槛,系统就不会被诱骗。
我在代码里让这个”标准差偏移量”可以在-2到+2之间调整,步长0.1。正值意味着追逐更剧烈的波动,负值则要求价格更贴近趋势线,追求稳健和干净的交易机会。
图3:利用波动率收缩和突破寻找高概率时机 | 来源:Dot Net Tutorials
四、那个让我惊出一身汗的回测结果
把短期SMVWAP、长期SMVWAP和标准差偏移这三个组件串起来,我扔进了优化器。这玩意儿就像个不知疲倦的实习生,能帮你测试几千种参数组合,而你只需要在旁边喝咖啡。
在某个主流资产的多年度数据上,最优参数组合是:
| 组件 | 最优参数 | 含义 |
|---|---|---|
| 短期SMVWAP | 16周期 | 约3周左右的短期视角 |
| 长期SMVWAP | 189周期 | 约9个月的大趋势判断 |
| 标准差偏移 | -0.7 | 偏向保守,要求价格更贴近趋势 |
回测结果如下(请注意,这只是历史数据模拟):
| 指标 | 动量策略 | 简单持有 | 差异 |
|---|---|---|---|
| 总交易次数 | 40次 | 1次 | – |
| 成功次数占比 | 约48% | 100% | 不到一半 |
| 累计增长 | 约10,000% | 约1,400% | 7倍左右 |
| 期末资金(1,000元起步) | 约101,000元 | 约15,000元 | 6.7倍 |
| 在场时间 | 675天 | 1,364天 | 仅50% |
最有意思的是最后那行:系统在1,364天中只参与了675天,剩下近2年都是空仓状态。这意味着它避开了大量的市场回调和震荡期。
重要提醒:看到这里你可能会心动,但我必须泼盆冷水。任何回测结果都可能是”过度优化”的产物——就是让历史数据拟合得太完美,实战却一塌糊涂。看到漂亮的收益曲线,第一反应应该是怀疑,而不是兴奋。
图4:典型的策略回测收益曲线示例(非本策略实际结果,仅供示意)
五、如何避免”数据拟合”的坑
我不建议任何人直接用上面那组参数。真正值得借鉴的是思路,不是数字。这里提供一个简化版的三步落地法:
第一步:选对标的
挑一个你熟悉的、流动性好的标的。可以是主流指数ETF、大型标的,或者你长期关注的品种。流动性差的东西,VWAP的意义会大打折扣。
第二步:搭建双周期框架
在日K线图上计算两条线:
- 长期线:150-250天的VWAP移动平均,判断大方向。
- 短期线:10-30天的VWAP移动平均,寻找时机。
第三步:加入波动缓冲
计算价格与长期线的偏离程度,只有当偏离超过一定阈值(比如用标准差衡量)时才行动。这个阈值要保守起步,宁可错过一些机会,也别被假信号反复打脸。
如果你不会写代码,TradingView这类平台基本都能实现这个逻辑。实在不行,用Excel手动算几组数据,也能感受其中的规律。
六、我现在是怎么用这套东西的
说实话,我现在并没有用那组”最优参数”做实盘。它现在对我有两个更实际的用途:
第一,当作市场环境过滤器。如果长期VWAP线往下走,我会自动降低参与意愿,或者干脆观望。这个动作帮我避免了无数次”接飞刀”的冲动。
第二,当作独立想法的校验器。如果我看中某个机会,但长期趋势线是平的或向下的,那面黄旗就会亮起。这种情况下即使参与,我也会降低预期,当成试探而非主攻。
这里有个可能得罪人的观点:大多数散户太在意”赢的次数”,却不够在意”资金暴露在风险中的时间”。在我的回测里,把参与时间砍掉一半,比把成功率从40%提到50%重要得多。
这套思路可以延伸出很多变体:应用到一篮子标的、设置收盘前提醒、打包成指标自动监控……但核心始终是那句话:区分真信号和噪音,区分该行动和该等待。
给新手的建议:别急着找”最优参数”。先用肉眼观察,在历史图表上手动标记几个信号,体会一下这个逻辑的节奏。这种手工回测的直觉培养,比跑一万次程序都有价值。
七、几个实用的操作原则
如果你打算尝试类似思路,这里有几条我踩坑踩出来的经验:
| 原则 | 具体做法 | 为什么重要 |
|---|---|---|
| 把VWAP当成本线 | 价格在线上=多数人获利,市场偏强;在线下=多数人被套,市场偏弱 | 它反映的是真金白银的聚集区,不是随便画的线 |
| 双周期搭配 | 长期定方向,短期找机会,再加个波动缓冲 | 避免在错误的时间做正确的事 |
| 看三个维度 | 收益、回撤、在场时间,三个一起看 | 高收益如果伴随高回撤和长期在场,风险可能很大 |
| 手动验证 | 至少在历史数据上手动标记10-20个信号 | 培养直觉,发现程序可能忽略的细节 |
| 保守起步 | 偏移量设得保守些,宁可错过别做错 | 假信号的成本往往比错过机会高得多 |
八、关于过度优化的冷思考
我得再强调一下过度优化(Overfitting)这个问题。我见过太多人在历史数据上把参数调得天花乱坠,实战一塌糊涂。
典型的过度优化长这样:
- 参数稍微变一点,收益断崖式下跌;
- 加入一堆条件后,回测曲线漂亮得不像真的;
- 回测胜率70%,实盘却连50%都保不住。
怎么识别?做个简单的参数敏感性测试。如果你的最优参数是13周期,试试12和14。如果收益从50%暴跌到10%,那这个参数可能就是”孤岛”——只适合那段特定的历史数据,换个环境就失效。
正确的做法是:在训练集上找参数,在测试集上验证,然后用滚动窗口持续检验。回测只是入学考试,实盘才是终身学习。
图5:波动率突破交易的完整流程示意 | 来源:TradingView
写在最后
这篇文章的核心不是让你抄我的参数,而是分享一个思路:真正的优势不在于预测得多准,而在于进场有多精、等待有多耐心、过滤有多严格。
当你发现长期趋势指标向下时,无论你的主观判断多么乐观,都请先让 ego(自负)退一步。市场不会因为你的坚持而 rewarded(奖励)你。
记住这个小规则:”当我的长期趋势线向下时,我没有资格看多。”
附录:Python代码手把手教学
这里我给大家提供一个简化的Python代码示例。
环境准备
首先你需要安装必要的库:
pip install pandas numpy yfinance matplotlib第一步:获取数据
import pandas as pd
import numpy as np
import yfinance as yf
import matplotlib.pyplot as plt
# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 获取数据(以沪深300ETF为例)
ticker = "510300.SS" # 沪深300ETF
data = yf.download(ticker, start="2020-01-01", end="2024-01-01")
print(data.head())第二步:计算VWAP
def calculate_vwap(df):
"""
计算VWAP(成交量加权平均价)
"""
# 典型价格 = (最高价 + 最低价 + 收盘价) / 3
df['Typical_Price'] = (df['High'] + df['Low'] + df['Close']) / 3
# 价格×成交量
df['Price_Volume'] = df['Typical_Price'] * df['Volume']
# 累计价格×成交量
df['Cumulative_PV'] = df['Price_Volume'].cumsum()
# 累计成交量
df['Cumulative_Volume'] = df['Volume'].cumsum()
# VWAP = 累计(价格×成交量) / 累计成交量
df['VWAP'] = df['Cumulative_PV'] / df['Cumulative_Volume']
return df
data = calculate_vwap(data)
print(data[['Close', 'VWAP']].tail())第三步:计算SMVWAP(VWAP的移动平均)
def calculate_smvwap(df, short_period=16, long_period=189):
"""
计算SMVWAP(VWAP的简单移动平均)
"""
# 先计算每日VWAP(这里简化为典型价格)
df['Typical_Price'] = (df['High'] + df['Low'] + df['Close']) / 3
# 短期SMVWAP
df['Short_SMVWAP'] = df['Typical_Price'].rolling(window=short_period).mean()
# 长期SMVWAP
df['Long_SMVWAP'] = df['Typical_Price'].rolling(window=long_period).mean()
return df
data = calculate_smvwap(data)
print(data[['Close', 'Short_SMVWAP', 'Long_SMVWAP']].tail())第四步:计算标准差和交易信号
def generate_signals(df, std_shift=-0.7):
"""
生成交易信号
"""
# 计算价格偏离长期SMVWAP的标准差
df['Price_Deviation'] = df['Close'] - df['Long_SMVWAP']
df['Std_Dev'] = df['Price_Deviation'].rolling(window=20).std()
# 计算进场阈值
df['Entry_Threshold'] = df['Long_SMVWAP'] + (std_shift * df['Std_Dev'])
# 判断长期趋势方向(长期SMVWAP是否向上)
df['Trend_Up'] = df['Long_SMVWAP'] > df['Long_SMVWAP'].shift(1)
# 生成信号
df['Signal'] = 0
# 买入信号:长期趋势向上 且 价格突破短期SMVWAP
df.loc[(df['Trend_Up']) &
(df['Close'] > df['Short_SMVWAP']) &
(df['Close'] > df['Entry_Threshold']), 'Signal'] = 1
# 卖出信号:长期趋势向下 或 价格跌破短期SMVWAP
df.loc[(~df['Trend_Up']) |
(df['Close'] < df['Short_SMVWAP']), 'Signal'] = -1
return df
data = generate_signals(data)
print(data[['Close', 'Signal']].tail(20))第五步:回测策略
def backtest_strategy(df, initial_capital=100000):
"""
简单回测
"""
df['Position'] = df['Signal'].replace(-1, 0) # 1表示持仓,0表示空仓
df['Position'] = df['Position'].shift(1).fillna(0) # 信号延迟一天执行
# 计算每日收益率
df['Daily_Return'] = df['Close'].pct_change()
# 计算策略收益率
df['Strategy_Return'] = df['Position'] * df['Daily_Return']
# 计算累计收益
df['Cumulative_Market_Return'] = (1 + df['Daily_Return']).cumprod()
df['Cumulative_Strategy_Return'] = (1 + df['Strategy_Return']).cumprod()
# 计算最终收益
final_market_value = initial_capital * df['Cumulative_Market_Return'].iloc[-1]
final_strategy_value = initial_capital * df['Cumulative_Strategy_Return'].iloc[-1]
print(f"初始资金: {initial_capital:,.0f}元")
print(f"买入持有最终价值: {final_market_value:,.0f}元")
print(f"策略最终价值: {final_strategy_value:,.0f}元")
print(f"买入持有收益率: {(final_market_value/initial_capital - 1)*100:.2f}%")
print(f"策略收益率: {(final_strategy_value/initial_capital - 1)*100:.2f}%")
# 计算在市场中的天数
days_in_market = df['Position'].sum()
total_days = len(df)
print(f"在市场中的天数: {days_in_market}/{total_days} ({days_in_market/total_days*100:.1f}%)")
return df
data = backtest_strategy(data)第六步:可视化结果
def plot_results(df):
"""
绘制回测结果
"""
fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(15, 12))
# 图1: 价格和SMVWAP
ax1.plot(df.index, df['Close'], label='收盘价', alpha=0.7)
ax1.plot(df.index, df['Short_SMVWAP'], label='短期SMVWAP(16)', alpha=0.7)
ax1.plot(df.index, df['Long_SMVWAP'], label='长期SMVWAP(189)', alpha=0.7)
ax1.set_title('价格与SMVWAP指标', fontsize=14, fontweight='bold')
ax1.set_ylabel('价格(元)')
ax1.legend()
ax1.grid(True, alpha=0.3)
# 图2: 交易信号
buy_signals = df[df['Signal'] == 1]
sell_signals = df[df['Signal'] == -1]
ax2.plot(df.index, df['Close'], label='收盘价', alpha=0.5, color='gray')
ax2.scatter(buy_signals.index, buy_signals['Close'],
color='green', marker='^', s=100, label='买入信号', zorder=5)
ax2.scatter(sell_signals.index, sell_signals['Close'],
color='red', marker='v', s=100, label='卖出信号', zorder=5)
ax2.set_title('交易信号', fontsize=14, fontweight='bold')
ax2.set_ylabel('价格(元)')
ax2.legend()
ax2.grid(True, alpha=0.3)
# 图3: 累计收益对比
ax3.plot(df.index, (df['Cumulative_Market_Return'] - 1) * 100,
label='买入持有', linewidth=2)
ax3.plot(df.index, (df['Cumulative_Strategy_Return'] - 1) * 100,
label='动量策略', linewidth=2)
ax3.set_title('累计收益率对比', fontsize=14, fontweight='bold')
ax3.set_xlabel('日期')
ax3.set_ylabel('收益率(%)')
ax3.legend()
ax3.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('动量策略回测结果.png', dpi=300, bbox_inches='tight')
plt.show()
print("\n图表已保存为: 动量策略回测结果.png")
plot_results(data)完整代码使用说明
- 复制上面所有代码块,按顺序粘贴到一个Python文件中(比如
momentum_strategy.py) - 运行代码:
python momentum_strategy.py - 查看结果:
- 控制台会输出回测统计数据;
- 会生成一张包含三个子图的图表;
- 图表会自动保存为PNG文件。
参数调优建议
你还可以尝试调整这些参数:
# 在calculate_smvwap函数中
short_period = 16 # 短期周期,可以尝试10-30
long_period = 189 # 长期周期,可以尝试150-250
# 在generate_signals函数中
std_shift = -0.7 # 标准差偏移,可以尝试-2到+2注意事项
- 这是一个简化版本,实际交易需要考虑:
- 交易手续费
- 滑点
- 仓位管理
- 止损止盈
- 历史回测不代表未来收益,务必在模拟盘充分测试后再考虑实盘。
- 代码中的数据来自Yahoo Finance,可能有延迟,实盘请使用实时数据源。
- 建议先在小资金上测试,逐步增加仓位。
#量化交易 #技术分析 #VWAP指标 #趋势跟踪 #波动率策略 #交易系统的构建 #回测方法 #风险管理
感谢阅读!愿本文为您带来新启发与实用知识。若觉有益,请点赞分享,您的支持是我创作的动力,欢迎留言必复。
风险提示:本文仅供参考,不构成投资建议。量化策略开发应以学习和技术交流为目的。投资有风险,入市需谨慎。
Be First to Comment