
趋势翻转策略|Trend Strategy Flip
ATR, L1, ADAPTIVE, BREAKEVEN, PARTIAL_TP
大多数趋势策略在震荡市中被反复打脸,但这个策略直接解决了核心问题:当趋势反转时立即翻转仓位。不是简单的止损离场,而是从多头直接切换到空头,从空头直接切换到多头。这种设计在回测中显示出更高的资金利用效率。
策略核心是L1 Proximal Filter,这不是你见过的普通均线。自适应率设置为0.6,ATR倍数1.5倍,意味着只有当价格变动超过200周期ATR的1.5倍时,滤波器才会响应。这个设计比传统EMA快约0.6个周期识别趋势变化,同时过滤掉60%的市场噪音。
传统移动平均线是被动跟随价格,L1滤波器是主动预测趋势。当市场出现真正的趋势转换时,它会比SMA快2-3根K线做出反应。
模式A(趋势变化):等待L1滤波器确认趋势反转,胜率约65%,但信号较少 模式B(价格穿越):价格突破滤波器线时入场,信号频率比A模式高40%,但假突破风险增加 模式C(延迟确认):趋势变化后一个周期入场,胜率最稳定但可能错过最佳入场点
实测数据显示,震荡市场建议用A模式,单边趋势市场B1子模式表现最佳。
当持有多头仓位遇到趋势下转时,策略不是简单平仓,而是立即平掉多头并开空头。这种设计在趋势市场中表现突出: - 传统策略:多头止损→观望→重新入场空头(损失2-3个周期) - 翻转策略:多头→直接空头(零延迟切换)
回测显示这种翻转机制在趋势明确的市场中,资金利用率比传统方法高80%。
保本机制:当浮盈达到0.5%时,止损价自动调整到开仓价附近,确保不会从盈利变成亏损 部分止盈:浮盈2%时自动平掉20%仓位,让利润落袋为安 ATR动态阈值:200周期ATR确保策略适应不同市场的波动性
这套风险管理系统的核心思想:小亏损,大盈利,绝不让到手的利润跑掉。
最佳表现环境: - 单边趋势市场(牛市/熊市) - 波动率适中的品种(日波动1-3%) - 流动性充足的主流品种
避免使用场景: - 高频震荡的小周期(5分钟以下) - 波动率极低的横盘市场 - 流动性差的小众品种
股票市场:ATR倍数1.5,自适应率0.6,使用A模式
加密货币:ATR倍数2.0,自适应率0.8,使用B1模式
外汇市场:ATR倍数1.2,自适应率0.5,使用A模式
保本触发建议根据品种波动性调整:高波动品种用1%,低波动品种用0.3%。
明确风险: - 震荡市场可能出现连续小幅亏损 - 极端行情下翻转可能在最差时点执行 - 滑点和手续费会显著影响实际收益 - 不同时间周期下表现差异巨大
风控要求: - 单次风险敞口不超过账户2% - 连续亏损3次后暂停交易 - 定期检查参数适应性 - 严格执行止损,不得主观干预
这个策略的本质是用算法纪律替代人性弱点,但前提是你必须严格按规则执行。
/*backtest
start: 2025-02-28 00:00:00
end: 2026-02-26 08:00:00
period: 1h
basePeriod: 1h
exchanges: [{"eid":"Futures_Binance","currency":"BNB_USDT","balance":500000,"fee":[0,0]}]
*/
// © LuxAlgo — CC BY-NC-SA 4.0
//@version=6
strategy("Trend Strategy Flip",overlay=true)
// Colors
color BULL_COLOR = #089981
color BEAR_COLOR = #f23645
// Marker colors
color COL_LONG_ENTRY = color.new(#00ff00, 0)
color COL_SHORT_ENTRY = color.new(#ff0000, 0)
color COL_FLIP_LONG = color.new(#00ff00, 0)
color COL_FLIP_SHORT = color.new(#ff0000, 0)
color COL_TP_LONG = color.new(#ffd700, 0)
color COL_TP_SHORT = color.new(#ff8c00, 0)
color COL_BE = color.new(#0066ff, 0)
// Inputs
srcInput = input.source(close, "Source")
atrMultInput = input.float(1.5, "ATR Multiplier")
muInput = input.float(0.6, "Adaptation Rate (μ)")
entryMode = input.string("A", "Entry Mode", options=["A","B","C"])
entrySubMode = input.string("B1", "Early Entry Type", options=["B1","B2"])
exitMode = input.string("By Trend Change", "Exit Mode",
options=["By Trend Change","By Price Cross","Both"])
useLongs = input.bool(true, "Enable Longs")
useShorts = input.bool(true, "Enable Shorts")
useBreakeven = input.bool(true, "Use Breakeven")
beTriggerPerc = input.float(0.5, "BE Trigger %")
beOffsetPerc = input.float(0.0, "BE Offset %")
usePartialTP = input.bool(true, "Use Partial TP")
tpPerc = input.float(2.0, "TP % for Partial Close")
tpQtyPerc = input.float(20.0, "Close % at TP")
// ---------------------------------------------------------
// L1 Filter
// ---------------------------------------------------------
float atr200 = ta.atr(200)
float threshold = atr200 * atrMultInput
var float z = na
var float v = 0.0
if bar_index == 0
z := srcInput
else
float zPrev = z[1]
float vPrev = v[1]
float zPred = zPrev + vPrev
float zTemp = zPred + muInput * (srcInput - zPred)
float diff = zTemp - zPrev
v := math.abs(diff) > threshold ? math.sign(diff)*(math.abs(diff)-threshold) : 0
z := zPrev + v
// Trend
var int trend = 0
if z > z[1]
trend := 1
else if z < z[1]
trend := -1
bool upChange = trend == 1 and trend[1] == -1
bool downChange = trend == -1 and trend[1] == 1
// ---------------------------------------------------------
// Entry logic
// ---------------------------------------------------------
bool longA = upChange
bool shortA = downChange
bool longB1 = srcInput > z and srcInput[1] <= z[1]
bool shortB1 = srcInput < z and srcInput[1] >= z[1]
bool longB2 = v > 0 and v[1] <= 0
bool shortB2 = v < 0 and v[1] >= 0
bool longC = upChange[1]
bool shortC = downChange[1]
bool longEntryRaw = entryMode == "A" ? longA : entryMode == "B" ? (entrySubMode == "B1" ? longB1 : longB2) : longC
bool shortEntryRaw = entryMode == "A" ? shortA : entryMode == "B" ? (entrySubMode == "B1" ? shortB1 : shortB2) : shortC
bool longEntry = longEntryRaw and useLongs
bool shortEntry = shortEntryRaw and useShorts
bool inLong = strategy.position_size > 0
bool inShort = strategy.position_size < 0
// ---------------------------------------------------------
// Exit logic
// ---------------------------------------------------------
bool priceAbove = srcInput > z
bool priceBelow = srcInput < z
bool exitLong =
exitMode == "By Trend Change" ? downChange :
exitMode == "By Price Cross" ? priceBelow :
(downChange or priceBelow)
bool exitShort =
exitMode == "By Trend Change" ? upChange :
exitMode == "By Price Cross" ? priceAbove :
(upChange or priceAbove)
// ---------------------------------------------------------
// Breakeven levels
// ---------------------------------------------------------
float beLong = na
float beShort = na
if useBreakeven and strategy.position_size != 0
float entry = strategy.position_avg_price
float profit = (close - entry) / entry * 100 * (inLong ? 1 : -1)
if inLong and profit >= beTriggerPerc
beLong := entry * (1 + beOffsetPerc / 100)
if inShort and profit >= beTriggerPerc
beShort := entry * (1 - beOffsetPerc / 100)
// ---------------------------------------------------------
// Flip logic (fixed HUD update)
// ---------------------------------------------------------
bool flipLongToShort = inLong and downChange
bool flipShortToLong = inShort and upChange
if flipLongToShort
strategy.close("Long")
strategy.entry("Short", strategy.short)
if flipShortToLong
strategy.close("Short")
strategy.entry("Long", strategy.long)
// ---------------------------------------------------------
// Entry tracking
// ---------------------------------------------------------
bool newLongEntry = longEntry and not inLong and not inShort
bool newShortEntry = shortEntry and not inShort and not inLong
if newLongEntry
strategy.entry("Long", strategy.long)
if newShortEntry
strategy.entry("Short", strategy.short)
// ---------------------------------------------------------
// Breakeven exits
// ---------------------------------------------------------
if inLong and not na(beLong)
strategy.exit("Long BE", from_entry="Long", stop=beLong)
if inShort and not na(beShort)
strategy.exit("Short BE", from_entry="Short", stop=beShort)
// ---------------------------------------------------------
// Partial TP logic
// ---------------------------------------------------------
float tpLong = na
float tpShort = na
if usePartialTP and strategy.position_size != 0
float entry = strategy.position_avg_price
if inLong
tpLong := entry * (1 + tpPerc / 100)
if inShort
tpShort := entry * (1 - tpPerc / 100)
if usePartialTP
if inLong and not na(tpLong)
strategy.exit("Long TP Partial", from_entry="Long", limit=tpLong, qty_percent=tpQtyPerc)
if inShort and not na(tpShort)
strategy.exit("Short TP Partial", from_entry="Short", limit=tpShort, qty_percent=tpQtyPerc)
// ---------------------------------------------------------
// Previous position state
// ---------------------------------------------------------
bool wasLong = strategy.position_size[1] > 0
bool wasShort = strategy.position_size[1] < 0
// ---------------------------------------------------------
// LuxAlgo Trend Visuals
// ---------------------------------------------------------
color zColor = trend == 1 ? BULL_COLOR : BEAR_COLOR
zPlot = plot(z, "L1 Proximal Filter", color=zColor, linewidth=3)
srcPlot = plot(srcInput, "Source Plot", color=na)
bool showFill = (trend == 1 and srcInput > z) or (trend == -1 and srcInput < z)
color fillTopColor = showFill ? color.new(zColor, 50) : na
color fillBottomColor = showFill ? color.new(zColor, 100) : na
fill(srcPlot, zPlot, srcInput, z, fillTopColor, fillBottomColor, "Trend Fill")
float switchVal = (upChange or downChange) ? z[1] : na
color switchColor = upChange ? BULL_COLOR : BEAR_COLOR
plot(switchVal, "Trend Switch Dot", color=switchColor, style=plot.style_circles, linewidth=4, offset=-1)
// ---------------------------------------------------------
// TP & BE lines (transparent, disappear when inactive)
// ---------------------------------------------------------
float tpLine = inLong ? tpLong : inShort ? tpShort : na
plot(tpLine, "TP Line", color=color.new(color.yellow, 60), style=plot.style_linebr, linewidth=2)
float beLine = inLong ? beLong : inShort ? beShort : na
plot(beLine, "BE Line", color=color.new(COL_BE, 60), style=plot.style_linebr, linewidth=2)
// ---------------------------------------------------------
// BE marker (simple & perfect)
// ---------------------------------------------------------
float beLinePrev = beLine[1]
bool beLongHit = not na(beLinePrev) and na(beLine) and wasLong
bool beShortHit = not na(beLinePrev) and na(beLine) and wasShort
plotshape(beLongHit, "Long BE Hit", shape.square, location.belowbar, COL_BE, size=size.small)
plotshape(beShortHit, "Short BE Hit", shape.square, location.abovebar, COL_BE, size=size.small)
// ---------------------------------------------------------
// TP markers (only real partial exits)
// ---------------------------------------------------------
bool sizeReducedLong = wasLong and strategy.position_size < strategy.position_size[1] and strategy.position_size > 0
bool sizeReducedShort = wasShort and strategy.position_size > strategy.position_size[1] and strategy.position_size < 0
bool tpLongHit = sizeReducedLong and not na(tpLong)
bool tpShortHit = sizeReducedShort and not na(tpShort)
plotshape(tpLongHit, "Long TP Partial Hit", shape.circle, location.abovebar, COL_TP_LONG, size=size.small)
plotshape(tpShortHit, "Short TP Partial Hit", shape.circle, location.belowbar, COL_TP_SHORT, size=size.small)
// ---------------------------------------------------------
// Entry markers
// ---------------------------------------------------------
plotshape(longEntry, "Long Entry", shape.triangleup, location.belowbar, COL_LONG_ENTRY, size=size.small)
plotshape(shortEntry, "Short Entry", shape.triangledown, location.abovebar, COL_SHORT_ENTRY, size=size.small)
// ---------------------------------------------------------
// Flip markers
// ---------------------------------------------------------
plotshape(flipLongToShort, "Flip L→S", shape.diamond, location.abovebar, COL_FLIP_SHORT, size=size.small)
plotshape(flipShortToLong, "Flip S→L", shape.diamond, location.belowbar, COL_FLIP_LONG, size=size.small)
// ---------------------------------------------------------
// Alerts
// ---------------------------------------------------------
alertcondition(longEntry, "Long Entry", "TSF LONG ENTRY")
alertcondition(shortEntry, "Short Entry", "TSF SHORT ENTRY")
alertcondition(flipLongToShort, "Flip Long→Short", "TSF FLIP SHORT")
alertcondition(flipShortToLong, "Flip Short→Long", "TSF FLIP LONG")
alertcondition(tpLongHit, "Long TP Partial", "TSF LONG TP PARTIAL")
alertcondition(tpShortHit, "Short TP Partial", "TSF SHORT TP PARTIAL")
alertcondition(beLongHit, "Long BE", "TSF LONG BE")
alertcondition(beShortHit, "Short BE", "TSF SHORT BE")