4 min read

基于双均线交叉与追踪止损的交易策略

在趋势跟踪策略中,移动平均线交叉是识别趋势转折的经典方法,而止损机制则是控制风险的关键补充。本文解析的 R 语言代码,基于quantstrat包实现了一套融合双均线交叉信号与追踪止损的交易策略,以 AAPL 为标的展示了从策略构建到回测的完整流程。通过明确的信号规则与风险控制机制,该策略为趋势跟踪的量化实现提供了可参考的框架。

核心指标与策略逻辑

策略的核心指标是两条简单移动平均线(SMA):

  • 快速 SMA(5 日):基于最近 5 个交易日的收盘价计算,反映短期价格趋势。
  • 慢速 SMA(75 日):基于最近 75 个交易日的收盘价计算,反映中长期价格趋势。

策略逻辑围绕两条均线的交叉展开:当快速 SMA 从下方上穿慢速 SMA 时,视为多头趋势确立,生成买入信号;当快速 SMA 从上方下穿慢速 SMA 时,视为趋势转弱,生成卖出信号。同时,为避免趋势反转时的大额亏损,策略加入了追踪止损(stoptrailing)机制 —— 当价格从持仓后的高点回落一定比例时,自动平仓离场,锁定部分收益。

策略代码

以下是完整的策略代码,包含逐段注释以解释各环节功能:

###############################################################################
# 一个使用简单均线交叉策略的演示脚本,包含追踪止损,用于测试
###############################################################################

# 加载quantstrat包,用于量化策略开发与回测
library(quantstrat)

# 策略参数设置
init_date <- "2017-01-01"  # 初始化日期
start_date <- "2017-01-01"  # 回测开始日期
end_date <- "2018-12-31"    # 回测结束日期
init_equity <- 1e4          # 初始资金1万美元
.orderqty <- 100            # 订单数量(后续规则中实际使用1000)
Sys.setenv(TZ = "UTC")      # 设置时区为UTC,避免日期处理问题
fast <- 5                   # 快速SMA周期(5日)
slow <- 75                  # 慢速SMA周期(75日)
symbols <- symbol <- "AAPL" # 交易标的为AAPL
tradesize = 1e4             # 交易规模(1万美元)

# 定义货币与标的属性:AAPL以美元计价,合约乘数为1
currency('USD')
## [1] "USD"
# 加载AAPL历史数据(此处使用内置数据,也可从雅虎财经下载)
data(AAPL)
stock(symbols, currency = "USD", multiplier = 1)
## [1] "AAPL"
# 初始化组合、账户与订单簿
portfolio.st <- "Port.Luxor"  # 组合名称
account.st <- "Acct.Luxor"    # 账户名称
strategy.st <- "Strat.Luxor"  # 策略名称

# 清理旧策略数据,确保回测环境纯净
rm.strat(strategy.st)
rm.strat(portfolio.st)
rm.strat(account.st)

# 初始化投资组合:指定标的与初始化日期
initPortf(
  name = portfolio.st,
  symbols = symbols,
  initDate = init_date
)
## [1] "Port.Luxor"
# 初始化账户:关联组合,设置初始资金
initAcct(
  name = account.st,
  portfolios = portfolio.st,
  initDate = init_date,
  initEq = init_equity
)
## [1] "Acct.Luxor"
# 初始化订单簿:记录交易订单
initOrders(
  portfolio = portfolio.st,
  symbols = symbols,
  initDate = init_date
)

# 创建策略对象并存储到环境中
strategy(strategy.st, store = TRUE)

# 添加技术指标:快速SMA与慢速SMA
add.indicator(
  strategy = strategy.st,
  name = "SMA",  # 使用SMA函数计算移动平均线
  arguments = list(x = quote(Cl(mktdata)), n = fast),  # 输入为收盘价,周期5日
  label = "nFast"  # 指标标签:快速SMA
)
## [1] "Strat.Luxor"
add.indicator(
  strategy = strategy.st,
  name = "SMA",
  arguments = list(x = quote(Cl(mktdata)), n = slow),  # 周期75日
  label = "nSlow"  # 指标标签:慢速SMA
)
## [1] "Strat.Luxor"
# 生成交易信号:基于双均线交叉
# 信号1:快速SMA上穿慢速SMA(大于关系),视为多头信号(LONG)
add.signal(
  strategy.st,
  name = "sigCrossover",  # 交叉信号函数
  arguments = list(
    column = c("nFast", "nSlow"),  # 对比快速与慢速SMA
    relationship = "gt"  # 大于(上穿)
  ),
  label = "LONG"  # 信号标签:买入
)
## [1] "Strat.Luxor"
# 信号2:快速SMA下穿慢速SMA(小于关系),视为空头信号(SHORT)
add.signal(
  strategy.st,
  name = "sigCrossover",
  arguments = list(
    column = c("nFast", "nSlow"),
    relationship = "lt"  # 小于(下穿)
  ),
  label = "SHORT"  # 信号标签:卖出
)
## [1] "Strat.Luxor"
# 入场规则:当LONG信号触发时,以限价单买入
add.rule(
  strategy.st,
  name = "ruleSignal",  # 基于信号的规则
  arguments = list(
    sigcol = "LONG",  # 触发信号列:LONG
    sigval = TRUE,  # 信号值为TRUE时执行
    orderside = "long",  # 做多方向
    ordertype = "limit",  # 限价单
    threshold = 1/100,  # 阈值(相对于参考价格的比例)
    tmult = T,  # 阈值为比例(而非固定值)
    orderqty = 1000,  # 订单数量:1000股
    prefer = "Open",  # 参考价格为开盘价
    TxnFees = -10,  # 交易费用:-10美元(每次交易成本)
    orderset = "ocolong",  # 订单集标签,用于管理相关订单
    replace = F  # 不替换未成交订单
  ),
  type = "enter",  # 入场规则
  enabled = T,  # 启用规则
  label = "EnterLONG"  # 规则标签
)
## [1] "Strat.Luxor"
# 出场规则:当SHORT信号触发时,平掉所有多头持仓
add.rule(
  strategy = strategy.st,
  name = "ruleSignal",
  arguments = list(
    sigcol = "SHORT",  # 触发信号列:SHORT
    sigval = TRUE,
    orderqty = 'all',  # 平仓全部持仓
    orderside = 'long',  # 针对多头持仓
    ordertype = "market",  # 市价单(确保快速平仓)
    prefer = "Open",  # 参考价格为开盘价
    TxnFees = -10,  # 交易费用
    orderset = "ocolong",
    replace = F
  ),
  type = "exit",  # 出场规则
  enabled = T,
  parent = "EnterLONG",  # 关联入场规则,确保对应持仓平仓
  label = "Exit2SHORT"
)
## [1] "Strat.Luxor"
# 止损规则:追踪止损,控制单边趋势反转风险
add.rule(
  strategy.st,
  name = "ruleSignal",
  arguments = list(
    sigcol = "LONG",  # 与入场信号关联
    sigval = TRUE,
    orderside = "long",
    ordertype = "stoptrailing",  # 追踪止损单
    orderqty = "all",  # 平仓全部持仓
    prefer = "Close",  # 参考价格为收盘价
    threshold = 2/100,  # 追踪止损比例:2%(从高点回落2%平仓)
    tmult = T,  # 阈值为比例
    TxnFees = -10,
    orderset = "ocolong",
    replace = F
  ),
  type = "chain",  # 链式规则(跟随入场规则触发)
  parent = "EnterLONG",  # 基于EnterLONG规则触发的持仓
  enabled = T,
  label = "StopTrailingLONG"  # 规则标签:多头追踪止损
)
## [1] "Strat.Luxor"
# 执行策略回测
applyStrategy(strategy = strategy.st, portfolios = portfolio.st)
## [1] "2017-07-27 00:00:00 AAPL 1000 @ 141.598281619503"
## [1] "2017-08-03 00:00:00 AAPL -1000 @ 150.70126151877"
## [1] "2017-10-04 00:00:00 AAPL 1000 @ 147.350684699421"
## [1] "2017-10-19 00:00:00 AAPL -1000 @ 151.241571914452"
## [1] "2018-03-22 00:00:00 AAPL 1000 @ 166.357919238892"
## [1] "2018-03-23 00:00:00 AAPL -1000 @ 163.030760854114"
## [1] "2018-04-19 00:00:00 AAPL 1000 @ 168.23487944963"
## [1] "2018-04-20 00:00:00 AAPL -1000 @ 164.870181860637"
## [1] "2018-11-23 00:00:00 AAPL 1000 @ 171.574945095388"
## [1] "2018-11-26 00:00:00 AAPL -1000 @ 168.14344619348"
# 更新组合与账户数据
updatePortf(portfolio.st)
## [1] "Port.Luxor"
daterange <- time(getPortfolio(portfolio.st)$summary)[-1]  # 获取交易日期范围
updateAcct(account.st, daterange)  # 更新账户数据至指定日期
## [1] "Acct.Luxor"
updateEndEq(account.st)  # 更新账户最终权益
## [1] "Acct.Luxor"
# 提取交易统计数据
tstats <- t(tradeStats(Portfolios = portfolio.st))

# 可视化回测结果:展示持仓变化与双均线
chart.Posn(
  Portfolio = portfolio.st, 
  Symbol = "AAPL", 
  TA = c("add_SMA(n=fast, col='blue')", "add_SMA(n=slow, col='red')")  # 叠加5日(蓝)与75日(红)均线
)

# 查看订单簿(用于调试与详细分析)
obook <- getOrderBook('Port.Luxor')

策略核心思路解析

该策略的实现遵循量化交易的标准化流程,各环节紧密衔接,形成完整的逻辑闭环:

环境与参数的初始化

代码首先清理旧策略数据,避免残留信息干扰;随后定义交易标的(AAPL)、初始资金(1 万美元)、均线周期(5 日 / 75 日)等核心参数,并通过initPortf、initAcct等函数初始化组合、账户与订单簿,为策略运行搭建基础环境。

指标与信号的层次构建

策略通过add.indicator添加双均线指标,将短期与长期趋势量化;再通过add.signal定义交叉信号 —— 上穿为买入、下穿为卖出,使趋势转折的判断从主观观察转化为客观规则。这种 “指标→信号” 的转化,是量化策略可复现性的核心。

交易规则的风险控制设计

策略的规则体系包含三类动作:

  • 入场:以限价单买入,控制入场成本;
  • 出场:以市价单平仓,确保趋势反转时及时离场;
  • 止损:追踪止损单(回落 2% 平仓),在趋势未按预期发展时限制亏损。

同时,规则中明确了交易费用(每次 - 10 美元),使回测更接近实盘成本结构;通过parent参数关联入场与出场规则,确保持仓与平仓的对应性。

回测执行与结果可视化

代码通过applyStrategy执行策略回测,更新组合与账户数据后,利用chart.Posn展示 AAPL 的持仓变化,并叠加双均线直观呈现交易时机与趋势的关系;tradeStats则提供胜率、盈亏比等统计指标,为策略评估提供数据支持。

策略的优势与局限性

该策略的优势体现在逻辑清晰与风险控制的结合:

  • 规则明确:基于均线交叉的信号无主观干扰,参数与动作均可量化,便于复现与优化;
  • 风险可控:同时包含趋势反转出场与追踪止损,既捕捉趋势收益,又控制极端风险;
  • 结构灵活:各环节模块化(指标、信号、规则独立),便于扩展(如添加成交量过滤、调整均线周期)。

但策略也存在趋势跟踪策略的共性局限:

  • 滞后性:SMA 基于历史价格计算,信号可能滞后于实际趋势,导致错过最佳入场 / 出场点;
  • 参数敏感:5 日 / 75 日的周期是固定的,可能仅适配特定时段,在震荡市或极端行情中表现不佳;
  • 单一标的依赖:策略仅针对 AAPL 设计,若扩展至其他标的,需重新优化参数以适应其波动性。

总结与改进方向

本策略展示了双均线交叉与追踪止损结合的量化实现,为趋势跟踪提供了一套可操作的框架。其核心价值在于将 “识别趋势→执行交易→控制风险” 的流程标准化,减少主观决策的干扰。

若要进一步优化,可从三方面入手:一是引入自适应参数(如根据市场波动率调整均线周期),提升策略对不同行情的适应性;二是添加过滤条件(如成交量放大时才确认信号),减少震荡市的虚假交易;三是扩展至多标的组合,通过分散投资降低单一标的风险。通过持续迭代,策略可在保持逻辑简洁的同时,提升实盘适用性。