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/vprice
must 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.timestamp
are not ordered, they will get sorted in time, andpl/vprice
will get appropriately sorted, too.For valuation, by contrast, prices need not be in temporal order; in fact,
valuation
does 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.names
invaluation
, which controls whether (column) names ofvaluation/vprice
are interpreted as instruments and hence elements ofvaluation/vprice
are matched accordingly. Default foruse.names
isFALSE
, so such information is not used and the elements invaluation/vprice
must directly correspond to the elements in theposition
matrix.Keeping the default at
FALSE
means 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/vprice
are always interpreted as instruments. The reason is simple: when computingpl
from 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/vprice
accordingly.