Title: | Limit Order Book Analytics |
---|---|
Description: | Data processing, visualisation and analysis of Limit Order Book event data. |
Authors: | Philip Stubbings [aut, cre], Petr Fedorov [ctb] |
Maintainer: | Philip Stubbings <[email protected]> |
License: | GPL (>= 2) |
Version: | 0.1.2 |
Built: | 2024-11-08 03:27:27 UTC |
Source: | https://github.com/phil8192/ob-analytics |
Limit order book analytics.
Limit order book event processing.
Visualise order book state and market impacts.
Order book reconstruction and analysis.
The main focus of this package is reconstruction of a limit order book.
The processData
function will perform data processing based on
a supplied CSV file, the schema of which is defined in the
processData
function documentation. Example preprocessed limit
order data are also provided (see lob.data
) which has been
derived from the example raw data provided the inst/extdata directory.
The data processing consists of a number of stages:
Cleaning of duplicate and erroneous data.
Identification of sequential event relationships.
Inference of trade events via order-matching.
Inference of order types (limit vs market).
Construction of volume by price level series.
Construction of order book summary statistics.
Limit order events are related to one another by volume deltas (the change in volume for a limit order). To simulate a matching-engine, and thus determine directional trade data, volume deltas from both sides of the limit order book are ordered by time, yielding a sequence alignment problem, to which the the Needleman-Wunsch algorithm has been applied.
The package provides a number of functions for the visualisation of limit order events and order book liquidity. The visualisations all make use of the ggplot2 plotting system:
General time series plotting.
Plot trades
data.
Visualise the shape of an
orderBook
.
Visualise volume by price level through time.
Visualise order book liquidity through time.
Visualise sequential limit order events by price level.
Visualise sequential limit order events by volume.
Convenience function.
The plotPriceLevels
visualisation is designed to show the
ebb and flow of limit order volume at all price levels including the
interplay between the bid/ask spread. It is possible to identify interesting
market participant behaviour and to visualise market shocks and resilience
with this function.
The plotEventMap
function is useful for studying systematic
market participant behaviour. Interesting sequential patterns can be observed
in this visualisation as algorithms react to various market events by
repositioning orders.
The plotVolumeMap
function shows a visualisation of cancelled
volume through time. It is possible to identify and filter out individual
trading algorithms from this graph.
The plotVolumePercentiles
visualisation is inspired by the size
map chart included in many articles from
Nanex research and is intended to
show available market liquidity.
In all visualisations it is possible to filter the data by time, price and volume.
In addition to the generated lob.data
which are intended to be
used as a basis for further research, the package currently provides a
limited set of trade and order book analysis functions:
Filter depth
data by time period.
Extract the bid/ask quotes from the
depth.summary
data.
Reconstruct a Limit order book from
events
data.
Group trades
into individual impact events.
Additional functionality will be added to the package in the future.
Philip Stubbings [email protected]
http://parasec.net/transmission/order-book-visualisation
Price level depth (liquidity) through time.
A data.frame consisting of the following fields:
Time at which volume was added or removed.
Order book price level.
Amount of remaining volume at this price level.
The side of the price level: bid or ask.
The depth data.frame describes the amount of available volume for all price levels in the limit order book through time. Each row corresponds to a limit order event, in which volume has been added or removed.
phil
Other Limit order book data: depth.summary
,
events
, trades
Limit order book summary statistics.
A data.frame consisting of the following fields:
Local timestamp corresponding to events
.
Best bid price.
Amount of volume available at the best bid.
The amount of volume available for 20 25bps percentiles below the best bid.
The best ask price.
Amount of volume available at the best ask.
The amount od volume available for 20 25bps percentiles above the best ask.
Various summary statistics describing the state of the order book after every limit order event. The metrics are intended to quantify the shape of the order book through time.
phil
Other Limit order book data: depth
,
events
, trades
A data.frame containing the lifecycle of limit orders.
A data.frame consisting of the following fields:
Event ID.
Limit Order ID.
Local timestamp for order update (create/modify/delete).
Exchange order creation time.
Limit order price level.
Remaining limit order volume.
Event action: created, changed, deleted.
Order book side: bid, ask.
For changed or deleted events, indicates the change in volume.
Matching event.id
if this event is part of a
trade. NA
otherwise.
Limit order type (see Event types below.)
The distance of the order from the edge of the book in Basis Points (BPS).
Each limit order type has been categorised as follows:
It was not possible to infer the order type given the available data.
Order was created then subsequently deleted. 96% of example data.
Order was created and left in order book indefinitely until filled.
Order was partially filled before landing in the order book at it's limit price.
Order was completely filled and did not come to rest in the order book.
A limit-price modified in situ (exchange algorithmic order).
The purpose of this table is to keep account of the lifecycle of all orders in both sides of the limit order book. The lifecycle of an individual limit order follows a sequence of events:
The order is created with a specified amount of volume and a limit price.
The order has been partially filled. On each modification, the remaining volume will decrease.
The order may be deleted at the request of the trader or, in the event that the order has been completely filled, deleted by the exchange. An order deleted by the exchange as a result of being filled will have 0 remaining volume at time of deletion.
phil
Other Limit order book data: depth.summary
,
depth
, trades
Given depth data calculated by priceLevelVolume
, filter
between a specified time range. The resulting data will contain price level
volume which is active only within the specified time range.
filterDepth(d, from, to)
filterDepth(d, from, to)
d |
|
from |
Beginning of range. |
to |
End of range. |
For price levels with volume > 0 before the time range starts, timestamps
will be set to the supplied from
parameter.
For volume > 0 after the time range ends, timestamps will be set to the
supplied to
parameter and volume set to 0.
For example, the following data taken from priceLevelVolume
for price level 243.29 shows the available volume through time at that
price level between 00:52:37.686
and 03:28:49.621
.
timestamp | price | volume | side |
2015-05-01 00:52:37.686 | 243.29 | 911500000 | ask |
2015-05-01 01:00:36.243 | 243.29 | 862200000 | ask |
2015-05-01 02:45:43.052 | 243.29 | 0 | ask |
2015-05-01 02:52:24.063 | 243.29 | 614700000 | ask |
2015-05-01 02:52:51.413 | 243.29 | 0 | ask |
2015-05-01 02:53:13.904 | 243.29 | 952300000 | ask |
2015-05-01 03:28:49.621 | 243.29 | 0 | ask |
applying filterDepth
to this data for a time range beteen
02:45
and 03:00
will result in the following:
timestamp | price | volume | side |
2015-05-01 02:45:00.000 | 243.29 | 862200000 | ask |
2015-05-01 02:45:43.052 | 243.29 | 0 | ask |
2015-05-01 02:52:24.063 | 243.29 | 614700000 | ask |
2015-05-01 02:52:51.413 | 243.29 | 0 | ask |
2015-05-01 02:53:13.904 | 243.29 | 952300000 | ask |
2015-05-01 03:00:00.000 | 243.29 | 0 | ask |
Note that the timestamps at the begining and end of the table have been clamped to the specified range and the volume set to 0 at the end.
Filtered depth data.
phil
# obtain price level volume for a 15 minute window. filtered <- with(lob.data, filterDepth(depth, from=as.POSIXct("2015-05-01 02:45:00.000", tz="UTC"), to=as.POSIXct("2015-05-01 03:00:00.000", tz="UTC"))) # top 5 most active price levels during this 15 minute window. head(sort(tapply(filtered$volume, filtered$price, length), decreasing=TRUE), 5) # extract available volume for price level 233.78, then plot it. level.233.78 <- filtered[filtered$price == 233.78, c("timestamp", "volume")] plotTimeSeries(level.233.78$timestamp, level.233.78$volume*10^-8)
# obtain price level volume for a 15 minute window. filtered <- with(lob.data, filterDepth(depth, from=as.POSIXct("2015-05-01 02:45:00.000", tz="UTC"), to=as.POSIXct("2015-05-01 03:00:00.000", tz="UTC"))) # top 5 most active price levels during this 15 minute window. head(sort(tapply(filtered$volume, filtered$price, length), decreasing=TRUE), 5) # extract available volume for price level 233.78, then plot it. level.233.78 <- filtered[filtered$price == 233.78, c("timestamp", "volume")] plotTimeSeries(level.233.78$timestamp, level.233.78$volume*10^-8)
Extracts the spread from the depth summary, removing any points in which a change to bid/ask price/volume did not occur.
getSpread(depth.summary)
getSpread(depth.summary)
depth.summary |
|
The spread (best bid and ask price) will change following a market order or upon the addition/cancellation of a limit order at, or within, the range of the current best bid/ask. A change to the spread that is not the result of a market order (an impact/market shock) is known as a quote.
The following table shows a market spread betwen 05:03:22.546
and
05:04:42.957
. During this time, the best ask price and volume changes
whilst the best bid price and volume remains static.
timestamp | bid.price | bid.vol | ask.price | ask.vol |
05:03:22.546 | 235.45 | 16235931 | 235.72 | 39375160 |
05:03:24.990 | 235.45 | 16235931 | 235.72 | 21211607 |
05:03:25.450 | 235.45 | 16235931 | 235.71 | 39375160 |
05:04:15.477 | 235.45 | 16235931 | 235.72 | 39058160 |
05:04:16.670 | 235.45 | 16235931 | 235.71 | 39058160 |
05:04:42.957 | 235.45 | 16235931 | 235.71 | 77019160 |
Bid/Ask spread quote data.
phil
# get the last 25 quotes (changes to the spread). with(lob.data, tail(getSpread(depth.summary), 25))
# get the last 25 quotes (changes to the spread). with(lob.data, tail(getSpread(depth.summary), 25))
Loads previously saved pre-processed data.
loadData(bin.file, ...)
loadData(bin.file, ...)
bin.file |
File location. |
... |
Convenience function.
Limit order, trade and depth data structure lob.data
.
phil
## Not run: lob.data <- loadData(bin.file="/tmp/lob.data.rds") ## End(Not run)
## Not run: lob.data <- loadData(bin.file="/tmp/lob.data.rds") ## End(Not run)
50,393 limit order events. 482 trades.
data(lob.data)
data(lob.data)
A list containing 4 data frames as returned by
processData
5 hours of limit order book event data obtained from the Bitstamp (bitcoin)
exchange on 2015-05-01 (midnight until 5am). The data has been preprocessed
with the processData
function.
phil
https://www.bitstamp.net/websocket
https://github.com/phil8192/ticker
events
,
trades
,
depth
,
depth.summary
Given a set of events
, reconstructs a limit order book for a
specific point in time.
orderBook(events, tp = as.POSIXlt(Sys.time(), tz = "UTC"), max.levels = NULL, bps.range = 0, min.bid = 0, max.ask = Inf)
orderBook(events, tp = as.POSIXlt(Sys.time(), tz = "UTC"), max.levels = NULL, bps.range = 0, min.bid = 0, max.ask = Inf)
events |
Limit order |
tp |
Time point to re-construct order book at. |
max.levels |
Max number of price levels to return. |
bps.range |
Max depth to return +- BPS from best bid/ask. |
min.bid |
Min bid to return. |
max.ask |
Max ask to return. |
An order book consists of 2 sides: bids and asks, an example of which is shown below:
id | price | volume | liquidity | bps |
65613703 | 236.58 | 910229141 | 6341547077 | 2.11 |
65613655 | 236.56 | 1320000000 | 5431317936 | 1.26 |
65613700 | 236.55 | 1320000000 | 4111317936 | 0.84 |
65613698 | 236.54 | 1600000000 | 2791317936 | 0.42 |
65613712 | 236.53 | 1191317936 | 1191317936 | 0.00 |
- | - | - | - | - |
65613225 | 236.36 | 16154172 | 16154172 | 0.00 |
65613681 | 236.31 | 200000000 | 216154172 | 2.11 |
65613220 | 236.30 | 100000000 | 316154172 | 2.53 |
65612978 | 236.28 | 100000000 | 416154172 | 3.38 |
65612388 | 236.17 | 100000000 | 516154172 | 8.03 |
Limit Order Book structure. A list containing 3 fields:
Timestamp the order book was reconstructed for.
A data.frame containing the Ask side of the order book.
A data.frame containing the Bid side of the order book.
The bids and asks data consists of the following:
Limit order Id.
Last modification time to limit order.
Time at which order was placed in order book.
Limit order price.
Limit orer volume.
Cumulative sum of volume from best bid/ask up until price.
Distance (in BPS) of order from best bid/ask.
Both the bids and asks data are ordered by descending price.
phil
tp <- as.POSIXct("2015-05-01 04:25:15.342", tz="UTC") orderBook(lob.data$events, max.levels=5)
tp <- as.POSIXct("2015-05-01 04:25:15.342", tz="UTC") orderBook(lob.data$events, max.levels=5)
Plots the cumalative volume on each side of the limit order book.
plotCurrentDepth(order.book, volume.scale = 1, show.quantiles = T, show.volume = T)
plotCurrentDepth(order.book, volume.scale = 1, show.quantiles = T, show.volume = T)
order.book |
A limit |
volume.scale |
Volume scale factor. |
show.quantiles |
If true, highlight top 1% highest volume. |
show.volume |
If true, also show non-cumulative volume. |
phil
# get a limit order book for a specific point in time, limited to +- 150bps # above/below best bid/ask price. lob <- orderBook(lob.data$events, tp=as.POSIXct("2015-05-01 04:38:17.429", tz="UTC"), bps.range=150) # visualise the order book liquidity. plotCurrentDepth(lob, volume.scale=10^-8)
# get a limit order book for a specific point in time, limited to +- 150bps # above/below best bid/ask price. lob <- orderBook(lob.data$events, tp=as.POSIXct("2015-05-01 04:38:17.429", tz="UTC"), bps.range=150) # visualise the order book liquidity. plotCurrentDepth(lob, volume.scale=10^-8)
Generates a visualisation of limit order events (excluding market and market limit orders).
plotEventMap(events, start.time = min(events$timestamp), end.time = max(events$timestamp), price.from = NULL, price.to = NULL, volume.from = NULL, volume.to = NULL, volume.scale = 1)
plotEventMap(events, start.time = min(events$timestamp), end.time = max(events$timestamp), price.from = NULL, price.to = NULL, volume.from = NULL, volume.to = NULL, volume.scale = 1)
events |
Limit order |
start.time |
Plot events from this time onward. |
end.time |
Plot events up until this time. |
price.from |
Plot events with price levels >= this value. |
price.to |
Plot events with price levels <= this value. |
volume.from |
Plot events with volume >= this value relevant to volume.scale |
volume.to |
Plot events with volume <= this value relevant to volume scale. |
volume.scale |
Volume scale factor. |
Ask side orders = red.
Bid side orders = blue.
Volume of order determines size of circle.
Opaque = volume was added.
Transparent = volume was removed.
phil
## Not run: # plot all orders with(lob.data, plotEventMap(events)) ## End(Not run) # 1 hour of activity and re-scale the volume with(lob.data, plotEventMap(events, start.time=as.POSIXct("2015-05-01 03:30:00.000", tz="UTC"), end.time=as.POSIXct("2015-05-01 04:00:00.000", tz="UTC"), volume.scale=10^-8)) # 15 minutes of activity >= 5 (re-scaled) volume within price range # $ [220, 245] with(lob.data, plotEventMap(events, start.time=as.POSIXct("2015-05-01 03:30:00.000", tz="UTC"), end.time=as.POSIXct("2015-05-01 03:45:00.000", tz="UTC"), price.from=220, price.to=245, volume.from=5, volume.scale=10^-8))
## Not run: # plot all orders with(lob.data, plotEventMap(events)) ## End(Not run) # 1 hour of activity and re-scale the volume with(lob.data, plotEventMap(events, start.time=as.POSIXct("2015-05-01 03:30:00.000", tz="UTC"), end.time=as.POSIXct("2015-05-01 04:00:00.000", tz="UTC"), volume.scale=10^-8)) # 15 minutes of activity >= 5 (re-scaled) volume within price range # $ [220, 245] with(lob.data, plotEventMap(events, start.time=as.POSIXct("2015-05-01 03:30:00.000", tz="UTC"), end.time=as.POSIXct("2015-05-01 03:45:00.000", tz="UTC"), price.from=220, price.to=245, volume.from=5, volume.scale=10^-8))
Convenience function for plotting event price and volume histograms. Will plot ask/bid bars side by side.
plotEventsHistogram(events, start.time = min(events$timestamp), end.time = max(events$timestamp), val = "volume", bw = NULL)
plotEventsHistogram(events, start.time = min(events$timestamp), end.time = max(events$timestamp), val = "volume", bw = NULL)
events |
Limit order |
start.time |
Include event data >= this time. |
end.time |
Include event data <= this time. |
val |
"volume" or "price". |
bw |
Bar width (for price, 0.5 = 50 cent buckets.) |
phil
# necessary columns from event data. events <- lob.data$events[, c("timestamp", "direction", "price", "volume")] # re-scale volume (if needed) events$volume <- events$volume * 10^-8 # histogram of all volume aggregated into 5 unit buckets. plotEventsHistogram(events[events$volume < 50, ], val="volume", bw=5) # histogram of 99% of limit prices during a 1 hour time frame. # bar width set to 0.25: counts are aggregated into 25 cent buckets. plotEventsHistogram(events[events$price <= quantile(events$price, 0.99) & events$price >= quantile(events$price, 0.01), ], start.time=as.POSIXct("2015-05-01 02:15:00.000", tz="UTC"), end.time=as.POSIXct("2015-05-01 03:15:00.000", tz="UTC"), val="price", bw=0.25)
# necessary columns from event data. events <- lob.data$events[, c("timestamp", "direction", "price", "volume")] # re-scale volume (if needed) events$volume <- events$volume * 10^-8 # histogram of all volume aggregated into 5 unit buckets. plotEventsHistogram(events[events$volume < 50, ], val="volume", bw=5) # histogram of 99% of limit prices during a 1 hour time frame. # bar width set to 0.25: counts are aggregated into 25 cent buckets. plotEventsHistogram(events[events$price <= quantile(events$price, 0.99) & events$price >= quantile(events$price, 0.01), ], start.time=as.POSIXct("2015-05-01 02:15:00.000", tz="UTC"), end.time=as.POSIXct("2015-05-01 03:15:00.000", tz="UTC"), val="price", bw=0.25)
Produces a visualisation of the limit order book depth through time.
plotPriceLevels(depth, spread = NULL, trades = NULL, show.mp = T, show.all.depth = F, col.bias = 0.1, start.time = head(depth$timestamp, 1), end.time = tail(depth$timestamp, 1), price.from = NULL, price.to = NULL, volume.from = NULL, volume.to = NULL, volume.scale = 1, price.by = NULL)
plotPriceLevels(depth, spread = NULL, trades = NULL, show.mp = T, show.all.depth = F, col.bias = 0.1, start.time = head(depth$timestamp, 1), end.time = tail(depth$timestamp, 1), price.from = NULL, price.to = NULL, volume.from = NULL, volume.to = NULL, volume.scale = 1, price.by = NULL)
depth |
The order book |
spread |
Spread to overlay obtained from |
trades |
|
show.mp |
If True, spread will be summarised as midprice. |
show.all.depth |
If True, show resting (and never hit) limit orders. |
col.bias |
1 = uniform colour spectrum. 0.25 = bias toward 0.25 (more red less blue). <= 0 enables logarithmic scaling. |
start.time |
Plot depth from this time onward. |
end.time |
Plot depth up until this time. |
price.from |
Plot depth with price levels >= this value. |
price.to |
Plot depth with price levels <= this value. |
volume.from |
Plot depth with volume >= this value relevant to volume.scale |
volume.to |
Plot depth with volume <= this value relevant to volume scale. |
volume.scale |
Volume scale factor. |
price.by |
The increment for the 'limit price' scale (y) |
The available volume at each price level is colour coded according to the range of volume at all price levels. The colour coding follows the visible spectrum, such that larger amounts of volume appear "hotter" than smaller amounts, where cold = blue, hot = red.
Since the distribution of limit order size exponentially decays, it can be difficult to visually differentiate: most values will appear to be blue. The function provides price, volume and a colour bias range to overcome this.
phil
# bid/ask spread. spread <- with(lob.data, getSpread(depth.summary)) ## Not run: # plot all depth levels, rescaling the volume by 10^-8. # produce 2 plots side-by-side: second plot contains depth levels with > 50 # units of volume. p1 <- with(lob.data, plotPriceLevels(depth, spread, col.bias=0.1, volume.scale=10^-8)) p2 <- with(lob.data, plotPriceLevels(depth, spread, col.bias=0.1, volume.scale=10^-8, volume.from=50)) library(grid) pushViewport(viewport(layout=grid.layout(1, 2))) print(p1, vp=viewport(layout.pos.row=1, layout.pos.col=1)) print(p2, vp=viewport(layout.pos.row=1, layout.pos.col=2)) ## End(Not run) # zoom into 1 hour of activity, show the spread and directional trades. with(lob.data, plotPriceLevels(depth, spread, trades, start.time=as.POSIXct("2015-05-01 03:25:00.000", tz="UTC"), end.time=as.POSIXct("2015-05-01 04:25:00.000", tz="UTC"), volume.scale=10^-8)) # zoom in to 15 minutes of activity, show the bid/ask midprice. with(lob.data, plotPriceLevels(depth, spread, show.mp=FALSE, start.time=as.POSIXct("2015-05-01 03:30:00.000", tz="UTC"), end.time=as.POSIXct("2015-05-01 03:45:00.000", tz="UTC")))
# bid/ask spread. spread <- with(lob.data, getSpread(depth.summary)) ## Not run: # plot all depth levels, rescaling the volume by 10^-8. # produce 2 plots side-by-side: second plot contains depth levels with > 50 # units of volume. p1 <- with(lob.data, plotPriceLevels(depth, spread, col.bias=0.1, volume.scale=10^-8)) p2 <- with(lob.data, plotPriceLevels(depth, spread, col.bias=0.1, volume.scale=10^-8, volume.from=50)) library(grid) pushViewport(viewport(layout=grid.layout(1, 2))) print(p1, vp=viewport(layout.pos.row=1, layout.pos.col=1)) print(p2, vp=viewport(layout.pos.row=1, layout.pos.col=2)) ## End(Not run) # zoom into 1 hour of activity, show the spread and directional trades. with(lob.data, plotPriceLevels(depth, spread, trades, start.time=as.POSIXct("2015-05-01 03:25:00.000", tz="UTC"), end.time=as.POSIXct("2015-05-01 04:25:00.000", tz="UTC"), volume.scale=10^-8)) # zoom in to 15 minutes of activity, show the bid/ask midprice. with(lob.data, plotPriceLevels(depth, spread, show.mp=FALSE, start.time=as.POSIXct("2015-05-01 03:30:00.000", tz="UTC"), end.time=as.POSIXct("2015-05-01 03:45:00.000", tz="UTC")))
Convenience function for plotting time series.
plotTimeSeries(timestamp, series, start.time = min(timestamp), end.time = max(timestamp), title = "time series", y.label = "series")
plotTimeSeries(timestamp, series, start.time = min(timestamp), end.time = max(timestamp), title = "time series", y.label = "series")
timestamp |
POSIXct timestamps. |
series |
The time series. |
start.time |
Plot from this time onward. |
end.time |
Plot up until this time. |
title |
Plot title. |
y.label |
Y axis label of the plot. |
phil
# plot trades. with(lob.data$trades, plotTimeSeries(timestamp, price)) # plot a general time series. timestamp <- seq(as.POSIXct("2015-05-01 00:00:00.000", tz="UTC"), as.POSIXct("2015-05-01 00:59:00.000", tz="UTC"), by=60) series <- rep(1:10, 6) plotTimeSeries(timestamp, series)
# plot trades. with(lob.data$trades, plotTimeSeries(timestamp, price)) # plot a general time series. timestamp <- seq(as.POSIXct("2015-05-01 00:00:00.000", tz="UTC"), as.POSIXct("2015-05-01 00:59:00.000", tz="UTC"), by=60) series <- rep(1:10, 6) plotTimeSeries(timestamp, series)
A convenience function for plotting the trades data.frame in a nice way.
plotTrades(trades, start.time = min(trades$timestamp), end.time = max(trades$timestamp))
plotTrades(trades, start.time = min(trades$timestamp), end.time = max(trades$timestamp))
trades |
|
start.time |
Plot from. |
end.time |
Plot to. |
phil
with(lob.data, plotTrades(trades))
with(lob.data, plotTrades(trades))
Plots the points at which volume was added or removed from the limit order book.
plotVolumeMap(events, action = "deleted", type = c("flashed-limit"), start.time = min(events$timestamp), end.time = max(events$timestamp), price.from = NULL, price.to = NULL, volume.from = NULL, volume.to = NULL, volume.scale = 1, log.scale = F)
plotVolumeMap(events, action = "deleted", type = c("flashed-limit"), start.time = min(events$timestamp), end.time = max(events$timestamp), price.from = NULL, price.to = NULL, volume.from = NULL, volume.to = NULL, volume.scale = 1, log.scale = F)
events |
Limit order |
action |
"deleted" for cancelled volume, "added" for added volume. |
type |
default = c("flashed-limit"). Set of types. |
start.time |
Plot events from this time onward. |
end.time |
Plot events up until this time. |
price.from |
Plot events with price levels >= this value. |
price.to |
Plot events with price levels <= this value. |
volume.from |
Plot events with volume >= this value relevant to volume.scale |
volume.to |
Plot events with volume <= this value relevant to volume scale. |
volume.scale |
Volume scale factor. |
log.scale |
If true, plot volume on logarithmic scale. |
A flashed limit-order is a "fleeting" limit order: an order was added, then removed (usually within a very short period of time). This plot is especially useful for identifying individual trading algorithms by price and volume.
phil
# plot all fleeting limit order volume using logarithmic scale. with(lob.data, plotVolumeMap(events, volume.scale=10^-8, log.scale=TRUE)) # "fleeting" order volume within 1 hour range up until 10 units of volume. with(lob.data, plotVolumeMap(events, volume.scale=10^-8, start.time=as.POSIXct("2015-05-01 02:30:00.000", tz="UTC"), end.time=as.POSIXct("2015-05-01 03:30:00.000", tz="UTC"), volume.to=10))
# plot all fleeting limit order volume using logarithmic scale. with(lob.data, plotVolumeMap(events, volume.scale=10^-8, log.scale=TRUE)) # "fleeting" order volume within 1 hour range up until 10 units of volume. with(lob.data, plotVolumeMap(events, volume.scale=10^-8, start.time=as.POSIXct("2015-05-01 02:30:00.000", tz="UTC"), end.time=as.POSIXct("2015-05-01 03:30:00.000", tz="UTC"), volume.to=10))
Plots the available volume in 25bps increments on each side of the order book in the form of a stacked area graph.
plotVolumePercentiles(depth.summary, start.time = head(depth.summary$timestamp, 1), end.time = tail(depth.summary$timestamp, 1), volume.scale = 1, perc.line = T, side.line = T)
plotVolumePercentiles(depth.summary, start.time = head(depth.summary$timestamp, 1), end.time = tail(depth.summary$timestamp, 1), volume.scale = 1, perc.line = T, side.line = T)
depth.summary |
|
start.time |
Plot events from this time onward. |
end.time |
Plot events up until this time. |
volume.scale |
Volume scale factor. |
perc.line |
If true, separate percentiles with subtle line. |
side.line |
If true, separate bid/ask side with subtle line. |
The top of the graph depicts the ask side of the book, whilst the bottom depicts the bid side. Percentiles and order book sides can be separated by an optional subtle line for improved legibility.
phil
# visualise 2 hours of order book liquidity. # data will be aggregated to minute-by-minute resolution. plotVolumePercentiles(lob.data$depth.summary, start.time=as.POSIXct("2015-05-01 02:30:00.000", tz="UTC"), end.time=as.POSIXct("2015-05-01 04:30:00.000", tz="UTC"), volume.scale=10^-8) ## Not run: # visualise 15 minutes of order book liquidity. # data will be aggregated to second-by-second resolution. plotVolumePercentiles(lob.data$depth.summary, start.time=as.POSIXct("2015-05-01 04:30:00.000", tz="UTC"), end.time=as.POSIXct("2015-05-01 04:35:00.000", tz="UTC"), volume.scale=10^-8) ## End(Not run)
# visualise 2 hours of order book liquidity. # data will be aggregated to minute-by-minute resolution. plotVolumePercentiles(lob.data$depth.summary, start.time=as.POSIXct("2015-05-01 02:30:00.000", tz="UTC"), end.time=as.POSIXct("2015-05-01 04:30:00.000", tz="UTC"), volume.scale=10^-8) ## Not run: # visualise 15 minutes of order book liquidity. # data will be aggregated to second-by-second resolution. plotVolumePercentiles(lob.data$depth.summary, start.time=as.POSIXct("2015-05-01 04:30:00.000", tz="UTC"), end.time=as.POSIXct("2015-05-01 04:35:00.000", tz="UTC"), volume.scale=10^-8) ## End(Not run)
Imports and performs preprocessing of limit order data contained in a CSV.
processData(csv.file, price.digits = 2, volume.digits = 8)
processData(csv.file, price.digits = 2, volume.digits = 8)
csv.file |
Location of CSV file to import |
price.digits |
an integer indicating the number of decimal places in 'price' column of the CSV file |
volume.digits |
an integer indicating the number of decimal places in 'volume' column of the CSV file |
The CSV file is expected to contain 7 columns:
Numeric limit order unique identifier
Time in milliseconds when event received locally
Time in milliseconds when order first created on the exchange
Price level of order event. It will be rounded by round(price, price.digits)
Remaining order volume. It will be rounded by round(price, volume.digits)
Event type (see below)
Side of order book (bid or ask)
action describes the limit order life-cycle:
The limit order has been created
The limit order has been modified (partial fill)
The limit order was deleted. If the remaining volume is 0, the order has been filled.
An example dataset returned from this function can be seen in
lob.data
which is the result of processing the example data
included in the inst/extdata
directory of this package.
A list containing 4 data frames:
Limit order events.
Inferred trades (executions).
Order book price level depth through time.
Limit order book summary statistics.
phil
## Not run: csv.file <- system.file("extdata", "orders.csv.xz", package="obAnalytics") lob.data <- processData(csv.file) ## End(Not run)
## Not run: csv.file <- system.file("extdata", "orders.csv.xz", package="obAnalytics") lob.data <- processData(csv.file) ## End(Not run)
Saves processed data to file.
saveData(lob.data, bin.file, ...)
saveData(lob.data, bin.file, ...)
lob.data |
|
bin.file |
File to save to. |
... |
Convenience function.
phil
## Not run: saveData(lob.data, bin.file="/tmp/lob.data.rds", compress="xz") ## End(Not run)
## Not run: saveData(lob.data, bin.file="/tmp/lob.data.rds", compress="xz") ## End(Not run)
Generates a data.frame containing order book impacts.
tradeImpacts(trades)
tradeImpacts(trades)
trades |
|
An impact consists of 1 or more limit orders being hit in order to fulfil a market order.
A data.frame containing a summary of market order impacts:
market order id
minimum executed price
maximum executed price
VWAP obtained by market order
number of limit orders hit by market order
total volume removed by this impact
(local) start time of this impact
(local) end time of this impact
direction of this impact (buy or sell)
phil
# get impacts data.frame from trades data. impacts <- tradeImpacts(lob.data$trades) # impacts (in bps) sell.bps <- with(impacts[impacts$dir == "sell", ], { (max.price-min.price)/max.price }) 10000*summary(sell.bps[sell.bps > 0])
# get impacts data.frame from trades data. impacts <- tradeImpacts(lob.data$trades) # impacts (in bps) sell.bps <- with(impacts[impacts$dir == "sell", ], { (max.price-min.price)/max.price }) 10000*summary(sell.bps[sell.bps > 0])
Inferred trades (executions).
A data.frame consisting of the following fields:
Local event timestamp.
Price at which the trade occured.
Amount of traded volume.
The trade direction: buy or sell.
Corresponding market making event id in
events
.
Corresponding market taking event id in
events
.
Id of the market making limit order in
events
.
Id of the market taking limit order in
events
.
The trades data.frame contains a log of all executions ordered by local
timestamp. In addition to the usual timestamp, price and volume information,
each row also contains the trade direction (buyer or seller initiated) and
maker/taker limit order ids. The maker/taker event and limit order ids can
be used to group trades into market impacts. See:
tradeImpacts
.
phil
Other Limit order book data: depth.summary
,
depth
, events