[分享] quantstrat 套件分享
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://www.ptt.cc/bbs/R_Language/M.1469300017.A.7D0.html
推
07/24 08:13, , 1F
07/24 08:13, 1F
推
07/24 10:10, , 2F
07/24 10:10, 2F
推
07/24 10:24, , 3F
07/24 10:24, 3F
推
07/24 21:07, , 4F
07/24 21:07, 4F
推
07/24 23:35, , 5F
07/24 23:35, 5F
推
07/25 08:00, , 6F
07/25 08:00, 6F
推
07/25 08:29, , 7F
07/25 08:29, 7F
推
07/25 20:31, , 8F
07/25 20:31, 8F
推
07/25 20:36, , 9F
07/25 20:36, 9F
※ 編輯: naturalsmen (140.113.136.217), 07/26/2016 19:18:52
推
07/28 09:44, , 10F
07/28 09:44, 10F
※ 編輯: naturalsmen (36.229.209.213), 09/28/2016 21:05:15
推
08/10 17:53, , 11F
08/10 17:53, 11F
R_Language 近期熱門文章
PTT數位生活區 即時熱門文章