Thursday, June 2, 2011

A Quantstrat to Build On

THIS IS NOT INVESTMENT ADVICE.  PLEASE DO NOT TRADE THIS SYSTEM AS IT CAN LOSE SIGNIFICANT AMOUNTS OF MONEY.  YOU ARE RESPONSIBLE FOR YOUR OWN GAINS AND LOSSES.

Some R finance powerhouses have been banging away on the quantstrat package for quite a while now with some very impressive results.  For my first quantstrat, I wanted to use some sort of really easy, but non-prebuilt ttr technical indicator, so I could learn some of the quantstrat intricacies.  I chose to enter and exit based on a sum of up (1) and down (-1) weeks over a rolling period, which I call CUD.  This specific example uses 20 weeks as the rolling period, and buys when this sum exceeds 0 and exits when the sum goes below 0.  I plan to adapt, refine, and extend this example to a much more workable system and example of quantstrat, but for now I want to put it out there for comments and criticisms as the end of the day approaches.

From TimelyPortfolio

R code:

#thanks so much to the developers of quantstrat
#99% of this code comes from the demos in the quantstrat package
#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
}    
try(rm("order_book.CUD",pos=.strategy),silent=TRUE)
try(rm("account.CUD","portfolio.CUD",pos=.blotter),silent=TRUE)
try(rm("account.st","portfolio.st","stock.str","stratCUD","initDate","initEq",'start_t','end_t'),silent=TRUE)  
# Initialize a strategy object
stratCUD <- strategy("CUD")  
# Add an indicator
stratCUD <- add.indicator(strategy = stratCUD, name = "CUD", arguments = list(price = quote(Cl(mktdata)),n=20), label="CUD")  
# enter when CUD > 0
stratCUD <- add.signal(strategy = stratCUD, name="sigThreshold",arguments = list(threshold=-0.5, column="CUD",relationship="gt", cross=TRUE),label="CUD.gteq.0")
# exit when CUD < 0
stratCUD <- add.signal(strategy = stratCUD, name="sigThreshold",arguments = list(threshold=-0.5, column="CUD",relationship="lt",cross=TRUE),label="CUD.lt.0")  
stratCUD <- add.rule(strategy = stratCUD, name='ruleSignal', arguments = list(sigcol="CUD.gteq.0", sigval=TRUE, orderqty=1000, ordertype='market', orderside='long', pricemethod='market', replace=FALSE), 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)      
currency("USD")
symbol = "GSPC"
stock(symbol, currency="USD",multiplier=1)
#use paste with ^ to get index data
getSymbols(paste("^",symbol,sep=""),adjust=T,from="1900-12-31")
#I use weekly but comment this out if you want to use daily
GSPC<-to.weekly(GSPC)    
initDate='1950-12-31'
initEq=100000
port.st<-'CUD' #use a string here for easier changing of parameters and re-trying  
initPortf(port.st, symbols=symbol, initDate=initDate)
initAcct(port.st, portfolios=port.st, initDate=initDate)
initOrders(portfolio=port.st, 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)    
start_t<-Sys.time()
updatePortf(Portfolio=port.st,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'  
chart.Posn(Portfolio=port.st,Symbol=symbol,theme=themelist,log=TRUE)

Created by Pretty R at inside-R.org

19 comments:

  1. Cool stuff - when I run this, via RStudio, I get the following error and warnings. Thoughts appreciated!

    [1] "setup completed"
    [1] "Strategy Loop:"
    Time difference of 18.456 secs
    [1] "trade blotter portfolio update:"
    Time difference of 0.6489999 secs
    Error in chart_Series(Prices, name = Symbol, TA = NULL, ...) :
    unused argument(s) (log = TRUE)
    In addition: There were 18 warnings (use warnings() to see them)
    > warnings()
    Warning messages:
    1: In rm("order_book.CUD", pos = .strategy) : object 'order_book.CUD' not found
    2: In rm("account.CUD", "portfolio.CUD", pos = .blotter) :
    object 'account.CUD' not found
    3: In rm("account.CUD", "portfolio.CUD", pos = .blotter) :
    object 'portfolio.CUD' not found
    4: In rm("account.st", "portfolio.st", "stock.str", "stratCUD", ... :
    object 'account.st' not found
    5: In rm("account.st", "portfolio.st", "stock.str", "stratCUD", ... :
    object 'portfolio.st' not found
    6: In rm("account.st", "portfolio.st", "stock.str", "stratCUD", ... :
    object 'stock.str' not found
    7: In rm("account.st", "portfolio.st", "stock.str", "stratCUD", ... :
    object 'stratCUD' not found
    8: In rm("account.st", "portfolio.st", "stock.str", "stratCUD", ... :
    object 'initDate' not found
    9: In rm("account.st", "portfolio.st", "stock.str", "stratCUD", ... :
    object 'initEq' not found
    10: In rm("account.st", "portfolio.st", "stock.str", "stratCUD", ... :
    object 'start_t' not found
    11: In rm("account.st", "portfolio.st", "stock.str", "stratCUD", ... :
    object 'end_t' not found
    12: In download.file(paste(yahoo.URL, "s=", Symbols.name, ... :
    downloaded length 837651 != reported length 200
    13: In download.file(paste(yahoo.URL, Symbol.name, "&a=", ... :
    downloaded length 15 != reported length 200
    14: In download.file(paste(yahoo.URL, Symbol.name, "&a=", ... :
    downloaded length 76 != reported length 200
    15: In max(i) : no non-missing arguments to max; returning -Inf
    16: In min(dindex[dindex > curIndex]) :
    no non-missing arguments to min; returning Inf
    17: In max(i) : no non-missing arguments to max; returning -Inf
    18: In max(i) : no non-missing arguments to max; returning -Inf
    >

    ReplyDelete
  2. All those warnings should be fine. First set will come when you run for the first time and the objects do not exist. The max inf comes on the last signal if I is open and is also not a problem. Sorry I left the log=TRUE in since I knew that did not work. Look forward to adding lots more graphics.

    Thanks for running,trying, and commenting.

    ReplyDelete
  3. But I'm not getting any output - no chart of the equity curve as you have - am I doing something wrong?

    ReplyDelete
  4. Damian shouldn't be getting warnings 12 though 14 about the downloaded file length. Maybe updating to the latest version of quantmod will help. Also, using T instead of TRUE may not do the same thing if the user has assigned a value to T.

    ReplyDelete
  5. Hello Kir,

    #now let's define function for PairTrading
    I have modified it.

    CUD <- function(pairratio,n) {
    # calculate STD deviation for the last n periods (say 100 days)
    # calculate the mean for the last n periodes (say 100 days)
    # if price is lower than (mean-2.7*SD) then long
    # if price is above (mean -0.75*SD) then short

    signal1 <-(ifelse(pairratio < (mean-2.7*SD),1,0),n)
    signal2 <-(ifelse(pairratio > (mean-0.75*SD),-1,0),n)
    temp <-signal1+signal2
    colnames(temp) <- "CUD"
    temp
    }

    Kindly tell me the whether the above function is correct or not?.
    Kindly provide me the correct code.
    thanking you

    regards
    veepsirtt

    ReplyDelete
  6. Will definitely check it out but I am computerless for the next day and a half. Thanks so much for commenting and adapting the example.

    ReplyDelete
  7. Hi klr,

    just wondering where you found info about setting rules...add.rule() syntax is quite unclear to me even after going through quantstrat demo examples.

    I hope you can shed some lights :-)

    Paolo

    ReplyDelete
  8. Sounds like we have the same sources:> Know that when a project is under development, it is self-defeating to document and demo extensively, so I think the source code will be our best guide. I'll try to build on this demo to provide more examples. Email me at kent.russell ((((@>> timelyportfolio.com if you would like me to do anything specific.

    ReplyDelete
  9. Trying to install, but
    package ‘quantstrat’ is not available (for R version 2.13.0)
    Why?

    ReplyDelete
  10. even after i rearrange the code, this line seems to blow up the stratCUD object

    *
    try(rm("account.st","portfolio.st","stock.str","stratCUD","initDate","initEq",'start_t','end_t'),silent=TRUE)
    *

    account.st, portfolio.st, and stock.str are all undefined objects.. also i can't see where they are defined later.

    i assume portfolio.st is = to port.st as defined later?
    also, stock.str is = GSPC?

    guesses here but still can't seem to get it to work.

    ReplyDelete
  11. I'll clean up those undefined objects, but the first run will still give you those errors. Might use some code from a friend that is cleaner but more destructive.

    rm is designed to blow it up and erase it, so we start from scratch each time. Let me know if I misread this.

    Thanks for reading and commenting. Keep them coming.

    ReplyDelete
  12. not sure why quantstrat is not installing under 2.13.0. R-forge had some problems that I noticed early June, but all those seem to have been resolved.

    ReplyDelete
  13. I copied the above code from web browsers (internet explorer) into RGUI and notepad. In both cases, I lose white space and lines get commingled.

    Am I doing something wrong ? how can I get this code into RGui?

    Thanks.

    ReplyDelete
  14. back in the old days before I figured out GIST. I pasted the code here https://gist.github.com/2554762. Hope this helps. Thanks for reading.

    ReplyDelete
  15. Thanks for this... you have saved me a few days with this example. I have just started to tinker with quantstrat and was trying to write a previously written system that does not use predefined indicators and this example has help enormously.

    ReplyDelete
  16. (KLR, Thank you for the very helpful blog!)
    (FYI - I don't have enough experience with programming/R.)
    Can someone please help me with this? Where above do I need to make changes to try the above example with a custom price series?
    > custom <- xts(rnorm(100,10,2), Sys.Date()-100:1)
    > colnames(custom) <- "CUSTOM"
    > head(custom)
    ## the output to the third line shows me that "custom" is an xts object with 100 daily price series that fluctuates around $10, and the column name (, which I understand as the ticker symbol) is set to "CUSTOM".

    Put differently:
    I'm having a hard time understanding the "stratCUD <- add.indicator()" line that follows "# Add an indicator" comment line. (about 20th line from the top...) It sort of appears that my question above can be resolved once I clearly understand where "mktdata" comes from and how it works/interacts with "add.indicator" and other related functions. Using the help() function, I've printed out the explanation for all the functions shown in the above example, but couldn't really figure out how to control/manipulate the price series to be used in the strategy testing. Can someone with more experience please help? Thank you.

    ReplyDelete
  17. My apologies -- I should have been more thorough with my attempt to learn quantstrat. (I have never used quantmod/TTR/FIs.) One thing I've realized/learned is that it's via getSymbols that I can( and probably "should") introduce a custom price series. Below is still not right in that the dates in the "custom" dataframe get messed up when loaded by getSymbols. However, at least I now have a better idea regarding how this works, and thought that I would share in case another inexperienced learner comes to this place:

    > custom <- data.frame(matrix(nr=100,nc=7), check.names=FALSE)
    > custom[,1] <- as.Date(Sys.Date()-100:1, format="Date")
    > custom[,5] <- rnorm(100,10,2)
    > write.csv(custom, file="CUSTOM.csv", row.names=FALSE)
    > getSymbols("CUSTOM", src='csv')
    [1] "CUSTOM"
    > temp <- getSymbols("CUSTOM", src='csv', auto.assign=FALSE)
    ### the last line is just to "see" the data being loaded...

    I don't think the author would have insisted on yahoo's format. In other words, I'd guess that there must be a way to import just one column for Close, for example. I would appreciate it if someone can help with this. Thank you...

    ReplyDelete
  18. Hi,

    I use R with version:R version 2.15.1 (2012-06-22).

    I am very interesting in quantstra packaget but I always have the same error:

    " > out<-try(applyStrategy(strategy=stratCUD , portfolios=port.st ) )
    Error in if (!is.na(timestamp) && (ruletype == "chain" ||
    (!is.na(data[timestamp][, : valor ausente donde TRUE/FALSE es necesario "

    doesn´t works applyStrategy.

    thank you for you help me :))

    ReplyDelete
  19. This comment has been removed by the author.

    ReplyDelete