View source: R/benchTradePerf.R
benchTradePerf | R Documentation |
This function gathers different benchmarking methods used to evaluate the
average execution price P_{avg}
of a given trading strategy.
When sensical the relavant quantities are compoted in a period-by-period
cumulative fashion.
The P_{avg}
is compared against a number of benchmark metrics, in order
to assess its performance in terms of profit or loss relative to a given benchmark.
These benchmarks are not mutually exclusive, each of them provides different
insights and may have shortcomings. They can be used in conjuction to account
for this aspects.
benchTradePerf(
Portfolio,
Symbol,
side = 1,
benchmark = c("TradeBench", "MktBench", "VWAP", "PWP", "RPM"),
type = list(price = c(), vwap = c("interval", "full")),
MktData,
POV = NULL,
priceToBench
)
Portfolio |
A portfolio name that points to a portfolio object structured with initPortf() |
Symbol |
A string identifying the traded symbol to benchmark |
side |
A numeric value, that indicates the side of the trade. Either 1 or -1, |
benchmark |
A string providing one of the benchmarks metrics 'TradeBench', 'MktBench', 'VWAP', 'PWP' or 'RPM' |
type |
A list with named elements, |
MktData |
An xts object containing 'MktPrice' and 'MktQty' required columns. Or a numeric value when |
POV |
A numeric value between 0 and 1, specifying the POV rate |
priceToBench |
A numeric value. The |
The performance is quantified by means of a Profit and Loss (PnL) metric. A positive PnL metric indicates that the trading strategy outperformed a chosen benchmark on average, vice versa negative values register an underperformance.
By and large, PnL metrics are computed as:
PnL = -1 . side . \frac{\bar{P} - P_{B}}{P_{B}} . 10^{4}
where P_{avg}
is the average execution price and P_{B}
is a given
benchmark price. It is worth stressing that they are expressed in basis points
(bps) units.
A common instance is given by the trading PnL, where we consider the
arrival price of the transactions, P_{B} = P_{0}
, in which case is of
interest the timing in entering the market.
Another common and simple benchmark used is the benchmark price, in
this case P_{B}
can be a single current open/close price, future ones
such as next day prices, or any other benchmark price specified.
A widely used one is the Volume Weighted Average Price (VWAP) benchamark. The benchmark is defined as:
VWAP = \frac{\sum{P_{j}Q_{j}}}{\sum{Q_{j}}}
P_{j}
is the market price and Q_{j}
the market volume, during j
trading periods activity of the market.
Two different types of VWAP benchmarks are included in the present function,
the Interval VWAP and the Full VWAP. Referring to the former as
the VWAP where the j
market trading periods considered are the ones during
which the order is being executed, whereas the latter includes all the j
market periods from order execution beginning to last transaction.
The VWAP benchmark varies by timespan considered and is commonly used as a proxy
for fair market price. It can differ by data vendors specific market data filtering.
There are recognized drawbacks of this benchamrk. First of all, the larger the
order the closer the execution will be to VWAP. Second, where large block trades
occur these could skew the benchmark. Lastly, it is not an indicated comparison
across stocks or different days for the same stock.
A variation of the VWAP benchmark is given by the Participation Weighted Price (PWP) benchmark, where the weighting is with respect to the PWP shares:
PWP shares = \frac{Traded shares}{POV}
being POV
the percentage of volume. The PWP benchwark is:
PWP price = \frac{\sum{P_{h}Q_{h}}}{\sum{Q_{h}}}
where h
are the periods from the arrival time of the order into the market
until when the PWP shares are completely executed.
As the VWAP, the PWP benchmark provides a glimpse into market fair prices.
However this benchmark have limitations similar to the VWAP. It is subject to
manipulation in that the market price can be kept inflated by larger orders.
Furthermore, as the VWAP, it is not comparable between stocks or across days
for the same stock. Also, the benchmark may be biased by temporary impact dissipation.
Lastly, the Relative Performance Measure (RPM), which differs from the PnL metrics above, is a percentile ranking of trading activity. Its expression depends on the side of the trade:
RPM_{buy} = 0.5 * \frac{Total volume + Volume at P > P_{avg} - Volume at P < P_{avg}}{Total volume}
RPM_{sell} = 0.5 * \frac{Total volume + Volume at P < P_{avg} - Volume at P > P_{avg}}{Total volume}
where P
is the market price specified.
The an RPM over 50% is considered as an indication of superior trades, more
precisely the RPM can be mapped to a qualitative score of the trades:
0 <= RPM < 20 | Fair |
20 <= RPM < 40 | Poor |
40 <= RPM <= 60 | Average |
60 < RPM <= 80 | Good |
80 < RPM <= 100 | Excellent |
This measure is considered as preferred to the VWAP metric because it overcomes some of its drawbacks: it can be used to compare performance across different stocks, days, and volatility; it is not less influenced by large blocks trade at extreme prices.
The priceToBench
parameter, relevant only when benchmark='MktBench'
,
is provided as a convenience parameter, to be used when the benchmark price to
compare the average execution price of the transactions belongs to the MktData
xts input. This allows to use the function having other benchmarks computations.
A different usage of the function is available, giving two ways to use an arbitrary
benchmark price: input this single price as an xts
object through the
MktData
parameter (note that of an object with length greater than one
only the first element will be used and the 'MktPrice' column requirement),
or alternatively input a single numeric value in MktData
.
The type
parameter allows different usages of the function.
In the benchmark='MktBench'
, the kind of market price used as a benchmark
is up to the analyst and his research. The string provided through type=list(price='')
is completely arbitrary and does not influence the corresponding PnL metric computation,
it is available only for customization purposes. In other words, tohave a way
to distinguish the elements of the return object in case different benchmarking
analyses are being carried, e.g. benchmarking against both 'Open' prices and 'Close'
prices (separately, providing each of these prices with a function call).
Whereas, when benchmark='VWAP'
, then type
is used to select
the VWAP benchmark to use in the PnL metric computation, namely the Interval VWAP
(type=list(vwap = 'interval')
) or the "Full VWAP" (type=list(vwap = 'full')
).
A list whose unique element is a data.frame
that can be one of the ones described below,
Depending on the benchmark
of choice.
For benchmark = 'TradeBench'
it contains:
Dates
: Dates of reference, the longer period between the trading period and a subset of MktData
Symbol
: A string identifying the traded symbol to benchmark
Side
: The side
of the trades, as "Buy" or "Sell"
Avg.Exec.Price
: Symbol transactions average execution price
TradeBench
: The arrival price of transactions
Performance
: The Trading PnL performance, in bps
For benchmark = 'MktBench'
it contains:
Dates
: Dates of reference, the longer period between the trading period and a subset of MktData
Symbol
: A string identifying the traded symbol to benchmark
Side
: The side
of the trades, as "Buy" or "Sell"
Avg.Exec.Price
: Symbol transactions average execution price
MktBench.*
: The benchmark and an arbitrary type=list(price)
provided as input (e.g. 'Open', 'Close')
Performance
: The Benchmark PnL performance, in bps
For benchmark = 'VWAP'
it contains:
Dates
: Dates of reference, the longer period between the trading period and a subset of MktData
Symbol
: A string identifying the traded symbol to benchmark
Side
: The side
of the trades, as "Buy" or "Sell"
Avg.Exec.Price
: Symbol transactions average execution price
VWAP.*
: The benchmark and depending on type=list(vwap)
parameter either 'interval' or 'full'
Performance
: The VWAP PnL metric, in bps
For benchmark = 'PWP'
it contains:
Dates
: Dates of reference, the longer period between the trading period and a subset of MktData
Symbol
: A string identifying the traded symbol to benchmark
Side
: The side
of the trades, as "Buy" or "Sell"
Cum.Txn.Qty
: The cumulative units quantity traded
POV
: The POV rate of the order
PWP.Shares
: The ratio between the total unit traded and the POV rate
Avg.Exec.Price
: Symbol transactions average execution price
PWP.Price
: Volume weighted price of the first PWP.Shares
traded
Performance
: The PWP PnL metric, in bps
For benchmark = 'RPM'
it contains:
Dates
: Dates of reference, the longer period between the trading period and a subset of MktData
Symbol
: A string identifying the traded symbol to benchmark
Side
: The side
of the trades, as "Buy" or "Sell".
Avg.Exec.Price
: Symbol transactions average execution price
Mkt.Price
: The market price in MktData
, retrived for console comparison
t.Mkt.Volmn
: Total market volume over the order timespan
t.Fav.Volmn
: Total market volume over the order timespan for which the average execution price of a 'Buy' ('Sell') order was lower (greater) than market prices
t.Unfav.Volmn
: The opposite of t.Fav.Volmn
RPM
: The relative performance measure. Decimal in the 0 to 1 range.
Quality
: A qualitiative RPM score over quintiles, bottom-up one of 'Poor', 'Fair', 'Average', 'Good', 'Excellent'. Present if verbose = TRUE
Vito Lestingi
Kissell, R. The Science of Algorithmic Trading and Portfolio Management (ISBN 978-0-12-401689-7)
initPortf
, addTxn
# examples consider daily data, a common use case for practitioners in the field
set.seed(333)
.blotter <- new.env()
data(ABC)
ABC.day <- ABC[which(as.Date(index(ABC)) == "2019-02-01"), ]
colnames(ABC.day) <- c('MktPrice', 'MktQty')
inds <- sample(nrow(ABC.day), 50)
abc.trades.day <- ABC.day[inds]
colnames(abc.trades.day) <- c('TxnPrice', 'TxnQty')
currency('USD')
stock('ABC', currency = 'USD', multiplier = 1, tick_size = 0.01)
initPortf('abc.port.day', symbols = 'ABC')
addTxns('abc.port.day', 'ABC', TxnData = abc.trades.day)
updatePortf('abc.port.day', 'ABC')
benchTradeBench <- benchTradePerf('abc.port.day', 'ABC', side = 1,
benchmark = 'TradeBench', MktData = ABC.day)
benchMktBenchOpen <- benchTradePerf('abc.port.day', 'ABC', side = 1, benchmark = 'MktBench',
type = list(price = 'Open'), MktData = ABC.day[1])
# performance against daily open price
benchMktBenchClose <- benchTradePerf('abc.port.day', 'ABC', side = 1,
benchmark = 'MktBench',
type = list(price = 'Close'),
MktData = ABC.day[nrow(ABC.day)])
# performance against daily closing price
benchMktBench <- benchTradePerf('abc.port.day', 'ABC', side = 1, benchmark = 'MktBench',
type = list(price = 'price-of-choice'), MktData = 5000)
benchVWAPinterv <- benchTradePerf('abc.port.day', 'ABC', side = 1, benchmark = 'VWAP',
type = list(vwap = 'interval'), MktData = ABC.day)
benchVWAPfull <- benchTradePerf('abc.port.day', 'ABC', side = 1, benchmark = 'VWAP',
type = list(vwap = 'full'), MktData = ABC.day)
benchPWP <- benchTradePerf('abc.port.day', 'ABC', side = 1, benchmark = 'PWP',
POV = 0.3, MktData = ABC.day)
benchRPM <- benchTradePerf('abc.port.day', 'ABC', side = 1, benchmark = 'RPM',
MktData = ABC.day)
plot(benchTradeBench, benchmark = 'TradeBench')
plot(benchMktBenchOpen, benchmark = 'MktBench')
plot(benchMktBenchClose, benchmark = 'MktBench')
plot(benchVWAPfull, benchmark = 'VWAP')
plot(benchPWP, benchmark = 'PWP')
plot(benchRPM, benchmark = 'RPM')
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.