You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TradingView PineScript策略订单偶发无法成交问题求助

偶发订单未成交问题排查请求

我开发了一款TradingView策略,核心逻辑如下:

  • 等待特定触发信号后发送入场订单
  • 订单成交后自动进行交易管理
  • 未成交则在1根K线后取消订单(模拟1天订单有效期)

我为每个订单分配了唯一ID,避免Broker Simulator混淆,且pyramiding设置大于1。曾开启bar magnifier,关闭后问题依旧。

多数场景策略运行正常,但存在偶发异常:入场订单已正确创建,下一根K线视觉上已触及入场价位(上布林带,十字光标已定位至该价位),但订单未成交并于1根K线后被取消。尝试改用市价单,仍无法成交。


策略代码

//@version=6
strategy("BB + MACD Trend/Contra Strategy", overlay=true, default_qty_type=strategy.percent_of_equity)

// === INPUTS ===
use_trend = input.bool(true, title="Enable Trend Strategy")
use_contra = input.bool(true, title="Enable Contra Strategy")
log_enabled = input.bool(true, title="Enable Logging")
capital_risk_percent = input.float(1.0, minval=0.1, maxval=10.0, title="Risk % of Equity per Trade")
bb_len = input.int(20, title="BB Length")
bb_mult = input.float(2.0, title="BB Multiplier")
macd_src = input.source(close, title="MACD Source")
fast_len = input.int(12, title="MACD Fast Length")
slow_len = input.int(26, title="MACD Slow Length")
signal_len = input.int(9, title="MACD Signal Length")

// === CALCULATIONS ===
basis = ta.sma(close, bb_len)
stddev = ta.stdev(close, bb_len)
upper_bb = basis + bb_mult * stddev
lower_bb = basis - bb_mult * stddev
macd_line = ta.ema(macd_src, fast_len) - ta.ema(macd_src, slow_len)
signal_line = ta.ema(macd_line, signal_len)
trendUp = macd_line > 0 and macd_line > signal_line
trendDown = macd_line < 0 and macd_line < signal_line
white_bg = not trendUp and not trendDown
first_colored = not (trendUp[1] or trendDown[1]) and (trendUp or trendDown)
trend_entry_long = trendUp and close < upper_bb
trend_entry_short = trendDown and close > lower_bb
valid_contra_bar = white_bg or first_colored

bgcolor(trendUp ? color.new(color.green, 90) : trendDown ? color.new(color.red, 90) : na)
plot(basis, "BB Basis", color=color.blue)
plot(upper_bb, "BB Upper", color=color.red)
plot(lower_bb, "BB Lower", color=color.green)

// === STATE VARIABLES ===
var string active_id = na
var float entry_price = na
var float active_tp = na
var float active_sl = na
var float active_risk = na
var float initial_risk = na
var bool active_is_short = false
var int barindex_entryorder = na
var bool entry_pending = false

// === ORDER SETUP ===
if strategy.opentrades == 0 and not entry_pending
    capital_at_risk = strategy.equity * capital_risk_percent / 100

    if use_contra and valid_contra_bar and close > upper_bb
        active_id := "Contra Down"
        entry_price := upper_bb
        active_tp := basis
        active_risk := math.abs(entry_price - active_tp)
        active_sl := entry_price + active_risk
        active_is_short := true
        qty = active_risk > 0 ? capital_at_risk / active_risk : 0
        strategy.entry(active_id, strategy.short, qty=qty, stop=entry_price)
        strategy.exit("TP " + active_id, from_entry=active_id, limit=active_tp)
        strategy.exit("SL " + active_id, from_entry=active_id, stop=active_sl)
        barindex_entryorder := bar_index
        entry_pending := true
        if log_enabled
            log.info("Order Placed\nID: " + active_id + "\nEntry: " + str.tostring(entry_price, "#.##") + "\nTP: " + str.tostring(active_tp, "#.##") + "\nSL: " + str.tostring(active_sl, "#.##") + "\nRisk: " + str.tostring(active_risk, "#.##") + "\nQty: " + str.tostring(qty, "#.##"))

    if use_contra and valid_contra_bar and close < lower_bb
        active_id := "Contra Up"
        entry_price := lower_bb
        active_tp := basis
        active_risk := math.abs(entry_price - active_tp)
        active_sl := entry_price - active_risk
        active_is_short := false
        qty = active_risk > 0 ? capital_at_risk / active_risk : 0
        strategy.entry(active_id, strategy.long, qty=qty, stop=entry_price)
        strategy.exit("TP " + active_id, from_entry=active_id, limit=active_tp)
        strategy.exit("SL " + active_id, from_entry=active_id, stop=active_sl)
        barindex_entryorder := bar_index
        entry_pending := true
        if log_enabled
            log.info("Order Placed\nID: " + active_id + "\nEntry: " + str.tostring(entry_price, "#.##") + "\nTP: " + str.tostring(active_tp, "#.##") + "\nSL: " + str.tostring(active_sl, "#.##") + "\nRisk: " + str.tostring(active_risk, "#.##") + "\nQty: " + str.tostring(qty, "#.##"))

// === ENTRY-FILL TRACKING ===
if entry_pending and strategy.opentrades > 0
    entry_price := strategy.opentrades.entry_price(0)
    active_tp := active_is_short ? basis : basis
    active_risk := math.abs(entry_price - active_tp)
    active_sl := active_is_short ? entry_price + active_risk : entry_price - active_risk
    initial_risk := active_risk
    strategy.exit("TP " + active_id, from_entry=active_id, limit=active_tp)
    strategy.exit("SL " + active_id, from_entry=active_id, stop=active_sl)
    entry_pending := false
    if log_enabled
        log.info("Order filled\nID: " + active_id + "\nEntry = " + str.tostring(entry_price, "#.##") + "\nSL = " + str.tostring(active_sl, "#.##") + "\nTP = " + str.tostring(active_tp, "#.##"))

if entry_pending and bar_index > barindex_entryorder + 1 and strategy.opentrades == 0
    entry_pending := false
    active_id := na
    entry_price := na
    active_sl := na
    active_tp := na
    active_risk := na
    barindex_entryorder := na
    if log_enabled
        log.info("Order expired – no fill\nBar: " + str.tostring(bar_index))

// === TRADE MANAGEMENT ===
if strategy.opentrades > 0 and not na(entry_price)
    if active_id == "Contra Up" or active_id == "Contra Down"
        new_tp = basis
        new_risk = math.abs(entry_price - new_tp)
        new_sl = active_is_short ? entry_price + new_risk : entry_price - new_risk
        if new_risk < active_risk
            active_tp := new_tp
            active_sl := new_sl
            active_risk := new_risk
            strategy.exit("TP " + active_id, from_entry=active_id, limit=active_tp)
            strategy.exit("SL " + active_id, from_entry=active_id, stop=active_sl)
            if log_enabled
                log.info("Trade Mgmt\nID: " + active_id + "\nTP: " + str.tostring(active_tp, "#.##") + "\nSL: " + str.tostring(active_sl, "#.##") + "\nRisk: " + str.tostring(active_risk, "#.##"))
        if (active_is_short and active_tp >= entry_price) or (not active_is_short and active_tp <= entry_price)
            strategy.close(active_id)
            if log_enabled
                log.info("Closed by Basis\nID: " + active_id + "\nReason: Basis >= Entry")
            active_id := na
            entry_price := na
            active_tp := na
            active_sl := na
            active_risk := na
            initial_risk := na

// === EXIT LABEL ===
if strategy.opentrades == 0 and not na(active_id)
    entry = strategy.closedtrades.entry_price(strategy.closedtrades - 1)
    exit = strategy.closedtrades.exit_price(strategy.closedtrades - 1)
    r = math.abs(entry - initial_risk > 0 ? initial_risk : 1)
    r_mult = (exit - entry) / r * (active_is_short ? -1 : 1)
    label_color = r_mult >= 0 ? color.green : color.red
    label_text = (r_mult >= 0 ? "TP" : "SL") + " " + active_id + " " + str.tostring(r_mult, format.percent)
    label.new(strategy.closedtrades.exit_bar_index(strategy.closedtrades - 1), exit, label_text, style=label.style_label_left, color=label_color, textcolor=color.white, size=size.normal)
    if log_enabled
        log.info("Trade Closed\nID: " + active_id + "\nR: " + str.tostring(r_mult, "#.##") + "\nEntry: " + str.tostring(entry, "#.##") + "\nExit: " + str.tostring(exit, "#.##") + "\nRisk: " + str.tostring(r, "#.##"))
    active_id := na
    entry_price := na
    active_sl := na
    active_tp := na
    active_risk := na
    initial_risk := na
    barindex_entryorder := na

// === PLOT MARKERS ===
plotshape(active_id == "Contra Down" and strategy.opentrades > 0 and use_contra ? active_sl : na, title="SL Contra Down", style=shape.triangleup, location=location.absolute, color=color.red, size=size.tiny)
plotshape(active_id == "Contra Down" and strategy.opentrades > 0 and use_contra ? active_tp : na, title="TP Contra Down", style=shape.triangledown, location=location.absolute, color=color.green, size=size.tiny)
plotshape(active_id == "Contra Up" and strategy.opentrades > 0 and use_contra ? active_sl : na, title="SL Contra Up", style=shape.triangleup, location=location.absolute, color=color.red, size=size.tiny)
plotshape(active_id == "Contra Up" and strategy.opentrades > 0 and use_contra ? active_tp : na, title="TP Contra Up", style=shape.triangledown, location=location.absolute, color=color.green, size=size.tiny)

相关场景说明

  • 异常场景:EUR/USD 10月23日,收盘价上穿布林带触发入场,订单已创建但未成交
  • 配置截图:展示当前策略的参数设置界面

可能的问题原因及排查方向
  • 订单触发逻辑的价格跳空:TradingView止损单触发条件是下一根K线价格触及或穿过止损价,若下一根K线开盘价直接跳过止损价(比如做空单的止损价是上布林带,开盘价高于该价位),订单不会触发。需检查异常场景下K线的开盘价与止损价的关系。
  • 布林带计算的周期匹配问题:当前用close计算布林带,若回测时K线周期与预期的“1天”不匹配(比如实际用1小时K线,订单过期逻辑是2小时而非1天),会导致视觉与实际逻辑不符。需确认K线周期设置。
  • 市价单的成交限制:策略模拟器中市价单以下一根K线开盘价成交,若滑点、手续费设置过高,或开盘价导致权益不足以开仓,会导致订单无法成交。需检查策略设置中的滑点、手续费参数。
  • 状态变量重置不彻底:entry_pending等状态变量在订单过期后是否正确重置?若某些场景下变量未重置,会导致后续订单逻辑异常。可在日志中增加状态变量打印,确认重置情况。
  • 订单ID与exit命名冲突:虽用active_id区分订单,但strategy.exit的命名是否唯一?若重复会导致订单被覆盖。可检查日志中的订单ID与exit名称是否对应。

内容的提问来源于stack exchange,提问作者user30449074

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 04:44:49