[Blotter-commits] r363 - in pkg/quantstrat: R man

noreply at r-forge.r-project.org noreply at r-forge.r-project.org
Wed Jul 28 18:49:58 CEST 2010


Author: braverock
Date: 2010-07-28 18:49:57 +0200 (Wed, 28 Jul 2010)
New Revision: 363

Removed:
   pkg/quantstrat/man/updateOrderMatrix.Rd
Modified:
   pkg/quantstrat/R/orders.R
   pkg/quantstrat/R/rules.R
   pkg/quantstrat/man/addOrder.Rd
   pkg/quantstrat/man/getOrders.Rd
Log:
- fix index bug that could insert bad orders in the order book
- clean up when we use assign, limit duplication of get/assign to avoid collisions
- add options to return indices or full records as needed

Modified: pkg/quantstrat/R/orders.R
===================================================================
--- pkg/quantstrat/R/orders.R	2010-07-28 16:47:39 UTC (rev 362)
+++ pkg/quantstrat/R/orders.R	2010-07-28 16:49:57 UTC (rev 363)
@@ -35,8 +35,8 @@
         orders<-list()
         orders[[portfolio]]<-list()
     }
-    ordertemplate<-xts(as.matrix(t(c(0,NA,"init","long",0,"closed",as.character(as.POSIXct(initDate))))),order.by=as.POSIXct(initDate))
-    colnames(ordertemplate) <- c("Order.Qty","Order.Price","Order.Type","Order.Side","Order.Threshold","Order.Status","Order.StatusTime")
+    ordertemplate<-xts(as.matrix(t(c(0,NA,"init","long",0,"closed",as.character(as.POSIXct(initDate)),1))),order.by=as.POSIXct(initDate))
+    colnames(ordertemplate) <- c("Order.Qty","Order.Price","Order.Type","Order.Side","Order.Threshold","Order.Status","Order.StatusTime","Order.Set")
     
     if(is.null(symbols)) {
         pfolio<-getPortfolio(portfolio)
@@ -67,15 +67,15 @@
 #' @param timespan xts-style character timespan to be the period to find orders of the given status and ordertype 
 #' @param ordertype one of NULL, "market","limit","stoplimit", or "stoptrailing" default NULL
 #' @param side one of NULL, "long" or "short", default NULL 
-#' @param starttime difference to current timestamp to search, in seconds(numeric) or as a POSIXct timestamp, defaults to -86400 (one day) 
+#' @param which.i if TRUE, return the row index numbers rather than the order rows matching the criteria, default FALSE
 #' @export
-getOrders <- function(portfolio,symbol,status="open",timespan=NULL,ordertype=NULL, side=NULL, starttime=-86400)
+getOrders <- function(portfolio,symbol,status="open",timespan=NULL,ordertype=NULL, side=NULL, which.i=FALSE)
 {
     if(is.null(timespan)) stop("timespan must be an xts style timestring")
     # get order book
     orderbook <- getOrderBook(portfolio)
     if(!length(grep(symbol,names(orderbook[[portfolio]])))==1) stop(paste("symbol",symbol,"does not exist in portfolio",portfolio,"having symbols",names(orderbook)))
-    orderset<-NULL
+    orderset<-orderbook[[portfolio]][[symbol]]
     
     #data quality checks
     if(!is.null(status) & !length(grep(status,c("open", "closed", "canceled","replaced")))==1) stop(paste("order status:",status,' must be one of "open", "closed", "canceled", or "replaced"'))
@@ -85,19 +85,19 @@
         } 
     } 
 
-    
-    # extract
-    orderset<-orderbook[[portfolio]][[symbol]][timespan]
-    if(!is.null(status) & !is.null(orderset) & nrow(orderset)>=1 ){
-        orderset<-orderset[which(orderset[,"Order.Status"]==status),]
-    }
-    if(!is.null(ordertype) & !is.null(orderset) & nrow(orderset)>=1 ) {
-        orderset<-orderset[which(orderset[,"Order.Type"]==ordertype),]    
-    }
-    if(!is.null(side) & !is.null(orderset) & nrow(orderset)>=1 ) {
-        orderset<-orderset[which(orderset[,"Order.Side"]==side),]    
-    }
-    return(orderset)
+	indices <- which(#if(!is.null(timespan)) orderset[timespan,which.i=TRUE] else TRUE &
+					 if(!is.null(status)) orderset[,"Order.Status"]==status else TRUE &
+					 if(!is.null(ordertype)) orderset[,"Order.Type"]==ordertype else TRUE &
+			         if(!is.null(status)) orderset[,"Order.Side"]==side else TRUE
+					)
+							
+	if(isTRUE(which.i)){
+		return(indices)
+	} else {
+		# extract
+		orderset<-orderbook[[portfolio]][[symbol]][indices,]
+		return(orderset)
+	}
 }
 
 #' add an order to the order book
@@ -119,13 +119,31 @@
 #' behave this way, set \code{replace=FALSE}.
 #'  
 #' We have modeled two types of stop orders, which should be sufficient to model most types of stops.  
+#' 
 #' We have modeled the simplest type, a 'stoplimit' order, which is just a limit order used to enter 
-#' or exit a position at a specific price.  There is no functional different between a regular 'limit'
-#' order and a 'stoplimit' order, but the distinction will likely be useful for reporting on when stops
-#' have been triggered.
+#' or exit a position at a specific price.  
+#' Threshold multipliers have also been added for stoplimit.  These allow a threshold to be set as a multiplier 
+#' of the current price. For example, to set a stoplimit order at the current price 
+#' plus ten percent, you would set the threshold multiplier to 1.10.  The threshold multiplier (or scalar) on a 
+#' stoplimit order will be converted to a price at order entry.   
+#' There is no functional different between a regular 'limit'
+#' order and a 'stoplimit' order once entered into the order book, but the distinction will likely 
+#' be useful for reporting on when stops have been triggered.
+#' 
 #' We have also modeled a 'stoptrailing' order, which may be used to model dynamic limit-based entry or exit.  
-#' The 'stoptrailing' order type is the only order type that makes use of the order \code{threshold}, which 
-#' is the difference either positive or negative from the current price when the order is entered.  
+#' If you set \code{tmult=TRUE} on a stoptrailing order, the size of the threshold will be set as a 
+#' difference between the multiplier times the price and the current price at order entry.  in this way, a 10%
+#' trailing entry (exit) will not change in size from the current price as the price changes.  It is effectively 
+#' converted to a scalar at order entry.  While this functionality could change in the future, 
+#' we believe this is more conservative than growing or shrinking the threshold distance from the current market price 
+#' in relation to the threshold, and will result in fewer unintended consequences and more understandable behavior. Comments Welcome.
+#' 
+#' The 'stop*' order types are the only order type that makes use of the order \code{threshold}.   
+#' Scalar thresholds \code{tmult=FALSE} on stoplimit or stoptrailing
+#' orders will be added to the current market price to set the limit price.  In other worlds, a
+#' scalar threshold is the difference either positive or negative from the current price when 
+#' the order is entered. With a stoptrailing order, the order may be moved (via cancel/replace) frequently.
+#' 
 #' Some markets and brokers recognize a stop that triggers a market order, when the stop is triggered, 
 #' a market order will be executed at the then-prevailing price.  We have not modeled this type of order.   
 #' 
@@ -155,11 +173,13 @@
 #' @param side one of either "long" or "short" 
 #' @param threshold numeric threshold to apply to trailing stop orders, default NULL
 #' @param status one of "open", "closed", "canceled", or "replaced", default "open"
-#' @param replace TRUE/FALSE, whether to replace any other open order(s) on this portfolio symbol, default TRUE 
 #' @param statustimestamp timestamp of a status update, will be blank when order is initiated 
 #' @param delay what delay to add to timestamp when inserting the order into the order book, in seconds
+#' @param tmult if TRUE, threshold is a percent multiplier for \code{price}, not a scalar to be added/subtracted from price.  threshold will be dynamically converted to a scalar at time of order entry
+#' @param replace TRUE/FALSE, whether to replace any other open order(s) on this portfolio symbol, default TRUE 
+#' @param return if TRUE, return the row that makes up the order, default FALSE (will assign into the environment)
 #' @export
-addOrder <- function(portfolio, symbol, timestamp, qty, price, ordertype, side, threshold=NULL, status="open", replace=TRUE, statustimestamp='' , delay=.00001)
+addOrder <- function(portfolio, symbol, timestamp, qty, price, ordertype, side, threshold=NULL, status="open", statustimestamp='' , delay=.00001, tmult=FALSE, replace=TRUE, return=FALSE)
 {
     # get order book
     #orderbook <- getOrderBook(portfolio)
@@ -177,11 +197,31 @@
     
     if(!length(grep(side,c('long','short')))==1) stop(paste("side:",side," must be one of 'long' or 'short'"))
     if(is.na(charmatch(ordertype,c("market","limit","stoplimit","stoptrailing")))) stop(paste("ordertype:",ordertype,' must be one of "market","limit","stoplimit", or "stoptrailing"'))
-    if(!is.null(threshold) & !length(grep(ordertype,c("stoplimit","stoptrailing")))==1){ 
-        stop(paste("Threshold may only be applied to a stop order type",ordertype,threshold))
-    }
-    if(is.null(threshold)) threshold=NA #NA is not ignored byc() like NULL is
-    if(!length(grep(status,c("open", "closed", "canceled","replaced")))==1) stop(paste("order status:",status,' must be one of "open", "closed", "canceled", or "replaced"'))
+    if(!is.null(threshold) ) {
+	    if(length(grep(ordertype,c("stoplimit","stoptrailing")))==1) {
+			#we have a threshold set on a stop* order, process it
+			switch(ordertype,
+					stoplimit = {
+						# handle setting the stop limit price
+						if(isTRUE(tmult)){
+							price = price*threshold
+						} else {
+							price = price+threshold
+						}
+					},
+					stoptrailing = {
+						if(isTRUE(tmult)){
+							#get the difference between the threshold*price and the price
+							threshold = (price*threshold)-price
+						} 
+					}
+			) #end type switch
+		} else { 
+			stop(paste("Threshold may only be applied to a stop order type",ordertype,threshold))
+    	}
+	} else { threshold=NA } #NA is not ignored like NULL is 
+    
+	if(!length(grep(status,c("open", "closed", "canceled","replaced")))==1) stop(paste("order status:",status,' must be one of "open", "closed", "canceled", or "replaced"'))
     # TODO do we need to check for collision, and increment timestamp?  or alternately update?
     
     # subset by time and symbol
@@ -191,26 +231,35 @@
         # construct the timespan of the entire series
         timespan=paste(index(first(orderbook),index(last(orderbook)),sep='::'))
     }
-    
-    if(isTRUE(replace)) updateOrders(portfolio=portfolio, symbol=symbol,timespan=timespan, ordertype=ordertype, side=side, oldstatus="open", newstatus="replaced", statustimestamp=timestamp)
-    # get order book
-    orderbook <- getOrderBook(portfolio)
-    statustimestamp=NA # new orders don't have a status time
+ 
+	statustimestamp=NA # new orders don't have a status time
+	
+	#handle order sets
+	order.set=NA
+	
     # insert new order
     if(is.timeBased(timestamp)) ordertime<-timestamp+delay
     else ordertime<-as.POSIXct(timestamp)+delay
-    order<-xts(as.matrix(t(c(qty, price, ordertype, side, threshold, status, statustimestamp))),order.by=(ordertime))
-    #colnames(order) <- c("Order.Qty","Order.Price","Order.Type","Order.Side","Order.Threshold","Order.Status","Order.StatusTime")
+    order<-xts(as.matrix(t(c(qty, price, ordertype, side, threshold, status, statustimestamp, order.set))),order.by=(ordertime))
+#    colnames(order) <- c("Order.Qty","Order.Price","Order.Type","Order.Side","Order.Threshold","Order.Status","Order.StatusTime","Order.Set")
     #print(order)
-    if(ncol(order)!=7) {
+    if(ncol(order)!=8) {
         print(paste("bad order:",order))
         next()
     }
-    orderbook[[portfolio]][[symbol]]<-rbind(orderbook[[portfolio]][[symbol]],order)
-    
-    # assign order book back into place (do we need a non-exported "put" function?)
-    assign(paste("order_book",portfolio,sep='.'),orderbook,envir=.strategy)
-    rm(orderbook)
+	
+	if(!isTRUE(return)){
+		if(isTRUE(replace)) updateOrders(portfolio=portfolio, symbol=symbol,timespan=timespan, ordertype=ordertype, side=side, oldstatus="open", newstatus="replaced", statustimestamp=timestamp)
+		# get order book
+		orderbook <- getOrderBook(portfolio)
+		orderbook[[portfolio]][[symbol]]<-rbind(orderbook[[portfolio]][[symbol]],order)
+		# assign order book back into place (do we need a non-exported "put" function?)
+		assign(paste("order_book",portfolio,sep='.'),orderbook,envir=.strategy)
+		rm(orderbook)
+		return()
+	} else {
+		return(order)
+	}    
 }
 
 #' update an order or orders
@@ -249,37 +298,21 @@
         stop(paste("ordertype:",ordertype,' must be one of "market","limit","stoplimit", or "stoptrailing"'))
     
     # need the ability to pass a range like we do in blotter
-    updatedorders<-getOrders(portfolio=portfolio, symbol=symbol, status=oldstatus, timespan=timespan, ordertype=ordertype, side=side) 
-    if(nrow(updatedorders>=1)){
-        
+    updatedorders<-getOrders(portfolio=portfolio, symbol=symbol, status=oldstatus, timespan=timespan, ordertype=ordertype, side=side,which.i=TRUE) 
+    if(length(updatedorders)>=1){
         # get order book 
         #TODO this gets the order book again after it was already retrieved by getOrdersByStatus.  
-        # at some point, we should eliminate the) double get
+        # at some point, we should eliminate the double get
         orderbook <- getOrderBook(portfolio)
         
-        orderbook[[portfolio]][[symbol]][index(updatedorders),"Order.Status"]<-newstatus
-        orderbook[[portfolio]][[symbol]][index(updatedorders),"Order.StatusTime"]<-as.character(statustimestamp)
-        
+        orderbook[[portfolio]][[symbol]][updatedorders,"Order.Status"]<-newstatus
+        orderbook[[portfolio]][[symbol]][updatedorders,"Order.StatusTime"]<-as.character(statustimestamp)
         # assign order book back into place (do we need a non-exported "put" function?)
         assign(paste("order_book",portfolio,sep='.'),orderbook,envir=.strategy)
-
     }
 }
 
-#' insert a block of updated orders
-#' @param portfolio text name of the portfolio to associate the order book with
-#' @param symbol identfier of the instrument to find orders for.  The name of any associated price objects (xts prices, usually OHLC) should match these
-#' @param updatedorders time series containing updated orders 
-#' @export
-updateOrderMatrix<-function(portfolio, symbol, updatedorders){
-    orderbook <- getOrderBook(portfolio)
 
-    orderbook[[portfolio]][[symbol]][index(updatedorders),]<-updatedorders
-    
-    # assign order book back into place (do we need a non-exported "put" function?)
-    assign(paste("order_book",portfolio,sep='.'),orderbook,envir=.strategy)
-}
-
 #' process open orders at time t, generating transactions or new orders
 #' 
 #' This function is meant to be sufficient for backtesting most strategies, 
@@ -296,10 +329,19 @@
 #' @export
 ruleOrderProc <- function(portfolio, symbol, mktdata, timespan, ordertype=NULL, ..., slippageFUN=NULL)
 {
+	orderbook <- getOrderBook(portfolio)
+	orderset <- orderbook[[portfolio]][[symbol]]
+#
+#    orderbook[[portfolio]][[symbol]][index(updatedorders),]<-updatedorders
+#    
+#    # assign order book back into place (do we need a non-exported "put" function?)
+#    assign(paste("order_book",portfolio,sep='.'),orderbook,envir=.strategy)
+	
     # get open orders
-    procorders<-getOrders(portfolio=portfolio, symbol=symbol, status="open", timespan=timespan, ordertype=ordertype)
+	procorders=NULL
+    procorders<-getOrders(portfolio=portfolio, symbol=symbol, status="open", timespan=timespan, ordertype=ordertype,which.i=TRUE)
     if (!is.null(procorders)){ 
-    if (nrow(procorders)>=1){
+    if (length(procorders)>=1){
         # get previous bar
         prevtime  <- time(mktdata[last(mktdata[timespan, which.i = TRUE])-1])
         timestamp <- time(last(mktdata[timespan]))
@@ -311,43 +353,45 @@
             monthly = ,
             daily = { 
                 # next process daily
-                for (ii in 1:nrow(procorders) ){
+                for (ii in procorders ){
                     txnprice=NULL
-                    txntime=as.character(index(procorders[ii,]))
-                    switch(procorders[ii,]$Order.Type,
-                        market = ,
-                        limit = {
-                            if (procorders[ii,]$Order.Type == 'market' ){
+                    txntime=as.character(index(orderset[ii,]))
+                    switch(orderset[ii,]$Order.Type,
+                        market = {
                                 txnprice=as.numeric(getPrice(mktdata[txntime], prefer='close'))
                                 #if(!is.null(ncol(txnprice)) & ncol(txnprice)>1) txnprice = as.numeric(getPrice(mktdata[timestamp], symbol=symbol, prefer='close'))
-                            } else {
-                                # check to see if price moved through the limit
-                                if(procorders[ii,]$Order.Price>Lo(mktdata[as.character(timestamp)]) & procorders[ii,]$Order.Price<Hi(mktdata[as.character(timestamp)]) ) {
-                                    txnprice=as.numeric(procorders[ii,]$Order.Price)
-                                    txntime=as.character(timestamp)
-                                } else {
-                                    # price did not move through my order
-                                    next() # should go to next order
-                                }   
-                            }   
                         },
+                        limit = ,
+						stoplimit = {
+		                                # check to see if price moved through the limit
+										tmpprices<-last(mktdata[timestamp])
+		                                if ( isTRUE(orderset[ii, ]$Order.Price > getPrice(tmpprices, prefer = "Lo")) &  
+										     isTRUE(orderset[ii, ]$Order.Price < getPrice(tmpprices, prefer = "Hi")) ) {
+		                                    txnprice=as.numeric(orderset[ii,]$Order.Price)
+		                                    txntime=as.character(timestamp)
+		                                } else {
+		                                    # price did not move through my order
+		                                    next() # should go to next order
+		                                }         
+                        },
                         {
-                            stop("order types other than market and limit not (yet?) supported for low-frequency strategies")
+                            stop("order types other than market and (stop)limit not (yet?) supported for low-frequency strategies")
                         }
                     )
                     if(!is.null(txnprice)){
-                        addTxn(Portfolio=portfolio, Symbol=symbol, TxnDate=txntime, TxnQty=as.numeric(procorders[ii,]$Order.Qty), TxnPrice=txnprice ,...=...)
-                        procorders[ii,]$Order.Status<-'closed'
-                        procorders[ii,]$Order.StatusTime<-txntime
+                        addTxn(Portfolio=portfolio, Symbol=symbol, TxnDate=txntime, TxnQty=as.numeric(orderset[ii,]$Order.Qty), TxnPrice=txnprice ,...=...)
+                        orderset[ii,]$Order.Status<-'closed'
+                        orderset[ii,]$Order.StatusTime<-txntime
                     }
                 } #end loop over open orders       
             }, #end daily and lower frequency processing
             {
                 # now do higher frequencies
-                for (ii in 1:nrow(procorders) ){
+				neworders<-NULL
+                for (ii in procorders ){
                     #browser()
                     txnprice=NULL
-                    switch(procorders[ii,]$Order.Type,
+                    switch(orderset[ii,]$Order.Type,
                             market = {
                                 txnprice = as.numeric(getPrice(mktdata[timestamp]))
                                 #TODO extend this to figure out which side to prefer
@@ -358,8 +402,8 @@
                             stoplimit = {
                                 if (is.OHLC(mktdata)){
                                     # check to see if price moved through the limit
-                                    if(procorders[ii,]$Order.Price>Lo(mktdata[timestamp]) & procorders[ii,]$Order.Price<Hi(mktdata[timestamp]) ) {
-                                        txnprice = as.numeric(procorders[ii,]$Order.Price)
+                                    if(orderset[ii,]$Order.Price>Lo(mktdata[timestamp]) & orderset[ii,]$Order.Price<Hi(mktdata[timestamp]) ) {
+                                        txnprice = as.numeric(orderset[ii,]$Order.Price)
                                         txntime  = as.character(timestamp)
                                     } else {
                                         # price did not move through my order
@@ -367,23 +411,23 @@
                                     }   
                                 } else if(is.BBO(mktdata)){
                                     # check side/qty
-                                    if(as.numeric(procorders[ii,]$Order.Qty)>0){ # positive quantity 'buy'
-                                        if(as.numeric(procorders[ii,]$Order.Price)>=as.numeric(getPrice(mktdata[timestamp],prefer='offer'))){
+                                    if(as.numeric(orderset[ii,]$Order.Qty)>0){ # positive quantity 'buy'
+                                        if(as.numeric(orderset[ii,]$Order.Price)>=as.numeric(getPrice(mktdata[timestamp],prefer='offer'))){
                                             # price we're willing to pay is higher than the offer price, so execute at the limit
-                                            txnprice = as.numeric(procorders[ii,]$Order.Price)
+                                            txnprice = as.numeric(orderset[ii,]$Order.Price)
                                             txntime  = as.character(timestamp)
                                         } else next()
                                     } else { # negative quantity 'sell'
-                                        if(as.numeric(procorders[ii,]$Order.Price) <= as.numeric(getPrice(mktdata[timestamp],prefer='bid'))){
+                                        if(as.numeric(orderset[ii,]$Order.Price) <= as.numeric(getPrice(mktdata[timestamp],prefer='bid'))){
                                             # we're willing to sell at a better price than the bid, so execute at the limit
-                                            txnprice = as.numeric(procorders[ii,]$Order.Price)
+                                            txnprice = as.numeric(orderset[ii,]$Order.Price)
                                             txntime  = as.character(timestamp)
                                         } else next() 
                                     } 
                                 } else {
                                     # no depth data, either OHLC or BBO, getPrice explicitly using symbol ?
-                                    if(procorders[ii,]$Order.Price==getPrice(mktdata[timestamp], symbol=symbol, prefer='price')){
-                                        txnprice = as.numeric(procorders[ii,]$Order.Price)
+                                    if(orderset[ii,]$Order.Price==getPrice(mktdata[timestamp], symbol=symbol, prefer='price')){
+                                        txnprice = as.numeric(orderset[ii,]$Order.Price)
                                         txntime  = as.character(timestamp)
                                     } else next()                                     
                                 }
@@ -391,40 +435,41 @@
                             },
                             stoptrailing = {
                                 # if market moved through my price, execute
-                                if(as.numeric(procorders[ii,]$Order.Qty)>0){ # positive quantity 'buy'
-                                    if(procorders[ii,]$Order.Price>=getPrice(mktdata[timestamp],prefer='offer')){
+                                if(as.numeric(orderset[ii,]$Order.Qty)>0){ # positive quantity 'buy'
+                                    if(orderset[ii,]$Order.Price>=getPrice(mktdata[timestamp],prefer='offer')){
                                         # price we're willing to pay is higher than the offer price, so execute at the limit
-                                        txnprice = as.numeric(procorders[ii,]$Order.Price)
+                                        txnprice = as.numeric(orderset[ii,]$Order.Price)
                                         txntime  = as.character(timestamp)
                                     } 
                                 } else { # negative quantity 'sell'
-                                    if(procorders[ii,]$Order.Price<=getPrice(mktdata[timestamp],prefer='bid')){
+                                    if(orderset[ii,]$Order.Price<=getPrice(mktdata[timestamp],prefer='bid')){
                                         # we're willing to sell at a better price than the bid, so execute at the limit
-                                        txnprice = as.numeric(procorders[ii,]$Order.Price)
+                                        txnprice = as.numeric(orderset[ii,]$Order.Price)
                                         txntime  = as.character(timestamp)
                                     }  
                                 } 
                                 # if market is beyond price+(-threshold), replace order
                                 if(is.null(txnprice)){ 
-                                    if(as.numeric(procorders[ii,]$Order.Qty)>0){
+                                    if(as.numeric(orderset[ii,]$Order.Qty)>0){
                                         prefer='offer'
                                     } else {
                                         prefer='bid'
                                     }
                                     # we didn't trade, so check to see if we need to move the stop
-                                    if( getPrice(mktdata[timestamp],prefer=prefer)-procorders[ii,]$Order.Threshold > procorders[ii,]$Order.Price ){
-                                        addOrder(portfolio=portfolio, 
+                                    if( getPrice(mktdata[timestamp],prefer=prefer)+orderset[ii,]$Order.Threshold > orderset[ii,]$Order.Price ){
+                                        neworder<-addOrder(portfolio=portfolio, 
                                                  symbol=symbol, 
                                                  timestamp=timestamp, 
-                                                 qty=as.numeric(procorders[ii,]$Order.Qty), 
-                                                 price=getPrice(mktdata[timestamp],prefer=prefer)-procorders[ii,]$Order.Threshold, 
-                                                 ordertype=procorders[ii,]$Order.Type, 
-                                                 side=procorders[ii,]$Order.Side, 
-                                                 threshold=procorders[ii,]$Order.Threshold, 
+                                                 qty=as.numeric(orderset[ii,]$Order.Qty), 
+                                                 price=getgetPrice(mktdata[timestamp],prefer=prefer)+orderset[ii,]$Order.Threshold, 
+                                                 ordertype=orderset[ii,]$Order.Type, 
+                                                 side=orderset[ii,]$Order.Side, 
+                                                 threshold=orderset[ii,]$Order.Threshold, 
                                                  status="open", 
-                                                 replace=TRUE)
-                                        procorders[ii,]$Order.Status<-'replaced'
-                                        procorders[ii,]$Order.StatusTime<-as.character(timestamp)
+                                                 replace=FALSE, return=TRUE)
+										if (is.null(neworders)) neworders=neworder else neworders = rbind(neworders,neworder) 
+                                        orderset[ii,]$Order.Status<-'replaced'
+                                        orderset[ii,]$Order.StatusTime<-as.character(timestamp)
                                         next()
                                     }
                                 }
@@ -432,15 +477,20 @@
                             }
                     )
                     if(!is.null(txnprice)& !is.na(txnprice)){
-                        addTxn(Portfolio=portfolio, Symbol=symbol, TxnDate=txntime, TxnQty=as.numeric(procorders[ii,]$Order.Qty), TxnPrice=txnprice ,...=...)
-                        procorders[ii,]$Order.Status<-'closed'
-                        procorders[ii,]$Order.StatusTime<-as.character(timestamp)
+                        addTxn(Portfolio=portfolio, Symbol=symbol, TxnDate=txntime, TxnQty=as.numeric(orderset[ii,]$Order.Qty), TxnPrice=txnprice ,...=...)
+                        orderset[ii,]$Order.Status<-'closed'
+                        orderset[ii,]$Order.StatusTime<-as.character(timestamp)
                     }
-                } #end loop over open orders       
+                } #end loop over open orders  
+				if(!is.null(neworders)) orderset=rbind(orderset,neworders)
+				
             } # end higher frequency processing
         ) # end switch on freq
         # now put the orders back in
-        updateOrderMatrix(portfolio=portfolio, symbol=symbol, updatedorders=procorders)
+		# assign order book back into place (do we need a non-exported "put" function?)
+		orderbook[[portfolio]][[symbol]] <- orderset
+		assign(paste("order_book",portfolio,sep='.'),orderbook,envir=.strategy)
+
         } # end check for open orders
     } #end is.null check
 }

Modified: pkg/quantstrat/R/rules.R
===================================================================
--- pkg/quantstrat/R/rules.R	2010-07-28 16:47:39 UTC (rev 362)
+++ pkg/quantstrat/R/rules.R	2010-07-28 16:49:57 UTC (rev 363)
@@ -50,6 +50,7 @@
     if(!is.strategy(strategy)) stop("You must pass in a strategy object to manipulate")
     type=type[1]
     if(is.null(type)) stop("You must specify a type")
+	if(is.na(charmatch(type,c("risk","order","rebalance","exit","enter")))) stop(paste("type:",type,' must be one of "risk", "order", "rebalance", "exit", or "enter"'))
     tmp_rule<-list()
     tmp_rule$name<-name
     tmp_rule$type<-type

Modified: pkg/quantstrat/man/addOrder.Rd
===================================================================
--- pkg/quantstrat/man/addOrder.Rd	2010-07-28 16:47:39 UTC (rev 362)
+++ pkg/quantstrat/man/addOrder.Rd	2010-07-28 16:49:57 UTC (rev 363)
@@ -2,8 +2,8 @@
 \alias{addOrder}
 \title{add an order to the order book...}
 \usage{addOrder(portfolio, symbol, timestamp, qty, price, ordertype, side,
-    threshold, status="open", replace=TRUE, statustimestamp="",
-    delay=1e-05)}
+    threshold, status="open", statustimestamp="", delay=1e-05,
+    tmult=FALSE, replace=TRUE, return=FALSE)}
 \description{add an order to the order book}
 \details{It is important to understand that all the order functionality included in \code{quantstrat}
 exists to more closely model a real trading environment both in backtesting and in production.
@@ -22,13 +22,31 @@
 behave this way, set \code{replace=FALSE}.
 
 We have modeled two types of stop orders, which should be sufficient to model most types of stops.  
+
 We have modeled the simplest type, a 'stoplimit' order, which is just a limit order used to enter 
-or exit a position at a specific price.  There is no functional different between a regular 'limit'
-order and a 'stoplimit' order, but the distinction will likely be useful for reporting on when stops
-have been triggered.
+or exit a position at a specific price.  
+Threshold multipliers have also been added for stoplimit.  These allow a threshold to be set as a multiplier 
+of the current price. For example, to set a stoplimit order at the current price 
+plus ten percent, you would set the threshold multiplier to 1.10.  The threshold multiplier (or scalar) on a 
+stoplimit order will be converted to a price at order entry.   
+There is no functional different between a regular 'limit'
+order and a 'stoplimit' order once entered into the order book, but the distinction will likely 
+be useful for reporting on when stops have been triggered.
+
 We have also modeled a 'stoptrailing' order, which may be used to model dynamic limit-based entry or exit.  
-The 'stoptrailing' order type is the only order type that makes use of the order \code{threshold}, which 
-is the difference either positive or negative from the current price when the order is entered.  
+If you set \code{tmult=TRUE} on a stoptrailing order, the size of the threshold will be set as a 
+difference between the multiplier times the price and the current price at order entry.  in this way, a 10%
+trailing entry (exit) will not change in size from the current price as the price changes.  It is effectively 
+converted to a scalar at order entry.  While this functionality could change in the future, 
+we believe this is more conservative than growing or shrinking the threshold distance from the current market price 
+in relation to the threshold, and will result in fewer unintended consequences and more understandable behavior. Comments Welcome.
+
+The 'stop*' order types are the only order type that makes use of the order \code{threshold}.   
+Scalar thresholds \code{tmult=FALSE} on stoplimit or stoptrailing
+orders will be added to the current market price to set the limit price.  In other worlds, a
+scalar threshold is the difference either positive or negative from the current price when 
+the order is entered. With a stoptrailing order, the order may be moved (via cancel/replace) frequently.
+
[TRUNCATED]

To get the complete diff run:
    svnlook diff /svnroot/blotter -r 363


More information about the Blotter-commits mailing list