Skip to content

Commit

Permalink
Ensure transactions are added in order
Browse files Browse the repository at this point in the history
The portfolio accounting could be wrong if transactions are added out
of order. Given that this is a strict requirement for correctness, add
a paragraph to the Note section of ?addTxn, and throw an error if
addTxn or addTxns are called with a date before the last date in the
portfolio.
  • Loading branch information
joshuaulrich committed Apr 24, 2017
1 parent c5b365f commit 24b5786
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 1 deletion.
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ importFrom(graphics,points)
importFrom(graphics,text)
importFrom(methods,hasArg)
importFrom(stats,complete.cases)
importFrom(stats,end)
importFrom(stats,lag)
importFrom(stats,median)
importFrom(stats,na.omit)
Expand Down
20 changes: 20 additions & 0 deletions R/addTxn.R
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@
#' @param eps value to add to force unique indices
#' @param TxnData An xts object containing all required txn fields (for addTxns)
#' @note
#' Transactions must be added in date-time order. All per-transaction
#' accounting is calculated when appending transactions to the 'txn' table.
#' Adding transactions out of order will cause incorrect accounting, and is
#' therefore not allowed. Both \code{addTxn} and \code{addTxns} will throw an
#' error if called with a \code{TxnDate} (or an xts index value) before the
#' last date in the 'txn' table.
#'
#' The addTxn function will eventually also handle other transaction types,
#' such as adjustments for corporate actions or expire/assign for options.
#' See \code{\link{addDiv}}
Expand All @@ -75,6 +82,12 @@ addTxn <- function(Portfolio, Symbol, TxnDate, TxnQty, TxnPrice, ..., TxnFees=0,
TxnDate<-as.POSIXct(TxnDate)
}

lastTxnDate <- end(Portfolio$symbols[[Symbol]]$txn)
if (TxnDate < lastTxnDate) {
stop("Transactions must be added in order. TxnDate (", TxnDate, ") is ",
"before last transaction in portfolio (", lastTxnDate, ") for ", Symbol)
}

# Coerce the transaction fees to a function if a string was supplied
if(is.character(TxnFees)) {
TF <- try(match.fun(TxnFees), silent=TRUE)
Expand Down Expand Up @@ -172,6 +185,13 @@ addTxns<- function(Portfolio, Symbol, TxnData , verbose=FALSE, ..., ConMult=NULL
addPortfInstr(Portfolio=pname, symbols=Symbol)
Portfolio <- .getPortfolio(pname)

TxnDate <- start(TxnData)
lastTxnDate <- end(Portfolio$symbols[[Symbol]]$txn)
if (TxnDate < lastTxnDate) {
stop("Transactions must be added in order. First TxnDate (", TxnDate, ") is ",
"before last transaction in portfolio (", lastTxnDate, ") for ", Symbol)
}

if(is.null(ConMult) | !hasArg(ConMult)){
tmp_instr<-try(getInstrument(Symbol), silent=TRUE)
if(inherits(tmp_instr,"try-error") | !is.instrument(tmp_instr)){
Expand Down
2 changes: 1 addition & 1 deletion R/blotter-package.R
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,6 @@
#'@importFrom graphics abline grid plot points
#'@importFrom grDevices dev.new
#'@importFrom methods hasArg
#'@importFrom stats complete.cases lag median na.omit quantile qnorm sd start
#'@importFrom stats complete.cases lag median na.omit quantile qnorm sd start end
#'@importFrom utils View head tail
NULL
7 changes: 7 additions & 0 deletions man/addTxn.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 42 additions & 0 deletions tests/unitTests/runitAddTxn.R
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,27 @@ test.addTxn <- function() {
# summary <- calcPortfSummary(portfolio)
}

test.addTxn_TxnDate_out_of_order_errors <- function() {
on.exit({
# remove objects created by unit tests
try(rm_currencies("USD"))
try(rm_stocks("A"))
try(rm(list=paste0("portfolio.",p), pos=.blotter))
})

currency("USD")
stock("A", currency = "USD")
initPortf("TxnDateOrder", "A")

# Initialize a portfolio object 'p'
# Creating portfolio:
p <- initPortf("runitAddTxn", symbols="A")

# Trades must be made in date order.
addTxn(p, "A", "2007-01-04", -50, 97.1)
checkException(addTxn(p, "A", "2007-01-03", 50, 96.5))
}

test.addTxns <- function() {
on.exit({
# remove objects created by unit tests
Expand Down Expand Up @@ -76,3 +97,24 @@ test.addTxns <- function() {
checkIdentical(t1, t2)
}

test.addTxns_TxnDate_out_of_order_errors <- function() {
on.exit({
# remove objects created by unit tests
try(rm_currencies("USD"), silent=TRUE)
try(rm_stocks("A"), silent=TRUE)
try(rm(list=c("account.amzn_acct","portfolio.amzn_txn","portfolio.amzn_txns"), pos=.blotter), silent=TRUE)
})

currency("USD")
stock("AMZN", currency="USD", multiplier=1)

# Initialize the account/portfolios
initAcct("amzn_acct", portfolios="amzn_txns", initEq=10000)
initPortf("amzn_txns", symbols="AMZN")

# Add the transactions to the portfolios
addTxns("amzn_txns", "AMZN", TxnData=amzn.trades)
# Trades must be made in date order.
checkException(addTxns("amzn_txns", "AMZN", TxnData=amzn.trades[1:2,]))
}

0 comments on commit 24b5786

Please sign in to comment.