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 1515 - (view) (download)

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

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