[Blotter-commits] r423 - in pkg/quantstrat: R man
noreply at r-forge.r-project.org
noreply at r-forge.r-project.org
Mon Oct 18 23:10:18 CEST 2010
Author: braverock
Date: 2010-10-18 23:10:17 +0200 (Mon, 18 Oct 2010)
New Revision: 423
Modified:
pkg/quantstrat/R/orders.R
pkg/quantstrat/R/rules.R
pkg/quantstrat/R/traderules.R
pkg/quantstrat/man/add.rule.Rd
pkg/quantstrat/man/getOrders.Rd
pkg/quantstrat/man/ruleOrderProc.Rd
pkg/quantstrat/man/ruleSignal.Rd
Log:
- add risk rules and stoptrailing support for higher frequency bars
- add timespan parameter for ISO-8601 time subset rule execution
- update docs
- minor as.numeric bug fixes
Modified: pkg/quantstrat/R/orders.R
===================================================================
--- pkg/quantstrat/R/orders.R 2010-10-14 19:26:32 UTC (rev 422)
+++ pkg/quantstrat/R/orders.R 2010-10-18 21:10:17 UTC (rev 423)
@@ -227,6 +227,8 @@
if(isTRUE(tmult)){
#get the difference between the threshold*price and the price
threshold = (price*threshold)-price
+ } else {
+ price = price+threshold
}
}
) #end type switch
@@ -347,12 +349,24 @@
}
-#' process open orders at time t, generating transactions or new orders
+#' process open orders at time \emph{t}, generating transactions or new orders
#'
#' This function is meant to be sufficient for backtesting most strategies,
#' but would need to be replaced for production use. It provides the interface
#' for taking the order book and determining when orders become trades.
#'
+#' For the purposes of backtesting, and compatibility with the trade accounting in
+#' \code{blotter}, this function will not allow a transaction to cross your current
+#' position through zero. The accounting rules for realizing gains in such cases
+#' are more complicated than we wish to support. Also, many brokers will break, revise,
+#' or split such transactions for the same reason. If you wish to do a "stop and reverse"
+#' system, first stop (flatten), and then reverse (initiate a new position).
+#'
+#' This function would need to be revised or replaced for connection to a live trading infrastructure.
+#'
+#' We would like to model slippage here via \code{slippageFUN}. Code contributions, suggestions,
+#' and requests appreciated.
+#'
#' @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 mktdata an xts object containing market data. depending on indicators, may need to be in OHLCV or BBO formats, default NULL
@@ -466,7 +480,7 @@
symbol=symbol,
timestamp=timestamp,
qty=as.numeric(ordersubset[ii,]$Order.Qty),
- price=getPrice(mktdata[timestamp],prefer=prefer)+as.numeric(ordersubset[ii,]$Order.Threshold),
+ price=as.numeric(getPrice(mktdata[timestamp],prefer=prefer)),
ordertype=ordersubset[ii,]$Order.Type,
side=ordersubset[ii,]$Order.Side,
threshold=ordersubset[ii,]$Order.Threshold,
@@ -490,22 +504,36 @@
stoptrailing = {
# if market moved through my price, execute
if(as.numeric(ordersubset[ii,]$Order.Qty)>0){ # positive quantity 'buy'
- if(as.numeric(ordersubset[ii,]$Order.Price)>=getPrice(mktdata[timestamp],prefer='offer')){ #TODO maybe use last(getPrice) to catch multiple prints on timestamp?
- # price we're willing to pay is higher than the offer price, so execute at the limit
- txnprice = as.numeric(ordersubset[ii,]$Order.Price)
- txntime = as.character(timestamp)
- }
+ if(is.BBO(mktdata)){
+ prefer='offer'
+ if(as.numeric(ordersubset[ii,]$Order.Price)>=getPrice(mktdata[timestamp],prefer=prefer)){ #TODO maybe use last(getPrice) to catch multiple prints on timestamp?
+ # price we're willing to pay is higher than the offer price, so execute at the limit
+ txnprice = as.numeric(ordersubset[ii,]$Order.Price)
+ txntime = as.character(timestamp)
+ }
+ }
} else { # negative quantity 'sell'
- if(as.numeric(ordersubset[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(ordersubset[ii,]$Order.Price)
- txntime = as.character(timestamp)
- }
+ if(is.BBO(mktdata)){
+ prefer='bid'
+ if(as.numeric(ordersubset[ii,]$Order.Price)<=getPrice(mktdata[timestamp],prefer=prefer)){
+ # we're willing to sell at a better price than the bid, so execute at the limit
+ txnprice = as.numeric(ordersubset[ii,]$Order.Price)
+ txntime = as.character(timestamp)
+ }
+ }
}
+ if(is.OHLC(mktdata)){
+ # 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]))) )
+ {
+ txnprice = as.numeric(ordersubset[ii,]$Order.Price)
+ txntime = as.character(timestamp)
+ }
+ }
# if market is beyond price+(-threshold), replace order
if(is.null(txnprice)) {
# we didn't trade, so check to see if we need to move the stop
-
# first figure out how to find a price
if (is.OHLC(mktdata)){
prefer='close'
@@ -518,15 +546,23 @@
} else {
prefer=NULL # see if getPrice can figure it out
}
- if( last(getPrice(mktdata[timestamp],prefer=prefer))+as.numeric(ordersubset[ii,]$Order.Threshold) > as.numeric(ordersubset[ii,]$Order.Price) ){
- neworder<-addOrder(portfolio=portfolio,
+ # check if we need to move the stop
+ mvstop=FALSE
+ if(as.numeric(ordersubset[ii,]$Order.Qty)>0){ # positive quantity 'buy'
+ if( as.numeric(last(getPrice(x=mktdata[timestamp],prefer=prefer)))+as.numeric(ordersubset[ii,]$Order.Threshold) < as.numeric(ordersubset[ii,]$Order.Price) ) mvstop=TRUE
+ } else { # negative quantity 'sell'
+ if( as.numeric(last(getPrice(x=mktdata[timestamp],prefer=prefer)))+as.numeric(ordersubset[ii,]$Order.Threshold) > as.numeric(ordersubset[ii,]$Order.Price) ) mvstop=TRUE
+
+ }
+ if( isTRUE(mvstop) ){
+ neworder<-addOrder(portfolio=portfolio,
symbol=symbol,
timestamp=timestamp,
qty=as.numeric(ordersubset[ii,]$Order.Qty),
- price=last(getPrice(mktdata[timestamp],prefer=prefer))+as.numeric(ordersubset[ii,]$Order.Threshold),
+ price=as.numeric(getPrice(mktdata[timestamp],prefer=prefer)),
ordertype=ordersubset[ii,]$Order.Type,
side=ordersubset[ii,]$Order.Side,
- threshold=ordersubset[ii,]$Order.Threshold,
+ threshold=as.numeric(ordersubset[ii,]$Order.Threshold),
status="open",
replace=FALSE, return=TRUE,
,...=..., TxnFees=ordersubset[ii,]$Txn.Fees)
@@ -540,10 +576,31 @@
}
)
if(!is.null(txnprice) & !isTRUE(is.na(txnprice))){
- addTxn(Portfolio=portfolio, Symbol=symbol, TxnDate=txntime, TxnQty=as.numeric(ordersubset[ii,]$Order.Qty), TxnPrice=txnprice , ...=..., TxnFees=txnfees)
- ordersubset[ii,]$Order.Status<-'closed'
- ordersubset[ii,]$Order.StatusTime<-as.character(timestamp)
- }
+ #make sure we don't cross through zero
+ pos<-getPosQty(portfolio,symbol,timestamp)
+ side=ordersubset[ii,]$Order.Side
+ TxnQty=as.numeric(ordersubset[ii,]$Order.Qty)
+ if(side=="long"){
+ remqty<-TxnQty+pos
+ if (remqty<0){
+ newqty<-TxnQty-remqty
+ warning("TxnQTy of",TxnQty,"would cross through zero, reducing qty to",newqty)
+ TxnQty<-newqty
+ }
+ } else {
+ if (remqty>0){
+ newqty<-TxnQty-remqty
+ warning("TxnQTy of",TxnQty,"would cross through zero, reducing qty to",newqty)
+ TxnQty<-newqty
+ }
+ }
+ if(TxnQty!=0){
+ #now add the transaction
+ addTxn(Portfolio=portfolio, Symbol=symbol, TxnDate=txntime, TxnQty=TxnQty, TxnPrice=txnprice , ...=..., TxnFees=txnfees)
+ ordersubset[ii,]$Order.Status<-'closed'
+ ordersubset[ii,]$Order.StatusTime<-as.character(timestamp)
+ }
+ }
} #end loop over open orders
if(!is.null(neworders)) ordersubset=rbind(ordersubset,neworders)
Modified: pkg/quantstrat/R/rules.R
===================================================================
--- pkg/quantstrat/R/rules.R 2010-10-14 19:26:32 UTC (rev 422)
+++ pkg/quantstrat/R/rules.R 2010-10-18 21:10:17 UTC (rev 423)
@@ -28,6 +28,13 @@
#' orders to rebalance the portfolio, and would hold other strategy processing
#' until the rebalancing period was over.
#'
+#' The \code{timespan} parameter will limit rule execution by time of day using
+#' time based subsetting. See ISO-8601 specification and xts documentation for
+#' more details. Note that these are only applicable to intra-day execution,
+#' and will remain that way barring patches (tests and documentatioon) from
+#' interested parties. The subsetting may (will likely) work with normal
+#' ISO/xts subset ranges, but consider it unsupported.
+#'
#' We anticipate that rules will be the portion of a strategy most likely to
#' not have suitable template code included with this package, as every strategy
#' and environment are different, especially in this respect.
@@ -44,9 +51,10 @@
#' @param enabled TRUE/FALSE whether the rule is enabled for use in applying the strategy, default TRUE
#' @param indexnum if you are updating a specific rule, the index number in the $rules[type] list to update
#' @param path.dep TRUE/FALSE whether rule is path dependent, default TRUE, see Details
+#' @param timespan an xts/ISO-8601 style \emph{time} subset, like "T08:00/T15:00", see Details
#' @param store TRUE/FALSE whether to store the strategy in the .strategy environment, or return it. default FALSE
#' @export
-add.rule <- function(strategy, name, arguments, parameters=NULL, label=NULL, type=c(NULL,"risk","order","rebalance","exit","enter"), ..., enabled=TRUE, indexnum=NULL, path.dep=TRUE, store=FALSE) {
+add.rule <- function(strategy, name, arguments, parameters=NULL, label=NULL, type=c(NULL,"risk","order","rebalance","exit","enter"), ..., enabled=TRUE, indexnum=NULL, path.dep=TRUE, timespan=NULL, store=FALSE) {
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")
@@ -60,6 +68,7 @@
tmp_rule$label<-label
tmp_rule$arguments<-arguments
if(!is.null(parameters)) tmp_rule$parameters = parameters
+ if(!is.null(timespan)) tmp_rule$timespan = timespan
tmp_rule$path.dep<-path.dep
if(length(list(...))) tmp_rule<-c(tmp_rule,list(...))
@@ -127,7 +136,10 @@
if(!isTRUE(rule$enabled)) next()
- # see 'S Programming p. 67 for this matching
+ # check to see if we should run in this timepan
+ if(!is.null(rule$timespan) & nrow(mktdata[rule$timespan]==0)) next()
+
+ # see 'S Programming' p. 67 for this matching
fun<-match.fun(rule$name)
nargs <-list(...)
Modified: pkg/quantstrat/R/traderules.R
===================================================================
--- pkg/quantstrat/R/traderules.R 2010-10-14 19:26:32 UTC (rev 422)
+++ pkg/quantstrat/R/traderules.R 2010-10-18 21:10:17 UTC (rev 423)
@@ -275,10 +275,11 @@
#sell long
if(orderqty<0 & orderside=='long'){
- if ((orderqty+pos)>=0) {
+ if(ruletype=='risk') return(orderqty)
+ if ((orderqty+pos)>=0) {
return(orderqty)
} else {
- orderqty<-pos #flatten position, don't cross through zero
+ orderqty<-pos #flatten position, don't cross through zero
#TODO add code to break into two orders?
return(orderqty)
}
@@ -303,7 +304,8 @@
#buy cover short
if(orderqty>0 & orderside=='short'){
- if ((orderqty+pos)<=0) {
+ if(ruletype=='risk') return(orderqty)
+ if ((orderqty+pos)<=0) {
return(orderqty)
} else {
orderqty<-pos #flatten position, don't cross through zero
Modified: pkg/quantstrat/man/add.rule.Rd
===================================================================
--- pkg/quantstrat/man/add.rule.Rd 2010-10-14 19:26:32 UTC (rev 422)
+++ pkg/quantstrat/man/add.rule.Rd 2010-10-18 21:10:17 UTC (rev 423)
@@ -3,7 +3,7 @@
\title{add a rule to a strategy...}
\usage{add.rule(strategy, name, arguments, parameters, label, type=c(NULL,
"risk", "order", "rebalance", "exit", "enter"), ..., enabled=TRUE,
- indexnum, path.dep=TRUE, store=FALSE)}
+ indexnum, path.dep=TRUE, timespan, store=FALSE)}
\description{add a rule to a strategy}
\details{Rules will be processed in a very particular manner, so it bears going over.
@@ -33,6 +33,13 @@
orders to rebalance the portfolio, and would hold other strategy processing
until the rebalancing period was over.
+The \code{timespan} parameter will limit rule execution by time of day using
+time based subsetting. See ISO-8601 specification and xts documentation for
+more details. Note that these are only applicable to intra-day execution,
+and will remain that way barring patches (tests and documentatioon) from
+interested parties. The subsetting may (will likely) work with normal
+ISO/xts subset ranges, but consider it unsupported.
+
We anticipate that rules will be the portion of a strategy most likely to
not have suitable template code included with this package, as every strategy
and environment are different, especially in this respect.
@@ -48,4 +55,5 @@
\item{enabled}{TRUE/FALSE whether the rule is enabled for use in applying the strategy, default TRUE}
\item{indexnum}{if you are updating a specific rule, the index number in the $rules[type] list to update}
\item{path.dep}{TRUE/FALSE whether rule is path dependent, default TRUE, see Details}
+\item{timespan}{an xts/ISO-8601 style \emph{time} subset, like "T08:00/T15:00", see Details}
\item{store}{TRUE/FALSE whether to store the strategy in the .strategy environment, or return it. default FALSE}}
Modified: pkg/quantstrat/man/getOrders.Rd
===================================================================
--- pkg/quantstrat/man/getOrders.Rd 2010-10-14 19:26:32 UTC (rev 422)
+++ pkg/quantstrat/man/getOrders.Rd 2010-10-18 21:10:17 UTC (rev 423)
@@ -13,6 +13,6 @@
\item{symbol}{identfier of the instrument to find orders for. The name of any associated price objects (xts prices, usually OHLC) should match these}
\item{status}{one of "open", "closed", "canceled", or "replaced", default "open"}
\item{timespan}{xts-style character timespan to be the period to find orders of the given status and ordertype}
-\item{ordertype}{one of NULL, "market","limit","stoplimit", or "stoptrailing" default NULL}
+\item{ordertype}{one of NULL, "market","limit","stoplimit", "stoptrailing", or "iceberg" default NULL}
\item{side}{one of NULL, "long" or "short", default NULL}
\item{which.i}{if TRUE, return the row index numbers rather than the order rows matching the criteria, default FALSE}}
Modified: pkg/quantstrat/man/ruleOrderProc.Rd
===================================================================
--- pkg/quantstrat/man/ruleOrderProc.Rd 2010-10-14 19:26:32 UTC (rev 422)
+++ pkg/quantstrat/man/ruleOrderProc.Rd 2010-10-18 21:10:17 UTC (rev 423)
@@ -3,10 +3,22 @@
\title{process open orders at time t, generating transactions or new orders...}
\usage{ruleOrderProc(portfolio, symbol, mktdata, timespan, ordertype, ...,
slippageFUN)}
-\description{process open orders at time t, generating transactions or new orders}
+\description{process open orders at time \emph{t}, generating transactions or new orders}
\details{This function is meant to be sufficient for backtesting most strategies,
but would need to be replaced for production use. It provides the interface
-for taking the order book and determining when orders become trades.}
+for taking the order book and determining when orders become trades.
+
+For the purposes of backtesting, and compatibility with the trade accounting in
+\code{blotter}, this function will not allow a transaction to cross your current
+position through zero. The accounting rules for realizing gains in such cases
+are more complicated than we wish to support. Also, many brokers will break, revise,
+or split such transactions for the same reason. If you wish to do a "stop and reverse"
+system, first stop (flatten), and then reverse (initiate a new position).
+
+This function would need to be revised or replaced for connection to a live trading infrastructure.
+
+We would like to model slippage here via \code{slippageFUN}. Code contributions, suggestions,
+and requests appreciated.}
\arguments{\item{portfolio}{text name of the portfolio to associate the order book with}
\item{symbol}{identfier of the instrument to find orders for. The name of any associated price objects (xts prices, usually OHLC) should match these}
\item{mktdata}{an xts object containing market data. depending on indicators, may need to be in OHLCV or BBO formats, default NULL}
Modified: pkg/quantstrat/man/ruleSignal.Rd
===================================================================
--- pkg/quantstrat/man/ruleSignal.Rd 2010-10-14 19:26:32 UTC (rev 422)
+++ pkg/quantstrat/man/ruleSignal.Rd 2010-10-18 21:10:17 UTC (rev 423)
@@ -25,7 +25,7 @@
\item{sigcol}{column name to check for signal}
\item{sigval}{signal value to match against}
\item{orderqty}{numeric quantity of the desired order, or 'all', modified by osFUN}
-\item{ordertype}{one of "market","limit","stoplimit", or "stoptrailing"}
+\item{ordertype}{one of "market","limit","stoplimit", "stoptrailing", or "iceberg"}
\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}
More information about the Blotter-commits
mailing list