在量化交易领域,移动均线交叉策略是一种被广泛应用的趋势跟踪方法。本 文将解析一段基于 R 语言 quantstrat 包的简单移动均线交叉策略代码,展示其实现思路、核心逻辑及回测分析过程。
策略核心思想
这段代码实现了一个经典的双均线交叉策略,其基本逻辑是通过比较短期均线与长期均线的相对位置来生成交易信号: - 当短期均线(50 日 SMA)上穿长期均线(200 日 SMA)时,产生买入信号 - 当短期均线(50 日 SMA)下穿长期均线(200 日 SMA)时,产生卖出信号
策略默认仅做多,代码中也预留了做空逻辑的实现方式
这种策略的理论基础在于,短期均线反映近期价格趋势,长期均线反映长期价格趋势,当短期均线从下方穿越长期均线时,通常被视为上升趋势的开始;反之则被视为下降趋势的开始。
策略实现的关键步骤
下面是代码的关键实现部分及解析:
# 加载必要的包
require(quantstrat)
# 环境初始化
ttz<-Sys.getenv('TZ')
Sys.setenv(TZ='UTC') # 时区设置,避免日期处理问题
# 清理旧策略数据
suppressWarnings(rm("order_book.macross",pos=.strategy))
suppressWarnings(rm("account.macross","portfolio.macross",pos=.blotter))
suppressWarnings(rm("account.st","portfolio.st","stock.str","stratMACROSS",'start_t','end_t'))
# 设置交易标的和货币
stock.str='AAPL' # 以苹果公司股票为例
currency('USD')
## [1] "USD"
stock(stock.str,currency='USD',multiplier=1)
## [1] "AAPL"
# 初始化回测参数
startDate="1999-12-31" # 回测起始日期
initEq=1000000 # 初始资金100万美元
portfolio.st='macross' # 投资组合名称
account.st='macross' # 账户名称
# 初始化投资组合、账户和订单簿
initPortf(portfolio.st,symbols=stock.str)
## [1] "macross"
initAcct(account.st,portfolios=portfolio.st, initEq=initEq)
## [1] "macross"
initOrders(portfolio=portfolio.st)
# 创建策略对象
stratMACROSS<- strategy(portfolio.st)
# 添加技术指标:50日和200日简单移动平均线
stratMACROSS <- add.indicator(strategy = stratMACROSS, name = "SMA",
arguments = list(x=quote(Cl(mktdata)), n=50),
label= "ma50" )
stratMACROSS <- add.indicator(strategy = stratMACROSS, name = "SMA",
arguments = list(x=quote(Cl(mktdata)[,1]), n=200),
label= "ma200")
# 添加交易信号:均线交叉信号
stratMACROSS <- add.signal(strategy = stratMACROSS, name="sigCrossover",
arguments = list(columns=c("ma50","ma200"), relationship="gte"),
label="ma50.gt.ma200") # 短期均线上穿长期均线
stratMACROSS <- add.signal(strategy = stratMACROSS, name="sigCrossover",
arguments = list(columns=c("ma50","ma200"), relationship="lt"),
label="ma50.lt.ma200") # 短期均线下穿长期均线
# 添加交易规则:基于信号的交易执行
stratMACROSS <- add.rule(strategy = stratMACROSS, name='ruleSignal',
arguments = list(sigcol="ma50.gt.ma200", sigval=TRUE,
orderqty=100, ordertype='market', orderside='long'),
type='enter') # 买入规则
stratMACROSS <- add.rule(strategy = stratMACROSS, name='ruleSignal',
arguments = list(sigcol="ma50.lt.ma200", sigval=TRUE,
orderqty='all', ordertype='market', orderside='long'),
type='exit') # 卖出规则
# 预留的做空策略代码(默认未启用)
# stratMACROSS <- add.rule(strategy = stratMACROSS, name='ruleSignal',
# arguments = list(sigcol="ma50.lt.ma200", sigval=TRUE,
# orderqty=-100, ordertype='market', orderside='short'),
# type='enter')
# stratMACROSS <- add.rule(strategy = stratMACROSS, name='ruleSignal',
# arguments = list(sigcol="ma50.gt.ma200", sigval=TRUE,
# orderqty=100, ordertype='market', orderside='short'),
# type='exit')
# 获取历史数据并进行复权处理
getSymbols(stock.str, from=startDate, src='yahoo')
## [1] "AAPL"
for(i in stock.str)
assign(i, adjustOHLC(get(i), use.Adjusted=TRUE))
# 执行策略回测
start_t<-Sys.time()
out<-applyStrategy(strategy=stratMACROSS, portfolios=portfolio.st)
## [1] "2001-06-27 00:00:00 AAPL 100 @ 0.350732564926147"
## [1] "2001-09-07 00:00:00 AAPL -100 @ 0.259667813777924"
## [1] "2002-01-07 00:00:00 AAPL 100 @ 0.344120860099792"
## [1] "2002-07-10 00:00:00 AAPL -100 @ 0.260269552469254"
## [1] "2003-05-16 00:00:00 AAPL 100 @ 0.28250914812088"
## [1] "2006-06-22 00:00:00 AAPL -100 @ 1.79062843322754"
## [1] "2006-09-26 00:00:00 AAPL 100 @ 2.33250570297241"
## [1] "2008-03-07 00:00:00 AAPL -100 @ 3.67412352561951"
## [1] "2008-05-19 00:00:00 AAPL 100 @ 5.51794862747192"
## [1] "2008-09-24 00:00:00 AAPL -100 @ 3.8682746887207"
## [1] "2009-05-14 00:00:00 AAPL 100 @ 3.69516229629517"
## [1] "2012-12-11 00:00:00 AAPL -100 @ 16.41552734375"
## [1] "2013-09-11 00:00:00 AAPL 100 @ 14.4533443450928"
## [1] "2015-08-31 00:00:00 AAPL -100 @ 25.3688125610352"
## [1] "2016-08-31 00:00:00 AAPL 100 @ 24.3808135986328"
## [1] "2018-12-24 00:00:00 AAPL -100 @ 34.9761238098145"
## [1] "2019-05-07 00:00:00 AAPL 100 @ 48.5301742553711"
## [1] "2022-06-06 00:00:00 AAPL -100 @ 143.832458496094"
## [1] "2022-09-27 00:00:00 AAPL 100 @ 149.571166992188"
## [1] "2022-10-10 00:00:00 AAPL -100 @ 138.394744873047"
## [1] "2023-03-23 00:00:00 AAPL 100 @ 157.137161254883"
## [1] "2024-03-15 00:00:00 AAPL -100 @ 171.583724975586"
## [1] "2024-06-14 00:00:00 AAPL 100 @ 211.500885009766"
## [1] "2025-04-08 00:00:00 AAPL -100 @ 172.194198608398"
end_t<-Sys.time()
print(end_t-start_t) # 输出回测执行时间
## Time difference of 0.2876902 secs
# 更新投资组合数据
start_t<-Sys.time()
updatePortf(Portfolio='macross', Dates=paste('::', as.Date(Sys.time()), sep=''))
## [1] "macross"
end_t<-Sys.time()
print("trade blotter portfolio update:")
## [1] "trade blotter portfolio update:"
print(end_t-start_t)
## Time difference of 0.04611683 secs
# 可视化回测结果
chart.Posn(Portfolio='macross', Symbol=stock.str)
add_SMA(n=50, on=1, col='blue') # 叠加50日均线(蓝色)
add_SMA(n=200, on=1) # 叠加200日均线(默认红色)
# 获取交易统计数据
book = getOrderBook('macross') # 订单簿
stats = tradeStats('macross') # 交易统计
ptstats = perTradeStats('macross') # 每笔交易统计
rets = PortfReturns('macross') # 组合收益
txns = getTxns('macross', stock.str) # 交易记录
# 恢复时区设置
Sys.setenv(TZ=ttz)
策略的核心组件涵盖了从环境搭建到结果分析的完整流程。在 环境初始化阶段,首先通过设置时区避免日期处理过程中可能出现的问题,同时清理旧有的策略数据,确保回测环境的纯净性,为后续分析排除干扰。在 此基础上,明确交易标的为 AAPL,并设定货币单位为 USD,为策略运行奠定基础。
指标与信号系统是策略的核心判断依据。通 过 add.indicator 函数添加了 50 日和 200 日两条简单移动平均线(SMA),前者反映短期价格趋势,后者代表中长期趋势。随 后,借助 add.signal 函数定义了均线交叉信号:当 50 日 SMA 上穿 200 日 SMA 时,生成买入信号;当 50 日 SMA 下穿 200 日 SMA 时,触发卖出信号,这一逻辑构成了策略的决策核心。
交易规则则将信号转化为具体操作。买 入规则设定为当 50 日 SMA 上穿 200 日 SMA 时,买入 100 股;卖出规则则在 50 日 SMA 下穿 200 日 SMA 时,卖出全部持仓,实现趋势跟踪的闭环。此 外,代码中还预留了做空规则,虽未启用,但为策略扩展至双向交易提供了可能,用户可根据需求取消注释启用。
数据处理与回测执行环节确保了策略的可操作性。从 Yahoo Finance 获取 AAPL 的历史数据后,进行复权处理以保证价格的准确性。通 过 applyStrategy 函数执行回测,并记录执行时间,便于评估策略效率;随后更新投资组合数据,为绩效指标的计算提供支持。 结果可视化与分析则让策略效果更加直观。利 用 chart.Posn 绘制持仓变化图,并叠加 50 日和 200 日均线,清晰展示交易时机与价格趋势的关系。同 时,通过获取订单簿、交易统计、每笔交易详情及组合收益等数据,为深入分析策略表现提供了全面依据。
这种双均线交叉策略的意义显著:它不仅实现了趋势跟踪的基本逻辑,能够捕捉中长期价格趋势,还提供了一套完整的量化交易框架,涵盖数据获取、信号生成、订单执行到绩效评估的全流程。代 码结构清晰,便于修改和扩展,例如调整均线周期或添加其他指标以优化策略。
然而,该策略也存在明显局限性。在 横盘震荡市场中,均线频繁交叉易产生虚假信号,导致频繁交易和不必要的亏损;固定的 100 股交易数量缺乏灵活的资金管理和风险控制机制;仅依赖收盘价计算均线,可能忽略日内价格波动中蕴含的信息;且未考虑交易成本和滑点,实际应用中的表现可能低于回测结果。
对于希望优化策略的交易者,可从多方面入手:调整均线周期参数以适应不同市场环境;引入交易量或其他技术指标过滤虚假信号,提高信号质量;实现动态仓位管理,根据市场波动性调整头寸大小,平衡风险与收益;增加止损和止盈机制,控制单笔交易的潜在风险。这 种基于技术指标的趋势跟踪策略是量化交易的基础,理解其实现原理和优缺点,能为构建更复杂、更有效的交易系统提供重要参考。