看板 R_Language 關於我們 聯絡資訊
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
cywhale: 傳這個給同事他應該就有動力學R了 詳細推~~ 07/24 08:13
ofspring: impressive, 想問MAC需要的套件組要到哪個網站查詢呢? 07/24 10:10
LinNine: 推~ 07/24 10:24
snoopyleo: 謝謝分享! 07/24 21:07
roqe: 好長,慢慢看~ 07/24 23:35
kevin0401: GOOD 07/25 08:00
psinqoo: 拍手~~ 07/25 08:29
howard40116: 推廣一下~~會有一堆人想來學 07/25 20:31
celestialgod: 推,謝謝大大分享 07/25 20:36
※ 編輯: naturalsmen (140.113.136.217), 07/26/2016 19:18:52
h310713: 跪下來了 07/28 09:44
※ 編輯: naturalsmen (36.229.209.213), 09/28/2016 21:05:15
poker0531: 不斷更新欸 給推 08/10 17:53