[Blotter-commits] r422 - in pkg/quantstrat: . R man
noreply at r-forge.r-project.org
noreply at r-forge.r-project.org
Thu Oct 14 21:26:39 CEST 2010
Author: braverock
Date: 2010-10-14 21:26:32 +0200 (Thu, 14 Oct 2010)
New Revision: 422
Modified:
pkg/quantstrat/NAMESPACE
pkg/quantstrat/R/orders.R
pkg/quantstrat/R/traderules.R
pkg/quantstrat/man/addOrder.Rd
pkg/quantstrat/man/ruleSignal.Rd
pkg/quantstrat/man/sigFormula.Rd
Log:
- add iceberg order type
- impute side in osMaxPos if necessary
- minor typo/bug fixes
- update docs
Modified: pkg/quantstrat/NAMESPACE
===================================================================
--- pkg/quantstrat/NAMESPACE 2010-10-14 18:54:07 UTC (rev 421)
+++ pkg/quantstrat/NAMESPACE 2010-10-14 19:26:32 UTC (rev 422)
@@ -7,8 +7,6 @@
export(addOrder)
export(updateOrders)
export(ruleOrderProc)
-export(add.parameter)
-export(add.paramLookupTable)
export(add.rule)
export(applyRules)
export(add.signal)
Modified: pkg/quantstrat/R/orders.R
===================================================================
--- pkg/quantstrat/R/orders.R 2010-10-14 18:54:07 UTC (rev 421)
+++ pkg/quantstrat/R/orders.R 2010-10-14 19:26:32 UTC (rev 422)
@@ -65,7 +65,7 @@
#' @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 status one of "open", "closed", "canceled", or "replaced", default "open"
#' @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 ordertype one of NULL, "market","limit","stoplimit", "stoptrailing", or "iceberg" default NULL
#' @param side one of NULL, "long" or "short", default NULL
#' @param which.i if TRUE, return the row index numbers rather than the order rows matching the criteria, default FALSE
#' @export
@@ -80,8 +80,8 @@
#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"'))
if(!is.null(ordertype)) {
- 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.na(charmatch(ordertype,c("market","limit","stoplimit","stoptrailing","iceberg")))){
+ stop(paste("ordertype:",ordertype,' must be one of "market","limit","stoplimit", "stoptrailing", or "iceberg"'))
}
}
@@ -138,7 +138,7 @@
#' 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}.
+#' The 'stop*' or 'iceberg' 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
@@ -147,6 +147,17 @@
#' 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.
#'
+#' We have also added the 'iceberg' order type. This order type should
+#' most often be paired with \code{delay} and \code{\link{osMaxPos}}. The
+#' iceberg order will enter when initially entered is treated like a limit
+#' order, with an optional threshold (which is applied at initial order
+#' entry, so be careful). Right now, they will enter a new order at price+threshold
+#' upon any execution of the prior iceberg order. This process could
+#' be infinite if \code{\link{osMaxPos}} or an equivalent order sizing
+#' function is not used to limit total position size. An order \code{delay}
+#' is also advisable to model the delay that occurs between getting the trade
+#' confirmation of the previous trade and entering the new order into the order book.
+#'
#' If you ever wanted to move from a backtesting mode to a production mode,
#' this function (and the linked funtion \code{\link{ruleOrderProc}}) would
#' need to be replaced by functions that worked against your execution environment.
@@ -169,7 +180,7 @@
#' @param timestamp timestamp coercible to POSIXct that will be the time to search for orders before this time
#' @param qty numeric quantity of the order
#' @param price numeric price at which the order is to be inserted
-#' @param ordertype one of "market","limit","stoplimit", or "stoptrailing"
+#' @param ordertype one of "market","limit","stoplimit", "stoptrailing",or "iceberg"
#' @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"
@@ -198,12 +209,13 @@
if(is.na(price)) stop("price",price,"must not be NA")
if(!is.null(side) & !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.na(charmatch(ordertype,c("market","limit","stoplimit","stoptrailing","iceberg")))) stop(paste("ordertype:",ordertype,' must be one of "market","limit","stoplimit","stoptrailing", or"iceberg"'))
if(!is.null(threshold) & length(price)>=1 ) {
- if(length(grep(ordertype,c("stoplimit","stoptrailing")))==1) {
+ if(length(grep(ordertype,c("stoplimit","stoptrailing","iceberg")))==1) {
#we have a threshold set on a stop* order, process it
switch(ordertype,
- stoplimit = {
+ stoplimit =,
+ iceberg = {
# handle setting the stop limit price
if(isTRUE(tmult)){
price = price*threshold
@@ -219,7 +231,7 @@
}
) #end type switch
} else {
- stop(paste("Threshold may only be applied to a stop order type",ordertype,threshold))
+ stop(paste("Threshold may only be applied to a stop or iceberg order type",ordertype,threshold))
}
}
@@ -316,8 +328,8 @@
if(!is.null(side) & !length(grep(side,c('long','short')))==1)
stop(paste("side:",side," must be one of 'long' or 'short'"))
#if(is.null(side)) side<-NA
- if(!is.null(ordertype) & 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(ordertype) & is.na(charmatch(ordertype,c("market","limit","stoplimit","stoptrailing","iceberg"))))
+ stop(paste("ordertype:",ordertype,' must be one of "market","limit","stoplimit","stoptrailing", or "iceberg"'))
# 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,which.i=TRUE)
@@ -417,8 +429,12 @@
txntime = as.character(timestamp)
},
limit= ,
- stoplimit = {
+ stoplimit =,
+ iceberg = {
if (is.OHLC(mktdata)){
+ if( ordersubset[ii,]$Order.Type == 'iceberg'){
+ stop("iceberg orders not supported for OHLC data")
+ }
# check to see if price moved through the limit
if( as.numeric(ordersubset[ii,]$Order.Price)>as.numeric(Lo(mktdata[timestamp]))
& as.numeric(ordersubset[ii,]$Order.Price)< as.numeric(Hi(mktdata[timestamp])) )
@@ -444,7 +460,25 @@
txntime = as.character(timestamp)
} else next()
}
- } else {
+ if( ordersubset[ii,]$Order.Type == 'iceberg'){
+ #we've transacted, so the old order was closed, put in a new one
+ neworder<-addOrder(portfolio=portfolio,
+ symbol=symbol,
+ timestamp=timestamp,
+ qty=as.numeric(ordersubset[ii,]$Order.Qty),
+ price=getPrice(mktdata[timestamp],prefer=prefer)+as.numeric(ordersubset[ii,]$Order.Threshold),
+ ordertype=ordersubset[ii,]$Order.Type,
+ side=ordersubset[ii,]$Order.Side,
+ threshold=ordersubset[ii,]$Order.Threshold,
+ status="open",
+ replace=FALSE, return=TRUE,
+ ,...=..., TxnFees=ordersubset[ii,]$Txn.Fees)
+ if (is.null(neworders)) neworders=neworder else neworders = rbind(neworders,neworder)
+ ordersubset[ii,]$Order.Status<-'replaced'
+ ordersubset[ii,]$Order.StatusTime<-as.character(timestamp)
+ next()
+ }
+ } else {
# no depth data, either OHLC or BBO, getPrice explicitly using symbol ?
if(as.numeric(ordersubset[ii,]$Order.Price)==getPrice(mktdata[timestamp], symbol=symbol, prefer='price')){
txnprice = as.numeric(ordersubset[ii,]$Order.Price)
Modified: pkg/quantstrat/R/traderules.R
===================================================================
--- pkg/quantstrat/R/traderules.R 2010-10-14 18:54:07 UTC (rev 421)
+++ pkg/quantstrat/R/traderules.R 2010-10-14 19:26:32 UTC (rev 422)
@@ -20,9 +20,10 @@
#' @param sigcol column name to check for signal
#' @param sigval signal value to match against
#' @param orderqty numeric quantity of the desired order, or 'all', modified by osFUN
-#' @param ordertype one of "market","limit","stoplimit", or "stoptrailing"
+#' @param ordertype one of "market","limit","stoplimit", "stoptrailing", or "iceberg"
#' @param orderside one of either "long" or "short", default NULL, see details
#' @param threshold numeric or function threshold to apply to trailing stop orders, default NULL, see Details
+#' @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 delay what delay to add to timestamp when inserting the order into the order book, in seconds
#' @param osFUN function or text descriptor of function to use for order sizing, default \code{\link{osNoOp}}
@@ -34,7 +35,7 @@
#' @param TxnFees numeric fees (usually negative) or function name for calculating TxnFees (processing happens later, not in this function)
#' @seealso \code{\link{osNoOp}} , \code{\link{add.rule}}
#' @export
-ruleSignal <- function(data=mktdata, timestamp, sigcol, sigval, orderqty=0, ordertype, orderside=NULL, threshold=NULL, replace=TRUE, delay=0.0001, osFUN='osNoOp', pricemethod=c('market','opside','maker'), portfolio, symbol, ..., ruletype, TxnFees=0 )
+ruleSignal <- function(data=mktdata, timestamp, sigcol, sigval, orderqty=0, ordertype, orderside=NULL, threshold=NULL, tmult=FALSE, replace=TRUE, delay=0.0001, osFUN='osNoOp', pricemethod=c('market','opside','maker'), portfolio, symbol, ..., ruletype, TxnFees=0)
{
if(!is.function(osFUN)) osFUN<-match.fun(osFUN)
#print(paste(symbol,timestamp))
@@ -103,7 +104,7 @@
}
)
if(inherits(orderprice,'try-error')) orderprice<-NULL
- if(length(orderprice>1) & !ordertype=='maker') orderprice<-last(orderprice[as.character(timestamp)])
+ if(length(orderprice>1) & !pricemethod=='maker') orderprice<-last(orderprice[as.character(timestamp)])
if(is.null(orderside) & !isTRUE(orderqty == 0)){
curqty<-getPosQty(Portfolio=portfolio, Symbol=symbol, Date=timestamp)
if (curqty>0 ){
@@ -121,7 +122,7 @@
}
}
if(!is.null(orderqty) & !orderqty == 0 & !is.null(orderprice)){
- addOrder(portfolio=portfolio, symbol=symbol, timestamp=timestamp, qty=orderqty, price=orderprice, ordertype=ordertype, side=orderside, threshold=threshold, status="open", replace=replace , delay=delay, ...=..., TxnFees=TxnFees)
+ addOrder(portfolio=portfolio, symbol=symbol, timestamp=timestamp, qty=orderqty, price=orderprice, ordertype=ordertype, side=orderside, threshold=threshold, status="open", replace=replace , delay=delay, tmult=tmult, ...=..., TxnFees=TxnFees)
}
}
}
@@ -233,9 +234,29 @@
pos<-getPosQty(portfolio,symbol,timestamp)
# check against max position
PosLimit<-getPosLimit(portfolio,symbol,timestamp)
- # check levels
-
- # buy long
+
+ #TODO add handling for orderqty='all', and handle risk ruletype separately
+
+ #check order side
+ if(is.null(orderside) & !isTRUE(orderqty == 0)){
+ curqty<-pos
+ if (curqty>0 ){
+ #we have a long position
+ orderside<-'long'
+ } else if (curqty<0){
+ #we have a short position
+ orderside<-'short'
+ } else {
+ # no current position, which way are we going?
+ if (orderqty>0)
+ orderside<-'long'
+ else
+ orderside<-'short'
+ }
+ }
+
+ # check levels
+ # buy long
if(orderqty>0 & orderside=='long'){
if ((orderqty+pos)<PosLimit[,"MaxPos"]) {
#we have room to expand the position
@@ -270,7 +291,7 @@
if(orderqty<=(PosLimit[,"MinPos"]/PosLimit[,"ShortLevels"]) ) {
orderqty=orderqty
} else {
- orderqty = round(PosLimit[,"MinPos"]/PosLimit[,"LongLevels"],0) #note no round lots
+ orderqty = round(PosLimit[,"MinPos"]/PosLimit[,"ShortLevels"],0) #note no round lots
}
} else {
# this order would put us over the MinPos limit
Modified: pkg/quantstrat/man/addOrder.Rd
===================================================================
--- pkg/quantstrat/man/addOrder.Rd 2010-10-14 18:54:07 UTC (rev 421)
+++ pkg/quantstrat/man/addOrder.Rd 2010-10-14 19:26:32 UTC (rev 422)
@@ -41,7 +41,7 @@
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}.
+The 'stop*' or 'iceberg' 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
@@ -50,6 +50,17 @@
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.
+We have also added the 'iceberg' order type. This order type should
+most often be paired with \code{delay} and \code{\link{osMaxPos}}. The
+iceberg order will enter when initially entered is treated like a limit
+order, with an optional threshold (which is applied at initial order
+entry, so be careful). Right now, they will enter a new order at price+threshold
+upon any execution of the prior iceberg order. This process could
+be infinite if \code{\link{osMaxPos}} or an equivalent order sizing
+function is not used to limit total position size. An order \code{delay}
+is also advisable to model the delay that occurs between getting the trade
+confirmation of the previous trade and entering the new order into the order book.
+
If you ever wanted to move from a backtesting mode to a production mode,
this function (and the linked funtion \code{\link{ruleOrderProc}}) would
need to be replaced by functions that worked against your execution environment.
@@ -71,7 +82,7 @@
\item{timestamp}{timestamp coercible to POSIXct that will be the time to search for orders before this time}
\item{qty}{numeric quantity of the order}
\item{price}{numeric price at which the order is to be inserted}
-\item{ordertype}{one of "market","limit","stoplimit", or "stoptrailing"}
+\item{ordertype}{one of "market","limit","stoplimit", "stoptrailing",or "iceberg"}
\item{side}{one of either "long" or "short"}
\item{threshold}{numeric threshold to apply to trailing stop orders, default NULL}
\item{status}{one of "open", "closed", "canceled", or "replaced", default "open"}
@@ -80,5 +91,5 @@
\item{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}
\item{replace}{TRUE/FALSE, whether to replace any other open order(s) on this portfolio symbol, default TRUE}
\item{return}{if TRUE, return the row that makes up the order, default FALSE (will assign into the environment)}
-\item{dots}{any other passthru parameters}
+\item{\dots}{any other passthru parameters}
\item{TxnFees}{numeric fees (usually negative) or function name for calculating TxnFees (processing happens later, not in this function)}}
Modified: pkg/quantstrat/man/ruleSignal.Rd
===================================================================
--- pkg/quantstrat/man/ruleSignal.Rd 2010-10-14 18:54:07 UTC (rev 421)
+++ pkg/quantstrat/man/ruleSignal.Rd 2010-10-14 19:26:32 UTC (rev 422)
@@ -2,9 +2,9 @@
\alias{ruleSignal}
\title{default rule to generate a trade order on a signal...}
\usage{ruleSignal(data=mktdata, timestamp, sigcol, sigval, orderqty=0,
- ordertype, orderside, threshold, replace=TRUE, delay=1e-04,
- osFUN="osNoOp", pricemethod=c("market", "opside", "maker"),
- portfolio, symbol, ..., ruletype, TxnFees=0)}
+ ordertype, orderside, threshold, tmult=FALSE, replace=TRUE,
+ delay=1e-04, osFUN="osNoOp", pricemethod=c("market", "opside",
+ "maker"), portfolio, symbol, ..., ruletype, TxnFees=0)}
\description{default rule to generate a trade order on a signal}
\details{\code{pricemethod} may be one of 'market', 'opside', or 'maker'
which will either try to get the price of the 'market' at \code{timestamp} and use this as the order price
@@ -28,6 +28,7 @@
\item{ordertype}{one of "market","limit","stoplimit", or "stoptrailing"}
\item{orderside}{one of either "long" or "short", default NULL, see details}
\item{threshold}{numeric or function threshold to apply to trailing stop orders, default NULL, see Details}
+\item{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}
\item{replace}{TRUE/FALSE, whether to replace any other open order(s) on this portfolio symbol, default TRUE}
\item{delay}{what delay to add to timestamp when inserting the order into the order book, in seconds}
\item{osFUN}{function or text descriptor of function to use for order sizing, default \code{\link{osNoOp}}}
@@ -35,4 +36,5 @@
\item{portfolio}{text name of the portfolio to place orders in}
\item{symbol}{identifier of the instrument to place orders for. The name of any associated price objects (xts prices, usually OHLC) should match these}
\item{...}{any other passthru parameters}
-\item{ruletype}{one of "risk","order","rebalance","exit","entry", see \code{\link{add.rule}}}}
+\item{ruletype}{one of "risk","order","rebalance","exit","entry", see \code{\link{add.rule}}}
+\item{TxnFees}{numeric fees (usually negative) or function name for calculating TxnFees (processing happens later, not in this function)}}
Modified: pkg/quantstrat/man/sigFormula.Rd
===================================================================
--- pkg/quantstrat/man/sigFormula.Rd 2010-10-14 18:54:07 UTC (rev 421)
+++ pkg/quantstrat/man/sigFormula.Rd 2010-10-14 19:26:32 UTC (rev 422)
@@ -7,7 +7,7 @@
This allows the columns of the data to be addressed without any major manipulation, simply by column name. In most cases in quantstrat, this will be either the price/return columns, or columns added by indicators or prior signals.
The formula will return TRUE/FALSE for each row comparison as a time series column which can then be used for rule execution. The \code{formula} will be evaluated using \code{\link{eval}} as though in an if statement.
-This code is adapted from the approach used by Vijay Vaidyanthan in his PAST/AAII/SIPRO code to construct arbitrary, formulaic, comparisons. Many thanks to Vijay for sharing his expertise.}
+This code is adapted from the approach used by Vijay Vaidyanthan in his PAST(AAII/SIPRO) code to construct arbitrary, formulaic, comparisons. Many thanks to Vijay for sharing his expertise.}
\arguments{\item{label}{text label to apply to the output}
\item{data}{data to apply formula to}
\item{formula}{a logical expression like that used in an if statement, will typically reference column names in \code{mktdata}}
More information about the Blotter-commits
mailing list