mirror of
				https://github.com/speice-io/marketdata-shootout
				synced 2025-11-04 02:21:01 -05:00 
			
		
		
		
	Add all the IEX parsers
This commit is contained in:
		
							
								
								
									
										1
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@ -105,6 +105,7 @@ dependencies = [
 | 
				
			|||||||
 "flatbuffers 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
					 "flatbuffers 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
				
			||||||
 "flatc-rust 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
					 "flatc-rust 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
				
			||||||
 "nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
					 "nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
				
			||||||
 | 
					 "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
 | 
				
			|||||||
@ -9,6 +9,7 @@ capnp = "0.10.1"
 | 
				
			|||||||
clap = "2.33.0"
 | 
					clap = "2.33.0"
 | 
				
			||||||
flatbuffers = "0.6.0"
 | 
					flatbuffers = "0.6.0"
 | 
				
			||||||
nom = "5.0.0"
 | 
					nom = "5.0.0"
 | 
				
			||||||
 | 
					smallvec = "0.6.10"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[build-dependencies]
 | 
					[build-dependencies]
 | 
				
			||||||
capnpc = "0.10"
 | 
					capnpc = "0.10"
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										6
									
								
								build.rs
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								build.rs
									
									
									
									
									
								
							@ -7,11 +7,13 @@ fn main() {
 | 
				
			|||||||
        .src_prefix("")
 | 
					        .src_prefix("")
 | 
				
			||||||
        .file("marketdata.capnp")
 | 
					        .file("marketdata.capnp")
 | 
				
			||||||
        .output_path("src/")
 | 
					        .output_path("src/")
 | 
				
			||||||
        .run().expect("Unable to compile capnpc");
 | 
					        .run()
 | 
				
			||||||
 | 
					        .expect("Unable to compile capnpc");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    flatc_rust::run(flatc_rust::Args {
 | 
					    flatc_rust::run(flatc_rust::Args {
 | 
				
			||||||
        inputs: &[Path::new("marketdata.fbs")],
 | 
					        inputs: &[Path::new("marketdata.fbs")],
 | 
				
			||||||
        out_dir: Path::new("src/"),
 | 
					        out_dir: Path::new("src/"),
 | 
				
			||||||
        ..Default::default()
 | 
					        ..Default::default()
 | 
				
			||||||
    }).expect("Unable to compile flatc");
 | 
					    })
 | 
				
			||||||
 | 
					        .expect("Unable to compile flatc");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										418
									
								
								src/iex.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										418
									
								
								src/iex.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,418 @@
 | 
				
			|||||||
 | 
					use std::convert::TryInto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use nom::{bytes::complete::take, IResult, number::complete::*, sequence::tuple};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					struct IexPayload {
 | 
				
			||||||
 | 
					    version: u8,
 | 
				
			||||||
 | 
					    _reserved: u8,
 | 
				
			||||||
 | 
					    proto_id: u16,
 | 
				
			||||||
 | 
					    channel_id: u32,
 | 
				
			||||||
 | 
					    session_id: u32,
 | 
				
			||||||
 | 
					    payload_len: u16,
 | 
				
			||||||
 | 
					    msg_count: u16,
 | 
				
			||||||
 | 
					    stream_offset: u64,
 | 
				
			||||||
 | 
					    first_seq_no: u64,
 | 
				
			||||||
 | 
					    send_time: i64,
 | 
				
			||||||
 | 
					    messages: smallvec::SmallVec<[IexMessage; 8]>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					enum IexMessage {
 | 
				
			||||||
 | 
					    SystemEvent(SystemEvent),
 | 
				
			||||||
 | 
					    SecurityDirectory(SecurityDirectory),
 | 
				
			||||||
 | 
					    TradingStatus(TradingStatus),
 | 
				
			||||||
 | 
					    OperationalHaltStatus(OperationalHaltStatus),
 | 
				
			||||||
 | 
					    ShortSalePriceTest(ShortSalePriceTest),
 | 
				
			||||||
 | 
					    SecurityEvent(SecurityEvent),
 | 
				
			||||||
 | 
					    PriceLevelUpdate(PriceLevelUpdate),
 | 
				
			||||||
 | 
					    TradeReport(TradeReport),
 | 
				
			||||||
 | 
					    OfficialPrice(OfficialPrice),
 | 
				
			||||||
 | 
					    TradeBreak(TradeBreak),
 | 
				
			||||||
 | 
					    AuctionInformation(AuctionInformation),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					macro_rules! parse_msg {
 | 
				
			||||||
 | 
					    ($input:ident, $msg_type:ident) => {{
 | 
				
			||||||
 | 
					        let (rem, msg) = $msg_type::parse($input)?;
 | 
				
			||||||
 | 
					        Ok((rem, IexMessage::$msg_type(msg)))
 | 
				
			||||||
 | 
					    }};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl IexMessage {
 | 
				
			||||||
 | 
					    // TODO: Benchmark a version where we cast a packed struct instead of parsing
 | 
				
			||||||
 | 
					    fn parse(input: &[u8]) -> IResult<&[u8], IexMessage> {
 | 
				
			||||||
 | 
					        let msg_type = input[0];
 | 
				
			||||||
 | 
					        match msg_type {
 | 
				
			||||||
 | 
					            0x53 => parse_msg!(input, SystemEvent),
 | 
				
			||||||
 | 
					            0x44 => parse_msg!(input, SecurityDirectory),
 | 
				
			||||||
 | 
					            0x48 => parse_msg!(input, TradingStatus),
 | 
				
			||||||
 | 
					            0x4f => parse_msg!(input, OperationalHaltStatus),
 | 
				
			||||||
 | 
					            0x50 => parse_msg!(input, ShortSalePriceTest),
 | 
				
			||||||
 | 
					            0x45 => parse_msg!(input, SecurityEvent),
 | 
				
			||||||
 | 
					            // Why the "match multiple" looks like bitwise-OR is beyond me.
 | 
				
			||||||
 | 
					            0x38 | 0x35 => parse_msg!(input, PriceLevelUpdate),
 | 
				
			||||||
 | 
					            0x54 => parse_msg!(input, TradeReport),
 | 
				
			||||||
 | 
					            0x58 => parse_msg!(input, OfficialPrice),
 | 
				
			||||||
 | 
					            0x42 => parse_msg!(input, TradeBreak),
 | 
				
			||||||
 | 
					            _ => panic!("Unrecognized message type"),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					struct SystemEvent {
 | 
				
			||||||
 | 
					    msg_type: u8,
 | 
				
			||||||
 | 
					    system_event: u8,
 | 
				
			||||||
 | 
					    timestamp: i64,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl SystemEvent {
 | 
				
			||||||
 | 
					    fn parse(input: &[u8]) -> IResult<&[u8], SystemEvent> {
 | 
				
			||||||
 | 
					        let (rem, (msg_type, system_event, timestamp)) = tuple((le_u8, le_u8, le_i64))(input)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok((
 | 
				
			||||||
 | 
					            rem,
 | 
				
			||||||
 | 
					            SystemEvent {
 | 
				
			||||||
 | 
					                msg_type,
 | 
				
			||||||
 | 
					                system_event,
 | 
				
			||||||
 | 
					                timestamp,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					struct SecurityDirectory {
 | 
				
			||||||
 | 
					    msg_type: u8,
 | 
				
			||||||
 | 
					    flags: u8,
 | 
				
			||||||
 | 
					    timestamp: i64,
 | 
				
			||||||
 | 
					    symbol: [u8; 8],
 | 
				
			||||||
 | 
					    lot_size: u32,
 | 
				
			||||||
 | 
					    previous_closing: u64,
 | 
				
			||||||
 | 
					    luld_tier: u8,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl SecurityDirectory {
 | 
				
			||||||
 | 
					    fn parse(input: &[u8]) -> IResult<&[u8], SecurityDirectory> {
 | 
				
			||||||
 | 
					        let (rem, (msg_type, flags, timestamp, symbol, lot_size, previous_closing, luld_tier)) =
 | 
				
			||||||
 | 
					            tuple((le_u8, le_u8, le_i64, take(8usize), le_u32, le_u64, le_u8))(input)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok((
 | 
				
			||||||
 | 
					            rem,
 | 
				
			||||||
 | 
					            SecurityDirectory {
 | 
				
			||||||
 | 
					                msg_type,
 | 
				
			||||||
 | 
					                flags,
 | 
				
			||||||
 | 
					                timestamp,
 | 
				
			||||||
 | 
					                symbol: symbol.try_into().unwrap(),
 | 
				
			||||||
 | 
					                lot_size,
 | 
				
			||||||
 | 
					                previous_closing,
 | 
				
			||||||
 | 
					                luld_tier,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					struct TradingStatus {
 | 
				
			||||||
 | 
					    msg_type: u8,
 | 
				
			||||||
 | 
					    trading_status: u8,
 | 
				
			||||||
 | 
					    timestamp: i64,
 | 
				
			||||||
 | 
					    symbol: [u8; 8],
 | 
				
			||||||
 | 
					    reason: [u8; 4],
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl TradingStatus {
 | 
				
			||||||
 | 
					    fn parse(input: &[u8]) -> IResult<&[u8], TradingStatus> {
 | 
				
			||||||
 | 
					        let (rem, (msg_type, trading_status, timestamp, symbol, reason)) =
 | 
				
			||||||
 | 
					            tuple((le_u8, le_u8, le_i64, take(8usize), take(4usize)))(input)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok((
 | 
				
			||||||
 | 
					            rem,
 | 
				
			||||||
 | 
					            TradingStatus {
 | 
				
			||||||
 | 
					                msg_type,
 | 
				
			||||||
 | 
					                trading_status,
 | 
				
			||||||
 | 
					                timestamp,
 | 
				
			||||||
 | 
					                symbol: symbol.try_into().unwrap(),
 | 
				
			||||||
 | 
					                reason: reason.try_into().unwrap(),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					struct OperationalHaltStatus {
 | 
				
			||||||
 | 
					    msg_type: u8,
 | 
				
			||||||
 | 
					    halt_status: u8,
 | 
				
			||||||
 | 
					    timestamp: i64,
 | 
				
			||||||
 | 
					    symbol: [u8; 8],
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl OperationalHaltStatus {
 | 
				
			||||||
 | 
					    fn parse(input: &[u8]) -> IResult<&[u8], OperationalHaltStatus> {
 | 
				
			||||||
 | 
					        let (rem, (msg_type, halt_status, timestamp, symbol)) =
 | 
				
			||||||
 | 
					            tuple((le_u8, le_u8, le_i64, take(8usize)))(input)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok((
 | 
				
			||||||
 | 
					            rem,
 | 
				
			||||||
 | 
					            OperationalHaltStatus {
 | 
				
			||||||
 | 
					                msg_type,
 | 
				
			||||||
 | 
					                halt_status,
 | 
				
			||||||
 | 
					                timestamp,
 | 
				
			||||||
 | 
					                symbol: symbol.try_into().unwrap(),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					struct ShortSalePriceTest {
 | 
				
			||||||
 | 
					    msg_type: u8,
 | 
				
			||||||
 | 
					    sspt_status: u8,
 | 
				
			||||||
 | 
					    timestamp: i64,
 | 
				
			||||||
 | 
					    symbol: [u8; 8],
 | 
				
			||||||
 | 
					    detail: u8,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl ShortSalePriceTest {
 | 
				
			||||||
 | 
					    fn parse(input: &[u8]) -> IResult<&[u8], ShortSalePriceTest> {
 | 
				
			||||||
 | 
					        let (rem, (msg_type, sspt_status, timestamp, symbol, detail)) =
 | 
				
			||||||
 | 
					            tuple((le_u8, le_u8, le_i64, take(8usize), le_u8))(input)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok((
 | 
				
			||||||
 | 
					            rem,
 | 
				
			||||||
 | 
					            ShortSalePriceTest {
 | 
				
			||||||
 | 
					                msg_type,
 | 
				
			||||||
 | 
					                sspt_status,
 | 
				
			||||||
 | 
					                timestamp,
 | 
				
			||||||
 | 
					                symbol: symbol.try_into().unwrap(),
 | 
				
			||||||
 | 
					                detail,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					struct SecurityEvent {
 | 
				
			||||||
 | 
					    msg_type: u8,
 | 
				
			||||||
 | 
					    security_event: u8,
 | 
				
			||||||
 | 
					    timestamp: i64,
 | 
				
			||||||
 | 
					    symbol: [u8; 8],
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl SecurityEvent {
 | 
				
			||||||
 | 
					    fn parse(input: &[u8]) -> IResult<&[u8], SecurityEvent> {
 | 
				
			||||||
 | 
					        let (rem, (msg_type, security_event, timestamp, symbol)) =
 | 
				
			||||||
 | 
					            tuple((le_u8, le_u8, le_i64, take(8usize)))(input)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok((
 | 
				
			||||||
 | 
					            rem,
 | 
				
			||||||
 | 
					            SecurityEvent {
 | 
				
			||||||
 | 
					                msg_type,
 | 
				
			||||||
 | 
					                security_event,
 | 
				
			||||||
 | 
					                timestamp,
 | 
				
			||||||
 | 
					                symbol: symbol.try_into().unwrap(),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					struct PriceLevelUpdate {
 | 
				
			||||||
 | 
					    msg_type: u8,
 | 
				
			||||||
 | 
					    event_flags: u8,
 | 
				
			||||||
 | 
					    timestamp: i64,
 | 
				
			||||||
 | 
					    symbol: [u8; 8],
 | 
				
			||||||
 | 
					    size: u32,
 | 
				
			||||||
 | 
					    price: u64,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl PriceLevelUpdate {
 | 
				
			||||||
 | 
					    fn parse(input: &[u8]) -> IResult<&[u8], PriceLevelUpdate> {
 | 
				
			||||||
 | 
					        let (rem, (msg_type, event_flags, timestamp, symbol, size, price)) =
 | 
				
			||||||
 | 
					            tuple((le_u8, le_u8, le_i64, take(8usize), le_u32, le_u64))(input)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok((
 | 
				
			||||||
 | 
					            rem,
 | 
				
			||||||
 | 
					            PriceLevelUpdate {
 | 
				
			||||||
 | 
					                msg_type,
 | 
				
			||||||
 | 
					                event_flags,
 | 
				
			||||||
 | 
					                timestamp,
 | 
				
			||||||
 | 
					                symbol: symbol.try_into().unwrap(),
 | 
				
			||||||
 | 
					                size,
 | 
				
			||||||
 | 
					                price,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					struct TradeReport {
 | 
				
			||||||
 | 
					    msg_type: u8,
 | 
				
			||||||
 | 
					    sale_condition: u8,
 | 
				
			||||||
 | 
					    timestamp: i64,
 | 
				
			||||||
 | 
					    symbol: [u8; 8],
 | 
				
			||||||
 | 
					    size: u32,
 | 
				
			||||||
 | 
					    price: u64,
 | 
				
			||||||
 | 
					    trade_id: u64,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl TradeReport {
 | 
				
			||||||
 | 
					    fn parse(input: &[u8]) -> IResult<&[u8], TradeReport> {
 | 
				
			||||||
 | 
					        let (rem, (msg_type, sale_condition, timestamp, symbol, size, price, trade_id)) =
 | 
				
			||||||
 | 
					            tuple((le_u8, le_u8, le_i64, take(8usize), le_u32, le_u64, le_u64))(input)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok((
 | 
				
			||||||
 | 
					            rem,
 | 
				
			||||||
 | 
					            TradeReport {
 | 
				
			||||||
 | 
					                msg_type,
 | 
				
			||||||
 | 
					                sale_condition,
 | 
				
			||||||
 | 
					                timestamp,
 | 
				
			||||||
 | 
					                symbol: symbol.try_into().unwrap(),
 | 
				
			||||||
 | 
					                size,
 | 
				
			||||||
 | 
					                price,
 | 
				
			||||||
 | 
					                trade_id,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					struct OfficialPrice {
 | 
				
			||||||
 | 
					    msg_type: u8,
 | 
				
			||||||
 | 
					    price_type: u8,
 | 
				
			||||||
 | 
					    timestamp: i64,
 | 
				
			||||||
 | 
					    symbol: [u8; 8],
 | 
				
			||||||
 | 
					    official_price: u64,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl OfficialPrice {
 | 
				
			||||||
 | 
					    fn parse(input: &[u8]) -> IResult<&[u8], OfficialPrice> {
 | 
				
			||||||
 | 
					        let (rem, (msg_type, price_type, timestamp, symbol, official_price)) =
 | 
				
			||||||
 | 
					            tuple((le_u8, le_u8, le_i64, take(8usize), le_u64))(input)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok((
 | 
				
			||||||
 | 
					            rem,
 | 
				
			||||||
 | 
					            OfficialPrice {
 | 
				
			||||||
 | 
					                msg_type,
 | 
				
			||||||
 | 
					                price_type,
 | 
				
			||||||
 | 
					                timestamp,
 | 
				
			||||||
 | 
					                symbol: symbol.try_into().unwrap(),
 | 
				
			||||||
 | 
					                official_price,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					struct TradeBreak {
 | 
				
			||||||
 | 
					    msg_type: u8,
 | 
				
			||||||
 | 
					    sale_condition: u8,
 | 
				
			||||||
 | 
					    timestamp: i64,
 | 
				
			||||||
 | 
					    symbol: [u8; 8],
 | 
				
			||||||
 | 
					    size: u32,
 | 
				
			||||||
 | 
					    price: u64,
 | 
				
			||||||
 | 
					    trade_id: u64,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl TradeBreak {
 | 
				
			||||||
 | 
					    fn parse(input: &[u8]) -> IResult<&[u8], TradeBreak> {
 | 
				
			||||||
 | 
					        let (rem, (msg_type, sale_condition, timestamp, symbol, size, price, trade_id)) =
 | 
				
			||||||
 | 
					            tuple((le_u8, le_u8, le_i64, take(8usize), le_u32, le_u64, le_u64))(input)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok((
 | 
				
			||||||
 | 
					            rem,
 | 
				
			||||||
 | 
					            TradeBreak {
 | 
				
			||||||
 | 
					                msg_type,
 | 
				
			||||||
 | 
					                sale_condition,
 | 
				
			||||||
 | 
					                timestamp,
 | 
				
			||||||
 | 
					                symbol: symbol.try_into().unwrap(),
 | 
				
			||||||
 | 
					                size,
 | 
				
			||||||
 | 
					                price,
 | 
				
			||||||
 | 
					                trade_id,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					struct AuctionInformation {
 | 
				
			||||||
 | 
					    msg_type: u8,
 | 
				
			||||||
 | 
					    auction_type: u8,
 | 
				
			||||||
 | 
					    timestamp: i64,
 | 
				
			||||||
 | 
					    symbol: [u8; 8],
 | 
				
			||||||
 | 
					    paired_shares: u32,
 | 
				
			||||||
 | 
					    reference_price: u64,
 | 
				
			||||||
 | 
					    indicative_clearing_price: u64,
 | 
				
			||||||
 | 
					    imbalance_shares: u32,
 | 
				
			||||||
 | 
					    imbalance_side: u8,
 | 
				
			||||||
 | 
					    extension_number: u8,
 | 
				
			||||||
 | 
					    scheduled_auction: u32,
 | 
				
			||||||
 | 
					    auction_book_clearing_price: u64,
 | 
				
			||||||
 | 
					    collar_reference_price: u64,
 | 
				
			||||||
 | 
					    lower_auction_collar: u64,
 | 
				
			||||||
 | 
					    upper_auction_collar: u64,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl AuctionInformation {
 | 
				
			||||||
 | 
					    fn parse(input: &[u8]) -> IResult<&[u8], AuctionInformation> {
 | 
				
			||||||
 | 
					        // Dear Lord, why?
 | 
				
			||||||
 | 
					        let (
 | 
				
			||||||
 | 
					            rem,
 | 
				
			||||||
 | 
					            (
 | 
				
			||||||
 | 
					                msg_type,
 | 
				
			||||||
 | 
					                auction_type,
 | 
				
			||||||
 | 
					                timestamp,
 | 
				
			||||||
 | 
					                symbol,
 | 
				
			||||||
 | 
					                paired_shares,
 | 
				
			||||||
 | 
					                reference_price,
 | 
				
			||||||
 | 
					                indicative_clearing_price,
 | 
				
			||||||
 | 
					                imbalance_shares,
 | 
				
			||||||
 | 
					                imbalance_side,
 | 
				
			||||||
 | 
					                extension_number,
 | 
				
			||||||
 | 
					                scheduled_auction,
 | 
				
			||||||
 | 
					                auction_book_clearing_price,
 | 
				
			||||||
 | 
					                collar_reference_price,
 | 
				
			||||||
 | 
					                lower_auction_collar,
 | 
				
			||||||
 | 
					                upper_auction_collar,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        ) = tuple((
 | 
				
			||||||
 | 
					            le_u8,
 | 
				
			||||||
 | 
					            le_u8,
 | 
				
			||||||
 | 
					            le_i64,
 | 
				
			||||||
 | 
					            take(8usize),
 | 
				
			||||||
 | 
					            le_u32,
 | 
				
			||||||
 | 
					            le_u64,
 | 
				
			||||||
 | 
					            le_u64,
 | 
				
			||||||
 | 
					            le_u32,
 | 
				
			||||||
 | 
					            le_u8,
 | 
				
			||||||
 | 
					            le_u8,
 | 
				
			||||||
 | 
					            le_u32,
 | 
				
			||||||
 | 
					            le_u64,
 | 
				
			||||||
 | 
					            le_u64,
 | 
				
			||||||
 | 
					            le_u64,
 | 
				
			||||||
 | 
					            le_u64,
 | 
				
			||||||
 | 
					        ))(input)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok((
 | 
				
			||||||
 | 
					            rem,
 | 
				
			||||||
 | 
					            AuctionInformation {
 | 
				
			||||||
 | 
					                msg_type,
 | 
				
			||||||
 | 
					                auction_type,
 | 
				
			||||||
 | 
					                timestamp,
 | 
				
			||||||
 | 
					                symbol: symbol.try_into().unwrap(),
 | 
				
			||||||
 | 
					                paired_shares,
 | 
				
			||||||
 | 
					                reference_price,
 | 
				
			||||||
 | 
					                indicative_clearing_price,
 | 
				
			||||||
 | 
					                imbalance_shares,
 | 
				
			||||||
 | 
					                imbalance_side,
 | 
				
			||||||
 | 
					                extension_number,
 | 
				
			||||||
 | 
					                scheduled_auction,
 | 
				
			||||||
 | 
					                auction_book_clearing_price,
 | 
				
			||||||
 | 
					                collar_reference_price,
 | 
				
			||||||
 | 
					                lower_auction_collar,
 | 
				
			||||||
 | 
					                upper_auction_collar,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -3,8 +3,9 @@ use std::io::Read;
 | 
				
			|||||||
use std::path::Path;
 | 
					use std::path::Path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use clap::{App, Arg};
 | 
					use clap::{App, Arg};
 | 
				
			||||||
 | 
					use nom::sequence::tuple;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use pcap_ng::Block;
 | 
					use parsers::Block;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Cap'n'Proto and Flatbuffers typically ask that you generate code on the fly to match
 | 
					// Cap'n'Proto and Flatbuffers typically ask that you generate code on the fly to match
 | 
				
			||||||
// the schemas. For purposes of auto-complete and easy browsing in the repository,
 | 
					// the schemas. For purposes of auto-complete and easy browsing in the repository,
 | 
				
			||||||
@ -13,7 +14,8 @@ pub mod marketdata_capnp;
 | 
				
			|||||||
#[allow(unused_imports)]
 | 
					#[allow(unused_imports)]
 | 
				
			||||||
pub mod marketdata_generated; // Flatbuffers
 | 
					pub mod marketdata_generated; // Flatbuffers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod pcap_ng;
 | 
					mod iex;
 | 
				
			||||||
 | 
					mod parsers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn main() {
 | 
					fn main() {
 | 
				
			||||||
    let matches = App::new("Marketdata Shootout")
 | 
					    let matches = App::new("Marketdata Shootout")
 | 
				
			||||||
@ -34,7 +36,7 @@ fn main() {
 | 
				
			|||||||
    file.read_to_end(&mut buf).expect(&format!("Unable to read file={}", path.display()));
 | 
					    file.read_to_end(&mut buf).expect(&format!("Unable to read file={}", path.display()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut rem = &buf[..];
 | 
					    let mut rem = &buf[..];
 | 
				
			||||||
    while let Ok((unparsed, block)) = pcap_ng::read_block(rem) {
 | 
					    while let Ok((unparsed, block)) = parsers::read_block(rem) {
 | 
				
			||||||
        let offset = (unparsed.as_ptr() as usize) - (buf.as_ptr() as usize);
 | 
					        let offset = (unparsed.as_ptr() as usize) - (buf.as_ptr() as usize);
 | 
				
			||||||
        rem = unparsed;
 | 
					        rem = unparsed;
 | 
				
			||||||
        match block {
 | 
					        match block {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,16 +1,11 @@
 | 
				
			|||||||
 | 
					use std::convert::TryInto;
 | 
				
			||||||
use std::mem::size_of;
 | 
					use std::mem::size_of;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use nom::{
 | 
					use nom::{
 | 
				
			||||||
    branch::alt,
 | 
					    branch::alt, bytes::complete::tag, bytes::complete::take, IResult, number::complete::*,
 | 
				
			||||||
    bytes::complete::tag,
 | 
					 | 
				
			||||||
    bytes::complete::take,
 | 
					 | 
				
			||||||
    IResult,
 | 
					 | 
				
			||||||
    number::complete::*,
 | 
					 | 
				
			||||||
    sequence::tuple,
 | 
					    sequence::tuple,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::pcap_ng::Block::EnhancedPacket;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub enum Block<'a> {
 | 
					pub enum Block<'a> {
 | 
				
			||||||
    SectionHeader(SectionHeaderBlock),
 | 
					    SectionHeader(SectionHeaderBlock),
 | 
				
			||||||
    InterfaceDescription(InterfaceDescriptionBlock),
 | 
					    InterfaceDescription(InterfaceDescriptionBlock),
 | 
				
			||||||
@ -21,49 +16,43 @@ pub fn read_block(input: &[u8]) -> IResult<&[u8], Block> {
 | 
				
			|||||||
    alt((
 | 
					    alt((
 | 
				
			||||||
        section_header_block,
 | 
					        section_header_block,
 | 
				
			||||||
        interface_description_block,
 | 
					        interface_description_block,
 | 
				
			||||||
        enhanced_packet_block
 | 
					        enhanced_packet_block,
 | 
				
			||||||
    ))(input)
 | 
					    ))(input)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug)]
 | 
				
			||||||
pub struct SectionHeaderBlock {
 | 
					pub struct SectionHeaderBlock {
 | 
				
			||||||
    block_len: u32
 | 
					    block_len: u32,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const SECTION_HEADER: [u8; 4] = [0x0a, 0x0d, 0x0d, 0x0a];
 | 
					const SECTION_HEADER: [u8; 4] = [0x0a, 0x0d, 0x0d, 0x0a];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn section_header_block(input: &[u8]) -> IResult<&[u8], Block> {
 | 
					pub fn section_header_block(input: &[u8]) -> IResult<&[u8], Block> {
 | 
				
			||||||
    let header_len = 12;
 | 
					    let header_len = 12;
 | 
				
			||||||
    let (rem, (_, block_len, _)) = tuple((
 | 
					    let (rem, (_, block_len, _)) =
 | 
				
			||||||
        tag(SECTION_HEADER),
 | 
					        tuple((tag(SECTION_HEADER), le_u32, tag([0x4d, 0x3c, 0x2b, 0x1a])))(input)?;
 | 
				
			||||||
        le_u32,
 | 
					 | 
				
			||||||
        tag([0x4d, 0x3c, 0x2b, 0x1a])
 | 
					 | 
				
			||||||
    ))(input)?;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    take(block_len - header_len)(rem)
 | 
					    take(block_len - header_len)(rem)
 | 
				
			||||||
        .map(|i| (i.0, Block::SectionHeader(SectionHeaderBlock {
 | 
					        .map(|i| (i.0, Block::SectionHeader(SectionHeaderBlock { block_len })))
 | 
				
			||||||
            block_len
 | 
					 | 
				
			||||||
        })))
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug)]
 | 
				
			||||||
pub struct InterfaceDescriptionBlock {
 | 
					pub struct InterfaceDescriptionBlock {
 | 
				
			||||||
    block_len: u32
 | 
					    block_len: u32,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const INTERFACE_DESCRIPTION: [u8; 4] = [0x01, 0x00, 0x00, 0x00];
 | 
					const INTERFACE_DESCRIPTION: [u8; 4] = [0x01, 0x00, 0x00, 0x00];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn interface_description_block(input: &[u8]) -> IResult<&[u8], Block> {
 | 
					pub fn interface_description_block(input: &[u8]) -> IResult<&[u8], Block> {
 | 
				
			||||||
    let header_len = 8;
 | 
					    let header_len = 8;
 | 
				
			||||||
    let (rem, (_, block_len)) = tuple((
 | 
					    let (rem, (_, block_len)) = tuple((tag(INTERFACE_DESCRIPTION), le_u32))(input)?;
 | 
				
			||||||
        tag(INTERFACE_DESCRIPTION),
 | 
					 | 
				
			||||||
        le_u32
 | 
					 | 
				
			||||||
    ))(input)?;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    take(block_len - header_len)(rem)
 | 
					    take(block_len - header_len)(rem).map(|i| {
 | 
				
			||||||
        .map(|i| (i.0, Block::InterfaceDescription(InterfaceDescriptionBlock {
 | 
					        (
 | 
				
			||||||
            block_len
 | 
					            i.0,
 | 
				
			||||||
        })))
 | 
					            Block::InterfaceDescription(InterfaceDescriptionBlock { block_len }),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct EnhancedPacketBlock<'a> {
 | 
					pub struct EnhancedPacketBlock<'a> {
 | 
				
			||||||
@ -82,7 +71,7 @@ pub fn enhanced_packet_block(input: &[u8]) -> IResult<&[u8], Block> {
 | 
				
			|||||||
        le_u32,
 | 
					        le_u32,
 | 
				
			||||||
        le_u32,
 | 
					        le_u32,
 | 
				
			||||||
        le_u32,
 | 
					        le_u32,
 | 
				
			||||||
        le_u32
 | 
					        le_u32,
 | 
				
			||||||
    ))(input)?;
 | 
					    ))(input)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let (rem, packet_data) = take(captured_len)(rem)?;
 | 
					    let (rem, packet_data) = take(captured_len)(rem)?;
 | 
				
			||||||
@ -91,9 +80,33 @@ pub fn enhanced_packet_block(input: &[u8]) -> IResult<&[u8], Block> {
 | 
				
			|||||||
    // seem to respect this
 | 
					    // seem to respect this
 | 
				
			||||||
    //let packet_total_len = (captured_len + 3) / 4 * 4;
 | 
					    //let packet_total_len = (captured_len + 3) / 4 * 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    take(block_len - header_len - captured_len)(rem)
 | 
					    take(block_len - header_len - captured_len)(rem).map(|i| {
 | 
				
			||||||
        .map(|i| (i.0, Block::EnhancedPacket(EnhancedPacketBlock {
 | 
					        (
 | 
				
			||||||
            block_len,
 | 
					            i.0,
 | 
				
			||||||
            packet_data,
 | 
					            Block::EnhancedPacket(EnhancedPacketBlock {
 | 
				
			||||||
        })))
 | 
					                block_len,
 | 
				
			||||||
 | 
					                packet_data,
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn extract_iex_data(input: &[u8]) -> IResult<&[u8], &[u8]> {
 | 
				
			||||||
 | 
					    // Get the IEX payload out of each packet
 | 
				
			||||||
 | 
					    // First step is the ethernet frame
 | 
				
			||||||
 | 
					    let (rem, (_, _, _)) = tuple((take(6usize), take(6usize), tag([0x08, 0x00])))(input)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Then the IP header
 | 
				
			||||||
 | 
					    let header_words = rem[0] & 0x0F;
 | 
				
			||||||
 | 
					    let header_len = header_words as u32 * 4;
 | 
				
			||||||
 | 
					    let (udp_data, _header_data) = take(header_len)(rem)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // And finally, extract out of UDP
 | 
				
			||||||
 | 
					    let (rem, (_, _, udp_len, _)) = tuple((be_u16, be_u16, be_u16, be_u16))(udp_data)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let udp_header_len = 8;
 | 
				
			||||||
 | 
					    let (rem, iex_data) = take(udp_len - udp_header_len)(rem)?;
 | 
				
			||||||
 | 
					    debug_assert!(rem.len() == 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok((rem, iex_data))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user