Type: | Package |
Title: | Portfolio Management with R |
Version: | 0.19-3 |
Date: | 2023-10-19 |
Maintainer: | Enrico Schumann <es@enricoschumann.net> |
Description: | Functions and examples for 'Portfolio Management with R': backtesting investment and trading strategies, computing profit/loss and returns, analysing trades, handling lists of transactions, reporting, and more. |
Imports: | NMOF, datetimeutils, fastmatch, orgutils, parallel, textutils, utils, zoo |
Suggests: | crayon, rbenchmark, tinytest |
Depends: | R (≥ 3.5) |
License: | GPL-3 |
LazyLoad: | yes |
LazyData: | yes |
ByteCompile: | yes |
URL: | http://enricoschumann.net/PMwR/ , https://git.sr.ht/~enricoschumann/PMwR , https://gitlab.com/enricoschumann/PMwR , https://github.com/enricoschumann/PMwR |
NeedsCompilation: | no |
Packaged: | 2023-10-19 06:53:41 UTC; es19 |
Author: | Enrico Schumann [aut, cre] |
Repository: | CRAN |
Date/Publication: | 2023-10-19 07:10:05 UTC |
Built: | R 4.5.0; ; 2024-04-30 09:11:26 UTC; unix |
Tools for the Management of Financial Portfolios
Description
Functions for the practical management of financial portfolios: backtesting investment and trading strategies, computing profit-and-loss and returns, analysing trades, reporting, and more.
Details
PMwR provides a small set of reliable, efficient and convenient tools that help in processing and analysing trade/portfolio data. The Manual provides all the details; it is available from http://enricoschumann.net/PMwR/. Examples and descriptions of new features are provided at http://enricoschumann.net/notes/PMwR/.
Author(s)
Enrico Schumann <es@enricoschumann.net>
References
Gilli, M., Maringer, D. and Schumann, E. (2019) Numerical Methods and Optimization in Finance. 2nd edition. Elsevier. doi:10.1016/C2017-0-01621-X
Schumann, E. (2023) Portfolio Management with R. http://enricoschumann.net/PMwR/
Schumann, E. (2022) Financial Optimisation with R (NMOF Manual). http://enricoschumann.net/NMOF.htm#NMOFmanual
Adjust Time Series for Dividends and Splits
Description
Adjust a time series for dividends and splits.
Usage
div_adjust(x, t, div, backward = TRUE, additive = FALSE)
split_adjust(x, t, ratio, backward = TRUE)
Arguments
x |
a numeric vector: the series to be adjusted |
t |
An integer vector, specifying the positions in
|
div |
A numeric vector, specifying the dividends (or
payments, cashflows). If necessary, recycled to
the length of |
ratio |
a numeric vector, specifying the split ratios. The ratio must be ‘American Style’: a 2-for-1 stock split, for example, corresponds to a ratio of 2. (In other countries, for instance Germany, a 2-for-1 stock split would be called a 1-for-1 split: you keep your shares and receive one new share per share that you own.) |
backward |
logical |
additive |
logical |
Details
With backward
set to TRUE
, which is the
default, the final prices in the unadjusted series
matches the final prices in the adjusted series.
Value
a numeric vector of length equal to length(x)
Author(s)
Enrico Schumann
References
Schumann, E. (2021) Portfolio Management with R. http://enricoschumann.net/PMwR/
Using div_adjust
for handling generic external cashflows:
http://enricoschumann.net/R/packages/PMwR/manual/PMwR.html#returns-with-external-cashflows
Examples
x <- c(9.777, 10.04, 9.207, 9.406)
div <- 0.7
t <- 3
div_adjust(x, t, div)
div_adjust(x, t, div, FALSE)
## assume there were three splits: adjust shares outstanding
shares <- c(100, 100, 200, 200, 1000, 1500)
t <- c(3, 5, 6)
ratio <- c(2, 5, 1.5)
### => invert ratio
split_adjust(shares, t, 1/ratio)
## [1] 1500 1500 1500 1500 1500 1500
split_adjust(shares, t, 1/ratio, backward = FALSE)
## [1] 100 100 100 100 100 100
Backtesting Investment Strategies
Description
Testing trading and investment strategies.
Usage
btest(prices, signal,
do.signal = TRUE, do.rebalance = TRUE,
print.info = NULL, b = 1, fraction = 1,
initial.position = 0, initial.cash = 0,
final.position = FALSE,
cashflow = NULL, tc = 0, ...,
add = FALSE, lag = 1, convert.weights = FALSE,
trade.at.open = TRUE, tol = 1e-5, tol.p = NA,
Globals = list(),
prices0 = NULL,
include.data = FALSE, include.timestamp = TRUE,
timestamp, instrument,
progressBar = FALSE,
variations, variations.settings, replications)
Arguments
prices |
For a single asset, a matrix of prices with four
columns: open, high, low and close. For The series in Prices must be ordered by time (though the timestamps need not be provided). |
signal |
A function that evaluates to the position in units
of the instruments suggested by the trading
rule. If |
do.signal |
Logical or numeric vector, a function that
evaluates to When a logical vector, its length must match the
number of observations in prices:
If |
do.rebalance |
Same as |
print.info |
A function, called at the very end of each period,
i.e. after rebalancing. Can also be |
cashflow |
A function or |
b |
burn-in (an integer). Defaults to 1. This may also
be a length-one timestamp of the same class as
|
fraction |
amount of rebalancing to be done: a scalar between 0 and 1 |
initial.position |
a numeric vector: initial portfolio in units of instruments. If supplied, this will also be the initial suggested position. |
initial.cash |
a numeric vector of length 1. Defaults to 0. |
final.position |
logical |
tc |
transaction costs as a fraction of turnover (e.g.,
0.001 means 0.1%). May also be a function that
evaluates to such a fraction. More-complex
computations may be specified with
argument |
... |
other named arguments. All functions (signal, do.signal, do.rebalance, print.info, cashflow) will have access to these arguments. See Details for reserved argument names. |
add |
Default is |
lag |
default is 1 |
convert.weights |
Default is |
trade.at.open |
A logical vector of length one; default is |
tol |
A numeric vector of length one: only rebalance if
the maximum absolute suggested change for at least
one position is greater than |
tol.p |
A numeric vector of length one: only rebalance
those positions for which the relative suggested
change is greater than |
Globals |
A |
prices0 |
A numeric vector (default is |
include.data |
logical. If |
include.timestamp |
logical. If |
timestamp |
a vector of timestamps, along prices (optional; mainly used for print method and journal) |
instrument |
character vector of instrument names (optional; mainly used for print method and journal) |
progressBar |
logical: display |
variations |
a list. See Details. |
variations.settings |
a list. See Details. |
replications |
an integer. If set, the function returns a list of
|
Details
The function provides infrastructure for testing
trading rules. Essentially, btest
does
accounting: keep track of transactions and positions,
value open positions, etc. The ingredients are price
time-series (single series or OHLC
bars), which need not be equally spaced; and several
functions that map these series and other pieces of
information into positions.
How btest
works
btest
runs a loop from b + 1
to
NROW(prices)
. In iteration t
, a
signal
can be computed based on information
from periods prior to t
. Trading then takes
place at the opening price of t
. For
slow-to-compute signals this is reasonable if there
is a time lag between close and open. For daily
prices, for instance, signals could be computed
overnight. For higher frequencies, such as every
minute, the signal function should be fast to
compute. Alternatively, it may be better to use a
larger time offset (i.e. use a longer time lag) and
to trade at the close of t
by setting
argument trade.at.open
to FALSE
.
If no OHLC bars are available, a single series per asset (assumed to be close prices) can be used.
The trade logic needs to be coded in the function
signal
. Arguments to that function must be
named and need to be passed with ...
.
Certain names are reserved and cannot be used as
arguments: Open
, High
, Low
,
Close
, Wealth
, Cash
,
Time
, Timestamp
, Portfolio
,
SuggestedPortfolio
, Globals
. Further
reserved names may be added in the future:
it is suggested to not start an argument
name with a capital letter.
The function signal
must evaluate to the
target position in units of the instruments. To
work with weights, set convert.weights
to
TRUE
, and btest
will translate the
weights into positions.
Accessing data
Within signal
(and also other function
arguments, such as do.signal
), you can
access data via special functions such as
Close
. These are automatically added as
arguments to signal
. Currently, the
following functions are available: Open
,
High
, Low
, Close
,
Wealth
, Cash
, Time
,
Timestamp
, Portfolio
,
SuggestedPortfolio
, Globals
.
Globals
is special: it is an
environment
, which can be used to
persistently store data during the run of
btest
. Use the argument Globals
to
add initial objects. See the Examples below and the
manual.
Additional functions may be added to btest
in the future. The names of those functions will
always be in title case. Hence, it is recommended
to not use argument names for signal
,
etc. that start with a capital letter.
Replications and variations
btest
allows to run backtests in
parallel. See the examples at
http://enricoschumann.net/notes/parallel-backtests.html.
The argument variations.settings
is a list with the
following defaults:
method
character: supported are
"loop"
,"parallel"
(or"snow"
) and"multicore"
load.balancing
logical
cores
numeric
Value
A list with class attribute btest
. The list comprises:
position |
actual portfolio holdings |
suggested.position |
suggested holdings |
cash |
cash |
wealth |
time-series of total portfolio value (aka equity curve) |
cum.tc |
transaction costs |
journal |
|
initial.wealth |
initial wealth |
b |
burn-in |
final.position |
final position if |
Globals |
environment |
When include.timestamp
is TRUE
, the
timestamp is included. If no timestamp
was
specified, integers 1, 2, ...
are used
instead.
When include.data
is TRUE
, essentially
all information (prices, instrument, the
actual call
and functions signal
etc.)
are stored in the list as well.
Author(s)
Enrico Schumann es@enricoschumann.net
References
Schumann, E. (2023) Portfolio Management with R.
http://enricoschumann.net/PMwR/;
in particular, see the chapter on backtesting:
http://enricoschumann.net/R/packages/PMwR/manual/PMwR.html#backtesting
Schumann, E. (2018) Backtesting.
https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3374195
Examples
## For more examples, please see the Manual
## http://enricoschumann.net/R/packages/PMwR/manual/PMwR.html
## 1 - a simple rule
timestamp <- structure(c(16679L, 16680L, 16681L, 16682L,
16685L, 16686L, 16687L, 16688L,
16689L, 16692L, 16693L),
class = "Date")
prices <- c(3182, 3205, 3272, 3185, 3201,
3236, 3272, 3224, 3194, 3188, 3213)
data.frame(timestamp, prices)
signal <- function() ## buy when last price is
if (Close() < 3200) ## below 3200, else sell
1 else 0 ## (more precisely: build position of 1
## when price < 3200, else reduce
## position to 0)
solution <- btest(prices = prices, signal = signal)
journal(solution)
## with Date timestamps
solution <- btest(prices = prices, signal = signal,
timestamp = timestamp)
journal(solution)
## 2 - a simple MA model
## Not run:
library("PMwR")
library("NMOF")
dax <- DAX[[1]]
n <- 5
ma <- MA(dax, n, pad = NA)
ma_strat <- function(ma) {
if (Close() > ma[Time()])
1
else
0
}
plot(as.Date(row.names(DAX)), dax, type = "l", xlab = "", ylab = "DAX")
lines(as.Date(row.names(DAX)), ma, type = "l")
res <- btest(prices = dax,
signal = ma_strat,
b = n, ma = ma)
par(mfrow = c(3, 1))
plot(as.Date(row.names(DAX)), dax, type = "l",
xlab = "", ylab = "DAX")
plot(as.Date(row.names(DAX)), res$wealth, type = "l",
xlab = "", ylab = "Equity")
plot(as.Date(row.names(DAX)), position(res), type = "s",
xlab = "", ylab = "Position")
## End(Not run)
Deutscher Aktienindex (DAX)
Description
Historical Prices of the DAX.
Usage
data("DAX")
Format
A data frame with 505 observations on the following variable:
DAX
a numeric vector
Details
Close prices.
Examples
str(DAX)
Compute Drawdowns
Description
Compute drawdown statistics.
Usage
drawdowns(x, ...)
## Default S3 method:
drawdowns(x, ...)
## S3 method for class 'zoo'
drawdowns(x, ...)
Arguments
x |
a |
... |
additional arguments, to be passed to methods |
Details
drawdowns
is a generic function. It computes drawdown
statistics: maximum; and time of peak, trough and recovery.
Value
a data.frame
:
peak |
peak before drawdown |
trough |
lowest point |
recover |
new high or |
max |
the max drawdown |
Author(s)
Enrico Schumann
References
Gilli, M., Maringer, D. and Schumann, E. (2019) Numerical Methods and Optimization in Finance. 2nd edition. Elsevier. doi:10.1016/C2017-0-01621-X
Schumann, E. (2023) Portfolio Management with R.
http://enricoschumann.net/PMwR/; in particular, see
http://enricoschumann.net/R/packages/PMwR/manual/PMwR.html#drawdowns-streaks
See Also
The actual computation of the drawdowns is done by function
drawdown
in package NMOF.
Series of uninterrupted up and down movements can be
computed with streaks
.
Examples
x <- c(100, 98)
drawdowns(x)
x <- c(100, 98, 102, 99)
dd <- drawdowns(x)
dd[order(dd$max, decreasing = TRUE), ]
Retrieve or Change Instrument
Description
Generic function for retrieving and changing instrument information.
Usage
instrument(x, ...)
instrument(x, ...) <- value
Arguments
x |
an object |
... |
arguments passed to methods |
value |
an object |
Details
Generic function: extract or, if meaningful, replace instrument information
Value
when used to extract instrument, a character vector
Author(s)
Enrico Schumann
References
Schumann, E. (2023) Portfolio Management with R. http://enricoschumann.net/R/packages/PMwR/manual/PMwR.html
See Also
Examples
jnl <- journal(instrument = "A",
amount = 100,
price = 1)
instrument(jnl)
instrument(jnl) <- "B"
Validate Security Identification Numbers
Description
Check whether a given ISIN or SEDOL is valid.
Usage
is_valid_ISIN(isin)
is_valid_SEDOL(SEDOL, NA.FALSE = FALSE)
Arguments
isin |
a character vector |
SEDOL |
a character vector |
NA.FALSE |
logical |
Details
Checks a character vector of ISINs and SEDOLs. The
function returns TRUE
if the ISIN is valid; else
FALSE
.
International Securities Identification Numbers (ISINs): The test procedure in ISO 6166 does not differentiate between cases. Thus, ISINs are transformed to uppercase before being tested.
Value
A named logical vector. For is_valid_SEDOL
, a character vector
is attached as an attribute note
.
Author(s)
Enrico Schumann
References
https://en.wikipedia.org/wiki/ISO_6166
https://en.wikipedia.org/wiki/SEDOL
https://anna-web.org/identifiers/
Examples
isin <- c("US0378331005", "AU0000XVGZA3",
"DE000A0C3743", "not_an_isin")
is_valid_ISIN(isin)
is_valid_ISIN(c("US0378331005",
"us0378331005")) ## case is ignored
SEDOL <- c("0263494", "B1F3M59", "0263491", "A", NA)
is_valid_SEDOL(SEDOL)
## 0263494 B1F3M59 0263491 A <NA>
## TRUE TRUE FALSE FALSE NA
is_valid_SEDOL(SEDOL, NA.FALSE = TRUE)
## 0263494 B1F3M59 0263491 A <NA>
## TRUE TRUE FALSE FALSE FALSE
Journal
Description
Create and manipulate a journal of financial transactions.
Usage
journal(amount, ...)
as.journal(x, ...)
is.journal(x)
## Default S3 method:
journal(amount, price, timestamp, instrument,
id = NULL, account = NULL, ...)
## S3 method for class 'journal'
c(..., recursive = FALSE)
## S3 method for class 'journal'
length(x)
## S3 method for class 'journal'
aggregate(x, by, FUN, ...)
## S3 method for class 'journal'
print(x, ...,
width = getOption("width"), max.print = getOption("max.print"),
exclude = NULL, include.only = NULL)
## S3 method for class 'journal'
sort(x, decreasing = FALSE, by = "timestamp", ..., na.last = TRUE)
## S3 method for class 'journal'
summary(object, by = "instrument", drop.zero = TRUE,
na.rm = FALSE, ...)
## S3 method for class 'journal'
subset(x, ...)
## S3 method for class 'journal'
x[i, match.against = NULL,
ignore.case = TRUE, perl = FALSE, fixed = FALSE,
useBytes = FALSE, ..., invert = FALSE]
## S3 replacement method for class 'journal'
x[i, match.against = NULL,
ignore.case = TRUE, ..., invert = FALSE] <- value
## S3 method for class 'journal'
as.data.frame(x, row.names = NULL, optional = FALSE, ...)
## S3 method for class 'journal'
head(x, n = 6L, ..., by = "instrument")
## S3 method for class 'journal'
tail(x, n = 6L, ..., by = "instrument")
Arguments
timestamp |
An atomic vector of mode numeric or character. Timestamps should typically be sortable. |
amount |
numeric |
price |
numeric |
instrument |
character or numeric (though typically character) |
id |
An atomic vector. Default is |
account |
An atomic vector. Default is |
... |
For For For For |
x |
a |
object |
a |
width |
integer. See |
decreasing |
passed to |
by |
|
na.rm |
logical |
drop.zero |
logical |
na.last |
arguments passed to sort |
max.print |
maximum number of transactions to print |
exclude |
character: fields that should not be printed |
include.only |
character: print only those fields. (Not supported yet.) |
row.names |
see |
optional |
see |
recursive |
ignored (see |
i |
integer, logical or character. The latter is interpreted as a
regexp (see |
n |
integer |
match.against |
character vector of field names. Default is |
ignore.case |
logical: passed to |
perl |
logical: passed to |
fixed |
logical: passed to |
useBytes |
logical: passed to |
invert |
logical. If |
FUN |
either a function that takes as input a journal and evaluates to a journal, or a list of named functions |
value |
a replacement value |
Details
The journal
function creates a list of its arguments
and attaches a class attribute (‘journal
’).
It is a generic function; the default method creates a
journal from atomic vectors. The btest
method
extracts the journal from the results of a backtest; see
btest
.
as.journal
coerces an object to a journal and is
primarily used for creating a journal from a
data.frame
.
Calling as.journal
on an unnamed numeric vector
interprets the vector as amounts. If the vector is named,
these are interpreted as instruments; see Examples. Calling
as.journal
on a journal returns the journal itself.
journal
methods are available for several generic
functions, for instance:
all.equal
compare contents of two journals
aggregate
Splits a journal according to
by
, applies a function to every sub-journal and recombines the results into a journal.as.data.frame
Coerce journal to
data.frame
.c
Combine several journals into one. Note that the first argument to
c.journal
must inherit fromjournal
, or else the method dispatch will fail. For empty journals, usejournal()
(notNULL
).length
number of transactions in a journal; it uses the length of
amount
split
Splits a journal according to
f
, yielding a list of journals. Often used interactively to have information per sub-journal printed.subset
evaluates an expression in an environment that can access all fields of the journal. The function is meant for interactive analysis; care is needed when it is used within other functions: see Examples and the Manual.
summary
provides summary statistics, such as number of trades and average buy/sell prices
toOrg
converts a journal to an Org table; package orgutils must be available
For journals that have a length, missing arguments will be
coded as NA
except for id
and
account
, which become NULL
. In
zero-length (i.e. ‘empty’) journals, all fields have
length 0. A zero-length journal is created, for instance,
by saying journal()
or when an zero-row
data.frame
is passed to as.journal
.
Value
An object of class journal
, which is a list of atomic
vectors.
Author(s)
Enrico Schumann <es@enricoschumann.net>
References
Schumann, E. (2023) Portfolio Management with R.
http://enricoschumann.net/R/packages/PMwR/;
in particular, see
http://enricoschumann.net/R/packages/PMwR/manual/PMwR.html#journals
See Also
Examples
j <- journal(timestamp = 1:3,
amount = c(1,2,3),
price = 101:103,
instrument = c("Stock A", "Stock A", "Stock B"))
## *** subset *** in functions
## this should work as expected ...
t0 <- 2.5
subset(j, timestamp > t0)
## ... but here?!
tradesAfterT <- function(j, t0)
subset(j, timestamp > t0)
tradesAfterT(j, 0)
## if really required
tradesAfterT <- function(j, t0) {
e <- substitute(timestamp > t0, list(t0 = t0))
do.call(subset, list(j, e))
}
tradesAfterT(j, 0)
## ... or much simpler
tradesAfterT <- function(j, t0)
j[j$timestamp > t0]
tradesAfterT(j, 0)
## *** aggregate ***
## several buys and sells on two days
## aim: find average buy/sell price per day
j <- journal(timestamp = structure(c(15950, 15951, 15950, 15951, 15950,
15950, 15951, 15951, 15951, 15951),
class = "Date"),
amount = c(-3, -4, -3, -1, 3, -2, 1, 3, 5, 3),
price = c(104, 102, 102, 110, 106, 104, 104, 106, 108, 107),
instrument = c("B", "B", "A", "A", "B", "B", "A", "B", "A", "A"))
by <- list(j$instrument, sign(j$amount), as.Date(j$timestamp))
fun <- function(x) {
journal(timestamp = as.Date(x$timestamp[1]),
amount = sum(x$amount),
price = sum(x$amount*x$price)/sum(x$amount),
instrument = x$instrument[1L])
}
aggregate(j, by = by, FUN = fun)
## *** iterate over transactions in (previously defined) journal ***
for (j in split(j, seq_along(j)))
print(j)
## as.journal with numeric vector
as.journal(1:3)
## amount
## 1 1
## 2 2
## 3 3
##
## 3 transactions
## as.journal with *named* numeric vector
x <- 1:3; names(x) <- LETTERS[1:3]
as.journal(x)
## instrument amount
## 1 A 1
## 2 B 2
## 3 C 3
##
## 3 transactions
x <- 1:3; names(x) <- c("A", "B", "A")
as.journal(x)
## instrument amount
## 1 A 1
## 2 B 2
## 3 A 3
##
## 3 transactions
Net-Asset-Value (NAV) Series
Description
Create a net-asset-value (NAV) series.
Usage
NAVseries(NAV, timestamp,
instrument = NULL, title = NULL, description = NULL,
drop.NA = NULL)
as.NAVseries(x, ...)
## S3 method for class 'NAVseries'
print(x, ... )
## S3 method for class 'NAVseries'
summary(object, ..., monthly.vol = TRUE,
bm = NULL, monthly.te = TRUE,
na.rm = FALSE, assume.daily = FALSE)
## S3 method for class 'NAVseries'
plot(x, y, ..., xlab = "", ylab = "", type = "l")
## S3 method for class 'NAVseries'
window(x, start = NULL, end = NULL, ...)
Arguments
NAV |
numeric |
timestamp |
|
instrument |
character |
title |
character |
description |
character |
x |
an |
object |
an |
... |
further arguments. For |
drop.NA |
logical |
bm |
an optional NAVseries. If |
monthly.vol |
if |
monthly.te |
if |
assume.daily |
logical |
na.rm |
logical |
y |
a second NAVseries to be plotted. Not supported yet. |
xlab |
character |
ylab |
character |
type |
character. See |
start |
same class as timestamp; |
end |
same class as timestamp; |
Details
NAV series
An NAVseries is a numeric vector (the actual series) and
additional information, attached as attributes: timestamp,
instrument, title, description. Of these attributes,
timestamp is the most useful, as it is used for several
computations (e.g. when calling summary
) or for
plotting.
Summaries
The summary
method returns a list of the original
NAVseries plus various statistics, such as return per year
and volatility. The method may receive several NAV series as
input
Value
an NAVseries
: see Details.
an NAVseries
summary: a list of lists. If a
benchmark series is present, the summary has an
attribute bm
: an integer, specifying the
position of the benchmark.
Note
The semantics of handling NAVseries are not stable
yet. Currently, objects of class NAVseries
are
univariate: you create a single NAVseries, summarise
it, plot it, and so one. In the future, at least some
of the methods will support the multi-variate case,
i.e. be able to handle several series at once.
Author(s)
Enrico Schumann <es@enricoschumann.net>
References
Schumann, E. (2023) Portfolio Management with R.
http://enricoschumann.net/PMwR/; in particular, see
http://enricoschumann.net/R/packages/PMwR/manual/PMwR.html#NAVseries
See Also
For handling external cashflows, see unit_prices
,
split_adjust
and div_adjust
.
Examples
summary(NAVseries(DAX[[1]], as.Date(row.names(DAX)), title = "DAX"))
Profit and Loss
Description
Compute profit and (or) loss of financial transactions.
Usage
pl(amount, ... )
## Default S3 method:
pl(amount, price, timestamp = NULL,
instrument = NULL, multiplier = 1,
multiplier.regexp = FALSE,
along.timestamp = FALSE, approx = FALSE,
initial.position = NULL, initial.price = NULL,
vprice = NULL, tol = 1e-10, do.warn = TRUE,
do.sum = FALSE, pl.only = FALSE,
footnotes = TRUE, ... )
## S3 method for class 'journal'
pl(amount, multiplier = 1,
multiplier.regexp = FALSE,
along.timestamp = FALSE, approx = FALSE,
initial.position = NULL, initial.price = NULL,
vprice = NULL, tol = 1e-10, do.warn = TRUE, ... )
## S3 method for class 'pl'
pl(amount, ... )
## S3 method for class 'pl'
print(x, ..., use.crayon = NULL, na.print = ".",
footnotes = TRUE)
## S3 method for class 'pl'
as.data.frame(x, ... )
.pl(amount, price, tol = 1e-10, do.warn = TRUE)
.pl_stats(amount, price, tol = sqrt(.Machine$double.eps))
Arguments
amount |
numeric or a |
price |
numeric |
instrument |
character or numeric (though typically character) |
timestamp |
An atomic vector of mode
|
along.timestamp |
logical; or a a vector of
timestamps. If the latter, |
initial.position |
a |
initial.price |
prices to evaluate initial position |
vprice |
valuation price; a numeric vector. With several instruments, the
prices must be named, e.g. |
multiplier |
numeric vector. When |
multiplier.regexp |
logical. If |
approx |
logical |
tol |
numeric: threshold to consider a position zero. |
x |
a |
... |
further argument |
use.crayon |
logical |
na.print |
character: how to print |
do.warn |
logical: issue warnings? |
do.sum |
logical: sum profit/loss across instruments? |
pl.only |
logical: if |
footnotes |
logical, with default |
Details
Computes profit and/or loss and returns a list with
several statistics (see Section Value, below). To get only
the profit/loss numbers as a numeric vector, set argument
pl.only
to TRUE
.
pl
is a generic function: The default input is
vectors for amount, price, etc. Alternatively (and often
more conveniently), the function may also be called with a
journal
or a data.frame
as its
input. For data frames, columns must be named
amount
, price
, and so on, as in a
journal
.
pl
may be called in two ways: either to compute
total profit/loss from a list of trades, possibly
broken down by instrument
and account
; or to
compute profit/loss over time. The latter case
typically requires setting arguments
along.timestamp
and/or vprice
(see
Examples). Profit/loss over time is always computed with
time in ascending order: so if the timestamps in
along.timestamp
are not sorted, the function will
sort them (and vprice
as well).
Using vprice
: when along.timestamp
is
logical (FALSE
or TRUE
),
vprice
can be used to value an open
position. For a single asset, it should be a single
number; for several assets, it should be named
vector, with names indicating the instrument
.
When along.timestamp
is used to pass a
custom timestamp: for a single asset, vprice
must be a vector with the same length as
along.timestamp
; for several assets, it must
be a numeric matrix with dimension
length(along.timestamp)
times number of
assets.
.pl
and .pl_stats
are helper functions
that are called by pl
. .pl_stats
requires amount and price to be sorted in time, and
to be of length > 0.
To use package crayon – which is only sensible
in interactive use –, either explicitly set
use.crayon
to TRUE
or set an option
PMwR.use.crayon
to TRUE
.
Value
For pl
, an object of class pl
, which is
a list of lists: one list for each instrument. Each
such list contains numeric vectors: pl
,
realised
, unrealised
, buy
,
sell
, volume
. If along.timestamp
is not FALSE
, a vector timestamp
is also present.
For .pl
, a numeric vector with four elements:
profit/loss in units of the instrument, sum of
absolute amounts, average buy price, average sell
price. For zero-length vector, the function evaluates to
c(0, 0, NaN, NaN)
.
For .pl_stats
, a list of two elements:
the average entry-price, and the realized profit/loss.
profit/loss in units of the instrument, sum of
absolute amounts, average buy price, average sell
price. For zero-length vector, the function evaluates to
c(0, 0, NaN, NaN)
.
Author(s)
Enrico Schumann <es@enricoschumann.net>
References
Schumann, E. (2023) Portfolio Management with R. http://enricoschumann.net/PMwR/; in particular http://enricoschumann.net/R/packages/PMwR/manual/PMwR.html#profit-and-loss
See Also
Examples
J <- journal(timestamp = c( 1, 2, 3),
amount = c( 1, 1, -2),
price = c(100, 102, 101))
pl(J)
pl(amount = c( 1, 1, -2),
price = c(100, 102, 101)) ## without a 'journal'
J <- journal(timestamp = c( 1, 2, 3, 1, 2, 3),
amount = c( 1, 1, -2, 1, 1, -2),
price = c(100, 102, 101, 100, 102, 105),
instrument = c(rep("Bond A", 3), rep("Bond B", 3)))
pl(J)
## Bond A
## P/L total 0
## average buy 101
## average sell 101
## cum. volume 4
##
## Bond B
## P/L total 8
## average buy 101
## average sell 105
## cum. volume 4
##
## 'P/L total' is in units of instrument;
## 'volume' is sum of /absolute/ amounts.
as.data.frame(pl(J)) ## a single data.frame
## pl buy sell volume
## Bond A 0 101 101 4
## Bond B 8 101 105 4
lapply(pl(J), as.data.frame) ## => a list of data.frames
## $`Bond A`
## pl realised unrealised buy sell volume
## 1 0 NA NA 101 101 4
##
## $`Bond B`
## pl realised unrealised buy sell volume
## 1 8 NA NA 101 105 4
pl(pl(J)) ## P/L as a numeric vector
## Bond A Bond B
## 0 8
## Example for 'vprice'
instrument <- c(rep("Bond A", 2), rep("Bond B", 2))
amount <- c(1, -2, 2, -1)
price <- c(100, 101, 100, 105)
## ... no p/l because positions not closed:
pl(amount, price, instrument = instrument, do.warn = FALSE)
## ... but with vprice specified, p/l is computed:
pl(amount, price, instrument = instrument,
vprice = c("Bond A" = 103, "Bond B" = 100))
### ... and is, except for volume, the same as here:
instrument <- c(rep("Bond A", 3), rep("Bond B", 3))
amount <- c(1, -2, 1, 2, -1, -1)
price <- c(100, 101, 103, 100, 105, 100)
pl(amount, price, instrument = instrument)
## p/l over time: example for 'along.timestamp' and 'vprice'
j <- journal(amount = c(1, -1),
price = c(100, 101),
timestamp = as.Date(c("2017-07-05", "2017-07-06")))
pl(j)
pl(j,
along.timestamp = TRUE)
pl(j,
along.timestamp = seq(from = as.Date("2017-07-04"),
to = as.Date("2017-07-07"),
by = "1 day"),
vprice = 101:104)
## Example for 'multiplier'
jnl <- read.table(text =
"instrument, price, amount
FGBL MAR 16, 165.20, 1
FGBL MAR 16, 165.37, -1
FGBL JUN 16, 164.12, 1
FGBL JUN 16, 164.13, -1
FESX JUN 16, 2910, 5
FESX JUN 16, 2905, -5",
header = TRUE, stringsAsFactors = FALSE, sep = ",")
jnl <- as.journal(jnl)
pl(jnl, multiplier.regexp = TRUE, ## regexp matching is case sensitive
multiplier = c("FGBL" = 1000, "FESX" = 10))
## use package 'crayon'
## Not run:
## on Windows, you may also need 'options(crayon.enabled = TRUE)'
options(PMwR.use.crayon = FALSE)
pl(amount = c(1, -1), price = c(1, 2))
options(PMwR.use.crayon = TRUE)
pl(amount = c(1, -1), price = c(1, 2))
## End(Not run)
Plot Time Series During Trading Hours
Description
Plot a time series after removing weekends and specific times of the day.
Usage
plot_trading_hours(x, t = NULL, interval = "5 min",
labels = "hours", label.format = NULL,
exclude.weekends = TRUE, holidays = NULL,
fromHHMMSS = "000000", toHHMMSS = "240000",
do.plot.axis = TRUE,
...,
from = NULL, to = NULL,
do.plot = TRUE,
axis1.par = list())
Arguments
x |
A numeric vector. Can also be of class |
t |
A vector that inherits from class |
interval |
A character string like “num units”, in which |
labels |
A character vector of length one, determining the grid for
|
label.format |
See |
exclude.weekends |
logical: default is |
holidays |
a vector of class |
fromHHMMSS |
a character vector of length one in format “HHMMSS” |
toHHMMSS |
a character vector of length one in format “HHMMSS” |
do.plot.axis |
logical. Should |
... |
|
from |
POSIXct: start plot at (if not specified, plot starts at first data point) |
to |
POSIXct: end plot at (if not specified, plot ends at last data point) |
do.plot |
logical. Should anything be plotted? Default is |
axis1.par |
a list of named elements |
Details
Plot a timeseries during specific times of day.
Value
A list (invisibly if do.plot
is TRUE
):
list(t, x, axis.pos = pos, axis.labels, timegrid)
t |
positions |
x |
values |
axis.pos |
positions of x-tickmarks |
axis.labels |
labels at x-ticks |
timegrid |
a POSIXct vector |
map |
a function. See the manual (a link is under References). |
Author(s)
Enrico Schumann <es@enricoschumann.net>
References
B.D. Ripley and K. Hornik. Date-Time Classes. R-News, 1(2):8–12, 2001.
E. Schumann (2023) Portfolio Management with R.
http://enricoschumann.net/PMwR/; in particular, see
http://enricoschumann.net/R/packages/PMwR/manual/PMwR.html#plot-trading-hours
See Also
Examples
t <- as.POSIXct("2012-08-31 08:00:00") + 0:32400
x <- runif(length(t))
par(tck = 0.001, mgp = c(3,1,0.5), bty = "n")
p <- plot_trading_hours(x, t,
interval = "5 min", labels = "hours",
xlab = "time", ylab = "random points",
col = "blue")
## with ?lines
t <- as.POSIXct("2012-08-31 10:00:00") + 0:9000
x <- seq(0, 1, length.out = 9001)
lines(p$map(t)$t, x[p$map(t)$ix], pch = 19)
Internal Functions
Description
Internal functions, not exported from the PMwR namespace.
Usage
.timestamp(x)
.timestamp(x) <- value
.may_be_Date(x, ...)
Details
.timestamp
extracts or replaces an object's timestamp.
.may_be_Date
checks whether a vector could be
coerced to class Date
, in which case it
evaluates to (a single) TRUE
; otherwise it returns
FALSE
. If TRUE
, the actual Date
s are
attached as an attribute Date
.
Author(s)
Enrico Schumann
References
E. Schumann (2023) Portfolio Management with R. http://enricoschumann.net/PMwR/
Aggregate Transactions to Positions
Description
Use information on single trades to compute a position at a specific point in time.
Usage
position(amount, ...)
## Default S3 method:
position(amount, timestamp, instrument, when,
drop.zero = FALSE, account = NULL,
use.names = NULL, ...)
## S3 method for class 'journal'
position(amount, when, drop.zero = FALSE,
use.account = FALSE, ...)
## S3 method for class 'position'
print(x, ..., sep = ":")
Arguments
when |
a timestamp or a vector of timestamps; alternatively, several keywords are supported. See Details. |
amount |
numeric or an object of class |
timestamp |
numeric or character: timestamps, must be sortable |
instrument |
character: symbols to identify different instruments |
account |
character: description of account. Ignored if |
use.account |
logical |
use.names |
logical |
drop.zero |
If logical, drop instruments that have a zero
position; default is |
x |
An object of type position. |
... |
arguments passed to |
sep |
A regular expression. Split instruments accordingly. Not implemented yet. |
Details
position
is a generic function; most useful is
the method for journal
s.
The function checks if timestamp
is sorted (see
is.unsorted
) and sorts the journal by timestamp
,
if required. If there are (some) NA
values in timestamp
,
but timestamp
is sorted otherwise, the function will proceed
(with a warning, though).
The argument when
can also be specified as one of several
keywords: last
(or newest
or latest
) provides the
position at the latest timestamp; first
(or oldest
)
provides the position at the earliest timestamp; all
provides
the positions at all timestamps in the journal. endofday
,
endofmonth
and endofyear
provide positions at the end of all calendar days, months and years within the
timestamp range of the journal. The latter keywords can only work if
timestamp
can be coerced to Date
.
Value
An object of class position
, which is a numeric matrix with
instrument
and timestamp
attributes. Note that
position
will never drop the result's dim
attribute: it
will always be a matrix of size length(when)
times
length(unique(instrument))
, which may not be obvious from the
printed output.
To extract the numeric position matrix, say as.matrix(p)
.
Author(s)
Enrico Schumann
References
Schumann, E. (2023) Portfolio Management with R.
http://enricoschumann.net/R/packages/PMwR/;
in particular, see
http://enricoschumann.net/R/packages/PMwR/manual/PMwR.html#computing-balances
See Also
Examples
position(amount = c(1, 1, -1, 3, -4), timestamp = 1:5, when = 4.9)
## using a journal
J <- journal(timestamp = 1:5, amount = c(1, 1, -1, 3, -4))
position(J, when = 4.9)
Price Table
Description
Create price table
Usage
pricetable(price, ...)
Arguments
price |
a matrix |
... |
further arguments, passed to methods |
Details
pricetable
is a helper function for extracting
prices of particular instrument at specified dates.
For this, it first creates a table that merges series
passed via ... and appends a class attribute. A [
method then allows to extract prices. Importantly, if
you ask for a subset of m rows and n columns, the
result will be a matrix of size m times n, even if
times or instruments are missing.
pricetable
is a generic function, currently
with methods for numeric vectors (including vectors
with a dim
, aka matrices) and for
zoo
objects.
Value
a numeric matrix with class attribute pricetable
Author(s)
Enrico Schumann
References
Schumann, E. (2020) Portfolio Management with R. http://enricoschumann.net/R/packages/PMwR/
See Also
Examples
## quickly creating a pricetable
pricetable(1:3)
pricetable(1:3, instrument = c("A", "B", "C"))
### ... and the same
pricetable(c(A = 1, B = 2, C = 3))
## subsetting examples
m <- 3
n <- 2
price <- array(c(1:m, 1:m + 100), dim = c(m,n))
colnames(price) <- LETTERS[1:n]
pt <- pricetable(price, timestamp = 1:m)
## A B
## 1 1 101
## 2 2 102
## 3 3 103
pt[ , "A"]
## A
## 1 1
## 2 2
## 3 3
pt[ , c("X", "A", "X")]
## X A X
## 1 NA 1 NA
## 2 NA 2 NA
## 3 NA 3 NA
pt[ , c("X", "A", "X"), missing = 0]
## X A X
## 1 0 1 0
## 2 0 2 0
## 3 0 3 0
pt[c(0, 1.5, 4), , missing = "locf"]
## A B
## 0 NA NA
## 1.5 2 102
## 4 3 103
Treasury Quotes with 1/32nds of Point
Description
Print treasury quotes with 1/32nds of points.
Usage
quote32(price, sep = "(-|'|:)", warn = TRUE)
q32(price, sep = "(-|'|:)", warn = TRUE)
Arguments
price |
numeric or character. See Details. |
sep |
character: a regular expression |
warn |
logical. Warn about rounding errors? |
Details
The function is meant for pretty-printing of US treasury bond quotes; it provides no other functionality.
If price
is numeric
, it is interpreted as a quote in
decimal notation and ‘translated’ into a price quoted in
fractions of a point.
If price
is character
, it is interpreted as a quote in
fractional notation.
q32
is a short-hand for quote32
.
Value
A numeric vector of class quote32
.
Author(s)
Enrico Schumann
References
CME Group (2015). Treasury Futures Price Rounding Conventions. https://www.cmegroup.com/education/articles-and-reports/treasury-futures-price-rounding-conventions.html
Examples
quote32(100 + 17/32 + 0.75/32)
q32("100-172")
q32("100-272") - q32("100-270")
as.numeric(q32("100-272") - q32("100-270"))
Return Contribution
Description
Return contribution of portfolio segments.
Usage
rc(R, weights, timestamp, segments = NULL,
R.bm = NULL, weights.bm = NULL,
method = "contribution",
linking.method = NULL,
allocation.minus.bm = TRUE,
tol = sqrt(.Machine$double.eps))
Arguments
R |
returns: a numeric matrix |
weights |
the segment weights: a numeric matrix.
|
timestamp |
character or numeric |
segments |
character. If missing, column names of |
method |
a string |
linking.method |
|
allocation.minus.bm |
logical |
tol |
numeric: weights whose absolute value is below
|
If portfolio returns are to be compared against benchmark returns, benchmark returns/weights must be supplied:
R.bm |
returns: a numeric matrix |
weights.bm |
the segment weights: a numeric matrix.
|
Details
The function computes segment contribution, potentially
over time. Returns and weights must be arranged in
matrices, with rows corresponding to time periods and
columns to portfolio segments. If weights
and
R
are not matrices (i.e. are atomic vectors), then
they are interpreted as cross-sectional weights/returns
for a single period.
Weights can be missing, in which case R
is
assumed to already comprise segment returns.
Value
A list of two components
period_contributions |
a data.frame |
total_contributions |
a numeric vector |
Author(s)
Enrico Schumann
References
Jon A. Christopherson and David R. CariƱo and Wayne E. Ferson (2009), Portfolio Performance Measurement and Benchmarking, McGraw-Hill.
Feibel, Bruce (2003), Investment Performance Measurement, Wiley.
http://enricoschumann.net/R/packages/PMwR/manual/PMwR.html#return-contribution
See Also
Examples
weights <- rbind(c( 0.25, 0.75),
c( 0.40, 0.60),
c( 0.25, 0.75))
R <- rbind(c( 1 , 0),
c( 2.5, -1.0),
c(-2 , 0.5))/100
rc(R, weights, segment = c("equities", "bonds"))
## contribution for btest:
## run a portfolio 10% equities, 90% bonds
P <- as.matrix(merge(DAX, REXP, by = "row.names")[, -1])
(bt <- btest(prices = list(P),
signal = function() c(0.1, 0.9),
convert.weights = TRUE,
initial.cash = 100))
W <- bt$position*P/bt$wealth
rc(returns(P)*W[-nrow(W), ])$total_contributions
Rebalance Portfolio
Description
Compute the differences between two portfolios.
Usage
rebalance(current, target, price,
notional = NULL, multiplier = 1,
truncate = TRUE, match.names = TRUE,
fraction = 1, drop.zero = FALSE,
current.weights = FALSE,
target.weights = TRUE)
## S3 method for class 'rebalance'
print(x, ..., drop.zero = TRUE)
replace_weight(weights, ..., prefix = TRUE, sep = "::")
Arguments
current |
the current holdings: a (typically named) vector of position sizes;
can also be a |
target |
the target holdings: a (typically named) vector of weights;
can also be a |
price |
a numeric vector: the current prices; may be named |
notional |
a single number: the value of the portfolio; if missing,
replaced by |
multiplier |
numeric vector, possibly named |
truncate |
truncate computed positions? Default is |
match.names |
logical |
fraction |
numeric |
x |
an object of class |
... |
|
drop.zero |
logical: should instruments with no difference
between Note the different defaults for computing and printing. |
current.weights |
logical. If |
target.weights |
logical. If |
weights |
a numeric vector with named components |
sep |
character |
prefix |
logical |
Details
The function computes the necessary trades to move from the
current
portfolio to a target
portfolio.
replace_weight
is a helper function to split
baskets into their components. All arguments passed
via ...
should be named vectors. If names are
not syntactically valid (see
make.names
), quote them. The passed
vectors themselves should be passed as named
arguments: see examples.
Value
An object of class rebalance
, which is a
data.frame
:
instrument |
character, or |
price |
prices |
current |
current portfolio |
target |
new portfolio |
difference |
the difference between current and target |
Attached to the data.frame are several attributes:
notional |
notional |
match.names |
logical |
multiplier |
multipliers |
Author(s)
Enrico Schumann
References
Schumann, E. (2023) Portfolio Management with R.
http://enricoschumann.net/R/packages/PMwR/;
in particular, see
http://enricoschumann.net/R/packages/PMwR/manual/PMwR.html#rebalance
See Also
Examples
r <- rebalance(current = c(a = 100, b = 20),
target = c(a = 0.2, c = 0.3),
price = c(a = 1, b = 2, c = 3))
as.journal(r)
## replace_weight: the passed vectors must be named;
## 'basket_3' is ignored because not
## component of weights is named
## 'basket_3'
replace_weight(c(basket_1 = 0.3,
basket_2 = 0.7),
basket_1 = c(a = 0.1, b = 0.4, c = .5),
basket_2 = c(x = 0.1, y = 0.4, z = .5),
basket_3 = c(X = 0.5, Z = 0.5),
sep = "|")
Compute Returns
Description
Convert prices into returns.
Usage
returns(x, ...)
## Default S3 method:
returns(x, t = NULL, period = NULL, complete.first = TRUE,
pad = NULL, position = NULL,
weights = NULL, rebalance.when = NULL,
lag = 1, na.rm = TRUE, ...)
## S3 method for class 'zoo'
returns(x, period = NULL, complete.first = TRUE,
pad = NULL, position = NULL,
weights = NULL, rebalance.when = NULL, lag = 1, na.rm = TRUE, ...)
## S3 method for class 'p_returns'
print(x, ..., year.rows = TRUE, month.names = NULL,
zero.print = "0", plus = FALSE, digits = 1,
na.print = NULL)
## S3 method for class 'p_returns'
toLatex(object, ...,
year.rows = TRUE, ytd = "YTD", month.names = NULL,
eol = "\\\\", stand.alone = FALSE)
## S3 method for class 'p_returns'
toHTML(x, ...,
year.rows = TRUE, ytd = "YTD", month.names = NULL,
stand.alone = TRUE, table.style = NULL, table.class = NULL,
th.style = NULL, th.class = NULL,
td.style = "text-align:right; padding:0.5em;",
td.class = NULL, tr.style = NULL, tr.class = NULL,
browse = FALSE)
.returns(x, pad = NULL, lag)
Arguments
x |
for the default method, a For |
t |
timestamps. See arguments |
period |
Typically a string. Supported are
All returns are computed as simple returns. They
will only be annualised with option |
complete.first |
logical. For holding-period returns such an monthly or yearly, should the first period (if incomplete) be used. |
pad |
either |
na.rm |
logical; see Details |
position |
either a numeric vector of the same length as the
number of assets (i.e. |
weights |
either a numeric vector of the same length as the
number of assets (i.e. |
rebalance.when |
logical or numeric. If |
... |
further arguments to be passed to methods |
year.rows |
logical. If |
zero.print |
character. How to print zero values. |
na.print |
character. How to print |
plus |
logical. Add a ‘ |
lag |
The lag for computing returns. A positive integer,
defaults to one; ignored for time-weighted returns
or if |
object |
an object of class |
month.names |
character: names of months. Default is an
abbreviated month name as provided by the
locale. That may cause trouble, notably with
|
digits |
number of digits in table |
ytd |
header for YTD |
eol |
character |
stand.alone |
logical or character |
table.class |
character |
table.style |
character |
th.class |
character |
th.style |
character |
td.class |
character |
td.style |
character |
tr.class |
character |
tr.style |
character |
browse |
logical: open table in browser? |
Details
returns
is a generic function. It computes
simple returns: current values divided by prior
values minus one. The default method works for
numeric vectors/matrices. The function
.returns
does the actual computations and may
be used when a ‘raw’ return computation is
needed.
Holding-Period Returns
When a timestamp is available, returns
can
compute returns for specific calendar periods. See
argument period
.
Portfolio Returns
returns
may compute returns for a portfolio
specified in weights
or position
. The
portfolio is rebalanced at rebalance.when
; the
default is every period. Weights need not sum to
one. A zero-weight portfolio, or a portfolio that never
rebalances (e.g. with rebalance.when
set to
FALSE
), will result in a zero return.
rebalance.when
may either be logical,
integers or of the same class as a timestamp
(e.g. Date
).
Handling missing values
Removing missing values (i.e. setting na.rm
to
TRUE
) only has effects when period
is
specified.
Value
If called as returns(x)
: a numeric vector or
matrix, possibly with a class attribute (e.g. for a
zoo
series).
If called with a period
argument: an object of
class "p_returns"
(period returns), which is a
numeric vector of returns with attributes t
(timestamp) and period
. Main use is to have
methods that pretty-print such period returns; currently,
there are methods for toLatex
and
toHTML
.
In some cases, additional attributes may be attached:
when portfolio returns were computed (i.e. argument
weights
was specified), there are attributes
holdings
and contributions
. For
holding-period returns, there may be a logical attribute
is.annualised
, and an attribute from.to
,
which tells the start and end date of the holding period.
Author(s)
Enrico Schumann <es@enricoschumann.net>
References
Schumann, E. (2023) Portfolio Management with R.
http://enricoschumann.net/R/packages/PMwR/;
in particular, see
http://enricoschumann.net/R/packages/PMwR/manual/PMwR.html#computing-returns
See Also
Examples
x <- 101:105
returns(x)
returns(x, pad = NA)
returns(x, pad = NA, lag = 2)
## monthly returns
t <- seq(as.Date("2012-06-15"), as.Date("2012-12-31"), by = "1 day")
x <- seq_along(t) + 1000
returns(x, t = t, period = "month")
returns(x, t = t, period = "month", complete.first = FALSE)
### formatting
print(returns(x, t = t, period = "month"), plus = TRUE, digits = 0)
## returns per year (annualised returns)
returns(x, t = t, period = "ann") ## less than one year, not annualised
returns(x, t = t, period = "ann!") ## less than one year, *but* annualised
is.ann <- function(x)
attr(x, "is.annualised")
is.ann(returns(x, t = t, period = "ann")) ## FALSE
is.ann(returns(x, t = t, period = "ann!")) ## TRUE
## with weights and fixed rebalancing times
prices <- cbind(p1 = 101:105,
p2 = rep(100, 5))
R <- returns(prices, weights = c(0.5, 0.5), rebalance.when = 1)
## ... => resulting weights
h <- attr(R, "holdings")
h*prices / rowSums(h*prices)
## p1 p2
## [1,] 0.5000000 0.5000000 ## <== only initial weights are .5/.5
## [2,] 0.5024631 0.4975369
## [3,] 0.5049020 0.4950980
## [4,] 0.5073171 0.4926829
## [5,] 0.5097087 0.4902913
REXP
Description
Historical Prices of the REXP.
Usage
data("REXP")
Format
A data frame with 502 observations on the following variable:
REXP
a numeric vector
Details
Daily prices.
Examples
str(REXP)
Scale Time Series
Description
Scale time series so that they can be better compared.
Usage
scale1(x, ...)
## Default S3 method:
scale1(x, ..., when = "first.complete", level = 1,
centre = FALSE, scale = FALSE, geometric = TRUE,
total.g = NULL)
## S3 method for class 'zoo'
scale1(x, ..., when = "first.complete", level = 1,
centre = FALSE, scale = FALSE, geometric = TRUE,
inflate = NULL, total.g = NULL)
Arguments
x |
a time series |
when |
origin: for the default method, either a string or
numeric (integer). Allowed strings are
|
level |
numeric |
centre |
logical |
scale |
logical or numeric |
geometric |
logical: if |
inflate |
numeric: an annual rate at which the series is inflated (or deflated if negative) |
total.g |
numeric: to total growth rate (or total return) of a series |
... |
other arguments passed to methods |
Details
This is a generic function, with methods for numeric
vectors and matrices, and zoo
objects.
Value
An object of the same type as x
.
Author(s)
Enrico Schumann
References
Schumann, E. (2023) Portfolio Management with R.
http://enricoschumann.net/PMwR/;
in particular, see
http://enricoschumann.net/R/packages/PMwR/manual/PMwR.html#scaling-series
See Also
Examples
scale1(cumprod(1 + c(0, rnorm(20, sd = 0.02))), level = 100)
Up and Down Streaks
Description
Compute up and down streaks for time-series.
Usage
streaks(x, ...)
## Default S3 method:
streaks(x, up = 0.2, down = -up,
initial.state = NA, y = NULL, relative = TRUE, ...)
## S3 method for class 'zoo'
streaks(x, up = 0.2, down = -up,
initial.state = NA, y = NULL, relative = TRUE, ...)
## S3 method for class 'NAVseries'
streaks(x, up = 0.2, down = -up,
initial.state = NA, bm = NULL, relative = TRUE, ...)
Arguments
x |
a price series |
initial.state |
|
up |
a number, such as 0.1 (i.e. 10%) |
down |
a negative number, such as -0.1 (i.e. -10%) |
y |
another price series |
bm |
another price series. Mapped to ‘ |
relative |
logical |
... |
other arguments passed to methods |
Details
streaks
is a generic function. It computes
series of uninterrupted up and down movements
(‘streaks’) in a price series. Uninterrupted
is meant in the sense that no countermovement of
down
(up
) percent or more occurs in up
(down) movements.
There are methods for numeric vectors, and
NAVseries
and zoo
objects.
The turning points (extreme points) are computed with the benefit of hindsight: the starting point (the low) of an up streak can only be determined once the streak is triggered, i.e. the up streak has already run its minimum amount. Vice versa for down streaks.
When ‘up
’ and ‘down
’ are
not equal, results may be inconsistent: in the
current implementation, streaks
alternates
between up and down streaks. Suppose up
is
large compared with down
, i.e. it takes long
to trigger up streaks, but they are easily
broken. Down streaks, on the other hand, are quickly
triggered but rarely broken. Now suppose that a down
streak is broken by an up streak: it may then well be
that the up streak would never have been counted as
such, because it was actually broken itself by
another down streak. The implementation for differing
values of ‘up
’ and ‘down
’
may change in the future.
Value
A data.frame
:
start |
beginning of streak |
end |
end of streak |
state |
|
return , change |
the return over the streak. If |
Author(s)
Enrico Schumann <es@enricoschumann.net>
References
Schumann, E. (2023) Portfolio Management with R.
http://enricoschumann.net/PMwR/; in particular, see
http://enricoschumann.net/R/packages/PMwR/manual/PMwR.html#drawdowns-streaks
See Also
Examples
streaks(DAX[[1]], t = as.Date(row.names(DAX)))
## results <- streaks(x = <...>, y = <...>)
##
## ===> *arithmetic* excess returns
## x[results$end]/x[results$start] -
## y[results$end]/y[results$start]
## ===> *geometric* excess returns
## x[results$end]/x[results$start] /
## (y[results$end]/y[results$start]) - 1
Import from package textutils
Description
The toHTML
function is imported from package textutils.
Help is available at textutils::toHTML
.
Say library("textutils")
in your code to use the function.
Analysing Trades: Compute Profit/Loss, Resize and more
Description
Functions to help analyse trades (as opposed to profit-and-loss series)
Usage
scale_trades(amount, price, timestamp, aggregate = FALSE,
fun = NULL, ...)
split_trades(amount, price, timestamp, aggregate = FALSE,
drop.zero = FALSE)
limit(amount, price, timestamp, lim, tol = 1e-8)
scale_to_unity(amount)
close_on_first(amount)
tw_exposure(amount, timestamp, start, end, abs.value = TRUE)
Arguments
amount |
notionals |
price |
a vector of prices |
timestamp |
a vector. |
aggregate |
|
fun |
a function |
lim |
a maximum absolute position size |
start |
optional time |
end |
optional time |
drop.zero |
logical. If |
abs.value |
logical. If |
... |
passed on to fun |
tol |
numeric |
Details
scale_trades
takes a vector of notionals, prices and
scales all trades along the paths so that the maximum
exposure is 1.
The default fun
divides every element of a vector
n
by max(abs(cumsum(n)))
. If user-specified,
the function fun
needs to take a vector of notionals
(changes in position.)
split_trades
decomposes a trade list into single
trades, where a single trade comprises those trades from a
zero position to the next zero position. Note that the
trades must be sorted chronologically.
Value
Either a list or a list-of-lists.
Author(s)
Enrico Schumann
See Also
Examples
n <- c(1,1,-3,-1,2)
p <- 100 + 1:length(n)
timestamp <- 1:length(n)
scale_trades(n, p, timestamp)
scale_trades(n, p, timestamp, TRUE) ## each _trade_ gets scaled
split_trades(n, p, timestamp)
split_trades(n, p, timestamp, TRUE) ## almost like the original series
## effect of 'drop.zero'
P <- c(100, 99, 104, 103, 102, 105, 104) ## price series
S <- c( 0, 1, 1, 0, 0, 1, 0) ## position to be held
dS <- c(0, diff(S)) ## change in position ==> trades
t <- seq_along(P)
#### ==> 1) with all zero amounts
split_trades(amount = dS, price = P, timestamp = t)
#### ==> 2) without zero-amount trades
split_trades(amount = dS, price = P, timestamp = t, drop.zero = TRUE)
#### ==> 3) without all zero-amounts
zero <- dS == 0
split_trades(amount = dS[!zero], price = P[!zero], timestamp = t[!zero])
Compute Prices for Portfolio Based on Units
Description
Compute prices for a portfolio based on outstanding shares.
Usage
unit_prices(NAV,
cashflows,
initial.price, initial.shares = 0,
cf.included = TRUE)
Arguments
NAV |
a dataframe of two columns: timestamp and net asset value |
cashflows |
a data.frame of two or three columns: timestamp, cashflow and (optionally) an id |
initial.price |
initial price |
initial.shares |
number of outstanding shares for first NAV |
cf.included |
logical |
Details
This function is experimental, and its interface is not stable yet.
The function may be used to compute the returns for a portfolio with external cashflows, i.e. what is usually called time-weighted returns.
Valuation (i.e. the computation of the NAV) must take place before external cashflows. Fairness suggests that: what price would you give an external investor if you had not valued the positions? And even if fairness mattered not: suppose we traded on a specific day, had a positive PL, and ended the day in cash. We could then not differentiate any more between a cash increase because of an external inflow and a cash increase because of a profitable trade.
Value
A data.frame
timestamp |
the timestamp |
NAV |
total NAV |
price |
NAV per share |
units |
outstanding units (i.e. shares) after cashflows |
Attached as an attribute is a data.frame
transactions
.
Author(s)
Enrico Schumann
References
Schumann, E. (2023) Portfolio Management with R. http://enricoschumann.net/PMwR/
See Also
Examples
NAV <- data.frame(timestamp = seq(as.Date("2017-01-01"),
as.Date("2017-01-10"),
by = "1 day"),
NAV = c(100:104, 205:209))
cf <- data.frame(timestamp = c(as.Date("2017-01-01"),
as.Date("2017-01-06")),
cashflow = c(100, 100))
unit_prices(NAV, cf, cf.included = TRUE)
## timestamp NAV price units
## 1 2017-01-01 100 100.0000 1.000000
## 2 2017-01-02 101 101.0000 1.000000
## 3 2017-01-03 102 102.0000 1.000000
## 4 2017-01-04 103 103.0000 1.000000
## 5 2017-01-05 104 104.0000 1.000000
## 6 2017-01-06 205 105.0000 1.952381
## 7 2017-01-07 206 105.5122 1.952381
## 8 2017-01-08 207 106.0244 1.952381
## 9 2017-01-09 208 106.5366 1.952381
## 10 2017-01-10 209 107.0488 1.952381
Valuation
Description
Valuation of financial objects: map an object into a quantity that is measured in a concrete (typically currency) unit.
Usage
valuation(x, ...)
## S3 method for class 'journal'
valuation(x, multiplier = 1,
cashflow = function(x, ...) x$amount * x$price,
instrument = function(x, ...) "cash",
flip.sign = TRUE, ...)
## S3 method for class 'position'
valuation(x, vprice, multiplier = 1,
do.sum = FALSE,
price.unit,
use.names = FALSE,
verbose = TRUE, do.warn = TRUE, ...)
Arguments
x |
an object |
multiplier |
a numeric vector, typically with named elements |
cashflow |
either a numeric vector or a function that takes on argument (a journal) and transforms it into a numeric vector |
instrument |
either a character vector or a function that takes on argument (a journal) and transforms it into a character vector |
flip.sign |
logical. If |
vprice |
numeric: a matrix whose elements
correspond to those in |
do.sum |
logical: sum over positions |
use.names |
logical: use names of |
price.unit |
a named character vector. Not implemented. |
verbose |
logical |
do.warn |
logical |
... |
other arguments passed to methods |
Details
This function is experimental, and the methods' interfaces are not stable yet.
valuation
is a generic function. Its
semantics suggest that an object (e.g. a financial
instrument or a position) is mapped into a concrete
quantity (such as an amount of some currency).
The journal
method transforms the
transactions in a journal into amounts of currency
(e.g, a sale of 100 shares of a company is
transformed into the value of these 100 shares).
The position
method takes a position
and returns the value (in currency units) of the
position.
Value
depends on the object: for journals, a journal
Author(s)
Enrico Schumann <es@enricoschumann.net>
References
Schumann, E. (2020) Portfolio Management with R. http://enricoschumann.net/R/packages/PMwR/
See Also
Examples
## valuing a JOURNAL
j <- journal(amount = 10, price = 2)
## amount price
## 1 10 2
##
## 1 transaction
valuation(j, instrument = NA)
## amount price
## 1 -20 1
##
## 1 transaction
## valuing a POSITION
pos <- position(c(AMZN = -10, MSFT = 200))
### contructing a price table:
### ==> P[i, j] must correspond to pos[i, j]
P <- array(c(2200, 170), dim = c(1, 2))
colnames(P) <- instrument(pos)
valuation(pos, vprice = P)
## AMZN MSFT
## [1,] -22000 34000
### contructing a price table, alternative:
### a named vector
### ==> only works when there is only a single timestamp
valuation(pos, vprice = c(MSFT = 170, AMZN = 2200))
all.equal(valuation(pos, vprice = P),
valuation(pos, vprice = c(MSFT = 170, AMZN = 2200)))