Profit/loss and position valuations over time
In this note we'll look into how to compute the profit/loss and the value of a position over time. Suppose we traded in two stocks, AMZN and MSFT.
library("PMwR") library("zoo") j <- journal(amount = c(10, -10, 20, 5), instrument = c("MSFT", "MSFT", "MSFT", "AMZN"), price = c(178, 178.5, 180.7, 2315), timestamp = as.Date(c("2020-05-04", "2020-05-04", "2020-05-05", "2020-05-05"))) j
instrument timestamp amount price 1 MSFT 2020-05-04 10 178.0 2 MSFT 2020-05-04 -10 178.5 3 MSFT 2020-05-05 20 180.7 4 AMZN 2020-05-05 5 2315.0 4 transactions
We want to look at the profit/loss and
position value for the week from May, 4th to May, 8th. We
store these dates in a variable timestamp.
timestamp <- seq(from = as.Date("2020-05-04"), to = as.Date("2020-05-08"), by = "1 day") timestamp
[1] "2020-05-04" "2020-05-05" "2020-05-06" "2020-05-07" "2020-05-08"
We'll need prices at which to value open position:
we prepare them as zoo series.
MSFT <- structure(c(178.84, 180.76, 182.54, 183.6, 184.68), .Dim = c(5L, 1L), .Dimnames = list(NULL, "us5949181045"), index = structure( c(18386, 18387, 18388, 18389, 18390), class = "Date"), class = "zoo") AMZN <- structure(c(2286.04, 2315.99, 2317.8, 2351.26, 2367.61, 2379.61), .Dim = c(6L, 1L), .Dimnames = list(NULL, "us0231351067"), index = structure( c(18383, 18386, 18387, 18388, 18389, 18390), class = "Date"), class = "zoo") AMZN <- AMZN[-4L] MSFT AMZN
us5949181045
2020-05-04 178.84
2020-05-05 180.76
2020-05-06 182.54
2020-05-07 183.60
2020-05-08 184.68
us0231351067
2020-05-01 2286.04
2020-05-04 2315.99
2020-05-05 2317.80
2020-05-07 2367.61
2020-05-08 2379.61
Note that the two series are, on purpose, not synchronous, and that AMZN is missing one price.
Next, we create a matrix of prices.
P <- pricetable(AMZN, MSFT, instrument = c("AMZN", "MSFT")) P <- P[timestamp, c("MSFT", "AMZN"), missing = "previous"] P
MSFT AMZN
2020-05-04 178.84 2315.99
2020-05-05 180.76 2317.80
2020-05-06 182.54 2317.80
2020-05-07 183.60 2367.61
2020-05-08 184.68 2379.61
Note that subsetting a pricetable results in a numeric matrix.
str(P)
num [1:5, 1:2] 179 181 183 184 185 ... - attr(*, "dimnames")=List of 2 ..$ : chr [1:5] "2020-05-04" "2020-05-05" "2020-05-06" "2020-05-07" ... ..$ : chr [1:2] "MSFT" "AMZN"
Let us first look at profit/loss first: we only need to
provide the journal j of the trades as created above,
the timestamps at which to compute profit/loss, and the
prices for valuing open positions.
pl(j, along.timestamp = timestamp, vprice = P)
AMZN timestamp 2020-05-04 2020-05-05 2020-05-06 2020-05-07 2020-05-08 P/L total 0.00 14.00 14.00 263.05 323.05 __ realised 0 0 0 0 0 __ unrealised 0.00 14.00 14.00 263.05 323.05 average buy 2315 average sell NaN cum. volume 0 5 5 5 5 MSFT timestamp 2020-05-04 2020-05-05 2020-05-06 2020-05-07 2020-05-08 P/L total 5.0 6.2 41.8 63.0 84.6 __ realised 5 5 5 5 5 __ unrealised 0.0 1.2 36.8 58.0 79.6 average buy 179.8 average sell 178.5 cum. volume 20 40 40 40 40 ‘P/L total’ is in units of instrument; ‘volume’ is sum of /absolute/ amounts.
We can also look at the position values over time.
Note that the call to valuation is similar to that of
pl: we pass the position, computed at the timestamps
defined above; the prices for valuing the open
positions, and we also set the argument use.names to
TRUE. This ensures that valuation will look at the
column names of P and associate the prices there with
the instruments specified in the journal j.
valuation(position(j, when = timestamp), vprice = P, use.names = TRUE)
AMZN MSFT
2020-05-04 0.00 0.0
2020-05-05 11589.00 3615.2
2020-05-06 11589.00 3650.8
2020-05-07 11838.05 3672.0
2020-05-08 11898.05 3693.6
attr(,"position")
AMZN MSFT
2020-05-04 0 0
2020-05-05 5 20
2020-05-06 5 20
2020-05-07 5 20
2020-05-08 5 20
Remarks
In function valuation, the argument vprice had been
called price.table prior to package version 0.15, but
has been renamed because since that version it very
much resembles vprice in function pl. There are
two differences, explained below. For clarity, I'll
write pl/vprice and valuation/vprice, to
differentiate between the two arguments.
pl/vpricemust always be ordered in time. That is, if you want to compute profit/loss over time, the timestamps must be in ascending order. If the timestamps passed viaalong.timestampare not ordered, they will get sorted in time, andpl/vpricewill get appropriately sorted, too.For valuation, by contrast, prices need not be in temporal order; in fact,
valuationdoes not care at all about timestamps. That should be intuitive: profit/loss is essentially a difference between values, so the order of the values matters, which is not the case for positions.There is an argument
use.namesinvaluation, which controls whether (column) names ofvaluation/vpriceare interpreted as instruments and hence elements ofvaluation/vpriceare matched accordingly. Default foruse.namesisFALSE, so such information is not used and the elements invaluation/vpricemust directly correspond to the elements in thepositionmatrix.Keeping the default at
FALSEmeans that the behaviour prior to package version 0.15 is kept (though this may change in a future release). This default is different frompl: names ofpl/vpriceare always interpreted as instruments. The reason is simple: when computingplfrom a journal, the user typically does not control the order of instruments in the resultingpl. Instruments get sorted by the function, but the user is not expected to anticipate this ordering.This is different when valuing a position: a position is essentially a matrix in which each row corresponds to a valuation timestamp, and each column corresponds to an instrument. So the user knows the structure of the position and can prepare
valuation/vpriceaccordingly.