作者naturalsmen (日日夜夜)
看板R_Language
标题[分享] quantstrat 套件分享
时间Sun Jul 24 02:53:28 2016
1. 套件名称:
quantstrat 0.9.1739
Depends:
R(>= 2.10),
quantmod,
xts(>= 0.8-2),
blotter(>= 0.9),
FinancialInstrument(>= 0.12.5),
foreach(>= 1.4.0)
Repos:
https://github.com/braverock/quantstrat
https://R-Forge.R-project.org
2. 套件主要用途:
提供股票和各国货币等资产组合模拟交易的框架
能够产生商品k线图、进出场flag、留仓部位等
也支援使用者定义的技术指标、买卖手续费设定
http://imgur.com/a/BJ5Zo
提供交易次数、Annual Sharpe Ratio、MAE、MFE、年化算术平均/几何报酬率等统计资料
http://imgur.com/a/RsTO1
还有基於平行运算的Walk Forward Analysis
http://imgur.com/a/vsm9l
但因为OS的问题 注册cluster的方式不一样 需要不同的package
Windows: doParallel、doRedis、snow等
Ubuntu: doMC
由於多数Finance相关的package都是基於quantmod的框架再延伸
所以有蛮多相依的套件,但我觉得主要功能是在quantstrat
所以标题就用quantstrat了XD
3. 套件主要函数列表:
quantstrat:
add.indicator
add.signal
add.rule
applyStrategy
apply.paramset
add.distribution
add.distribution.constraint
walk.forward
blotter:
tradeStats
chart.Posn
FinancialInstrument:
currency
stock
4. 分享内容:
其实网路上资料蛮多的,但都是英文
所以我很纳闷怎麽都没有人写中文的教学文
台湾看到的回测程式大多是用Python、VBA或现成套装软体居多
想说可以推广一下R这个还蛮方便的package
Analysis、Visualization和用平行运算处理参数最佳化和WFA是他的特色
不过觉得他的documentation不是很完整
由於我今年GSoC的project是整合Josh新的xts_0.10-0和quantmod、quantstrat等套件
和加入dynamic graphs feature
顺利的话在计画结束前应该能把他补得更完整
如果想要自动交易
台湾券商的话 可以将R作为computing server
将结果pass到C++进行下单
国外券商的话
可以用IBroker直接用R跟国外券商API串接
就可以下单了
如果跟shiny结合 就可以做出一个简单的线上应用
https://naturalsmen.shinyapps.io/Backtest
这是小弟有段时间之前写来自用的
已经内建策略了
所以请忽略首页设定参数的tab
直接到Backtest 选择股号就可以run了
run完请选择Backtest result
highchart是我之前加上去的
不在本文介绍的范围内
日後有时间应该会把他完善
这边就先用来做个简单的范例
------以下正题------
以Windows为例
library(doParallel) # for apply.paramset() and walk.forward()
library(quantstrat) # main package
library(IKTrading) # quantstrat extensions, mainly for asset allocation
# and order-sizing functions
library(quantmod) # retrieve symbols from yahoo finance, google finance, etc
getSymbols("^GSPC")
# 由於之前可能有策略存在.blotter environment里,我们要先把他清空
rm(list = ls(.blotter), envir = .blotter)
# 设定货币是TWD、USD或EUR等
currency("TWD")
# 设定时区,一般是Universal Time(UTC)或GreenWich Mean Time(GMT)
# 也可以用xts::indexTZ(GSPC)来确认时区
Sys.setenv(TZ = "UTC")
# 可以有多个symbols,多个symbols会形成投资组合
# 如symbols = c("GSPC", "GDAXI")
# 为了方便说明,这边只用一个指数商品
symbols = "GSPC"
# multiplier为杠杆乘数,比如90元买入中华电(2412)一张,100卖出
# 当multiplier=m,不计手续费下赚m*10000元
stock(symbols, currency = "TWD", multiplier = 1)
# 设定日期、策略名称和初始权益(本金)
initDate = "1999-01-01"
strategy.st <- portfolio.st <- account.st <- "myStrat"
tradeSize <- 1000000
initEq <- tradeSize*length(symbols)
# 避免策略重复,先将投组、策略、帐号等初始化
# 并从initDate开始执行策略
rm.strat(portfolio.st)
rm.strat(strategy.st)
initPortf(portfolio.st, symbols = symbols, initDate = initDate, currency = "TWD")
initAcct(account.st, portfolios = portfolio.st, initDate = initDate, currency = "TWD", initEq = initEq)
initOrders(portfolio.st, initDate = initDate)
strategy(strategy.st, store = TRUE)
# 设定会使用到的参数
nRSI = 2
thresh1 = 10
thresh2 = 6
nSMAexit = 10
nSMAfilter = 200
period = 10
pctATR = .02 # 2% ATR, size the position based on risk
maxPct = .04
# 设定indicator
# name是这个indicator的名称,会由名称呼叫该函数
# 所以你也可以自己定义技术指标或加入ML函数
# mktdata在function内会被替换成你的symbols所以不用更动
# label会让你指标的栏位名称变成<column1>.label
add.indicator(strategy.st, name = "lagATR",
arguments = list(HLC=quote(HLC(mktdata)), n=period),
label = "atrX")
add.indicator(strategy.st, name = "RSI",
arguments = list(price=quote(Cl(mktdata)), n=nRSI),
label = "rsi")
# 举例来说,这里coloumn name 会变成 SMA.quickMA
add.indicator(strategy.st, name = "SMA",
arguments = list(x=quote(Cl(mktdata)), n=nSMAexit),
label = "quickMA")
add.indicator(strategy.st, name = "SMA",
arguments = list(x=quote(Cl(mktdata)), n=nSMAfilter),
label = "filterMA")
# 设定signals
# 主要有sigComparison、sigThreshold、sigCrossover和sigAND
# sigComparison:比较两个设定好的indicator
# sigThreshold:比较设定好的indicator和一个定值
# sigCrossover:也是比较两个indicator,但default是t-1时和t时的交叉
# 若要其他lag期间需要使用者自行定义函数
# 效果等同於sigComparison(..., cross=TRUE)
# sigAND:两个signal需要同时成立
# relationship有gt(greater than)、lt(lower than)、
# gte(greater than or equal to)、lte(lower than or equal to)
# 这边设定close price > 200日MA时产生讯号,并设定label为upTrend
add.signal(strategy.st, name = "sigComparison",
arguments = list(columns = c("Close", "filterMA"), relationship = "gt"),
label = "upTrend")
# cross = FALSE, for current signal
# cross = TRUE, for both previous day and current signal
# rsi < 10
add.signal(strategy.st, name = "sigThreshold",
arguments = list(column = "rsi", threshold = thresh1,
relationship = "lt", cross = FALSE),
label = "rsiThresh1")
# rsi < 6
add.signal(strategy.st, name = "sigThreshold",
arguments = list(column = "rsi", threshold = thresh2,
relationship = "lt", cross = FALSE),
label = "rsiThresh2")
# 进场讯号1:今天的rsi<10 且 close price > 200日MA,昨天则否(任一不成立)
# long
add.signal(strategy.st, name = "sigAND",
arguments = list(columns = c("rsiThresh1", "upTrend"), cross = TRUE),
label = "longEntry1")
# 进场讯号2:今天的rsi<6 且 close price > 200日MA,昨天则否(任一不成立)
# 可以看出这是反向加码策略
add.signal(strategy.st, name = "sigAND",
arguments = list(columns = c("rsiThresh2", "upTrend"), cross = TRUE),
label = "longEntry2")
# 出场讯号1:今天的close price > 10日MA,昨天则否
add.signal(strategy.st, name = "sigCrossover",
arguments = list(columns = c("Close", "quickMA"), relationship = "gt"),
label = "exitLongNormal")
# 出场讯号2:今天的close price < 200日MA,昨天则否
add.signal(strategy.st, name = "sigCrossover",
arguments = list(columns = c("Close", "filterMA"), relationship = "lt"),
label = "exitLongFilter")
# 这边设定台湾的手续费和交易税,买入0.001425,卖出0.004425
# function args必须有TxnQty, TxnPrice和Symbol
buyCost <- 0.001425
# custom transaction fee function based on value of transaction
buyFee <- function(TxnQty, TxnPrice, Symbol, ...)
{
abs(TxnQty) * TxnPrice * -buyCost
}
sellCost <- 0.004425
sellFee <- function(TxnQty, TxnPrice, Symbol, ...)
{
abs(TxnQty) * TxnPrice * -sellCost
}
# 设定规则,为了方便讲解,这边都以做多为例
# name: rule的名称,用来呼叫ruleSignal(),所以也可以自己定义
# sigcol: 上面讯号的label
# sigval: 当讯号产生,是否给讯号赋值,TRUE为1、FALSE为0
# ordertype: 下单类型,有市价单、限价单、停损单等
# 这边以市价单为例
# orderside: 做多或放空
# replace: 如果两个讯号同时成立,这个讯号是否取代另一个
# prefer: 因为是回测,所以我们需要假定买入和卖出的价格
# 这边假设是开盘价,若要考虑滑价的话需要自行定义function
# osFUN: 下单的数量,default为osDollarATR和osMaxDollar
# osDollarATR: 依照现有部位产品的波动度和风险承受度加权
# osMaxDollar: 每次都依现有资金买满,ex.一股160的2330,本金50w,会买满3张
# 零股需另外定义function
# tradeSize: 用多少本金交易
# pctATR, maxPctATR: 风险承受度的百分比
# atrMod: 搭配ATR,最早的indicator,lastATR的label必须要是atrX
# TxnFees: 额外交易费用,如上所定义之函数
# type: 进场或出场
# path.dep: 当这个rule未close掉,其他的rule不会执行
# call ruleSignal时需要设为TRUE
add.rule(strategy.st, name = "ruleSignal",
arguments = list(sigcol = "longEntry1",
sigval = TRUE,
ordertype = "market",
orderside = "long",
replace = FALSE, # replace = TRUE may choose just one rule
prefer = "Open", # tomorrow because today has closed
osFUN = osDollarATR, # order size function
tradeSize = tradeSize,
pctATR = pctATR,
maxPctATR = pctATR, # set an upper limit of orders
atrMod = "X",
TxnFees = "buyFee"), # atrx above
type = "enter", path.dep = TRUE, label = "enterLong1")
add.rule(strategy.st, name = "ruleSignal",
arguments = list(sigcol = "longEntry2",
sigval = TRUE,
ordertype = "market",
orderside = "long",
replace = FALSE, # replace = TRUE may choose just one rule
prefer = "Open", # tomorrow because today has closed
osFUN = osDollarATR, # order size function
tradeSize = tradeSize,
pctATR = pctATR,
maxPctATR = maxPct, # set an upper limit of orders
atrMod = "X",
TxnFees = "buyFee"), # atrx above
type = "enter", path.dep = TRUE, label = "enterLong2")
# 出场规则
# orderqty: 'all'为卖出所有部位,可以是数字或自定义function
# ordertype是停损单(stoplimit)时,需额外设定停损点
# 如果tmult=TRUE,threshold需为百分比,如0.2(=20%),买入价*threshold=停损价
# 如果tmult=FALSE,threshold为绝对数字,如158,停损价就是跌破158时平仓
# 这边都以市价单出场
add.rule(strategy.st, name = "ruleSignal",
arguments = list(sigcol = "exitLongNormal",
sigval = TRUE,
orderqty = "all", # order quantity, in all and out all
ordertype = "market",
orderside = "long",
replace = FALSE,
prefer = "Open",
TxnFees = "sellFee"),
type = "exit", path.dep = TRUE, label = "normalExitLong")
add.rule(strategy.st, name = "ruleSignal",
arguments = list(sigcol = "exitLongFilter",
sigval = TRUE,
orderqty = "all",
ordertype = "market",
orderside = "long",
replace = FALSE,
prefer = "Open",
TxnFees = "sellFee"),
type = "exit", path.dep = TRUE, label = "filterExitLong")
# 开始进行回测
out <- applyStrategy(strategy = strategy.st, portfolios = portfolio.st)
# 更新投组资料
updatePortf(portfolio.st)
tradeDetails <- getPortfolio(portfolio.st)
posPL <- tradeDetails$symbols$tsmc$posPL
# 更新帐户资料和期末权益金额
dateRange <- time(tradeDetails$summary)[-1]
updateAcct(portfolio.st, dateRange)
updateEndEq(account.st)
# 统计资料
tStats <- tradeStats(Portfolios = portfolio.st, use = "trades", inclZeroDays = FALSE)
tStats[, 4:ncol(tStats)] <- round(tStats[, 4:ncol(tStats)], 2)
(aggPF <- sum(tStats$Gross.Profits)/-sum(tStats$Gross.Losses))
(aggCorrect <- mean(tStats$Percent.Positive))
(numTrades <- sum(tStats$Num.Trades))
(meanAvgwLR <- mean(tStats$Avg.WinLoss.Ratio[tStats$Avg.WinLoss.Ratio < Inf], na.rm = TRUE))
# 画k线图、进出场flag、持有部位和累积报酬
myTheme<-chart_theme()
myTheme$col$dn.col<-'lightgray'
myTheme$col$dn.border <- 'lightgray'
myTheme$col$up.border <- 'lightgray'
chart.Posn(Portfolio = portfolio.st, Symbol=symbols, theme= myTheme)
# 自由加入技术指标
sma <- SMA(x = Cl(tsmc), n = 200)
sma2 <- SMA(x = Cl(tsmc), n = 10)
rsi <- RSI(price = Cl(tsmc), n = 2)
atr <- lagATR(HLC = HLC(tsmc), n = 10)
add_TA(sma2, on = 1, col = "red")
add_TA(sma, on = 1, col = "green")
add_TA(rsi, col = "green", lwd = 1.5)
### 参数最佳化
# 设定参数范围
# add distribution and distribution contraints
# rsi
# paramset.label: 参数组合名称
# label: 这个参数的范围
add.distribution(strategy.st,
paramset.label = 'allParam',
component.type = 'signal',
component.label = 'rsiThresh1',
variable = list(threshold = 5:10),
label = 'up.rsi'
)
add.distribution(strategy.st,
paramset.label = 'allParam',
component.type = 'signal',
component.label = 'rsiThresh2',
variable = list(threshold = 5:10),
label = 'dn.rsi'
)
# 加入参数范围限制
add.distribution.constraint(strategy.st,
paramset.label = 'allParam',
distribution.label.1 = 'up.rsi',
distribution.label.2 = 'dn.rsi',
operator = '>',
label = 'RSI'
)
# MA
add.distribution(strategy.st,
paramset.label = 'allParam',
component.type = 'indicator',
component.label = 'quickMA',
variable = list(threshold = 5:15),
label = 'quickMA'
)
add.distribution(strategy.st,
paramset.label = 'allParam',
component.type = 'indicator',
component.label = 'filterMA',
variable = list(threshold = 220:260),
label = 'filterMA'
)
# Walk Forward Analysis
# paramset.label: 设定的参数范围
# k.training: 样本期间
# k.testing: 测试期间
# nsamples: 等於0代表所有组合
# obj.func: objective function,目标函数,由於default的最佳化是以
# 报酬率最大为指标,若要改成Max drawdown或其他条件则要自定义函数
# anchored: 是否是moving time window
# verbose: 是否印出交易明细
registerDoParallel(cores=detectCores())
resultsWFA <- walk.forward(
strategy.st=strategy.st,
paramset.label='allParam',
portfolio.st=portfolio.st,
account.st=account.st,
period='years',
k.training=4,
k.testing=2,
nsamples=0,
audit.prefix='wfa',
#obj.func=my.obj,
#obj.args=my.args,
anchored=TRUE,
verbose=TRUE,
include.insamples=TRUE
)
结果会自动输出样本内和样本外的.RData档到使用者的预设路径,可以
用ls(.audit)看到training和testing的名称
举例来说:
chart.forward.training("wfa.GSPC.2007-01-03.2010-12-31.RData")
http://imgur.com/a/Scgc3
如果是testing的部分:
chart.forward("wfa.results.RData")
如果不清楚Out of sample和In sample的关系
可以参考我网页上WFA的tab
https://naturalsmen.shinyapps.io/Backtest
因为quantstrat的help page真的不太友善,所以打了很多arguments的定义和用法说明
code不知不觉也打了一长串@@
虽然看起来有点长
但我觉得这是用quantstrat搭配其他package建立自己的交易系统蛮完整的code了
如果排版或内容有哪里需要改进再请板友指教
之後会再慢慢修改与完善这篇
或者可能直接搬到我的部落格了@@
下一个打算分享IBrokers 如果没有被其他板友抢先的话XD
还有Josh新版的xts_0.10-0、quantmod、PerformanceAnalytics等
或者是等到正式release之後再写了
5. 备注:
因为自己埋头研究quantstrat和IBrokers这两个package非常非常久
一直希望找个时间把自己经验分享出去
一方面是让大家知道有这个很好用的package来写交易程式
另一方面是让大家少走些弯路
因为可能碰到的bug我大概都遇过 也open issue过了@@:
但是自己一直懒懒的 又在忙GSoC的计画 以至於一拖再拖
感谢c大提供这个机会让我上来骗骗p币
也给我个动机写了第一篇弱弱的分享文
感觉板上大大多是讨论技术为主
所以就想说来个比较应用的范例好了
之後会慢慢把这篇补完、弄得更通顺更好阅读一点
感谢耐心看完或直接End的大家XD
参考资料:
Guy Yollin,
Computational Finance & Risk Management,
University of Washington, Department of Applied Mathematics:
http://www.r-programming.org/papers
QuantStrat TradeR:
https://quantstrattrader.wordpress.com/
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 61.228.18.39
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/R_Language/M.1469300017.A.7D0.html
1F:推 cywhale: 传这个给同事他应该就有动力学R了 详细推~~ 07/24 08:13
2F:推 ofspring: impressive, 想问MAC需要的套件组要到哪个网站查询呢? 07/24 10:10
3F:推 LinNine: 推~ 07/24 10:24
4F:推 snoopyleo: 谢谢分享! 07/24 21:07
5F:推 roqe: 好长,慢慢看~ 07/24 23:35
6F:推 kevin0401: GOOD 07/25 08:00
7F:推 psinqoo: 拍手~~ 07/25 08:29
8F:推 howard40116: 推广一下~~会有一堆人想来学 07/25 20:31
9F:推 celestialgod: 推,谢谢大大分享 07/25 20:36
※ 编辑: naturalsmen (140.113.136.217), 07/26/2016 19:18:52
10F:推 h310713: 跪下来了 07/28 09:44
※ 编辑: naturalsmen (36.229.209.213), 09/28/2016 21:05:15
11F:推 poker0531: 不断更新欸 给推 08/10 17:53