-
Notifications
You must be signed in to change notification settings - Fork 5
/
read_functions.R
776 lines (694 loc) · 24.9 KB
/
read_functions.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
#' @name read_functions
#' @rdname read_functions
#' @title Reads certain messages of an ITCH-file into a data.table
#'
#' @description
#'
#' For faster file-reads (at the tradeoff of increased memory usages), you can
#' increase the `buffer_size` to 1GB (1e9) or more.
#'
#' If you access the same file multiple times, you can provide the message
#' counts as outputted from [count_messages()] to the `n_max`
#' argument, this allows skipping one pass over the file per read instruction.
#'
#' If you need to read in multiple message classes, you can specify multiple
#' message classes to `read_itch`, which results in only a single file pass.
#'
#' If the file is too large to be loaded into the workspace at once, you can
#' specify different `skip` and `n_max` to load only
#' a specific range of messages.
#' Alternatively, you can filter certain messages to another file using
#' [filter_itch()], which is substantially faster than parsing a file
#' and filtering it.
#'
#' Note that all read functions allow both plain ITCH files as well as gzipped
#' files.
#' If a gzipped file is found, it will look for a plain ITCH file with
#' the same name and use that instead.
#' If this file is not found, it will be created by unzipping the archive.
#' Note that the unzipped file is NOT deleted by default (the file will be
#' created in the current working directory).
#' It might result in increased disk usage but reduces future read times for
#' that specific file.
#' To force RITCH to delete "temporary" files after uncompressing, use
#' `force_cleanup = TRUE` (only deletes the files if they were extracted
#' before, does not remove the archive itself).
#'
#' @param file the path to the input file, either a gz-archive or a plain ITCH file
#' @param filter_msg_class a vector of classes to load, can be "orders", "trades",
#' "modifications", ... see also [get_msg_classes()].
#' Default value is to take all message classes.
#' @param skip Number of messages to skip before starting parsing messages,
#' note the skip parameter applies to the specific message class, i.e., it would
#' skip the messages for each type (e.g., skip the first 10 messages for each class).
#' @param n_max Maximum number of messages to parse, default is to read all values.
#' Can also be a data.frame of msg_types and counts, as returned by
#' [count_messages()].
#' Note the n_max parameter applies to the specific message class not the whole
#' file.
#' @param filter_msg_type a character vector, specifying a filter for message types.
#' Note that this can be used to only return 'A' orders for instance.
#' @param filter_stock_locate an integer vector, specifying a filter for locate codes.
#' The locate codes can be looked up by calling [read_stock_directory()]
#' or by downloading from NASDAQ by using [download_stock_directory()].
#' Note that some message types (e.g., system events, MWCB, and IPO) do not use
#' a locate code.
#' @param min_timestamp an 64 bit integer vector (see also [bit64::as.integer64()])
#' of minimum timestamp (inclusive).
#' Note: min and max timestamp must be supplied with the same length or left empty.
#' @param max_timestamp an 64 bit integer vector (see also [bit64::as.integer64()])
#' of maxium timestamp (inclusive).
#' Note: min and max timestamp must be supplied with the same length or left empty.
#' @param filter_stock a character vector, specifying a filter for stocks.
#' Note that this a shorthand for the `filter_stock_locate` argument, as it
#' tries to find the stock_locate based on the `stock_directory` argument,
#' if this is not found, it will try to extract the stock directory from the file,
#' else an error is thrown.
#' @param stock_directory A data.frame containing the stock-locate code relationship.
#' As outputted by [read_stock_directory()].
#' Only used if `filter_stock` is set. To download the stock directory from
#' NASDAQs server, use [download_stock_directory()].
#' @param buffer_size the size of the buffer in bytes, defaults to 1e8 (100 MB),
#' if you have a large amount of RAM, 1e9 (1GB) might be faster
#' @param quiet if TRUE, the status messages are suppressed, defaults to FALSE
#' @param add_meta if TRUE, the date and exchange information of the file are added, defaults to TRUE
#' @param force_gunzip only applies if the input file is a gz-archive and a file with the same (gunzipped) name already exists.
#' if set to TRUE, the existing file is overwritten. Default value is FALSE
#' @param gz_dir a directory where the gz archive is extracted to.
#' Only applies if file is a gz archive. Default is [tempdir()].
#' @param force_cleanup only applies if the input file is a gz-archive.
#' If force_cleanup=TRUE, the gunzipped raw file will be deleted afterwards.
#' Only applies when the gunzipped raw file did not exist before.
#' @param ... Additional arguments passed to `read_itch`
#' @param add_descriptions add longer descriptions to shortened variables.
#' The added information is taken from the official ITCH documentation
#' see also [open_itch_specification()]
#'
#' @details
#' The details of the different messages types can be found in the official
#' ITCH specification (see also [open_itch_specification()])
#'
#' @references <https://www.nasdaqtrader.com/content/technicalsupport/specifications/dataproducts/NQTVITCHspecification.pdf>
#'
#' @return a data.table containing the messages
#'
#' @examples
#' \dontshow{
#' data.table::setDTthreads(2)
#' }
#' file <- system.file("extdata", "ex20101224.TEST_ITCH_50", package = "RITCH")
#' od <- read_orders(file, quiet = FALSE) # note quiet = FALSE is the default
#' tr <- read_trades(file, quiet = TRUE)
#'
#' ## Alternatively
#' od <- read_itch(file, "orders", quiet = TRUE)
#'
#' ll <- read_itch(file, c("orders", "trades"), quiet = TRUE)
#'
#' od
#' tr
#' str(ll, max.level = 1)
#'
#' ## additional options:
#'
#' # take only subset of messages
#' od <- read_orders(file, skip = 3, n_max = 10)
#'
#' # a message count can be provided for slightly faster reads
#' msg_count <- count_messages(file, quiet = TRUE)
#' od <- read_orders(file, n_max = msg_count)
#'
#' ## .gz archive functionality
#' # .gz archives will be automatically unzipped
#' gz_file <- system.file("extdata", "ex20101224.TEST_ITCH_50.gz", package = "RITCH")
#' od <- read_orders(gz_file)
#' # force a decompress and delete the decompressed file afterwards
#' od <- read_orders(gz_file, force_gunzip = TRUE, force_cleanup = TRUE)
NULL
#' @rdname read_functions
#' @export
#' @details
#' - `read_itch`: Reads a message class message, can also read multiple
#' classes in one file-pass.
#'
#' @examples
#'
#' ## read_itch()
#' otm <- read_itch(file, c("orders", "trades"), quiet = TRUE)
#' str(otm, max.level = 1)
read_itch <- function(file, filter_msg_class = NA,
skip = 0, n_max = -1,
filter_msg_type = NA_character_,
filter_stock_locate = NA_integer_,
min_timestamp = bit64::as.integer64(NA),
max_timestamp = bit64::as.integer64(NA),
filter_stock = NA_character_, stock_directory = NA,
buffer_size = -1, quiet = FALSE, add_meta = TRUE,
force_gunzip = FALSE, gz_dir = tempdir(), force_cleanup = TRUE) {
t0 <- Sys.time()
if (!file.exists(file))
stop(sprintf("File '%s' not found!", file))
msg_classes <- list(
"system_events" = "S",
"stock_directory" = "R",
"trading_status" = c("H", "h"),
"reg_sho" = "Y",
"market_participant_states" = "L",
"mwcb" = c("V", "W"),
"ipo" = "K",
"luld" = "J",
"orders" = c("A", "F"),
"modifications" = c("E", "C", "X", "D", "U"),
"trades" = c("P", "Q", "B"),
"noii" = "I",
"rpii" = "N"
)
if (length(filter_msg_class) == 1 && is.na(filter_msg_class))
filter_msg_class <- names(msg_classes)
if (!all(filter_msg_class %in% names(msg_classes)))
stop("Invalid filter_msg_class detected")
# treat n_max
n_max_is_dataframe <- is.data.frame(n_max)
if (n_max_is_dataframe) {
if (!all(c("msg_type", "count") %in% names(n_max)))
stop("If n_max is a data.frame/table, it must contain 'msg_type' and 'count'!")
skip <- 0
n_max <- as.integer(n_max[msg_type %in% msg_classes[[filter_msg_class]],
sum(count)])
}
# +1 as we want to skip
start <- max(skip, 0)
end <- max(skip + n_max - 1, -1)
if (end < start) end <- -1
if (is.numeric(n_max) && n_max != -1 && !quiet && !n_max_is_dataframe)
cat("[Note] n_max overrides counting the messages. Number of messages may be off\n")
if (!quiet && (start != 0 | end >= 0))
cat(sprintf("[Filter] skip: %i n_max: %i (%i - %i)\n",
skip, n_max, start + 1, end + 1))
# Treat filters
# Message types
filter_msg_type <- check_msg_types(filter_msg_type, quiet)
# locate code
filter_stock_locate <- filter_stock_locate[!is.na(filter_stock_locate)]
filter_stock_locate <- as.integer(filter_stock_locate)
# Timestamp
t <- check_timestamps(min_timestamp, max_timestamp, quiet)
min_timestamp <- t$min
max_timestamp <- t$max
# Stock
filter_stock_locate <- check_stock_filters(filter_stock, stock_directory,
filter_stock_locate, file)
if (!quiet && length(filter_stock_locate) > 0)
cat(paste0("[Filter] stock_locate: '",
paste(filter_stock_locate, collapse = "', '"),
"'\n"))
if (any(length(filter_stock_locate) > 0,
length(filter_msg_type) > 0,
length(min_timestamp) > 0,
length(max_timestamp) > 0) && !quiet)
cat("NOTE: as filter arguments were given, the number of messages may be off\n")
# Set the default value of the buffer size
buffer_size <- check_buffer_size(buffer_size, file)
filedate <- get_date_from_filename(file)
orig_file <- file
# only needed for gz files; gz files are not deleted when the raw file already existed
raw_file_existed <- file.exists(gsub("\\.gz$", "", file))
file <- check_and_gunzip(file, gz_dir, buffer_size, force_gunzip, quiet)
res_raw <- read_itch_impl(filter_msg_class, file, start, end,
filter_msg_type, filter_stock_locate,
min_timestamp, max_timestamp,
buffer_size, quiet)
if (!quiet) cat("[Converting] to data.table\n")
res <- lapply(res_raw, data.table::setalloccol)
if (add_meta) {
# add the date and exchange
res <- lapply(res, function(df) {
dtime <- nanotime::nanotime(NULL)
if (nrow(df) > 0)
dtime <- nanotime::nanotime(as.Date(filedate)) + df$timestamp
df[, ':=' (
date = filedate,
datetime = dtime,
exchange = get_exchange_from_filename(file)
)]
})
}
# remove messages with empty msg_types, this can be the case if n_max was set
# to a large value
res <- lapply(res, function(df) df[msg_type != ""])
# if the res list has only one element, unlist on one level!
if (length(res) == 1) {
res <- res[[1]]
} else {
# take only messages with nrow > 0
res <- res[sapply(res, nrow) > 0]
if (length(res) == 0 && !quiet)
warning("No messages found for selected filters")
}
a <- gc()
report_end(t0, quiet, orig_file)
# if the file was gzipped and the force_cleanup=TRUE, delete unzipped file
if (grepl("\\.gz$", orig_file) && force_cleanup && !raw_file_existed) {
if (!quiet) cat(sprintf("[Cleanup] Removing file '%s'\n", file))
unlink(gsub("\\.gz$", "", file))
}
return(res)
}
## convenient wrapper for read functions for the different classes
#' @rdname read_functions
#' @export
#' @details
#' - `read_system_events`: Reads system event messages. Message type `S`
#'
#' @examples
#'
#' ## read_system_events()
#' se <- read_system_events(file, add_descriptions = TRUE, quiet = TRUE)
#' se
read_system_events <- function(file, ..., add_descriptions = FALSE) {
dots <- list(...)
dots$file <- file
dots$filter_msg_class <- "system_events"
res <- do.call(read_itch, dots)
if (add_descriptions) {
names_ <- names(res)
ei <- data.table::data.table(
event_code = c("O", "S", "Q", "M", "E", "C"),
event_name = c("Start of Messages", "Start of System Hours", "Start of Market Hours", "End of Market Hours", "End of System Hours", "End of Messages"),
event_note = c(
"Outside of time stamp messages, the start of day message is the first message sent in any trading day",
"This message indicates that NASDAQ is open and ready to start accepting orders",
"This message is intended to indicate that Market Hours orders are available for execution",
"This message is intended to indicate that Market Hours orders are no longer available for execution",
"It indicates that Nasdaq is now closed and will not accept any new orders today. It is still possible to receive Broken Trade messages and Order Delete messages after the End of Day",
"This is always the last message sent in any trading day."
)
)
res <- merge(res, ei, by = "event_code", all.x = TRUE)
data.table::setcolorder(res, names_)
}
return(res)
}
#' @rdname read_functions
#' @export
#' @details
#' - `read_stock_directory`: Reads stock trading messages. Message type `R`
#'
#' @examples
#'
#' ## read_stock_directory()
#' sd <- read_stock_directory(file, add_descriptions = TRUE, quiet = TRUE)
#' sd
read_stock_directory <- function(file, ..., add_descriptions = FALSE) {
dots <- list(...)
dots$file <- file
dots$filter_msg_class <- "stock_directory"
res <- do.call(read_itch, dots)
if (add_descriptions) {
names_ <- names(res)
mcat <- data.table::data.table(
market_category = c("Q", "G", "S", "N", "A", "P", "Z", "V", " "),
market_category_note = c(
"Nasdaq Global Select Market",
"Nasdaq Global Market",
"Nasdaq Capital Market",
"New York Stock Exchange",
"NYSE MKT",
"NYSE Arca",
"BATS Z Exchange",
"Investor's Exchange, LLC",
NA_character_
)
)
res <- merge(res, mcat, by = "market_category", all.x = TRUE)
finstat <- data.table::data.table(
financial_status = c("D", "E", "Q", "S", "G", "H", "J", "K", "C", "N", " "),
financial_status_note = c(
"Deficient", "Delinquent", "Bankrupt", "Suspended", "Deficient and Bankrupt",
"Deficient and Delinquent", "Delinquent and Bankrupt", "Deficient, Delinquent and Bankrupt",
"Creations and/or Redemptions Suspended for Exchange Traded Product",
"Normal", NA_character_
)
)
res <- merge(res, finstat, by = "financial_status", all.x = TRUE)
luld <- data.table::data.table(
luld_price_tier = c("1", "2", " "),
luld_price_tier_note = c(
"Tier 1 NMS Stock and selected ETPs",
"Tier 2 NMS Stocks",
NA_character_
)
)
res <- merge(res, luld, by = "luld_price_tier", all.x = TRUE)
data.table::setcolorder(res, names_)
}
return(res)
}
#' @rdname read_functions
#' @export
#' @details
#' - `read_trading_status`: Reads trading status messages. Message type `H`
#' and `h`
#'
#' @examples
#'
#' ## read_trading_status()
#' ts <- read_trading_status(file, add_descriptions = TRUE, quiet = TRUE)
#' ts
read_trading_status <- function(file, ..., add_descriptions = FALSE) {
dots <- list(...)
dots$file <- file
dots$filter_msg_class <- "trading_status"
res <- do.call(read_itch, dots)
if (add_descriptions) {
names_ <- names(res)
trs <- data.table::data.table(
trading_state = c("H", "P", "Q", "T"),
trading_state_note = c(
"Halted across all US equity markets / SROs",
"Paused across all US equity markets / SROs (Nasdaq-listed securities only",
"Quotation only period for cross-SRO halt or pause",
"Trading on Nasdaq"
)
)
res <- merge(res, trs, by = "trading_state", all.x = TRUE)
mkt <- data.table::data.table(
market_code = c("Q", "B", "X"),
market_code_note = c("Nasdaq", "BX", "PSX")
)
res <- merge(res, mkt, by = "market_code", all.x = TRUE)
data.table::setcolorder(res, names_)
}
return(res)
}
#' @rdname read_functions
#' @export
#' @details
#' - `read_reg_sho`: Reads messages regarding reg SHO. Message type `Y`
#'
#' @examples
#'
#' ## read_reg_sho()
#' \dontrun{
#' # note the example file has no reg SHO messages
#' rs <- read_reg_sho(file, add_descriptions = TRUE, quiet = TRUE)
#' rs
#' }
read_reg_sho <- function(file, ..., add_descriptions = FALSE) {
dots <- list(...)
dots$file <- file
dots$filter_msg_class <- "reg_sho"
res <- do.call(read_itch, dots)
if (add_descriptions) {
names_ <- names(res)
ac <- data.table::data.table(
regsho_action = c("0", "1", "2"),
regsho_action_note = c(
"No price test in place",
"Reg SHO Short Sale Price Test Restriction in effect due to an intra-day price drop in security",
"Reg SHO Short Sale Price Test Restriction remains in effect"
)
)
res <- merge(res, ac, by = "regsho_action", all.x = TRUE)
data.table::setcolorder(res, names_)
}
return(res)
}
#' @rdname read_functions
#' @export
#' @details
#' - `read_market_participant_states`: Reads messages regarding the
#' status of market participants. Message type `L`
#'
#' @examples
#'
#' ## read_market_participant_states()
#' \dontrun{
#' # note the example file has no market participant states
#' mps <- read_market_participant_states(file, add_descriptions = TRUE,
#' quiet = TRUE)
#' mps
#' }
read_market_participant_states <- function(file, ..., add_descriptions = FALSE) {
dots <- list(...)
dots$file <- file
dots$filter_msg_class <- "market_participant_states"
res <- do.call(read_itch, dots)
if (add_descriptions) {
names_ <- names(res)
mmm <- data.table::data.table(
mm_mode = c("N", "P", "S", "R", "L"),
mm_mode_note = c("Normal", "Passive", "Syndicate", "Pre-Syndicate", "Penalty")
)
res <- merge(res, mmm, by = "mm_mode", all.x = TRUE)
ps <- data.table::data.table(
participant_state = c("A", "E", "W", "S", "D"),
participant_state_note = c("Active", "Excused/withdrawn", "Withdrawn",
"Suspended", "Deleted")
)
res <- merge(res, ps, by = "participant_state", all.x = TRUE)
data.table::setcolorder(res, names_)
}
return(res)
}
#' @rdname read_functions
#' @export
#' @details
#' - `read_mwcb`: Reads messages regarding Market-Wide-Circuit-Breakers
#' (MWCB). Message type `V` and `W`
#'
#' @examples
#'
#' ## read_mwcb()
#' \dontrun{
#' # note the example file has no circuit breakers messages
#' mwcb <- read_mwcb(file, quiet = TRUE)
#' mwcb
#' }
read_mwcb <- function(file, ...) {
dots <- list(...)
dots$file <- file
dots$filter_msg_class <- "mwcb"
# no filter for mwcb... they are always set to 0! in the messages
if ((length(dots$filter_stock_locate) > 0 && !is.na(dots$filter_stock_locate)) ||
(length(dots$filter_stock) > 0 && !is.na(dots$filter_stock)))
warning("filter_stock and filter_stock_locate not used for MWCB messages!")
dots$filter_stock_locate <- NA_integer_
dots$filter_stock <- NA_character_
do.call(read_itch, dots)
}
#' @rdname read_functions
#' @export
#' @details
#' - `read_ipo`: Reads messages regarding IPOs. Message type `K`
#'
#' @examples
#'
#' ## read_ipo()
#' \dontrun{
#' # note the example file has no IPOs
#' ipo <- read_ipo(file, add_descriptions = TRUE, quiet = TRUE)
#' ipo
#' }
read_ipo <- function(file, ..., add_descriptions = FALSE) {
dots <- list(...)
dots$file <- file
dots$filter_msg_class <- "ipo"
res <- do.call(read_itch, dots)
if (add_descriptions) {
names_ <- names(res)
desc <- data.table::data.table(
release_qualifier = c("A", "C"),
release_qualifier_note = c(
"Anticipated Quotation Release Time: This value would be used when Nasdaq Market Operations initially enters the IPO instrument for release",
"IPO Release Canceled/Postponed: This value would be sued when Nasdaq Market Operations cancels or postpones the release of the new IPO instrument"
)
)
res <- merge(res, desc, by = "release_qualifier", all.x = TRUE)
data.table::setcolorder(res, names_)
}
return(res)
}
#' @rdname read_functions
#' @export
#' @details
#' - `read_luld`: Reads messages regarding LULDs (limit up-limit down)
#' auction collars. Message type `J`
#'
#' @examples
#'
#' ## read_luld()
#' \dontrun{
#' # note the example file has no LULD messages
#' luld <- read_luld(file, quiet = TRUE)
#' luld
#' }
read_luld <- function(file, ...) {
dots <- list(...)
dots$file <- file
dots$filter_msg_class <- "luld"
do.call(read_itch, dots)
}
#' @rdname read_functions
#' @export
#' @details
#' - `read_orders`: Reads order messages. Message type `A` and `F`
#'
#' @examples
#'
#' ## read_orders()
#' od <- read_orders(file, quiet = TRUE)
#' od
read_orders <- function(file, ...) {
dots <- list(...)
dots$file <- file
dots$filter_msg_class <- "orders"
do.call(read_itch, dots)
}
#' @rdname read_functions
#' @export
#' @details
#' - `read_modifications`: Reads order modification messages. Message
#' type `E`, `C`, `X`, `D`, and `U`
#'
#' @examples
#'
#' ## read_modifications()
#' mod <- read_modifications(file, quiet = TRUE)
#' mod
read_modifications <- function(file, ...) {
dots <- list(...)
dots$file <- file
dots$filter_msg_class <- "modifications"
do.call(read_itch, dots)
}
#' @rdname read_functions
#' @export
#' @details
#' - `read_trades`: Reads trade messages. Message type `P`, `Q` and `B`
#'
#' @examples
#'
#' ## read_trades()
#' tr <- read_trades(file, quiet = TRUE)
#' tr
read_trades <- function(file, ...) {
dots <- list(...)
dots$file <- file
dots$filter_msg_class <- "trades"
do.call(read_itch, dots)
}
#' @rdname read_functions
#' @export
#' @details
#' - `read_noii`: Reads Net Order Imbalance Indicatio (NOII) messages.
#' Message type `I`
#'
#' @examples
#'
#' ## read_noii()
#' \dontrun{
#' # note the example file has no NOII messages
#' noii <- read_noii(file, add_descriptions = TRUE, quiet = TRUE)
#' noii
#' }
read_noii <- function(file, ..., add_descriptions = FALSE) {
dots <- list(...)
dots$file <- file
dots$filter_msg_class <- "noii"
res <- do.call(read_itch, dots)
if (add_descriptions) {
names_ <- names(res)
desc <- data.table::data.table(
imbalance_direction = c("B", "S", "N", "O"),
imbalance_direction_note = c("Buy Imbalance", "Sell Imbalance", "No Imbalance", "Insufficient Orders to Calculate")
)
res <- merge(res, desc, by = "imbalance_direction", all.x = TRUE)
desc <- data.table::data.table(
cross_type = c("O", "C", "H"),
cross_type_note = c("Nasdaq Opening Cross", "Nasdaq Closing Cross",
"Cross for IPO and halted/paused securities")
)
res <- merge(res, desc, by = "cross_type", all.x = TRUE)
ll <- list(
"L" = "Less than 1%",
"1" = "1 to 1.99%",
"2" = "2 to 2.99%",
"2" = "2 to 2.99%",
"3" = "3 to 3.99%",
"4" = "4 to 4.99%",
"5" = "5 to 5.99%",
"6" = "6 to 6.99%",
"7" = "7 to 7.99%",
"8" = "8 to 8.99%",
"9" = "9 to 9.99%",
"A" = "10 to 19.99%",
"B" = "20 to 29.99%",
"C" = "30% or greater",
" " = "Cannot be calculated"
)
desc <- data.table::data.table(
variation_indicator = names(ll),
variation_indicator_note = as.character(ll)
)
res <- merge(res, desc, by = "variation_indicator", all.x = TRUE)
data.table::setcolorder(res, names_)
}
return(res)
}
#' @rdname read_functions
#' @export
#' @details
#' - `read_rpii`: Reads Retail Price Improvement Indicator (RPII)
#' messages. Message type `N`
#'
#' @examples
#'
#' ## read_rpii()
#' \dontrun{
#' # note the example file has no RPII messages
#' rpii <- read_rpii(file, add_descriptions = TRUE, quiet = TRUE)
#' rpii
#' }
read_rpii <- function(file, ..., add_descriptions = FALSE) {
dots <- list(...)
dots$file <- file
dots$filter_msg_class <- "rpii"
res <- do.call(read_itch, dots)
if (add_descriptions) {
names_ <- names(res)
desc <- data.table::data.table(
interest_flag = c("B", "S", "A", "N"),
interest_flag_note = c(
"RPI orders available on the buy side",
"RPI orders available on the sell side",
"RPI orders available on both sides (buy and sell)",
"No RPI orders available"
)
)
res <- merge(res, desc, by = "interest_flag", all.x = TRUE)
data.table::setcolorder(res, names_)
}
return(res)
}
# For backwards compatibility only...
#' @rdname read_functions
#' @export
#' @details
#' For backwards compatability reasons, the following functions are provided as
#' well:
#'
#' - `get_orders`: Redirects to `read_orders`
#' @export
get_orders <- read_orders
#' @rdname read_functions
#' @export
#' @details
#' - `get_trades`: Redirects to `read_trades`
#' @export
get_trades <- read_trades
#' @rdname read_functions
#' @export
#' @details
#' - `get_modifications`: Redirects to `read_modifications`
get_modifications <- read_modifications