A Quantstrat to Build On Part 6
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
THIS IS NOT INVESTMENT ADVICE. ACTING ON THIS MAY LOSE LOTS OF MONEY.
In A Quantstrat to Build on Part 5, I promised some performance reporting on quantstrat portfolios, but then in REIT Momentum in Quantstrat, I discovered it is not nearly as easy as I thought. With some help from experts (please tell me if you would like me to mention you by name), I found a couple of ways to accomplish this crucial component of systems development. We’ll compare the original simple CUD system with buy and hold but this time using the osFUN option in our entry rule to buy an amount equal to the proceeds from the previous trade. Minimizing drawdown on the way down allows potentially larger trade quantities on the way back up.
From TimelyPortfolio |
From TimelyPortfolio |
From TimelyPortfolio |
From TimelyPortfolio |
From TimelyPortfolio |
From TimelyPortfolio |
#thanks so much to the developers of quantstrat #99% of this code comes from the demos in the quantstrat package #in this I use osFun to size orders to ending equity #for a more appropriate comparison to buy hold #takes longer than I would like but is acceptable #another way to accomplish testing and reporting #is use 1 for orderqty and then generate return series #with this as signal #however despite extra time I like to see growth in order size #with performance #makes a very valid point for drawdown reduction require(quantstrat) require(PerformanceAnalytics) #now let's define our silly countupdown function CUD <- function(price,n) { #CUD takes the n-period sum of 1 (up days) and -1 (down days) temp <- runSum(ifelse(ROC(price,1,type="discrete") > 0,1,-1),n) colnames(temp) <- "CUD" temp } BuyHold <- function(price,periodtobuy) { #just enter true (1) the period specified as buy and hold #for the remainder temp <- as.xts(rep(0,NROW(price)),order.by=index(price)) colnames(temp) <- "BuyHold" temp[periodtobuy,1]<-1 temp } osFillErUp <- function (data, timestamp, orderqty, ordertype, orderside, portfolio, symbol, ruletype, ..., orderprice) { #get date in usable xts format datePos <- format(timestamp,"%Y-%m-%d") #update the portfolio to date of trade rule updatePortf(Portfolio=portfolio,Symbol=symbol,Dates=paste('::',datePos,sep='')) portf <- getPortfolio(portfolio) #get price of symbol for the date of trade rule price <- getPrice(get(symbol))[datePos] #get amount generated from the last trade #best way for me to overcome lack of cumulative p/l for portfolio by symbol #all this logic is at the account level trades <- getOrderBook(portfolio)[[portfolio]][[symbol]] #if first trade, just use order quantity specified #if not get order quantity equal to #last trade proceeds divided by price of symbol at trade date if(NROW(trades)>1) { trades <- trades[NROW(trades)] endEq <- as.numeric(trades$Order.Qty) * as.numeric(trades$Order.Price) # orderqty <- abs(endEq/price) orderqty <- abs(floor(endEq/price)) } #return the new orderqty osFillErUp <- orderqty } try(rm("order_book.CUD",pos=.strategy),silent=TRUE) try(rm("order_book.BuyHold",pos=.strategy),silent=TRUE) try(rm("account.CUD","portfolio.CUD",pos=.blotter),silent=TRUE) try(rm("account.BuyHold","portfolio.BuyHold",pos=.blotter),silent=TRUE) try(rm("port.st","symbols","symbol","stratCUD","initDate","initEq", 'start_t','end_t','num_periods'),silent=TRUE) #specify this for the rolling periods to use for our signal num_periods=50 # Initialize a strategy object stratCUD <- strategy("CUD") # Add an indicator stratCUD <- add.indicator(strategy = stratCUD, name = "CUD", arguments = list(price = quote(Cl(mktdata)),n=num_periods), label="CUD") # enter when CUD > 0 stratCUD <- add.signal(strategy = stratCUD, name="sigThreshold", arguments = list(threshold=0, column="CUD",relationship="gte", cross=TRUE), label="CUD.gte.0") # exit when CUD < 0 stratCUD <- add.signal(strategy = stratCUD, name="sigThreshold", arguments = list(threshold=0, column="CUD",relationship="lt",cross=TRUE), label="CUD.lt.0") stratCUD <- add.rule(strategy = stratCUD, name='ruleSignal', arguments = list(sigcol="CUD.gte.0", sigval=TRUE, orderqty=100, ordertype='market', orderside='long', pricemethod='market', replace=FALSE, osFUN='osFillErUp'), type='enter', path.dep=TRUE) stratCUD <- add.rule(strategy = stratCUD, name='ruleSignal', arguments = list(sigcol="CUD.lt.0", sigval=TRUE, orderqty='all', ordertype='market', orderside='long', pricemethod='market', replace=FALSE), type='exit', path.dep=TRUE) #Initialize a buy/hold strategy object stratBuyHold <- strategy("BuyHold") stratBuyHold <- add.indicator(strategy = stratBuyHold, name = "BuyHold", arguments = list(price = quote(Cl(mktdata)),periodtobuy=num_periods), label = "BuyHold") stratBuyHold <- add.rule(strategy=stratBuyHold, name='ruleSignal', arguments = list(sigcol="BuyHold",sigval=TRUE,orderqty=100,ordertype='market', orderside='long', pricemethod='market', replace=FALSE), type='enter', path.dep=TRUE) currency("USD") symbols = c("GSPC","GDAXI") for (symbol in symbols) { stock(symbol, currency="USD",multiplier=1) #use paste with ^ to get index data getSymbols(paste("^",symbol,sep=""),adjust=T,from="1900-12-31") assign(symbol,to.weekly(get(symbol))) } initDate='1949-12-31' initEq=1000000 port.st<-'CUD' #use a string here for easier changing of parameters and re-trying port.buyhold <- 'BuyHold' initPortf(port.st, symbols=symbols, initDate=initDate) initAcct(port.st, portfolios=port.st, initDate=initDate, initEq=initEq) initOrders(portfolio=port.st, initDate=initDate) initPortf(port.buyhold, symbols=symbols, initDate=initDate) initAcct(port.buyhold, portfolios=port.buyhold, initDate=initDate,, initEq=initEq) initOrders(portfolio=port.buyhold, initDate=initDate) print("setup completed") # Process the indicators and generate trades start_t<-Sys.time() out<-try(applyStrategy(strategy=stratCUD , portfolios=port.st ) ) end_t<-Sys.time() print("Strategy Loop:") print(end_t-start_t) # Process buy and hold strategy start_t<-Sys.time() out<-try(applyStrategy(strategy=stratBuyHold , portfolios=port.buyhold ) ) end_t<-Sys.time() print("Strategy Loop:") print(end_t-start_t) start_t<-Sys.time() updatePortf(Portfolio=port.st,Dates=paste('::',as.Date(Sys.time()),sep='')) updatePortf(Portfolio=port.buyhold,Dates=paste('::',as.Date(Sys.time()),sep='')) end_t<-Sys.time() print("trade blotter portfolio update:") print(end_t-start_t) # hack for new quantmod graphics, remove later themelist<-chart_theme() themelist$col$up.col<-'lightgreen' themelist$col$dn.col<-'pink' for(symbol in symbols){ #dev.new() #chart.Posn(Portfolio=port.st,Symbol=symbol,theme=themelist) #add the CUD indicator to the bottom of the chart #jpeg(filename=paste(symbol," Reconcile.jpg",sep=""),quality=100, # width=6.5, height = 6.5, units="in",res=96) chart.Reconcile(port.buyhold,port.st,symbol) plot(add_TA(CUD(get(symbol)[,4],n=num_periods))) #dev.off() } #tradeStats(port.st) #backwards way to get returns #again to bypass account p/l logic port <- getPortfolio(port.st) for(symbol in symbols) { #get posPL for the symbol in the portfolio posPLTable <- port$symbols[[symbol]][["posPL"]] #easier this way to get ROC for each day when position is held (Pos.Qty > 0) #rets <- lag(ifelse(posPLTable$Pos.Qty>0,1,0),k=1)*ROC(get(symbol)[,4],type="discrete",n=1) #the previous commented method is not exactly correct #since we can only hold integer positions #to account for this difference we can get rets <- posPLTable$Gross.Trading.PL/lag(posPLTable$Pos.Value,k=1) rets[is.na(rets)] <- 0 rets[which(rets[,1]==Inf)] <- 0 rets[which(rets[,1]==-Inf)] <- 0 retCompare <- merge(rets,ROC(get(symbol)[,4],type="discrete",n=1)) colnames(retCompare) <- c(paste(symbol," CUD System",sep=""),symbol) #jpeg(filename=paste(symbol," Performance.jpeg",sep=""),quality=100,width=6.5, height = 6.5, # units="in",res=96) charts.PerformanceSummary(retCompare,ylog=TRUE, colorset=c("black","gray70"), main = paste(symbol," CUD System and Index Performance Summary",sep="")) #dev.off() #jpeg(filename=paste(symbol," Capture.jpeg",sep=""),quality=100,width=6.5, height = 6.5, # units="in",res=96) chart.CaptureRatios(retCompare[,1],retCompare[,2], main = paste(symbol," CUD System and Index Capture Ratios",sep="")) #dev.off() }
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.