SCM

SCM Repository

[blotter] Annotation of /pkg/quantstrat/demo/pair_trade.R
ViewVC logotype

Annotation of /pkg/quantstrat/demo/pair_trade.R

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1376 - (view) (download)

1 : braverock 1020 #Kindly contributed to quantstrat by Garrett See
2 :     #code borrowed heavily from existing quantstrat demos
3 :    
4 :     #This is a simple pairs trading example intended to illustrate how you can extend
5 :     #existing quantstrat functionality. It uses addPosLimits to specify levels and
6 :     #position limits, and shows how to pass a custom order sizing function to osFUN
7 :    
8 :     #Note that it would be easier to build a spread first and treat it as a single instrument
9 :     #instead of dealing with a portfolio of stocks.
10 :    
11 :     ## given 2 stocks, calculate the ratio of their notional values. If the ratio falls below it's
12 :     # 2 stdev band, then when it crosses back above it, buy stock 1 and sell stock 2.
13 :     # If the ratio rises above it's 2 stdev band, then when it crosses back below
14 :     # it, sell stock 1 and buy stock 2. If the ratio crosses it's moving average,
15 :     # then flatten any open positions.
16 :    
17 :     # The Qty of Stock A that it buys (sells) = MaxPos / lvls
18 :     # The Qty of Stock B that is sells (buys) = MaxPos * Ratio / lvls
19 :    
20 :     suppressWarnings(rm("order_book.pair1",pos=.strategy))
21 :     suppressWarnings(rm("account.pairs", "portfolio.pair1", pos=.blotter))
22 :     suppressWarnings(rm("initDate", "endDate", "startDate", "initEq", "SD", "N", "symb1", "symb2",
23 :     "portfolio1.st", "account.st", "pairStrat", "out1"))
24 :    
25 :     require(quantstrat)
26 : milktrader 1370
27 :    
28 : milktrader 1376
29 :     ##### PLACE DEMO AND TEST DATES HERE #################
30 :     #
31 :     #if(isTRUE(options('in_test')$in_test))
32 :     # # use test dates
33 :     # {initDate="2011-01-01"
34 :     # endDate="2012-12-31"
35 :     # } else
36 :     # # use demo defaults
37 :     # {initDate="1999-12-31"
38 :     # endDate=Sys.Date()}
39 :    
40 : braverock 1020 initDate = '2009-01-01'
41 :     endDate = '2011-05-01'
42 :     startDate = '2009-01-02'
43 :     initEq = 100000
44 :     SD = 2
45 :     N = 20
46 :    
47 :     MaxPos = 1500 #max position in stockA;
48 :     #max position in stock B will be max * ratio, i.e. no hard position limit in Stock B
49 :     lvls = 3 #how many times to fade; Each order's qty will = MaxPos/lvls
50 :    
51 :     symb1 <- 'SPY' #change these to try other pairs
52 :     symb2 <- 'DIA' #if you change them, make sure position limits still make sense
53 :    
54 :     portfolio1.st <- 'pair1'
55 :     account.st <- 'pairs'
56 :    
57 :     getSymbols(c(symb1, symb2), from=startDate, to=endDate, adjust=TRUE)
58 :    
59 :     #generic used to make sure the timestamps of all symbols are the same
60 :     #deletes rows where one of the stocks is missing data
61 :     alignSymbols <- function(symbols, env=.GlobalEnv) {
62 :     if (length(symbols) < 2)
63 :     stop("Must provide at least 2 symbols")
64 :     if (any(!is.character(symbols)))
65 :     stop("Symbols must be vector of character strings.")
66 :     ff <- get(symbols[1],env=env)
67 :     for (sym in symbols[-1]) {
68 :     tmp.sym <- get(sym,env=env)
69 :     ff <- merge(ff,tmp.sym,all=FALSE)
70 :     }
71 :     for (sym in symbols) {
72 :     assign(sym,ff[,grep(sym,colnames(ff))],env=env)
73 :     }
74 :     symbols
75 :     }
76 :     alignSymbols(c(symb1,symb2))
77 :    
78 :     #Define Instruments
79 :     currency("USD")
80 :     stock(symb1, currency="USD", multiplier=1)
81 :     stock(symb2, currency="USD", multiplier=1)
82 :    
83 :     #Initialize Portfolio, Account, and Orders
84 :     initPortf(name=portfolio1.st, c(symb1,symb2), initDate=initDate)
85 :     initAcct(account.st, portfolios=portfolio1.st, initDate=initDate, initEq=initEq)
86 :     initOrders(portfolio=portfolio1.st,initDate=initDate)
87 :    
88 :     #osFUN will need to know which symbol is leg 1 and which is leg 2 as well as what the
89 :     #values are for MaxPos and lvls. So, create a slot in portfolio to hold this info.
90 :     pair <- c(1,2,MaxPos,lvls)
91 :     names(pair) <- c(symb1,symb2,"MaxPos","lvls")
92 :     .blotter[[paste('portfolio',portfolio1.st,sep='.')]]$pair <- pair
93 :    
94 :     # Create initial position limits and levels by symbol
95 :     # allow 3 entries for long and short if lvls=3.
96 :     addPosLimit(portfolio=portfolio1.st, timestamp=initDate, symbol=symb1, maxpos=MaxPos, longlevels=lvls, minpos=-MaxPos, shortlevels=lvls)
97 :     addPosLimit(portfolio=portfolio1.st, timestamp=initDate, symbol=symb2, maxpos=MaxPos, longlevels=lvls, minpos=-MaxPos, shortlevels=lvls)
98 :    
99 :     # Create a strategy object
100 :     pairStrat <- strategy('pairStrat')
101 :    
102 :     # Indicator function
103 :     calcRatio <- function(x) { #returns the ratio of notional close prices for 2 symbols
104 :     x1 <- get(x[1])
105 :     x2 <- get(x[2])
106 :     mult1 <- getInstrument(x[1])$multiplier
107 :     mult2 <- getInstrument(x[2])$multiplier
108 :     rat <- (mult1 * Cl(x1)) / (mult2 * Cl(x2))
109 :     colnames(rat) <- 'Ratio'
110 :     rat
111 :     }
112 :     Ratio <- calcRatio(c(symb1[1],symb2[1])) #Indicator used for determining entry/exits
113 :    
114 :     #Put a slot in portfolio to hold hedge ratio so that it's available for order sizing function.
115 :     #In this example, the hedge ratio happens to be the same as the Ratio indicator.
116 :     .blotter[[paste('portfolio',portfolio1.st,sep='.')]]$HedgeRatio <- Ratio
117 :     #and make a function to get the most recent HedgeRatio
118 :     getHedgeRatio <- function(portfolio, timestamp) {
119 :     portf <- getPortfolio(portfolio)
120 :     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
121 :     toDate <- paste("::", timestamp, sep="")
122 :     Ratio <- last(portf$HedgeRatio[toDate])
123 :     as.numeric(Ratio)
124 :     }
125 :    
126 :     # Create an indicator - BBands on the Ratio
127 :     pairStrat <- add.indicator(strategy = pairStrat, name = "calcRatio", arguments = list(x=c(symb1,symb2)))
128 :     pairStrat <- add.indicator(strategy = pairStrat, name = "BBands", arguments = list(HLC=quote(Ratio), sd=SD, n=N, maType='SMA'))
129 :    
130 :     #applyIndicators(strategy=pairStrat,mktdata=get(symb1[1])) #for debugging
131 :    
132 :     # 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
133 :     pairStrat <- add.signal(strategy = pairStrat, name = "sigCrossover", arguments= list(columns=c("Ratio","up"), relationship="lt"), label="cross.up")
134 :     pairStrat <- add.signal(strategy = pairStrat, name = "sigCrossover", arguments= list(columns=c("Ratio","dn"), relationship="gt"), label="cross.dn")
135 :     pairStrat <- add.signal(strategy = pairStrat, name = "sigCrossover", arguments= list(columns=c("Ratio","mavg"), relationship="lt"), label="cross.mid.fa")
136 :     pairStrat <- add.signal(strategy = pairStrat, name = "sigCrossover", arguments= list(columns=c("Ratio","mavg"), relationship="gt"), label="cross.mid.fb")
137 :    
138 :     #make an order sizing function
139 :     #######################_ORDER SIZING FUNCTION_##########################################################
140 :     #check to see which stock it is. If it's the second stock, reverse orderqty and orderside
141 :     osSpreadMaxPos <- function (data, timestamp, orderqty, ordertype, orderside, portfolio, symbol, ruletype, ..., orderprice)
142 :     {
143 :     portf <- getPortfolio(portfolio)
144 :     #check to make sure pair slot has the things needed for this function
145 :     if (!any(portf$pair == 1) && !(any(portf$pair == 2))) stop('pair must contain both values 1 and 2')
146 :     if (!any(names(portf$pair) == "MaxPos") || !any(names(portf$pair) == "lvls")) stop('pair must contain MaxPos and lvls')
147 :    
148 :     if (portf$pair[symbol] == 1) legside <- "long"
149 :     if (portf$pair[symbol] == 2) legside <- "short"
150 :     MaxPos <- portf$pair["MaxPos"]
151 :     lvls <- portf$pair["lvls"]
152 :     ratio <- getHedgeRatio(portfolio, timestamp)
153 :     pos <- getPosQty(portfolio, symbol, timestamp)
154 :     PosLimit <- getPosLimit(portfolio, symbol, timestamp)
155 :     qty <- orderqty
156 :     if (legside == "short") {#symbol is 2nd leg
157 :     ## Comment out next line to use equal ordersizes for each stock.
158 :     addPosLimit(portfolio=portfolio, timestamp=timestamp, symbol=symbol, maxpos=round(MaxPos*ratio,0), longlevels=lvls, minpos=round(-MaxPos*ratio,0), shortlevels=lvls)
159 :     ##
160 :     qty <- -orderqty #switch orderqty for Stock B
161 :     }
162 :    
163 :     if (qty > 0) orderside = 'long'
164 :     if (qty < 0) orderside = 'short'
165 :    
166 :     orderqty <- osMaxPos(data=data,timestamp=timestamp,orderqty=qty,ordertype=ordertype,
167 :     orderside=orderside,portfolio=portfolio,symbol=symbol,ruletype=ruletype, ...)
168 :    
169 :     #Add the order here instead of in the ruleSignal function
170 :     if (!is.null(orderqty) & !orderqty == 0 & !is.null(orderprice)) {
171 :     addOrder(portfolio = portfolio, symbol = symbol,
172 :     timestamp = timestamp, qty = orderqty, price = as.numeric(orderprice),
173 :     ordertype = ordertype, side = orderside, replace = FALSE,
174 :     status = "open", ... = ...)
175 :     }
176 :     return(0) #so that ruleSignal function doesn't also try to place an order
177 :     }
178 :     ########################################################################################################
179 :    
180 :     # 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.
181 :     # 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)
182 :     pairStrat <- add.rule(strategy = pairStrat, name='ruleSignal', arguments = list(sigcol="cross.dn", sigval=TRUE, orderqty=1e6, ordertype='market', orderside=NULL, osFUN='osSpreadMaxPos'), type='enter' )
183 :     pairStrat <- add.rule(strategy = pairStrat, name='ruleSignal', arguments = list(sigcol="cross.up", sigval=TRUE, orderqty=-1e6, ordertype='market', orderside=NULL, osFUN='osSpreadMaxPos'), type='enter')
184 :     pairStrat <- add.rule(strategy = pairStrat, name='ruleSignal', arguments = list(sigcol="cross.mid.fb", sigval=TRUE, orderqty='all', ordertype='market', orderside=NULL), type='exit')
185 :     pairStrat <- add.rule(strategy = pairStrat, name='ruleSignal', arguments = list(sigcol="cross.mid.fa", sigval=TRUE, orderqty='all', ordertype='market', orderside=NULL), type='exit')
186 :    
187 :     #applySignals(strategy=pairStrat, mktdata=applyIndicators(strategy=pairStrat,mktdata=get(symb1))) #for debugging
188 :    
189 :     out1<-applyStrategy(strategy=pairStrat, portfolios=portfolio1.st)
190 :    
191 :     updatePortf(Portfolio=portfolio1.st,Dates=paste("::",as.Date(Sys.time()),sep=''))
192 :     updateAcct(account.st,Dates=paste(startDate,endDate,sep="::"))
193 :     updateEndEq(account.st,Dates=paste(startDate,endDate,sep="::"))
194 :     getEndEq(account.st,Sys.time())
195 :    
196 :     dev.new()
197 :     chart.Posn(Portfolio=portfolio1.st,Symbol=symb1)
198 :     dev.new()
199 :     chart.Posn(Portfolio=portfolio1.st,Symbol=symb2)
200 :     dev.new()
201 :     chartSeries(Cl(get(symb1))/Cl(get(symb2)),TA="addBBands(n=N,sd=SD)")
202 :    
203 :     ret1 <- PortfReturns(account.st)
204 :     ret1$total <- rowSums(ret1)
205 :     #ret1
206 :    
207 :     if("package:PerformanceAnalytics" %in% search() || require("PerformanceAnalytics",quietly=TRUE)) {
208 :     # getSymbols("SPY", from='1999-01-01')
209 :     # SPY.ret <- Return.calculate(SPY$SPY.Close)
210 :     # tmp <- merge(SPY.ret,ret1$total,all=FALSE)
211 :     dev.new()
212 :     charts.PerformanceSummary(ret1$total,geometric=FALSE,wealth.index=TRUE)
213 :     }
214 :    
215 :    
216 :     ###############################################################################
217 :     # R (http://r-project.org/) Quantitative Strategy Model Framework
218 :     #
219 : braverock 1194 # Package Copyright (c) 2009-2012
220 : braverock 1020 # Peter Carl, Dirk Eddelbuettel, Brian G. Peterson, Jeffrey Ryan, and Joshua Ulrich
221 :     #
222 :     # This library is distributed under the terms of the GNU Public License (GPL)
223 :     # for full details see the file COPYING
224 :     #
225 :     # $Id$
226 :     #
227 :     ###############################################################################
228 : milktrader 1370
229 :     ##### PLACE THIS BLOCK AT END OF DEMO SCRIPT ###################
230 :     # book = getOrderBook(port)
231 :     # stats = tradeStats(port)
232 :     # rets = PortfReturns(acct)
233 :     ################################################################

root@r-forge.r-project.org
ViewVC Help
Powered by ViewVC 1.0.0  
Thanks to:
Vienna University of Economics and Business Powered By FusionForge