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

noreply at r-forge.r-project.org noreply at r-forge.r-project.org
Tue May 8 17:26:32 CEST 2012


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



More information about the Blotter-commits mailing list