[Blotter-commits] r605 - pkg/quantstrat/demo

noreply at r-forge.r-project.org noreply at r-forge.r-project.org
Sat May 21 16:05:33 CEST 2011


Author: braverock
Date: 2011-05-21 16:05:33 +0200 (Sat, 21 May 2011)
New Revision: 605

Modified:
   pkg/quantstrat/demo/pair_trade.R
Log:
- updates to pairs demo from Garrett See

Modified: pkg/quantstrat/demo/pair_trade.R
===================================================================
--- pkg/quantstrat/demo/pair_trade.R	2011-05-19 19:04:14 UTC (rev 604)
+++ pkg/quantstrat/demo/pair_trade.R	2011-05-21 14:05:33 UTC (rev 605)
@@ -2,13 +2,13 @@
 #code borrowed heavily from existing quantstrat demos
 
 #This is a simple pairs trading example intended to illustrate how you can extend
-#existing quantstrat functionality.  Also, it uses addPosLimits to specify
-#levels and position limits, and shows how to pass a custom order sizing function to osFUN 
+#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 their ratio.  If the ratio falls below it's
+## 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,
@@ -34,14 +34,34 @@
 #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 <- 'CVX' #change these to try other pairs
-symb2 <- 'XOM' #if you change them, make sure position limits still make sense
+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)
@@ -51,34 +71,41 @@
 initAcct(account.st, portfolios=portfolio1.st, initDate=initDate, initEq=initEq)
 initOrders(portfolio=portfolio1.st,initDate=initDate)
 
-#create a slot in portfolio for symb1 and symb2 to make them available to osFUN
-pair <- c('long','short')
-names(pair) <- c(symb1,symb2)
+#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.
+# 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')
 
-calcRatio <- function(x) { #returns the ratio of close prices for 2 symbols
+# Indicator function
+calcRatio <- function(x) { #returns the ratio of notional close prices for 2 symbols
 	x1 <- get(x[1])
 	x2 <- get(x[2])
-	rat <- Ad(x1) / Ad(x2)
+	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]))
-#let's go ahead and put this in a slot in portfolio
-.blotter[[paste('portfolio',portfolio1.st,sep='.')]]$Ratio <- Ratio
-#and make a function to get the most recent Ratio
-getRatio <- function(portfolio, timestamp) {
+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$Ratio[toDate])
+	Ratio <- last(portf$HedgeRatio[toDate])
 	as.numeric(Ratio)
 }
 
@@ -100,17 +127,22 @@
 osSpreadMaxPos <- function (data, timestamp, orderqty, ordertype, orderside, portfolio, symbol, ruletype, ..., orderprice) 
 {
 	portf <- getPortfolio(portfolio)
-    legside <- portf$pair[symbol] #"long" if symbol=symb1, "short" if symbol=symb2
-	if (legside != "long" && legside != "short") stop('pair must contain "long" and "short"')
-	ratio <- getRatio(portfolio, timestamp)
-	
+	#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) 
+	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=MaxPos*ratio, longlevels=lvls, minpos=-MaxPos*ratio, shortlevels=lvls)
-		#TODO: is it okay that MaxPos and lvls come from .GlobalEnv ?
+		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
 	}
 	
@@ -119,8 +151,7 @@
  
 	orderqty <- osMaxPos(data=data,timestamp=timestamp,orderqty=qty,ordertype=ordertype,
 					orderside=orderside,portfolio=portfolio,symbol=symbol,ruletype=ruletype, ...)
-	orderqty <- round(orderqty,0)
-
+					
 	#Add the order here instead of in the ruleSignal function
 	if (!is.null(orderqty) & !orderqty == 0 & !is.null(orderprice)) {
             addOrder(portfolio = portfolio, symbol = symbol, 
@@ -153,18 +184,18 @@
 dev.new()
 chart.Posn(Portfolio=portfolio1.st,Symbol=symb2)
 dev.new()
-chartSeries(Cl(get(symb1))/Cl(get(symb2)),TA="addBBands()")
+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)
+#	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(cbind(tmp[,2],tmp[,1]),geometric=FALSE,wealth.index=TRUE)
+	charts.PerformanceSummary(ret1$total,geometric=FALSE,wealth.index=TRUE)
 }
 
 



More information about the Blotter-commits mailing list