320 lines
14 KiB
Plaintext
320 lines
14 KiB
Plaintext
//@version=6
|
|
strategy(title="EMA+MACD 趋势跟踪策略", shorttitle="EMA+MACD策略", overlay=true,
|
|
default_qty_type=strategy.percent_of_equity, default_qty_value=10,
|
|
initial_capital=100000, commission_type=strategy.commission.percent, commission_value=0.1,
|
|
pyramiding=0, calc_on_every_tick=false)
|
|
|
|
// ========================= 策略参数 =========================
|
|
// 交易方向
|
|
trade_direction = input.string("双向交易", "交易方向", options=["只做多", "只做空", "双向交易"], group="策略设置")
|
|
|
|
// 止损止盈
|
|
use_sl = input.bool(true, "启用止损", group="风控设置")
|
|
sl_pct = input.float(3.0, "止损比例(%)", minval=0.5, maxval=20, step=0.5, group="风控设置")
|
|
use_tp = input.bool(true, "启用止盈", group="风控设置")
|
|
tp_pct = input.float(6.0, "止盈比例(%)", minval=1, maxval=50, step=0.5, group="风控设置")
|
|
|
|
// 平仓逻辑:趋势结束即平仓
|
|
|
|
// 移动止损
|
|
use_trailing = input.bool(false, "启用移动止损", group="风控设置")
|
|
trail_pct = input.float(2.0, "移动止损比例(%)", minval=0.5, maxval=10, step=0.5, group="风控设置")
|
|
|
|
// ========================= EMA 参数 =========================
|
|
ema1_len = input.int(9, "EMA1 快线周期", minval=1, group="EMA设置")
|
|
ema2_len = input.int(21, "EMA2 中线周期", minval=1, group="EMA设置")
|
|
ema3_len = input.int(55, "EMA3 慢线周期", minval=1, group="EMA设置")
|
|
ema4_len = input.int(200, "EMA4 超慢线周期", minval=1, group="EMA设置")
|
|
use_ema4 = input.bool(true, "启用第4条均线(EMA200)", group="EMA设置")
|
|
|
|
// ========================= MACD 参数 =========================
|
|
macd_fast = input.int(12, "MACD快线周期", minval=1, group="MACD设置")
|
|
macd_slow = input.int(26, "MACD慢线周期", minval=1, group="MACD设置")
|
|
macd_signal = input.int(9, "MACD信号线周期", minval=1, group="MACD设置")
|
|
|
|
// ========================= 趋势判断参数 =========================
|
|
ema_spread_thresh = input.float(0.3, "EMA收束阈值(%)", minval=0.1, maxval=2.0, step=0.1, group="趋势判断") / 100
|
|
macd_zero_zone = input.float(0.1, "MACD零轴区域(%)", minval=0.01, maxval=0.5, step=0.01, group="趋势判断") / 100
|
|
confirm_bars = input.int(2, "趋势确认周期", minval=1, maxval=5, group="趋势判断")
|
|
|
|
// ========================= 显示设置 =========================
|
|
show_ema = input.bool(true, "显示EMA线", group="显示设置")
|
|
show_background = input.bool(true, "背景着色", group="显示设置")
|
|
show_signals = input.bool(true, "显示趋势笔", group="显示设置")
|
|
|
|
// ========================= 颜色设置 =========================
|
|
col_uptrend = color.new(color.teal, 80)
|
|
col_downtrend = color.new(color.red, 80)
|
|
col_consolidation = color.new(color.gray, 85)
|
|
|
|
// ========================= 指标计算 =========================
|
|
// EMA计算
|
|
ema1 = ta.ema(close, ema1_len)
|
|
ema2 = ta.ema(close, ema2_len)
|
|
ema3 = ta.ema(close, ema3_len)
|
|
ema4 = use_ema4 ? ta.ema(close, ema4_len) : na
|
|
|
|
// MACD计算
|
|
[macdLine, signalLine, histLine] = ta.macd(close, macd_fast, macd_slow, macd_signal)
|
|
macd_normalized = macdLine / close
|
|
|
|
// ========================= EMA排列判断 =========================
|
|
bull_alignment_3 = ema1 > ema2 and ema2 > ema3
|
|
bull_alignment = use_ema4 ? (bull_alignment_3 and ema3 > ema4) : bull_alignment_3
|
|
|
|
bear_alignment_3 = ema1 < ema2 and ema2 < ema3
|
|
bear_alignment = use_ema4 ? (bear_alignment_3 and ema3 < ema4) : bear_alignment_3
|
|
|
|
// EMA方向
|
|
ema1_rising = ema1 > ema1[1]
|
|
ema2_rising = ema2 > ema2[1]
|
|
ema3_rising = ema3 > ema3[1]
|
|
ema4_rising = use_ema4 ? ema4 > ema4[1] : true
|
|
|
|
ema1_falling = ema1 < ema1[1]
|
|
ema2_falling = ema2 < ema2[1]
|
|
ema3_falling = ema3 < ema3[1]
|
|
ema4_falling = use_ema4 ? ema4 < ema4[1] : true
|
|
|
|
all_rising = ema1_rising and ema2_rising and ema3_rising and (not use_ema4 or ema4_rising)
|
|
all_falling = ema1_falling and ema2_falling and ema3_falling and (not use_ema4 or ema4_falling)
|
|
|
|
// EMA收束
|
|
max_ema = math.max(math.max(ema1, ema2), ema3)
|
|
min_ema = math.min(math.min(ema1, ema2), ema3)
|
|
ema_spread = (max_ema - min_ema) / close
|
|
emas_converged = ema_spread < ema_spread_thresh
|
|
|
|
// ========================= MACD状态判断 =========================
|
|
macd_bullish = macdLine > signalLine
|
|
macd_bearish = macdLine < signalLine
|
|
macd_above_zero = macdLine > 0
|
|
macd_below_zero = macdLine < 0
|
|
macd_near_zero = math.abs(macd_normalized) < macd_zero_zone
|
|
hist_shrinking = math.abs(histLine) < math.abs(histLine[1])
|
|
|
|
// MACD评分
|
|
macd_score = 0
|
|
macd_score := macd_bullish ? macd_score + 1 : macd_score - 1
|
|
macd_score := macd_above_zero ? macd_score + 1 : macd_below_zero ? macd_score - 1 : macd_score
|
|
|
|
// ========================= 综合趋势判断 =========================
|
|
price_above_emas = close > ema1 and close > ema2 and close > ema3
|
|
uptrend_ema = bull_alignment or (all_rising and price_above_emas)
|
|
uptrend_macd = macd_score >= 1 or (macd_bullish and not macd_near_zero)
|
|
uptrend_raw = uptrend_ema and uptrend_macd
|
|
|
|
price_below_emas = close < ema1 and close < ema2 and close < ema3
|
|
downtrend_ema = bear_alignment or (all_falling and price_below_emas)
|
|
downtrend_macd = macd_score <= -1 or (macd_bearish and not macd_near_zero)
|
|
downtrend_raw = downtrend_ema and downtrend_macd
|
|
|
|
consolidation_ema = emas_converged or (not bull_alignment and not bear_alignment)
|
|
consolidation_macd = macd_near_zero or hist_shrinking
|
|
consolidation_raw = (consolidation_ema and consolidation_macd) or (not uptrend_raw and not downtrend_raw)
|
|
|
|
// ========================= 连续确认计数 =========================
|
|
var int uptrend_count = 0
|
|
var int downtrend_count = 0
|
|
var int consolidation_count = 0
|
|
|
|
uptrend_count := uptrend_raw ? uptrend_count + 1 : 0
|
|
downtrend_count := downtrend_raw ? downtrend_count + 1 : 0
|
|
consolidation_count := consolidation_raw ? consolidation_count + 1 : 0
|
|
|
|
uptrend_confirmed = uptrend_count >= confirm_bars
|
|
downtrend_confirmed = downtrend_count >= confirm_bars
|
|
consolidation_confirmed = consolidation_count >= confirm_bars
|
|
|
|
// ========================= 状态机 =========================
|
|
var int trend_state = 0
|
|
prev_state = nz(trend_state[1], 0)
|
|
|
|
int new_state = prev_state
|
|
|
|
if prev_state == 0
|
|
if uptrend_confirmed
|
|
new_state := 1
|
|
else if downtrend_confirmed
|
|
new_state := -1
|
|
|
|
else if prev_state == 1
|
|
if downtrend_confirmed
|
|
new_state := -1
|
|
else if consolidation_confirmed and not uptrend_raw
|
|
new_state := 0
|
|
|
|
else if prev_state == -1
|
|
if uptrend_confirmed
|
|
new_state := 1
|
|
else if consolidation_confirmed and not downtrend_raw
|
|
new_state := 0
|
|
|
|
trend_state := barstate.isconfirmed ? new_state : prev_state
|
|
|
|
// 状态切换信号
|
|
entered_up = barstate.isconfirmed and trend_state == 1 and prev_state != 1
|
|
entered_down = barstate.isconfirmed and trend_state == -1 and prev_state != -1
|
|
entered_consolidation = barstate.isconfirmed and trend_state == 0 and prev_state != 0
|
|
exit_up = barstate.isconfirmed and prev_state == 1 and trend_state != 1
|
|
exit_down = barstate.isconfirmed and prev_state == -1 and trend_state != -1
|
|
|
|
// ========================= 交易逻辑 =========================
|
|
// 做多条件
|
|
can_long = trade_direction == "只做多" or trade_direction == "双向交易"
|
|
can_short = trade_direction == "只做空" or trade_direction == "双向交易"
|
|
|
|
// 开仓信号
|
|
long_signal = entered_up and can_long
|
|
short_signal = entered_down and can_short
|
|
|
|
// 平仓信号:趋势结束即平仓
|
|
close_long_signal = exit_up // 上涨趋势结束 → 平多仓
|
|
close_short_signal = exit_down // 下跌趋势结束 → 平空仓
|
|
|
|
// 止损止盈价格
|
|
long_sl = use_sl ? strategy.position_avg_price * (1 - sl_pct / 100) : na
|
|
long_tp = use_tp ? strategy.position_avg_price * (1 + tp_pct / 100) : na
|
|
short_sl = use_sl ? strategy.position_avg_price * (1 + sl_pct / 100) : na
|
|
short_tp = use_tp ? strategy.position_avg_price * (1 - tp_pct / 100) : na
|
|
|
|
// 执行交易
|
|
if long_signal
|
|
strategy.entry("做多", strategy.long)
|
|
|
|
if short_signal
|
|
strategy.entry("做空", strategy.short)
|
|
|
|
// 平仓
|
|
if close_long_signal and strategy.position_size > 0
|
|
strategy.close("做多", comment="趋势结束")
|
|
|
|
if close_short_signal and strategy.position_size < 0
|
|
strategy.close("做空", comment="趋势结束")
|
|
|
|
// 止损止盈
|
|
if strategy.position_size > 0
|
|
if use_trailing
|
|
strategy.exit("多头止损止盈", "做多", stop=long_sl, limit=long_tp, trail_points=close * trail_pct / 100 / syminfo.mintick, trail_offset=close * trail_pct / 100 / syminfo.mintick)
|
|
else
|
|
strategy.exit("多头止损止盈", "做多", stop=long_sl, limit=long_tp)
|
|
|
|
if strategy.position_size < 0
|
|
if use_trailing
|
|
strategy.exit("空头止损止盈", "做空", stop=short_sl, limit=short_tp, trail_points=close * trail_pct / 100 / syminfo.mintick, trail_offset=close * trail_pct / 100 / syminfo.mintick)
|
|
else
|
|
strategy.exit("空头止损止盈", "做空", stop=short_sl, limit=short_tp)
|
|
|
|
// ========================= 可视化 =========================
|
|
// 绘制EMA线
|
|
plot(show_ema ? ema1 : na, "EMA1 快线", color=color.new(color.red, 30), linewidth=1)
|
|
plot(show_ema ? ema2 : na, "EMA2 中线", color=color.new(color.orange, 30), linewidth=1)
|
|
plot(show_ema ? ema3 : na, "EMA3 慢线", color=color.new(color.blue, 30), linewidth=2)
|
|
plot(show_ema and use_ema4 ? ema4 : na, "EMA4 超慢线", color=color.new(color.purple, 30), linewidth=2)
|
|
|
|
// 背景着色
|
|
bg_color = trend_state == 1 ? col_uptrend : trend_state == -1 ? col_downtrend : col_consolidation
|
|
bgcolor(show_background ? bg_color : na, title="趋势背景")
|
|
|
|
// ========================= 类缠论笔的绘制 =========================
|
|
trend_line_width = input.int(2, "趋势笔宽度", minval=1, maxval=5, group="显示设置")
|
|
|
|
var int up_start_bar = na
|
|
var float up_start_price = na
|
|
|
|
var int down_start_bar = na
|
|
var float down_start_price = na
|
|
|
|
// 上涨趋势开始 - 记录起点
|
|
if entered_up
|
|
up_start_bar := bar_index
|
|
up_start_price := low
|
|
|
|
// 上涨趋势结束 - 画笔(终点 = 结束时的K线)
|
|
if exit_up and show_signals and not na(up_start_bar)
|
|
line.new(up_start_bar, up_start_price, bar_index, close, color=color.teal, width=trend_line_width, style=line.style_solid)
|
|
|
|
// 下跌趋势开始 - 记录起点
|
|
if entered_down
|
|
down_start_bar := bar_index
|
|
down_start_price := high
|
|
|
|
// 下跌趋势结束 - 画笔(终点 = 结束时的K线)
|
|
if exit_down and show_signals and not na(down_start_bar)
|
|
line.new(down_start_bar, down_start_price, bar_index, close, color=color.red, width=trend_line_width, style=line.style_solid)
|
|
|
|
// 当前进行中的笔(实时显示)
|
|
var line current_up_line = na
|
|
var line current_down_line = na
|
|
|
|
if show_signals
|
|
if not na(current_up_line)
|
|
line.delete(current_up_line)
|
|
if not na(current_down_line)
|
|
line.delete(current_down_line)
|
|
|
|
// 上涨中 - 画到当前K线
|
|
if trend_state == 1 and not na(up_start_bar)
|
|
current_up_line := line.new(up_start_bar, up_start_price, bar_index, close, color=color.new(color.teal, 40), width=trend_line_width, style=line.style_dotted)
|
|
|
|
// 下跌中 - 画到当前K线
|
|
if trend_state == -1 and not na(down_start_bar)
|
|
current_down_line := line.new(down_start_bar, down_start_price, bar_index, close, color=color.new(color.red, 40), width=trend_line_width, style=line.style_dotted)
|
|
|
|
// ========================= 信息面板 =========================
|
|
var table info_table = table.new(position.top_right, 2, 8, bgcolor=color.new(color.black, 80), border_width=1)
|
|
|
|
if barstate.islast
|
|
table.cell(info_table, 0, 0, "EMA+MACD策略", text_color=color.white, bgcolor=color.new(color.gray, 60), text_size=size.small)
|
|
table.cell(info_table, 1, 0, "", bgcolor=color.new(color.gray, 60))
|
|
|
|
state_text = trend_state == 1 ? "📈 上涨" : trend_state == -1 ? "📉 下跌" : "📊 盘整"
|
|
state_color = trend_state == 1 ? color.teal : trend_state == -1 ? color.red : color.gray
|
|
table.cell(info_table, 0, 1, "趋势状态", text_color=color.white, text_size=size.small)
|
|
table.cell(info_table, 1, 1, state_text, text_color=state_color, text_size=size.small)
|
|
|
|
pos_text = strategy.position_size > 0 ? "多头持仓" : strategy.position_size < 0 ? "空头持仓" : "空仓"
|
|
pos_color = strategy.position_size > 0 ? color.teal : strategy.position_size < 0 ? color.red : color.gray
|
|
table.cell(info_table, 0, 2, "持仓状态", text_color=color.white, text_size=size.small)
|
|
table.cell(info_table, 1, 2, pos_text, text_color=pos_color, text_size=size.small)
|
|
|
|
if strategy.position_size != 0
|
|
table.cell(info_table, 0, 3, "入场价", text_color=color.white, text_size=size.small)
|
|
table.cell(info_table, 1, 3, str.tostring(strategy.position_avg_price, "#.##"), text_color=color.white, text_size=size.small)
|
|
|
|
pnl_pct = (close - strategy.position_avg_price) / strategy.position_avg_price * 100 * (strategy.position_size > 0 ? 1 : -1)
|
|
pnl_color = pnl_pct > 0 ? color.teal : color.red
|
|
table.cell(info_table, 0, 4, "浮动盈亏", text_color=color.white, text_size=size.small)
|
|
table.cell(info_table, 1, 4, str.tostring(pnl_pct, "#.##") + "%", text_color=pnl_color, text_size=size.small)
|
|
|
|
if use_sl
|
|
sl_price = strategy.position_size > 0 ? long_sl : short_sl
|
|
table.cell(info_table, 0, 5, "止损价", text_color=color.white, text_size=size.small)
|
|
table.cell(info_table, 1, 5, str.tostring(sl_price, "#.##"), text_color=color.red, text_size=size.small)
|
|
|
|
if use_tp
|
|
tp_price = strategy.position_size > 0 ? long_tp : short_tp
|
|
table.cell(info_table, 0, 6, "止盈价", text_color=color.white, text_size=size.small)
|
|
table.cell(info_table, 1, 6, str.tostring(tp_price, "#.##"), text_color=color.teal, text_size=size.small)
|
|
|
|
table.cell(info_table, 0, 7, "总交易次数", text_color=color.white, text_size=size.small)
|
|
table.cell(info_table, 1, 7, str.tostring(strategy.closedtrades), text_color=color.white, text_size=size.small)
|
|
|
|
// ========================= 策略说明 =========================
|
|
// 【EMA+MACD 趋势跟踪策略】
|
|
//
|
|
// 交易逻辑:
|
|
// 1. 上涨趋势确认时 → 开多仓
|
|
// 2. 下跌趋势确认时 → 开空仓(或平多仓)
|
|
// 3. 进入盘整时 → 根据设置决定是否平仓
|
|
//
|
|
// 风控设置:
|
|
// - 止损:默认3%,可自定义
|
|
// - 止盈:默认6%,可自定义
|
|
// - 移动止损:可选开启
|
|
//
|
|
// 交易方向:
|
|
// - 只做多:仅在上涨趋势开仓
|
|
// - 只做空:仅在下跌趋势开仓
|
|
// - 双向交易:趋势转换时反向开仓
|