本文介绍了一套针对加密货币的中期动量交易策略,核心思想是运用低通滤波器(Low Pass Filter)平滑价格序列,并借助模糊逻辑识别趋势状态。策略历经七轮迭代优化:从参数优化到风险控制,最终实现了夏普比率 1.26、盈利因子 1.81的稳健表现。本文将完整呈现从假设提出、参数扫描到样本外验证的全流程,为读者提供一套可复用的策略开发方法论。
1. 核心逻辑
1.1 为什么选择低通滤波器?
在量化交易中,趋势跟随策略的核心难题在于如何在噪声中提取有效信号。传统移动平均线(MA)存在滞后性,而低通滤波器通过频域设计,能够在平滑价格的同时保留关键的转折点信息。
策略核心假设:当价格围绕趋势线窄幅震荡时,市场处于无序状态,策略应避免交易;当价格沿趋势线方向运动时,市场处于有序状态,策略应顺势交易。
1.2 模糊逻辑
仅凭单一滤波器难以应对市场的非线性特征。我们引入模糊逻辑(Fuzzy Logic),通过概率化的方式判断趋势方向:
- 上涨概率(RisingProb):衡量趋势向上的置信度
- 下跌概率(FallingProb):衡量趋势向下的置信度
当置信度超过阈值(EntryProbThreshold = 0.6)时,触发相应方向的交易信号。
2. 回溯检验设置
我们将使用 Zorro 进行策略开发。
| Asset | BTCUSDT, ETHUSDT |
|---|---|
| BarPeriod | 60 |
| StartDate | 20180101 |
| EndDate | 20251230 |
| Lookback | 5000 |
| Leverage | 5 |
| Commission | -0.1 |
| Slippage | 5 |
| NumWFOCycles | 10 |
| DataSplit | 85 |
| FixedCapital | 10000 |
测试时间约 8 年,涵盖 2018 年熊市、2021 年牛市及 2022 年震荡市,具备较强的样本代表性。Walk-Forward Optimization(WFO)采用 10 个周期、85% 数据分割比例,以验证参数的稳定性和泛化能力。
3. 迭代优化
3.1 基础策略
初始参数设定:
- 趋势线回溯期
TrendPeriod = 800 - 模糊因子
FuzzyFactor = 0.1 - 进场概率阈值
EntryProbThreshold = 0.6
回测表现分析:
基础策略展现出长期正向收益 ,净值曲线整体向上。然而,在窄幅震荡行情 中,价格围绕趋势线小幅波动,策略频繁触发假信号,导致持续亏损,这是后续优化需要解决的核心问题。

3.2 优化趋势线回溯期
优化假设:低通滤波器的回溯期直接决定了趋势识别的灵敏度。过短的回溯期会引入噪声,过长则导致滞后。通过参数扫描寻找稳健区间。
参数扫描设置:
- 区间:500-1200
- 步长:50
测试结果:


- BTC 的平稳参数区间:900-1000
- ETH 的平稳参数区间:700-1000
样本外表现:
- 盈利因子从 1.57 微升至 1.60
- PRR 从 1.35 提升至 1.37
- 交易次数减少约 10%
结论:虽然策略表现提升有限,但优化回溯期能够更好地适应市场变异。建议采用优化参数,以增强策略对市场结构变化的适应能力。
3.3 优化模糊因子
优化假设:模糊因子是模糊逻辑的核心参数,它决定了识别趋势和震荡行情的准确度。优化该参数或许能够减少震荡行情中的亏损。
参数扫描设置:
- 区间:0-0.3
- 步长:0.02
测试结果:


- BTC 的局部高点区间:0.2-0.26
- ETH 的局部高点区间:0.12-0.20
样本外表现:
- 盈利因子和 PRR 几乎无变化
- 夏普比率从 0.89 下降至 0.83
结论 :优化模糊因子可能导致过拟合 。局部高点在样本内表现优异,但在样本外无法保持优势。因此,不优化模糊因子,继续使用固定参数 0.1。这一决策体现了量化开发中"稳健优先于最优"的原则。
3.4 距离滤波器
优化假设 :当价格偏离趋势线过远时,市场存在强烈的均值回归需求 。此时进场往往面临回调风险,导致亏损。通过引入波动性滤波器(距离滤波器),仅在价格偏离度低于阈值时允许进场,目标是降低宽幅震荡行情的亏损。
参数扫描设置:
- 区间:1-6(百分比距离)
- 步长:0.5
测试结果:


- BTC 的平稳参数区间:4-6
- ETH 的平稳参数区间:4-5
样本外表现(显著提升):
| 指标 | 优化前 | 优化后 | 提升幅度 |
|---|---|---|---|
| 盈利因子 | 1.60 | 1.75 | +9.4% |
| PRR | 1.37 | 1.47 | +7.3% |
| 夏普比率 | 0.89 | 1.02 | +14.6% |
关键洞察:
⚠️ 距离阈值是一把双刃剑:它既能过滤宽幅震荡的亏损,也可能导致错过历史级别的趋势行情。
然而,从风险调整后收益的角度,降低宽幅震荡的亏损比贪婪地捕捉每一轮趋势更为重要。因此,保留距离滤波器机制。
3.5 固定止损
优化假设 :趋势策略面临的最大风险是逆趋势大幅回撤。通过基于 ATR(Average True Range)的动态止损,可以在趋势反转时及时退出。
参数扫描设置:
- 止损距离 =
k * ATR(100) - 区间:3-8
- 步长:0.5
测试结果:
- 两个交易对的平稳参数区间均集中在 5-8
样本外表现:
- 总收益率大幅下降
- 连续亏损次数大幅增加
- 风险调整收益率仅小幅下降
- 最长回撤周数显著减少(从 34 周降至 24 周)
结论 :固定止损的核心价值在于控制回撤风险,尽管它以牺牲部分收益为代价。保留该机制。
3.6 盈亏平衡(Breakeven)
优化假设:当交易产生一定浮盈后,将止损价调整至进场价,确保该笔交易至少不亏损。这有助于改善交易心理,并降低连续亏损对资金曲线的冲击。
参数扫描设置:
- 浮盈阈值 =
k * ATR(100) - 区间:3-8
- 步长:0.5
样本外表现:
- 风险调整收益率进一步下降
- 胜率提升至 45%
- 最长连续亏损次数从 27 笔锐减至 9 笔
结论:盈亏平衡机制带来了显著改善,胜率的提升和连续亏损的锐减显著改善了资金曲线的平滑度。保留该机制。
3.7 空头部分止盈
优化假设:空头交易往往面临不对称风险(价格上涨无上限)。当浮盈达到阈值后,先平仓 50% 以锁定利润,然后对剩余头寸启用动态跟踪止损。
机制设计:
- 设定盈利阈值,当浮盈超过阈值后平仓 50%
- 对剩余 50% 头寸启动跟踪止损,锁定已获利润
- 进入"观望模式":不再新开空头,直到价格出现强劲反弹才允许再进场
测试结果:
- 盈利因子从 1.61 提升至 1.69
- PRR 从 1.41 提升至 1.52
- 夏普比率从 0.91 提升至 1.19
- 两个交易对的空头盈利因子均有提升
3.8 资金管理
优化假设:固定资金头寸法难以控制下行风险。采用固定风险头寸法(Fixed Risk),每笔交易使用账户净值的固定比例(2%)进行冒险,可以实现收益的复利增长,且有效控制回撤风险。
样本外表现:
- 净值曲线更加平滑
- 夏普比率提升至 1.26

4. 总结
迭代优化记录总表。
| 核心指标 | 基础策略 | 优化趋势线 | 距离滤波器 | 固定止损 | 盈亏平衡 | 优化空头平仓 | 资金管理 |
|---|---|---|---|---|---|---|---|
| 最大回撤 | 12% | 12.5% | 11% | 15% | 14% | 12% | 10.7% |
| MAE | 18% | 18.7% | 20% | 25% | 24% | 16% | 14% |
| 最长回撤周数 | 34 | 34 | 34 | 24 | 34 | 24 | 24 |
| 交易次数 | 440 | 403 | 332 | 346 | 467 | 748 | 748 |
| 每周交易 | 2 | 2 | 1 | 1 | 2 | 3 | 3 |
| 胜率 | 28% | 27% | 28% | 26% | 45% | 45% | 45% |
| 最长连续亏损 | 18 | 14 | 13 | 27 | 9 | 18 | 18 |
| 年收益率 | 119% | 116% | 121% | 99% | 100% | 117% | 45.8% |
| 盈利因子 | 1.57 | 1.60 | 1.75 | 1.70 | 1.61 | 1.69 | 1.81 |
| PRR | 1.35 | 1.37 | 1.47 | 1.43 | 1.41 | 1.52 | 1.63 |
| 夏普比率 | 0.91 | 0.89 | 1.02 | 0.98 | 0.91 | 1.19 | 1.26 |
| R2 | 77% | 75% | 83% | 77% | 80% | 89% | 83% |
| 平均回撤 | 7.2% | 7.3% | 8.1% | 9.4% | 9.5% | 5.8% | 5.2% |
| BTC PF | 1.43 | 1.57 | 1.84 | 1.80 | 1.74 | 1.83 | 1.86 |
| ETH PF | 1.80 | 1.62 | 1.68 | 1.62 | 1.51 | 1.58 | 1.76 |
关键发现:
- 距离滤波器是风险调整后收益提升的核心驱动力,夏普比率首次突破 1.0
- 盈亏平衡机制大幅改善了胜率(从 28% 跃升至 45%)和降低连续亏损次数
- 空头优化显著提升了策略的夏普比率,证明了单独优化空头平仓的价值
- 固定风险资金管理虽然降低了名义收益率(45.8% vs 117%),但实现了更平滑的资金曲线和更高的夏普比率(1.26)
长期表现特征:
- 表现最糟糕的时期:2022 年下半年,连续四个月亏损
- 整体表现:其余年份均能稳定盈利,平均每年约四个月亏损,三分之二时间盈利
- 震荡市应对 :当市场陷入窄幅震荡时,策略会持续亏损。未来可考虑加入波动性滤波器(当波动性过低时暂停交易)作为进一步优化方向
5. 稳健性检验
为验证策略的稳健性,我们对 Walk-Forward Optimization(WFO)的周期数进行敏感性分析。
5.1 测试设置
| Asset | BTCUSDT, ETHUSDT |
|---|---|
| BarPeriod | 60 |
| StartDate | 20180101 |
| EndDate | 20251230 |
| Lookback | 5000 |
| Leverage | 5 |
| Commission | -0.1 |
| Slippage | 5 |
| NumWFOCycles | 2-30 |
| DataSplit | 85 |
5.2 固定资金模式(每交易对 10000 美元)
当周期数处于 10-16 时,PRR 创局部高点并保持稳定。

5.3 固定风险模式(初始资金 10000,风险率 2%)
当周期数处于 10-20 时,PRR 创局部高点并保持稳定,局部高点区间为 1.6-1.7。

5.4 稳健性结论
策略通过稳健性检验。
推荐的 WFO 参数设置:
StartDate = 20180101EndDate = 20251230NumWFOCycles = 15DataSplit = 85
附录:策略源码
以下代码完整呈现了策略的实现逻辑,包含指标计算、信号生成、状态管理、风险控制、资金管理和订单执行等模块。
c
/*
基于低通滤波器识别中期趋势反转
*/
#include <profile.c>
#include <utils.c>
#include <myindicators.c>
function tradeStrategy() {
// ---------------------------------------------------------
// 参数设置
// ---------------------------------------------------------
int TrendPeriod = 800;
var FuzzyFactor = 0.1;
var EntryProbThreshold = 0.6;
var DeviationThreshold = 0;
var AtrStopFactor = 0;
var AtrTrailFactor = 0;
var ShortProfitThreshold = 0;
var FixedCapital = 10000;
var FixedRisk = 0;
// ---------------------------------------------------------
// 指标计算
// ---------------------------------------------------------
vars Prices = series(price());
vars Trends = series(LowPass(Prices, TrendPeriod));
var ATR100 = ATR(100);
var PercentDeviation = abs(Prices[0]-Trends[0])/Trends[0]*100;
var LongNormalizedProfit = getLongNormalizedProfit(ATR100);
var ShortNormalizedProfit = getShortNormalizedProfit(ATR100);
// ---------------------------------------------------------
// 交易信号
// ---------------------------------------------------------
FuzzyRange = FuzzyFactor*ATR100;
var RisingProb = risingF(Trends);
var FallingProb = fallingF(Trends);
bool LongEntry = RisingProb >= EntryProbThreshold;
bool LongExit = peak(Trends);
bool ShortEntry = FallingProb >= EntryProbThreshold;
bool ShortExit = valley(Trends);
if(DeviationThreshold > 0) {
LongEntry = LongEntry && PercentDeviation <= DeviationThreshold;
ShortEntry = ShortEntry && PercentDeviation <= DeviationThreshold;
}
// ---------------------------------------------------------
// 状态管理:再进场限制 (AlgoVar[0])
// ---------------------------------------------------------
// 触发部分止盈后,开始跟踪跟踪剩余头寸,并进入观望期
if(AlgoVar[0] == 1) {
// 限制进场
ShortEntry = false;
// 管理剩余持仓:锁定 50% 利润
for(current_trades) {
if(TradeIsOpen && TradeIsShort) {
TradeTrailLock = 0.5;
}
}
// 解锁条件:空仓且趋势反转
if(NumOpenShort == 0) {
if(RisingProb > 0.4 || valley(Trends) || peak(Trends)) {
AlgoVar[0] = 0;
}
}
}
// ---------------------------------------------------------
// 风控
// ---------------------------------------------------------
Stop = AtrStopFactor*ATR100;
Trail = AtrTrailFactor*ATR100;
TrailLock = 10;
TrailSlope = 0;
// ---------------------------------------------------------
// 资金管理
// ---------------------------------------------------------
Lots = 100;
var MinLots = getMinLots();
if(!Train && FixedCapital > 0)
Lots = floor(FixedCapital/priceC()/LotAmount);
if(!Train && Capital > 0 && FixedRisk > 0 && AtrStopFactor > 0)
Lots = fixedRiskLots(FixedRisk);
if(Lots < MinLots) Lots = MinLots;
// ---------------------------------------------------------
// 订单管理
// ---------------------------------------------------------
if(NumOpenLong > 0 && LongExit)
exitLong();
if(NumOpenShort > 0 && ShortExit)
exitShort();
if(NumOpenLong == 0 && LongEntry)
enterLong();
if(NumOpenShort == 0 && ShortEntry) {
if(ShortProfitThreshold > 0)
enterShortMultipleOrders(2, MinLots);
else
enterShort();
}
// 空头部分止盈逻辑
if(ShortProfitThreshold > 0 && NumOpenShort > 0 && AlgoVar[0] == 0) {
if(ShortNormalizedProfit >= ShortProfitThreshold) {
// 平仓50%
exitProfitShortOrders(1);
// 标记进入剩余头寸管理模式
AlgoVar[0] = 1;
}
}
// ---------------------------------------------------------
// 绘图
// ---------------------------------------------------------
if((Test && !is(LOOKBACK)) || Live) {
plot2("Trend", Trends[0], MAIN, rising(Trends), GREEN, RED);
var BullishState = ifelse(RisingProb >= EntryProbThreshold, 1, 0);
var BearishState = ifelse(FallingProb >= EntryProbThreshold, 1, 0);
plot("BullishState", BullishState, NEW|BARS, GREEN+TRANSP);
plot("BearishState", BearishState, 0|BARS, RED+TRANSP);
plot("RisingProb", RisingProb, 0|LINE, BLUE);
plot("BullT", EntryProbThreshold, 0, GREY);
plot("BearT", 1-EntryProbThreshold, 0, GREY);
if(DeviationThreshold > 0) {
plot("Deviation", PercentDeviation, NEW, RED);
plot("Deviation+", DeviationThreshold, 0, GREY);
}
if(ShortProfitThreshold > 0) {
plot("LongProfit", LongNormalizedProfit, NEW|BARS, GREEN+TRANSP);
plot("ShortProfit", ShortNormalizedProfit, 0|BARS, RED+TRANSP);
plot("ShortProfitT", ShortProfitThreshold, 0, RED);
}
if(AtrStopFactor > 0 && NumOpenTotal > 0) {
var CurrentStop = 0;
for(current_trades) {
CurrentStop = (var)TradeStopLimit;
break_trades;
}
if(CurrentStop > 0) plot("Stop", CurrentStop, MAIN|DOT, LIGHTBLUE);
}
// if(AlgoVar[0] > 0) plot("LockMode", 10, NEW|BARS, BLUE);
}
}
function run() {
// --------------------------------------------------------- //
// Zorro 设置
// --------------------------------------------------------- //
// 日志
set(LOGFILE);
Verbose = 3;
// 图表
set(PLOTNOW);
setf(PlotMode,PL_DIFF);
PlotScale = 8;
PlotHeight2 = 320;
if(Live) PlotBars = -250;
// 训练
set(PARAMETERS);
setf(TrainMode,TRADES);
DataSplit = 85;
NumWFOCycles = 10;
NumCores = 4;
// 实盘
if(Live) {
set(NOLOCK); // 禁止同步 zorro 实例
setf(SaveMode,SV_STATS|SV_TRADES|SV_ALGOVARS|SV_SLIDERS);
TickTime = 1000;
TockTime = 60000;
slider(1,2000,0,3000,".Risk(%)",0); // 通过滑块设置风险率
}
// k线生成规则,7*24交易
resf(BarMode,BR_WEEKEND);
StartWeek = 0;
EndWeek = 62359;
StartMarket = 0;
EndMarket = 2359;
BarPeriod = 60;
BarZone = UTC;
BarOffset = 0;
TickFix = 60000;
if(Live) TickFix = 0;
// 测试样本
StartDate = 20180101;
EndDate = 20251230;
LookBack = 5000;
// 初始资本
if(Train) Capital = 0;
if(Test) Capital = 0;
if(Live) Capital = 0;
// 下一根k线开盘时进场/平仓
Fill = 3;
// --------------------------------------------------------- //
// 交易策略
// --------------------------------------------------------- //
while(asset(loop("BTCUSDT","ETHUSDT"))) {
Leverage = 5;
MarginCost = priceClose()*LotAmount/Leverage;
tradeStrategy();
}
}