策略源码
//@version=5
strategy(title='EMA+MACD共振策略(15M) V5 - 固定风险', overlay=true, default_qty_type=strategy.cash, default_qty_value=100)
// === 输入参数 ===
emaFast = input.int(28, "EMA快线周期")
emaSlow = input.int(58, "EMA慢线周期")
macdFast = input.int(9, "MACD快线")
macdSlow = input.int(19, "MACD慢线")
macdSignal = input.int(9, "MACD信号线")
rrRatio = input.float(2.0, "盈亏比", minval=1.0)
riskPercent = input.float(10.0, "单笔风险%", minval=1.0, maxval=50.0, step=1.0, tooltip="每笔交易最大亏损占账户权益的百分比,如10%表示500账户止损50")
// === 指标计算 ===
emaF = ta.ema(close, emaFast)
emaS = ta.ema(close, emaSlow)
[dif, dea, hist] = ta.macd(close, macdFast, macdSlow, macdSignal)
// === 信号检测 ===
emaGoldenCross = ta.crossover(emaF, emaS)
emaDeadCross = ta.crossunder(emaF, emaS)
macdGoldenCross = ta.crossover(dif, dea)
macdDeadCross = ta.crossunder(dif, dea)
aboveZero = dif > 0
belowZero = dif < 0
// === 状态机 ===
var int longPhase = 0
var int shortPhase = 0
var float longStopLoss = na
var float longTakeProfit = na
var float shortStopLoss = na
var float shortTakeProfit = na
var float zoneLow = na
var float zoneHigh = na
var float longEntryPrice = na
var float shortEntryPrice = na
// === 仓位计算函数 ===
// 固定风险金额 = 账户权益 × 风险百分比
// 仓位大小 = 风险金额 / 止损点数
calcPositionSize(float entryPrice, float stopLoss) =>
float equity = strategy.equity
float riskAmount = equity * (riskPercent / 100) // 固定风险金额
float stopDistance = math.abs(entryPrice - stopLoss) // 止损距离
float posSize = stopDistance > 0 ? riskAmount / stopDistance : 0
posSize
// ==================== 做多逻辑 ====================
// Phase 0→1: EMA28上穿EMA58
if longPhase == 0 and emaGoldenCross
longPhase := 1
// Phase 1→2: DIF上零轴
if longPhase == 1 and aboveZero
longPhase := 2
// Phase 2→3: 零轴上第一个死叉
if longPhase == 2 and macdDeadCross and aboveZero
longPhase := 3
zoneLow := low
// Phase 3: 等零轴上金叉入场
if longPhase == 3
zoneLow := math.min(zoneLow, low)
if macdGoldenCross and aboveZero
float entryPrice = close
float stopLoss = zoneLow
float risk = entryPrice - stopLoss
if risk > 0
longStopLoss := stopLoss
longTakeProfit := entryPrice + risk * rrRatio
longEntryPrice := entryPrice
// 计算仓位:固定风险 / 止损距离
float qty = calcPositionSize(entryPrice, stopLoss)
strategy.entry("Long", strategy.long, qty=qty)
strategy.exit("Long Exit", "Long", stop=longStopLoss, limit=longTakeProfit)
longPhase := 4
if belowZero
longPhase := 0
// Phase 4: 持仓,平仓后重置
if longPhase == 4 and strategy.position_size <= 0
longPhase := 0
// 做多重置: EMA死叉时重置(除了持仓状态)
if longPhase > 0 and longPhase != 4 and emaDeadCross
longPhase := 0
// ==================== 做空逻辑 ====================
// Phase 0→1: EMA28下穿EMA58
if shortPhase == 0 and emaDeadCross
shortPhase := 1
// Phase 1→2: DIF下零轴
if shortPhase == 1 and belowZero
shortPhase := 2
// Phase 2→3: 零轴下第一个金叉
if shortPhase == 2 and macdGoldenCross and belowZero
shortPhase := 3
zoneHigh := high
// Phase 3: 等零轴下死叉入场
if shortPhase == 3
zoneHigh := math.max(zoneHigh, high)
if macdDeadCross and belowZero
float entryPrice = close
float stopLoss = zoneHigh
float risk = stopLoss - entryPrice
if risk > 0
shortStopLoss := stopLoss
shortTakeProfit := entryPrice - risk * rrRatio
shortEntryPrice := entryPrice
// 计算仓位:固定风险 / 止损距离
float qty = calcPositionSize(entryPrice, stopLoss)
strategy.entry("Short", strategy.short, qty=qty)
strategy.exit("Short Exit", "Short", stop=shortStopLoss, limit=shortTakeProfit)
shortPhase := 4
if aboveZero
shortPhase := 0
// Phase 4: 持仓,平仓后重置
if shortPhase == 4 and strategy.position_size >= 0
shortPhase := 0
// 做空重置: EMA金叉时重置(除了持仓状态)
if shortPhase > 0 and shortPhase != 4 and emaGoldenCross
shortPhase := 0
// === 绘图 ===
plot(emaF, "EMA28", color=color.red, linewidth=1)
plot(emaS, "EMA58", color=color.blue, linewidth=1)
// 入场标记
plotshape(longPhase[1] == 3 and longPhase == 4, "做多入场", shape.triangleup, location.belowbar, color.green, size=size.small, text="L")
plotshape(shortPhase[1] == 3 and shortPhase == 4, "做空入场", shape.triangledown, location.abovebar, color.red, size=size.small, text="S")
// 止损止盈线
plot(strategy.position_size > 0 ? longStopLoss : na, "多止损", color=color.red, style=plot.style_linebr, linewidth=1)
plot(strategy.position_size > 0 ? longTakeProfit : na, "多止盈", color=color.green, style=plot.style_linebr, linewidth=1)
plot(strategy.position_size < 0 ? shortStopLoss : na, "空止损", color=color.red, style=plot.style_linebr, linewidth=1)
plot(strategy.position_size < 0 ? shortTakeProfit : na, "空止盈", color=color.green, style=plot.style_linebr, linewidth=1)
// 背景色
longBgColor = longPhase == 1 ? color.new(color.blue, 93) : longPhase == 2 ? color.new(color.aqua, 90) : longPhase == 3 ? color.new(color.green, 88) : na
shortBgColor = shortPhase == 1 ? color.new(color.purple, 93) : shortPhase == 2 ? color.new(color.fuchsia, 90) : shortPhase == 3 ? color.new(color.red, 88) : na
bgcolor(longBgColor)
bgcolor(shortBgColor)
// 状态标签(含仓位信息)
var label statusLabel = na
if barstate.islast
label.delete(statusLabel)
longText = longPhase == 0 ? "L:等EMA金叉" : longPhase == 1 ? "L:等DIF上零轴" : longPhase == 2 ? "L:等零轴上死叉" : longPhase == 3 ? "L:等金叉入场" : longPhase == 4 ? "L:持仓中" : ""
shortText = shortPhase == 0 ? "S:等EMA死叉" : shortPhase == 1 ? "S:等DIF下零轴" : shortPhase == 2 ? "S:等零轴下金叉" : shortPhase == 3 ? "S:等死叉入场" : shortPhase == 4 ? "S:持仓中" : ""
riskText = "风险:" + str.tostring(riskPercent, "#.#") + "% | 权益:" + str.tostring(strategy.equity, "#.##")
statusLabel := label.new(bar_index, high, longText + "\n" + shortText + "\n" + riskText, color=color.new(color.black, 70), textcolor=color.white, size=size.normal)