[Blotter-commits] r1020 - in pkg/quantstrat: R demo

G See gsee000 at gmail.com
Tue May 8 17:32:46 CEST 2012


Thanks for patching -- I haven't been following all of these
quantstrat commits, but the next step should probably be to get rid of
the hundreds of "no non-missing arguments to max; returning -Inf"
warnings

Thanks,
Garrett

On Tue, May 8, 2012 at 10:26 AM,  <noreply at r-forge.r-project.org> wrote:
> Author: braverock
> Date: 2012-05-08 17:26:31 +0200 (Tue, 08 May 2012)
> New Revision: 1020
>
> Added:
>   pkg/quantstrat/demo/pair_trade.R
> Modified:
>   pkg/quantstrat/R/orders.R
>   pkg/quantstrat/R/ruleSignal.R
> Log:
> - add further code to call osFUN when required, and not when not.  patch from Jan Humme
> - fix qty to numeric in addOrder to support order sets
> - re-expose pair_trade demo, since it works again
>
> Modified: pkg/quantstrat/R/orders.R
> ===================================================================
> --- pkg/quantstrat/R/orders.R   2012-05-06 15:15:05 UTC (rev 1019)
> +++ pkg/quantstrat/R/orders.R   2012-05-08 15:26:31 UTC (rev 1020)
> @@ -280,7 +280,16 @@
>     else ordertime<-as.POSIXct(timestamp)+delay
>     orders<-NULL
>     for (i in 1:length(price)) {
> -        neworder<-xts(as.matrix(t(c(qty[i], price[i], ordertype[i], side, threshold[i], status, statustimestamp, orderset[i], TxnFees, label))), order.by=(ordertime))
> +        neworder<-xts(as.matrix(t(c(as.numeric(qty[i]),
> +                                    price[i],
> +                                    ordertype[i],
> +                                    side,
> +                                    threshold[i],
> +                                    status,
> +                                    statustimestamp,
> +                                    orderset[i],
> +                                    TxnFees, label))),
> +                                order.by=(ordertime))
>         if(is.null(orders)) orders<-neworder
>         else orders <- rbind(orders,neworder)
>     }
>
> Modified: pkg/quantstrat/R/ruleSignal.R
> ===================================================================
> --- pkg/quantstrat/R/ruleSignal.R       2012-05-06 15:15:05 UTC (rev 1019)
> +++ pkg/quantstrat/R/ruleSignal.R       2012-05-08 15:26:31 UTC (rev 1020)
> @@ -141,15 +141,19 @@
>             }
>         }
>
> -        ## now size the order
> -        #TODO add fancy formals matching for osFUN
> -        if(orderqty=='all' && (ordertype=='market' || (ruletype!='risk' && ruletype!='exit')))
> +       ## now size the order
> +       #TODO add fancy formals matching for osFUN
> +       if(orderqty!='all' || ordertype=='market' || (ruletype!='risk' && ruletype!='exit'))
>        {
> -            orderqty <- osFUN(strategy=strategy, data=data, timestamp=timestamp, orderqty=orderqty, ordertype=ordertype, orderside=orderside, portfolio=portfolio, symbol=symbol,...=...,ruletype=ruletype, orderprice=as.numeric(orderprice))
> +               orderqty <- osFUN(strategy=strategy, data=data, timestamp=timestamp, orderqty=orderqty, ordertype=ordertype, orderside=orderside, portfolio=portfolio, symbol=symbol,...=...,ruletype=ruletype, orderprice=as.numeric(orderprice))
> +
> +               if(ruletype=='exit' && ((orderqty>0 && orderside=='long') || (orderqty<0 && orderside=='short')))
> +                       orderqty = NULL         # dirty trick to suppress adding order below JH
>         }
> -
> -        if(!is.null(orderqty) && !orderqty == 0 && !is.null(orderprice)){ #orderqty could have length > 1
> -            addOrder(portfolio=portfolio, symbol=symbol, timestamp=timestamp, qty=orderqty, price=as.numeric(orderprice), ordertype=ordertype, side=orderside, orderset=orderset, threshold=threshold, status="open", replace=replace , delay=delay, tmult=tmult, ...=..., TxnFees=TxnFees,label=label)
> +
> +       if(!is.null(orderqty) && orderqty!=0 && !is.null(orderprice)) #orderqty could have length > 1
> +       {
> +               addOrder(portfolio=portfolio, symbol=symbol, timestamp=timestamp, qty=orderqty, price=as.numeric(orderprice), ordertype=ordertype, side=orderside, orderset=orderset, threshold=threshold, status="open", replace=replace , delay=delay, tmult=tmult, ...=..., TxnFees=TxnFees,label=label)
>         }
>     }
>     if(sethold) hold <<- TRUE
>
> Added: pkg/quantstrat/demo/pair_trade.R
> ===================================================================
> --- pkg/quantstrat/demo/pair_trade.R                            (rev 0)
> +++ pkg/quantstrat/demo/pair_trade.R    2012-05-08 15:26:31 UTC (rev 1020)
> @@ -0,0 +1,213 @@
> +#Kindly contributed to quantstrat by Garrett See
> +#code borrowed heavily from existing quantstrat demos
> +
> +#This is a simple pairs trading example intended to illustrate how you can extend
> +#existing quantstrat functionality.  It uses addPosLimits to specify levels and
> +#position limits, and shows how to pass a custom order sizing function to osFUN
> +
> +#Note that it would be easier to build a spread first and treat it as a single instrument
> +#instead of dealing with a portfolio of stocks.
> +
> +## given 2 stocks, calculate the ratio of their notional values.  If the ratio falls below it's
> +# 2 stdev band, then when it crosses back above it, buy stock 1 and sell stock 2.
> +# If the ratio rises above it's 2 stdev band, then when it crosses back below
> +# it, sell stock 1 and buy stock 2.  If the ratio crosses it's moving average,
> +# then flatten any open positions.
> +
> +# The Qty of Stock A that it buys (sells) = MaxPos / lvls
> +# The Qty of Stock B that is sells (buys) = MaxPos * Ratio / lvls
> +
> +suppressWarnings(rm("order_book.pair1",pos=.strategy))
> +suppressWarnings(rm("account.pairs", "portfolio.pair1", pos=.blotter))
> +suppressWarnings(rm("initDate", "endDate", "startDate", "initEq", "SD", "N", "symb1", "symb2",
> +       "portfolio1.st", "account.st", "pairStrat", "out1"))
> +
> +require(quantstrat)
> +initDate = '2009-01-01'
> +endDate = '2011-05-01'
> +startDate = '2009-01-02'
> +initEq = 100000
> +SD = 2
> +N = 20
> +
> +MaxPos = 1500  #max position in stockA;
> +#max position in stock B will be max * ratio, i.e. no hard position limit in Stock B
> +lvls = 3       #how many times to fade; Each order's qty will = MaxPos/lvls
> +
> +symb1 <- 'SPY' #change these to try other pairs
> +symb2 <- 'DIA' #if you change them, make sure position limits still make sense
> +
> +portfolio1.st <- 'pair1'
> +account.st <- 'pairs'
> +
> +getSymbols(c(symb1, symb2), from=startDate, to=endDate, adjust=TRUE)
> +
> +#generic used to make sure the timestamps of all symbols are the same
> +#deletes rows where one of the stocks is missing data
> +alignSymbols <- function(symbols, env=.GlobalEnv) {
> +       if (length(symbols) < 2)
> +               stop("Must provide at least 2 symbols")
> +       if (any(!is.character(symbols)))
> +               stop("Symbols must be vector of character strings.")
> +       ff <- get(symbols[1],env=env)
> +       for (sym in symbols[-1]) {
> +               tmp.sym <- get(sym,env=env)
> +               ff <- merge(ff,tmp.sym,all=FALSE)
> +       }
> +       for (sym in symbols) {
> +               assign(sym,ff[,grep(sym,colnames(ff))],env=env)
> +       }
> +       symbols
> +}
> +alignSymbols(c(symb1,symb2))
> +
> +#Define Instruments
> +currency("USD")
> +stock(symb1, currency="USD", multiplier=1)
> +stock(symb2, currency="USD", multiplier=1)
> +
> +#Initialize Portfolio, Account, and Orders
> +initPortf(name=portfolio1.st, c(symb1,symb2), initDate=initDate)
> +initAcct(account.st, portfolios=portfolio1.st, initDate=initDate, initEq=initEq)
> +initOrders(portfolio=portfolio1.st,initDate=initDate)
> +
> +#osFUN will need to know which symbol is leg 1 and which is leg 2 as well as what the
> +#values are for MaxPos and lvls.  So, create a slot in portfolio to hold this info.
> +pair <- c(1,2,MaxPos,lvls)
> +names(pair) <- c(symb1,symb2,"MaxPos","lvls")
> +.blotter[[paste('portfolio',portfolio1.st,sep='.')]]$pair <- pair
> +
> +# Create initial position limits and levels by symbol
> +# allow 3 entries for long and short if lvls=3.
> +addPosLimit(portfolio=portfolio1.st, timestamp=initDate, symbol=symb1, maxpos=MaxPos, longlevels=lvls, minpos=-MaxPos, shortlevels=lvls)
> +addPosLimit(portfolio=portfolio1.st, timestamp=initDate, symbol=symb2, maxpos=MaxPos, longlevels=lvls, minpos=-MaxPos, shortlevels=lvls)
> +
> +# Create a strategy object
> +pairStrat <- strategy('pairStrat')
> +
> +# Indicator function
> +calcRatio <- function(x) { #returns the ratio of notional close prices for 2 symbols
> +       x1 <- get(x[1])
> +       x2 <- get(x[2])
> +       mult1 <- getInstrument(x[1])$multiplier
> +       mult2 <- getInstrument(x[2])$multiplier
> +       rat <- (mult1 * Cl(x1)) / (mult2 * Cl(x2))
> +       colnames(rat) <- 'Ratio'
> +       rat
> +}
> +Ratio <- calcRatio(c(symb1[1],symb2[1])) #Indicator used for determining entry/exits
> +
> +#Put a slot in portfolio to hold hedge ratio so that it's available for order sizing function.
> +#In this example, the hedge ratio happens to be the same as the Ratio indicator.
> +.blotter[[paste('portfolio',portfolio1.st,sep='.')]]$HedgeRatio <- Ratio
> +#and make a function to get the most recent HedgeRatio
> +getHedgeRatio <- function(portfolio, timestamp) {
> +       portf <- getPortfolio(portfolio)
> +       timestamp <- format(timestamp,"%Y-%m-%d %H:%M:%S") #ensures you don't get last value of next day if using intraday data and timestamp=midnight
> +       toDate <- paste("::", timestamp, sep="")
> +       Ratio <- last(portf$HedgeRatio[toDate])
> +       as.numeric(Ratio)
> +}
> +
> +# Create an indicator - BBands on the Ratio
> +pairStrat <- add.indicator(strategy = pairStrat, name = "calcRatio", arguments = list(x=c(symb1,symb2)))
> +pairStrat <- add.indicator(strategy = pairStrat, name = "BBands", arguments = list(HLC=quote(Ratio), sd=SD, n=N, maType='SMA'))
> +
> +#applyIndicators(strategy=pairStrat,mktdata=get(symb1[1])) #for debugging
> +
> +# Create signals - buy when crossing lower band from below, sell when crossing upper band from above, flatten when crossing mavg from above or from below
> +pairStrat <- add.signal(strategy = pairStrat, name = "sigCrossover", arguments= list(columns=c("Ratio","up"), relationship="lt"), label="cross.up")
> +pairStrat <- add.signal(strategy = pairStrat, name = "sigCrossover", arguments= list(columns=c("Ratio","dn"), relationship="gt"), label="cross.dn")
> +pairStrat <- add.signal(strategy = pairStrat, name = "sigCrossover", arguments= list(columns=c("Ratio","mavg"), relationship="lt"), label="cross.mid.fa")
> +pairStrat <- add.signal(strategy = pairStrat, name = "sigCrossover", arguments= list(columns=c("Ratio","mavg"), relationship="gt"), label="cross.mid.fb")
> +
> +#make an order sizing function
> +#######################_ORDER SIZING FUNCTION_##########################################################
> +#check to see which stock it is. If it's the second stock, reverse orderqty and orderside
> +osSpreadMaxPos <- function (data, timestamp, orderqty, ordertype, orderside, portfolio, symbol, ruletype, ..., orderprice)
> +{
> +       portf <- getPortfolio(portfolio)
> +       #check to make sure pair slot has the things needed for this function
> +       if (!any(portf$pair == 1) && !(any(portf$pair == 2))) stop('pair must contain both values 1 and 2')
> +       if (!any(names(portf$pair) == "MaxPos") || !any(names(portf$pair) == "lvls")) stop('pair must contain MaxPos and lvls')
> +
> +       if (portf$pair[symbol] == 1) legside <- "long"
> +       if (portf$pair[symbol] == 2) legside <- "short"
> +       MaxPos <- portf$pair["MaxPos"]
> +       lvls <- portf$pair["lvls"]
> +       ratio <- getHedgeRatio(portfolio, timestamp)
> +       pos <- getPosQty(portfolio, symbol, timestamp)
> +       PosLimit <- getPosLimit(portfolio, symbol, timestamp)
> +       qty <- orderqty
> +       if (legside == "short") {#symbol is 2nd leg
> +               ## Comment out next line to use equal ordersizes for each stock.
> +               addPosLimit(portfolio=portfolio, timestamp=timestamp, symbol=symbol, maxpos=round(MaxPos*ratio,0), longlevels=lvls, minpos=round(-MaxPos*ratio,0), shortlevels=lvls)
> +               ##
> +               qty <- -orderqty #switch orderqty for Stock B
> +       }
> +
> +       if (qty > 0) orderside = 'long'
> +       if (qty < 0) orderside = 'short'
> +
> +       orderqty <- osMaxPos(data=data,timestamp=timestamp,orderqty=qty,ordertype=ordertype,
> +                                       orderside=orderside,portfolio=portfolio,symbol=symbol,ruletype=ruletype, ...)
> +
> +       #Add the order here instead of in the ruleSignal function
> +       if (!is.null(orderqty) & !orderqty == 0 & !is.null(orderprice)) {
> +            addOrder(portfolio = portfolio, symbol = symbol,
> +                timestamp = timestamp, qty = orderqty, price = as.numeric(orderprice),
> +                ordertype = ordertype, side = orderside, replace = FALSE,
> +                status = "open", ... = ...)
> +    }
> +       return(0) #so that ruleSignal function doesn't also try to place an order
> +}
> +########################################################################################################
> +
> +# Create entry and exit rules for longs  and for shorts. Both symbols will get the same buy/sell signals, but osMaxPos will reverse those for the second symbol.
> +# orderqty's are bigger than PosLimits allow. osMaxPos will adjust the orderqty down to 1/3 the max allowed. (1/3 is because we are using 3 levels in PosLimit)
> +pairStrat <- add.rule(strategy = pairStrat, name='ruleSignal', arguments = list(sigcol="cross.dn", sigval=TRUE, orderqty=1e6, ordertype='market', orderside=NULL, osFUN='osSpreadMaxPos'), type='enter' )
> +pairStrat <- add.rule(strategy = pairStrat, name='ruleSignal', arguments = list(sigcol="cross.up", sigval=TRUE, orderqty=-1e6, ordertype='market', orderside=NULL, osFUN='osSpreadMaxPos'), type='enter')
> +pairStrat <- add.rule(strategy = pairStrat, name='ruleSignal', arguments = list(sigcol="cross.mid.fb", sigval=TRUE, orderqty='all', ordertype='market', orderside=NULL), type='exit')
> +pairStrat <- add.rule(strategy = pairStrat, name='ruleSignal', arguments = list(sigcol="cross.mid.fa", sigval=TRUE, orderqty='all', ordertype='market', orderside=NULL), type='exit')
> +
> +#applySignals(strategy=pairStrat, mktdata=applyIndicators(strategy=pairStrat,mktdata=get(symb1))) #for debugging
> +
> +out1<-applyStrategy(strategy=pairStrat, portfolios=portfolio1.st)
> +
> +updatePortf(Portfolio=portfolio1.st,Dates=paste("::",as.Date(Sys.time()),sep=''))
> +updateAcct(account.st,Dates=paste(startDate,endDate,sep="::"))
> +updateEndEq(account.st,Dates=paste(startDate,endDate,sep="::"))
> +getEndEq(account.st,Sys.time())
> +
> +dev.new()
> +chart.Posn(Portfolio=portfolio1.st,Symbol=symb1)
> +dev.new()
> +chart.Posn(Portfolio=portfolio1.st,Symbol=symb2)
> +dev.new()
> +chartSeries(Cl(get(symb1))/Cl(get(symb2)),TA="addBBands(n=N,sd=SD)")
> +
> +ret1 <- PortfReturns(account.st)
> +ret1$total <- rowSums(ret1)
> +#ret1
> +
> +if("package:PerformanceAnalytics" %in% search() || require("PerformanceAnalytics",quietly=TRUE)) {
> +#      getSymbols("SPY", from='1999-01-01')
> +#      SPY.ret <- Return.calculate(SPY$SPY.Close)
> +#      tmp <- merge(SPY.ret,ret1$total,all=FALSE)
> +       dev.new()
> +       charts.PerformanceSummary(ret1$total,geometric=FALSE,wealth.index=TRUE)
> +}
> +
> +
> +###############################################################################
> +# R (http://r-project.org/) Quantitative Strategy Model Framework
> +#
> +# Package Copyright (c) 2009-2010
> +# Peter Carl, Dirk Eddelbuettel, Brian G. Peterson, Jeffrey Ryan, and Joshua Ulrich
> +#
> +# This library is distributed under the terms of the GNU Public License (GPL)
> +# for full details see the file COPYING
> +#
> +# $Id$
> +#
> +###############################################################################
>
>
> Property changes on: pkg/quantstrat/demo/pair_trade.R
> ___________________________________________________________________
> Added: svn:keywords
>   + Revision Id Date Author
>
> _______________________________________________
> Blotter-commits mailing list
> Blotter-commits at lists.r-forge.r-project.org
> https://lists.r-forge.r-project.org/cgi-bin/mailman/listinfo/blotter-commits


More information about the Blotter-commits mailing list