SCM

SCM Repository

[blotter] View of /pkg/blotter/R/addTxn.R
ViewVC logotype

View of /pkg/blotter/R/addTxn.R

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1081 - (download) (annotate)
Thu Jun 28 09:02:35 2012 UTC (9 years, 7 months ago) by opentrades
File size: 13514 byte(s)
* removed :: in front of verbose date print in addTxn() 

#' Add transactions to a portfolio.
#' 
#' When a trade or adjustment is made to the Portfolio, the addTxn function 
#' calculates the value and average cost of the transaction,  the change in 
#' position, the resulting positions average cost, and any realized profit 
#' or loss (net of fees) from the transaction. Then it stores the transaction 
#' and calculations in the Portfolio object.
#'
#' Fees are indicated as negative values and will be subtracted from the 
#' transaction value. TxnFees can either be a fixed amount, or a function 
#' in which case the function is evaluated to 
#' determine the fee amount.
#' The \code{pennyPerShare} function provides a simple example of a transaction cost
#' function.
#' 
#' Transactions which would cross your position through zero will be split 
#' into two transactions, one to flatten the position, and another to initiate 
#' a new position on the opposite side of the market.  The new (split) 
#' transaction will have it's timestamp inclremented by eps to preserve ordering. 
#' This transaction splitting vastly simplifies realized P&L calculations elsewhere in the code.
#' 
#' @param Portfolio  A portfolio name that points to a portfolio object structured with \code{initPortf()}
#' @param Symbol An instrument identifier for a symbol included in the portfolio, e.g., "IBM"
#' @param TxnDate  Transaction date as ISO 8601, e.g., '2008-09-01' or '2010-01-05 09:54:23.12345'
#' @param TxnQty Total units (such as shares or contracts) transacted.  Positive values indicate a 'buy'; negative values indicate a 'sell'
#' @param TxnPrice  Price at which the transaction was done
#' @param \dots Any other passthrough parameters
#' @param TxnFees Fees associated with the transaction, e.g. commissions., See Details
#' @param ConMult Contract/instrument multiplier for the Symbol if it is not dened in an instrument specication
#' @param verbose If TRUE (default) the function prints the elements of the transaction in a line to the screen, e.g., "2007-01-08 IBM 50 @@ 77.6". Suppress using FALSE.
#' @param eps value to add to force unique indices
#' @note 
#' The addTxn function will eventually also handle other transaction types, 
#' such as adjustments for corporate actions or expire/assign for options. 

#' @seealso \code{\link{addTxns}}, \code{\link{pennyPerShare}}, \code{\link{initPortf}}
#' @author Peter Carl
#' @export
addTxn <- function(Portfolio, Symbol, TxnDate, TxnQty, TxnPrice, ..., TxnFees=0, ConMult=NULL, verbose=TRUE, eps=1e-06)
{ # @author Peter Carl

    pname <- Portfolio
    PrevPosQty = getPosQty(pname, Symbol, TxnDate)
    
    if(!is.timeBased(TxnDate) ){
        TxnDate<-as.POSIXct(TxnDate)
    }
    
    # split transactions that would cross through zero
    if(PrevPosQty!=0 && sign(PrevPosQty+TxnQty)!=sign(PrevPosQty) && PrevPosQty!=-TxnQty){
        addTxn(Portfolio=pname, Symbol=Symbol, TxnDate=TxnDate, TxnQty=-PrevPosQty, TxnPrice=TxnPrice, ..., 
                TxnFees = TxnFees, ConMult = ConMult, verbose = verbose, eps=eps)
        TxnDate=TxnDate+2*eps #transactions need unique timestamps, so increment a bit
        TxnQty=TxnQty+PrevPosQty
        PrevPosQty=0
    }
    
    Portfolio<-get(paste("portfolio",pname,sep='.'),envir=.blotter)

    if(is.null(ConMult) | !hasArg(ConMult)){
        tmp_instr<-try(getInstrument(Symbol))
        if(inherits(tmp_instr,"try-error") | !is.instrument(tmp_instr)){
            warning(paste("Instrument",Symbol," not found, using contract multiplier of 1"))
            ConMult<-1
        } else {
            ConMult<-tmp_instr$multiplier
        }
    }

	#If there is no table for the symbol then create a new one
	if (is.null(Portfolio$symbols[[Symbol]])){ 
		addPortfInstr(Portfolio=pname, symbols=Symbol)
		Portfolio<-get(paste("portfolio",pname,sep='.'),envir=.blotter)
	}


    # FUNCTION
    # Compute transaction fees if a function was supplied
    if (is.function(TxnFees)) txnfees <- TxnFees(TxnQty, TxnPrice) else txnfees<- as.numeric(TxnFees)
    if(is.null(txnfees) | is.na(txnfees)) txnfees = 0
    if(txnfees>0) warning('Positive Transaction Fees should only be used in the case of broker/exchange rebates for TxnFees ',TxnFees,'. See Documentation.')
    
    # Calculate the value and average cost of the transaction
    TxnValue = .calcTxnValue(TxnQty, TxnPrice, 0, ConMult) # Gross of Fees
    TxnAvgCost = .calcTxnAvgCost(TxnValue, TxnQty, ConMult)

    # Calculate the change in position
    PosQty = PrevPosQty + TxnQty


    # Calculate the resulting position's average cost
    PrevPosAvgCost = .getPosAvgCost(pname, Symbol, TxnDate)
    PosAvgCost = .calcPosAvgCost(PrevPosQty, PrevPosAvgCost, TxnValue, PosQty, ConMult)

	
    # Calculate any realized profit or loss (net of fees) from the transaction
    GrossTxnRealizedPL = TxnQty * ConMult * (PrevPosAvgCost - TxnAvgCost)

	# if the previous position is zero, RealizedPL = 0
	# if previous position is positive and position is larger, RealizedPL =0
	# if previous position is negative and position is smaller, RealizedPL =0
	if(abs(PrevPosQty) < abs(PosQty) | (PrevPosQty = 0))
		GrossTxnRealizedPL = 0
	
	NetTxnRealizedPL = GrossTxnRealizedPL + txnfees

    # Store the transaction and calculations
    NewTxn = xts(t(c(TxnQty, TxnPrice, TxnValue, TxnAvgCost, PosQty, PosAvgCost, GrossTxnRealizedPL, txnfees, NetTxnRealizedPL, ConMult)), order.by=TxnDate)
    #colnames(NewTxns) = c('Txn.Qty', 'Txn.Price', 'Txn.Value', 'Txn.Avg.Cost', 'Pos.Qty', 'Pos.Avg.Cost', 'Gross.Txn.Realized.PL', 'Txn.Fees', 'Net.Txn.Realized.PL', 'Con.Mult')
    Portfolio$symbols[[Symbol]]$txn<-rbind(Portfolio$symbols[[Symbol]]$txn, NewTxn)

    if(verbose)
#        print(paste(TxnDate, Symbol, TxnQty, "@",TxnPrice, sep=" "))
        print(paste(format(TxnDate, "%Y-%m-%d %H:%M:%S"), Symbol, TxnQty, "@",TxnPrice, sep=" "))
        #print(Portfolio$symbols[[Symbol]]$txn)
    
    assign(paste("portfolio",pname,sep='.'),Portfolio,envir=.blotter)
}

#' Example TxnFee cost function
#' @param TxnQty total units (such as shares or contracts) transacted.  Positive values indicate a 'buy'; negative values indicate a 'sell'
#' This is an example intended to demonstrate how a cost function could be used in place of a flat numeric fee.
#' @export
pennyPerShare <- function(TxnQty) {
    return(abs(TxnQty) * -0.01)
}

#' Add multiple transactions to a portfolio
#' @param Portfolio  A portfolio name that points to a portfolio object structured with \code{\link{initPortf}}
#' @param Symbol An instrument identifier for a symbol included in the portfolio, e.g., "IBM"
#' @param TxnData  An xts object containing all required txn fields
#' @param \dots Any other passthrough parameters
#' @param verbose If TRUE (default) the function prints the elements of the transaction in a line to the screen, e.g., "2007-01-08 IBM 50 @@ 77.6". Suppress using FALSE.
#' @param ConMult Contract or instrument multiplier for the Symbol if it is not dened in an instrument specication
#' @seealso \code{\link{addTxn}}, \code{\link{initPortf}}
#' @note
#' TODO figure out if we can fully vectorize this function to make it faster
addTxns<- function(Portfolio, Symbol, TxnData , verbose=TRUE, ..., ConMult=NULL)
{
    pname<-Portfolio
    Portfolio<-get(paste("portfolio",pname,sep='.'),envir=.blotter)

    if(is.null(ConMult) | !hasArg(ConMult)){
        tmp_instr<-try(getInstrument(Symbol))
        if(inherits(tmp_instr,"try-error") | !is.instrument(tmp_instr)){
            warning(paste("Instrument",Symbol," not found, using contract multiplier of 1"))
            ConMult<-1
        } else {
            ConMult<-tmp_instr$multiplier
        }  
    }

    for (row in 1:nrow(TxnData)) {
        if(row==1) {
            PrevPosQty     <- getPosQty(pname, Symbol, index(TxnData[row,]))
            PrevPosAvgCost <- .getPosAvgCost(pname, Symbol, index(TxnData[row,]))
        }
        #TODO create vectorized versions of all these functions so we don't have to loop
        TxnQty         <- as.numeric(TxnData[row,'Quantity'])
        TxnPrice       <- as.numeric(TxnData[row,'Price'])
        TxnFee         <- 0 #TODO FIXME support transaction fees in addTxns
        #TxnFee         <- ifelse( is.function(TxnFees), TxnFees(TxnQty, TxnPrice), TxnFees)
        TxnValue       <- .calcTxnValue(TxnQty, TxnPrice, TxnFee, ConMult)
        TxnAvgCost     <- .calcTxnAvgCost(TxnValue, TxnQty, ConMult)
        #PrevPosQty     <- getPosQty(pname, Symbol, index(TxnData[row,]))
        PosQty         <- PrevPosQty+TxnQty
        PosAvgCost     <- .calcPosAvgCost(PrevPosQty, PrevPosAvgCost, 0, PosQty, ConMult) # lag this over the data?
		GrossTxnRealizedPL = TxnQty * ConMult * (PrevPosAvgCost - TxnAvgCost)
		NetTxnRealizedPL = GrossTxnRealizedPL - TxnFee
        PrevPosQty     <- PosQty
        PrevPosAvgCost <- PosAvgCost
        
        NewTxn = xts(t(c(TxnQty, 
                         TxnPrice, 
                         TxnValue, 
                         TxnAvgCost, 
                         PosQty, 
                         PosAvgCost, 
                         GrossTxnRealizedPL,
                         TxnFee,
                         NetTxnRealizedPL,
                         ConMult)),
                         order.by=index(TxnData[row,]))

        if(row==1){
            NewTxns <- NewTxn
            colnames(NewTxns) = c('Txn.Qty', 'Txn.Price', 'Txn.Value', 'Txn.Avg.Cost', 'Pos.Qty', 'Pos.Avg.Cost', 'Gross.Txn.Realized.PL', 'Txn.Fees', 'Net.Txn.Realized.PL', 'Con.Mult')
        } else {
            NewTxns<-rbind(NewTxns, NewTxn)
        }
    }
    Portfolio$symbols[[Symbol]]$txn<-rbind(Portfolio$symbols[[Symbol]]$txn,NewTxns) 

    if(verbose) print(NewTxns)
    
    assign(paste("portfolio",pname,sep='.'),Portfolio,envir=.blotter)    
}

#' Add cash dividend transactions to a portfolio.
#' 
#' Adding a cash dividend does not affect position quantity, like a split would.
#' 
#' @param Portfolio  A portfolio name that points to a portfolio object structured with \code{\link{initPortf}}.
#' @param Symbol An instrument identifier for a symbol included in the portfolio, e.g., IBM.
#' @param TxnDate  Transaction date as ISO 8601, e.g., '2008-09-01' or '2010-01-05 09:54:23.12345'.
#' @param DivPerShare The amount of the cash dividend paid per share or per unit quantity.
#' @param \dots Any other passthrough parameters.
#' @param TxnFees Fees associated with the transaction, e.g. commissions. See Details.
#' @param verbose If TRUE (default) the function prints the elements of the transaction in a line to the screen, e.g., "2007-01-08 IBM 50 @@ 77.6". Suppress using FALSE.
#' @param ConMult Contract or instrument multiplier for the Symbol if it is not dened in an instrument specication.
#' @export
#' @note
#' # TODO add TxnTypes to $txn table
#' 
#' # TODO add AsOfDate 
#' 
addDiv <- function(Portfolio, Symbol, TxnDate, DivPerShare, ..., TxnFees=0, ConMult=NULL, verbose=TRUE)
{ # @author Peter Carl
    pname<-Portfolio
    Portfolio<-get(paste("portfolio",pname,sep='.'),envir=.blotter)

    if(is.null(ConMult) | !hasArg(ConMult)){
        tmp_instr<-try(getInstrument(Symbol))
        if(inherits(tmp_instr,"try-error") | !is.instrument(tmp_instr)){
            warning(paste("Instrument",Symbol," not found, using contract multiplier of 1"))
            ConMult<-1
        } else {
            ConMult<-tmp_instr$multiplier
        }
    }

    # FUNCTION
    # 
    TxnQty = 0
    TxnPrice = 0
#     TxnType = "Dividend"
# TODO add TxnTypes to $txn table

    # Get the current position quantity
    PrevPosQty = getPosQty(pname, Symbol, TxnDate)
    PosQty = PrevPosQty # no change to position, but carry it forward
    # Calculate the value and average cost of the transaction
    # The -1 multiplier allows a positive DivPerShare value to create a
    # positive realized gain
    TxnValue = -1 * PrevPosQty * DivPerShare * ConMult # Calc total dividend paid
    TxnAvgCost = DivPerShare

    # No change to the the resulting position's average cost
    PrevPosAvgCost = .getPosAvgCost(pname, Symbol, TxnDate)
    PosAvgCost = PrevPosAvgCost # but carry it forward in $txn

    # Calculate any realized profit or loss (net of fees) from the transaction
    GrossTxnRealizedPL = PrevPosQty * DivPerShare * ConMult
    NetTxnRealizedPL = GrossTxnRealizedPL + TxnFees

    # Store the transaction and calculations
    NewTxn = xts(t(c(TxnQty, TxnPrice, TxnValue, TxnAvgCost, PosQty, PosAvgCost, GrossTxnRealizedPL, TxnFees, NetTxnRealizedPL, ConMult)), order.by=as.POSIXct(TxnDate))
    #colnames(NewTxns) = c('Txn.Qty', 'Txn.Price', 'Txn.Value', 'Txn.Avg.Cost', 'Pos.Qty', 'Pos.Avg.Cost', 'Gross.Txn.Realized.PL', 'Txn.Fees', 'Net.Txn.Realized.PL', 'Con.Mult')
    Portfolio$symbols[[Symbol]]$txn<-rbind(Portfolio$symbols[[Symbol]]$txn, NewTxn)

    if(verbose)
        print(paste(TxnDate, Symbol, "Dividend", DivPerShare, "on", PrevPosQty, "shares:", -TxnValue, sep=" "))
        #print(Portfolio$symbols[[Symbol]]$txn)

    assign(paste("portfolio",pname,sep='.'),Portfolio,envir=.blotter)
}
###############################################################################
# Blotter: Tools for transaction-oriented trading systems development
# for R (see http://r-project.org/)
# Copyright (c) 2008-2011 Peter Carl and Brian G. Peterson
#
# This library is distributed under the terms of the GNU Public License (GPL)
# for full details see the file COPYING
#
# $Id$
#
###############################################################################

root@r-forge.r-project.org
ViewVC Help
Powered by ViewVC 1.0.0  
Thanks to:
Vienna University of Economics and Business Powered By FusionForge