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

如何用QuantStrat实现单仓位限制的多级布林带交易策略?

问题根源与修复方案

你的代码重复触发订单的核心原因是没有给入场规则添加仓位状态过滤——只要价格交叉布林带就触发下单,完全没考虑当前是否已经持有该级别对应方向的仓位。以下是具体修复方案和优化后的完整代码:

关键修改点

  1. 修复指标调用笔误:原代码中add.indicator的name=strategy.st是错误的,应改为"BBands",否则会找不到指标函数。
  2. 添加仓位过滤逻辑:每个入场信号必须同时满足「价格交叉布林带」和「当前无对应方向仓位」两个条件,用sigAND组合交叉信号与sigIsFlat(无仓位)信号。
  3. 补充均线指标:最低级别平仓用的Cross.mid需要先通过SMA指标生成,否则会找不到对应数据列。
  4. 约束订单唯一性:确保每个级别的入场订单仅在无对应仓位时触发,避免同一级别重复开仓。

优化后的完整代码

initEQ <- 10000
MAperiod_input <- 200
stdev_input <- 3.5
transaction_cost_input <- 0
levels_input <- 10
profit_ticks_input <- 1

strategy.st <- portfolio.st <- account.st <- "BBands"
stdev_list <- seq(stdev_input/levels_input, stdev_input, by = stdev_input/levels_input)

initPortf(portfolio.st, symbols = c("PairsFinal"))
initAcct(account.st, portfolios = portfolio.st, initEq = initEQ)
initOrders(portfolio.st)

strategy(strategy.st, store = T)

# 添加最低级别平仓用的均线(MA)
add.indicator(strategy.st, name = "SMA", arguments = list(x = quote(Cl(mktdata)), n = MAperiod_input), label = "Cross.mid")

#####
# 循环生成每个布林带级别的进出规则
for(i in c(1:length(stdev_list))){
  BollbandLabel <- paste0("BollBand",i)
  upperBandCrossLabel <- paste0("upperBandCross",i)
  lowerBandCrossLabel <- paste0("lowerBandCross",i)
  exitBollBandLabel <- paste0("exitBollBand",i)
  upperExitBandCrossLabel <- ifelse(i - profit_ticks_input > 0,  paste0("upperExitBandCross",i), "Cross.mid")
  lowerExitBandCrossLabel <- ifelse(i - profit_ticks_input > 0,  paste0("lowerExitBandCross",i), "Cross.mid")
  
  # 修正:调用正确的BBands指标
  add.indicator(strategy.st, name = "BBands", arguments = list(HLC = quote(HLC(mktdata)) , n = MAperiod_input, sd =  stdev_list[i]), label = BollbandLabel)
  
  ## 入场规则(添加仓位过滤)
  # 空单入场:价格下穿下轨 + 当前无空单仓位
  add.signal(strategy.st, "sigCrossover", arguments = list(columns = c("Cl", paste0("dn.",BollbandLabel)), relationship = "lt"), label = upperBandCrossLabel)
  add.signal(strategy.st, "sigIsFlat", arguments = list(orderside = 'short'), label = paste0("flatShort", i))
  add.signal(strategy.st, "sigAND", arguments = list(columns = c(upperBandCrossLabel, paste0("flatShort", i))), label = paste0("enterShort", i))
  add.rule(strategy.st, name = "ruleSignal", arguments = list(sigcol = paste0("enterShort", i), orderqty = -100, orderside='short', sigval = T, ordertype = "market", TxnFees = transaction_cost_input),type = 'enter')
  
  # 多单入场:价格上穿上轨 + 当前无多单仓位
  add.signal(strategy.st, "sigCrossover", arguments = list(columns = c("Cl", paste0("up.",BollbandLabel)), relationship = "gt"), label = lowerBandCrossLabel)
  add.signal(strategy.st, "sigIsFlat", arguments = list(orderside = 'long'), label = paste0("flatLong", i))
  add.signal(strategy.st, "sigAND", arguments = list(columns = c(lowerBandCrossLabel, paste0("flatLong", i))), label = paste0("enterLong", i))
  add.rule(strategy.st, name = "ruleSignal", arguments = list(sigcol = paste0("enterLong", i), orderqty = 100, orderside='long', sigval = T, ordertype = "market", TxnFees = transaction_cost_input),type = 'enter')
  
  ## 平仓规则
  if(upperExitBandCrossLabel != "Cross.mid"){
    add.indicator(strategy.st, name = "BBands", arguments = list(HLC = quote(HLC(mktdata)) , n = MAperiod_input, sd = stdev_list[i - profit_ticks_input]), label = exitBollBandLabel)
    add.signal(strategy.st, "sigCrossover", arguments = list(columns = c("Cl", paste0("up.",exitBollBandLabel)), relationship = "lt"), label = upperExitBandCrossLabel)
    add.rule(strategy.st, name = "ruleSignal", arguments = list(sigcol = upperExitBandCrossLabel, orderqty = 100, orderside='long', sigval = T, ordertype = "market", TxnFees = transaction_cost_input),type = 'exit')
    add.signal(strategy.st, "sigCrossover", arguments = list(columns = c("Cl", paste0("dn.",exitBollBandLabel)), relationship = "gt"), label = lowerExitBandCrossLabel)
    add.rule(strategy.st, name = "ruleSignal", arguments = list(sigcol = lowerExitBandCrossLabel, orderqty = -100, orderside='short', sigval = T, ordertype = "market", TxnFees = transaction_cost_input),type = 'exit')
  }else{
    add.signal(strategy.st, "sigCrossover", arguments = list(columns = c("Cl", "Cross.mid"), relationship = "lt"), label = upperExitBandCrossLabel)
    add.rule(strategy.st, name = "ruleSignal", arguments = list(sigcol = upperExitBandCrossLabel, orderqty = 100, orderside='long', sigval = T, ordertype = "market", TxnFees = transaction_cost_input),type = 'exit')
    add.signal(strategy.st, "sigCrossover", arguments = list(columns = c("Cl", "Cross.mid"), relationship = "gt"), label = lowerExitBandCrossLabel)
    add.rule(strategy.st, name = "ruleSignal", arguments = list(sigcol = lowerExitBandCrossLabel, orderqty = -100, orderside='short', sigval = T, ordertype = "market", TxnFees = transaction_cost_input),type = 'exit')
  }
}

# 执行策略并统计
strat <- applyStrategy(strategy.st, portfolio.st, mktdata = OHLC(PairsFinal))
updatePortf(portfolio.st)
strat <- getPortfolio(portfolio.st)$summary
tradeStats(portfolio.st)

多级布林带策略额外注意事项

  • 仓位跟踪精细化:如果需要按级别单独跟踪仓位(比如某级别开仓后,其他级别信号不干扰),可以给每个级别的订单添加独特标签,或自定义osFUN函数实现更细粒度的仓位过滤。
  • 信号优先级控制:若多个级别信号同时触发,可通过orderqty设置或rule的prefer参数调整优先级,避免订单冲突。
  • 平仓规则对应性:确保每个级别的入场订单对应唯一的平仓规则,避免出现“错平”其他级别仓位的情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 15:36:28