mod utils; const EXCHANGE_NAME: &str = "kucoin"; #[cfg(test)] mod trade { use super::EXCHANGE_NAME; use crypto_market_type::MarketType; use crypto_message::TradeSide; use crypto_msg_parser::{extract_symbol, extract_timestamp, parse_trade}; #[test] fn spot() { let raw_msg = r#"{"data":{"symbol":"BTC-USDT","sequence":"1614503482134","side":"buy","size":"0.00013064","price":"57659.6","takerOrderId":"6057bb821220fc00060f26bf","time":"1616362370760468781","type":"match","makerOrderId":"6057bb81b5ab390006532c9d","tradeId":"6057bb822e113d292396c272"},"subject":"trade.l3match","topic":"/market/match:BTC-USDT","type":"message"}"#; let trades = &parse_trade(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap(); assert_eq!(trades.len(), 1); let trade = &trades[0]; crate::utils::check_trade_fields( EXCHANGE_NAME, MarketType::Spot, "BTC/USDT".to_string(), extract_symbol(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap(), trade, raw_msg, ); assert_eq!( 1616362370760, extract_timestamp(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap().unwrap() ); assert_eq!(trade.quantity_base, 0.00013064); assert_eq!(trade.quantity_contract, None); assert_eq!(trade.side, TradeSide::Buy); } #[test] fn linear_swap() { let raw_msg = r#"{"data":{"makerUserId":"5e568500eb029b0008715785","symbol":"XBTUSDTM","sequence":8267947,"side":"buy","size":16,"price":57850,"takerOrderId":"6057bc95660a7d0006dc1171","makerOrderId":"6057bc92652ce800067e841a","takerUserId":"601f35b4d42fad0006b2df21","tradeId":"6057bc953c7feb667195bac9","ts":1616362645429686578},"subject":"match","topic":"/contractMarket/execution:XBTUSDTM","type":"message"}"#; let trades = &parse_trade(EXCHANGE_NAME, MarketType::LinearSwap, raw_msg).unwrap(); assert_eq!(trades.len(), 1); let trade = &trades[0]; crate::utils::check_trade_fields( EXCHANGE_NAME, MarketType::LinearSwap, "BTC/USDT".to_string(), extract_symbol(EXCHANGE_NAME, MarketType::LinearSwap, raw_msg).unwrap(), trade, raw_msg, ); assert_eq!( 1616362645429, extract_timestamp(EXCHANGE_NAME, MarketType::LinearSwap, raw_msg).unwrap().unwrap() ); assert_eq!(trade.quantity_base, 0.001 * 16.0); assert_eq!(trade.quantity_quote, 0.001 * 16.0 * 57850.0); assert_eq!(trade.quantity_contract, Some(16.0)); assert_eq!(trade.side, TradeSide::Buy); } #[test] fn inverse_swap() { let raw_msg = r#"{"data":{"makerUserId":"5d85a240c788c62738732dd9","symbol":"XBTUSDM","sequence":5174061,"side":"buy","size":5000,"price":57798,"takerOrderId":"6057bc692cfab900061f8b11","makerOrderId":"6057bc4df4b11f0006a7743b","takerUserId":"5dba895d134ab72ce156079a","tradeId":"6057bc693c7feb6705f9a248","ts":1616362601277456186},"subject":"match","topic":"/contractMarket/execution:XBTUSDM","type":"message"}"#; let trades = &parse_trade(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap(); assert_eq!(trades.len(), 1); let trade = &trades[0]; crate::utils::check_trade_fields( EXCHANGE_NAME, MarketType::InverseSwap, "BTC/USD".to_string(), extract_symbol(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap(), trade, raw_msg, ); assert_eq!( 1616362601277, extract_timestamp(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap().unwrap() ); assert_eq!(trade.quantity_base, 5000.0 / 57798.0); assert_eq!(trade.quantity_quote, 5000.0); assert_eq!(trade.quantity_contract, Some(5000.0)); assert_eq!(trade.side, TradeSide::Buy); } #[test] fn inverse_future() { let raw_msg = r#"{"data":{"makerUserId":"5f802947889eb80006a1ba0f","symbol":"XBTMH21","sequence":31319,"side":"sell","size":1510,"price":57963.0,"takerOrderId":"6057be2685c6a0000610a89a","makerOrderId":"6057be11652ce800067fafb9","takerUserId":"5f802947889eb80006a1ba0f","tradeId":"6057be2677a0c431d1d1f5b6","ts":1616363046546528915},"subject":"match","topic":"/contractMarket/execution:XBTMH21","type":"message"}"#; let trades = &parse_trade(EXCHANGE_NAME, MarketType::InverseFuture, raw_msg).unwrap(); assert_eq!(trades.len(), 1); let trade = &trades[0]; crate::utils::check_trade_fields( EXCHANGE_NAME, MarketType::InverseFuture, "BTC/USD".to_string(), extract_symbol(EXCHANGE_NAME, MarketType::InverseFuture, raw_msg).unwrap(), trade, raw_msg, ); assert_eq!( 1616363046546, extract_timestamp(EXCHANGE_NAME, MarketType::InverseFuture, raw_msg).unwrap().unwrap() ); assert_eq!(trade.quantity_base, 1510.0 / 57963.0); assert_eq!(trade.quantity_quote, 1510.0); assert_eq!(trade.quantity_contract, Some(1510.0)); assert_eq!(trade.side, TradeSide::Sell); } } #[cfg(test)] mod l2_event { use super::EXCHANGE_NAME; use crypto_market_type::MarketType; use crypto_msg_parser::{extract_symbol, extract_timestamp, parse_l2}; use crypto_msg_type::MessageType; #[test] fn spot_update() { let raw_msg = r#"{"data":{"sequenceStart":1617071937790,"symbol":"BTC-USDT","changes":{"asks":[],"bids":[["39272","0.0530867","1617071937790"]]},"sequenceEnd":1617071937790},"subject":"trade.l2update","topic":"/market/level2:BTC-USDT","type":"message"}"#; let received_at = 1625097804231_i64; let orderbook = &parse_l2(EXCHANGE_NAME, MarketType::Spot, raw_msg, Some(received_at)).unwrap()[0]; assert_eq!(orderbook.asks.len(), 0); assert_eq!(orderbook.bids.len(), 1); assert!(!orderbook.snapshot); assert_eq!(orderbook.timestamp, received_at); assert_eq!(orderbook.seq_id, Some(1617071937790)); crate::utils::check_orderbook_fields( EXCHANGE_NAME, MarketType::Spot, MessageType::L2Event, "BTC/USDT".to_string(), extract_symbol(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap(), orderbook, raw_msg, ); assert_eq!(None, extract_timestamp(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap()); assert_eq!(orderbook.bids[0].price, 39272.0); assert_eq!(orderbook.bids[0].quantity_base, 0.0530867); assert_eq!(orderbook.bids[0].quantity_quote, 39272.0 * 0.0530867); } #[test] fn spot_sample_2() { let raw_msg = r#"{"type":"message","topic":"/market/level2:RUNE-USDC","subject":"trade.l2update","data":{"changes":{"asks":[],"bids":[["3.0037","267.7895","4197479"]]},"sequenceEnd":4197479,"sequenceStart":4197479,"symbol":"RUNE-USDC","time":1658449800752}}"#; let orderbook = &parse_l2(EXCHANGE_NAME, MarketType::Spot, raw_msg, Some(1625097804231_i64)).unwrap() [0]; assert_eq!(orderbook.asks.len(), 0); assert_eq!(orderbook.bids.len(), 1); assert!(!orderbook.snapshot); crate::utils::check_orderbook_fields( EXCHANGE_NAME, MarketType::Spot, MessageType::L2Event, "RUNE/USDC".to_string(), extract_symbol(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap(), orderbook, raw_msg, ); assert_eq!( 1658449800752, extract_timestamp(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap().unwrap() ); assert_eq!("RUNE-USDC", extract_symbol(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap()); assert_eq!(orderbook.timestamp, 1658449800752); assert_eq!(orderbook.seq_id, Some(4197479)); assert_eq!(orderbook.bids[0].price, 3.0037); assert_eq!(orderbook.bids[0].quantity_base, 267.7895); assert_eq!(orderbook.bids[0].quantity_quote, 3.0037 * 267.7895); } #[test] fn inverse_swap_update() { let raw_msg = r#"{"data":{"sequence":1617852459594,"change":"39069.0,buy,23960","timestamp":1622718985044},"subject":"level2","topic":"/contractMarket/level2:XBTUSDM","type":"message"}"#; let orderbook = &parse_l2(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg, None).unwrap()[0]; assert_eq!(orderbook.asks.len(), 0); assert_eq!(orderbook.bids.len(), 1); assert!(!orderbook.snapshot); crate::utils::check_orderbook_fields( EXCHANGE_NAME, MarketType::InverseSwap, MessageType::L2Event, "BTC/USD".to_string(), extract_symbol(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap(), orderbook, raw_msg, ); assert_eq!( 1622718985044, extract_timestamp(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap().unwrap() ); assert_eq!(orderbook.timestamp, 1622718985044); assert_eq!(orderbook.seq_id, Some(1617852459594)); assert_eq!(orderbook.bids[0].price, 39069.0); assert_eq!(orderbook.bids[0].quantity_base, 23960.0 / 39069.0); assert_eq!(orderbook.bids[0].quantity_quote, 23960.0); assert_eq!(orderbook.bids[0].quantity_contract.unwrap(), 23960.0); } #[test] fn linear_swap_update() { let raw_msg = r#"{"data":{"sequence":1618232029293,"change":"38962.0,buy,4374","timestamp":1622719195286},"subject":"level2","topic":"/contractMarket/level2:XBTUSDTM","type":"message"}"#; let orderbook = &parse_l2(EXCHANGE_NAME, MarketType::LinearSwap, raw_msg, None).unwrap()[0]; assert_eq!(orderbook.asks.len(), 0); assert_eq!(orderbook.bids.len(), 1); assert!(!orderbook.snapshot); crate::utils::check_orderbook_fields( EXCHANGE_NAME, MarketType::LinearSwap, MessageType::L2Event, "BTC/USDT".to_string(), extract_symbol(EXCHANGE_NAME, MarketType::LinearSwap, raw_msg).unwrap(), orderbook, raw_msg, ); assert_eq!( 1622719195286, extract_timestamp(EXCHANGE_NAME, MarketType::LinearSwap, raw_msg).unwrap().unwrap() ); assert_eq!(orderbook.timestamp, 1622719195286); assert_eq!(orderbook.seq_id, Some(1618232029293)); assert_eq!(orderbook.bids[0].price, 38962.0); assert_eq!(orderbook.bids[0].quantity_base, 4.374); assert_eq!(orderbook.bids[0].quantity_quote, 38962.0 * 4.374); assert_eq!(orderbook.bids[0].quantity_contract.unwrap(), 4374.0); } #[test] fn inverse_future_update() { let raw_msg = r#"{"data":{"sequence":1616827077941,"change":"39006.0,sell,11450","timestamp":1622719594867},"subject":"level2","topic":"/contractMarket/level2:XBTMM21","type":"message"}"#; let orderbook = &parse_l2(EXCHANGE_NAME, MarketType::InverseFuture, raw_msg, None).unwrap()[0]; assert_eq!(orderbook.asks.len(), 1); assert_eq!(orderbook.bids.len(), 0); assert!(!orderbook.snapshot); crate::utils::check_orderbook_fields( EXCHANGE_NAME, MarketType::InverseFuture, MessageType::L2Event, "BTC/USD".to_string(), extract_symbol(EXCHANGE_NAME, MarketType::InverseFuture, raw_msg).unwrap(), orderbook, raw_msg, ); assert_eq!( 1622719594867, extract_timestamp(EXCHANGE_NAME, MarketType::InverseFuture, raw_msg).unwrap().unwrap() ); assert_eq!(orderbook.timestamp, 1622719594867); assert_eq!(orderbook.seq_id, Some(1616827077941)); assert_eq!(orderbook.asks[0].price, 39006.0); assert_eq!(orderbook.asks[0].quantity_base, 11450.0 / 39006.0); assert_eq!(orderbook.asks[0].quantity_quote, 11450.0); assert_eq!(orderbook.asks[0].quantity_contract.unwrap(), 11450.0); } } #[cfg(test)] mod l2_topk { use super::EXCHANGE_NAME; use crypto_market_type::MarketType; use crypto_msg_parser::{extract_symbol, extract_timestamp, parse_l2_topk, round}; use crypto_msg_type::MessageType; #[test] fn spot() { let raw_msg = r#"{"type":"message","topic":"/spotMarket/level2Depth5:BTC-USDT","subject":"level2","data":{"asks":[["31530.2","2.90121626"],["31530.4","0.00026686"],["31531.5","0.01934176"],["31531.6","0.425"],["31531.7","0.09467136"]],"bids":[["31530.1","0.74468602"],["31530","0.27077267"],["31529.9","0.48567212"],["31528.9","0.000849"],["31528.8","0.15853762"]],"timestamp":1653989906722}}"#; let orderbook = &parse_l2_topk(EXCHANGE_NAME, MarketType::Spot, raw_msg, None).unwrap()[0]; assert_eq!(orderbook.asks.len(), 5); assert_eq!(orderbook.bids.len(), 5); assert!(orderbook.snapshot); crate::utils::check_orderbook_fields( EXCHANGE_NAME, MarketType::Spot, MessageType::L2TopK, "BTC/USDT".to_string(), extract_symbol(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap(), orderbook, raw_msg, ); assert_eq!( 1653989906722, extract_timestamp(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap().unwrap() ); assert_eq!(orderbook.timestamp, 1653989906722); assert_eq!(orderbook.seq_id, None); assert_eq!(orderbook.prev_seq_id, None); assert_eq!(orderbook.bids[0].price, 31530.1); assert_eq!(orderbook.bids[0].quantity_base, 0.74468602); assert_eq!(orderbook.bids[0].quantity_quote, 31530.1 * 0.74468602); assert_eq!(orderbook.bids[0].quantity_contract, None); assert_eq!(orderbook.bids[4].price, 31528.8); assert_eq!(orderbook.bids[4].quantity_base, 0.15853762); assert_eq!(orderbook.bids[4].quantity_quote, 31528.8 * 0.15853762); assert_eq!(orderbook.bids[4].quantity_contract, None); assert_eq!(orderbook.asks[0].price, 31530.2); assert_eq!(orderbook.asks[0].quantity_base, 2.90121626); assert_eq!(orderbook.asks[0].quantity_quote, 31530.2 * 2.90121626); assert_eq!(orderbook.asks[0].quantity_contract, None); assert_eq!(orderbook.asks[4].price, 31531.7); assert_eq!(orderbook.asks[4].quantity_base, 0.09467136); assert_eq!(orderbook.asks[4].quantity_quote, 31531.7 * 0.09467136); assert_eq!(orderbook.asks[4].quantity_contract, None); } #[test] fn inverse_swap() { let raw_msg = r#"{"type":"message","topic":"/contractMarket/level2Depth5:XBTUSDM","subject":"level2","data":{"sequence":1638556032307,"asks":[[31529,12725],[31530,21214],[31531,6090],[31532,44385],[31538,85]],"bids":[[31528.00000000,2856],[31527.00000000,10034],[31525,6266],[31524,5043],[31521,85]],"ts":1653991142662,"timestamp":1653991142662}}"#; let orderbook = &parse_l2_topk(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg, None).unwrap()[0]; assert_eq!(orderbook.asks.len(), 5); assert_eq!(orderbook.bids.len(), 5); assert!(orderbook.snapshot); crate::utils::check_orderbook_fields( EXCHANGE_NAME, MarketType::InverseSwap, MessageType::L2TopK, "BTC/USD".to_string(), extract_symbol(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap(), orderbook, raw_msg, ); assert_eq!( 1653991142662, extract_timestamp(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap().unwrap() ); assert_eq!(orderbook.timestamp, 1653991142662); assert_eq!(orderbook.seq_id, Some(1638556032307)); assert_eq!(orderbook.prev_seq_id, None); assert_eq!(orderbook.bids[0].price, 31528.0); assert_eq!(orderbook.bids[0].quantity_base, 2856.0 / 31528.0); assert_eq!(orderbook.bids[0].quantity_quote, 2856.0); assert_eq!(orderbook.bids[0].quantity_contract.unwrap(), 2856.0); assert_eq!(orderbook.bids[4].price, 31521.0); assert_eq!(orderbook.bids[4].quantity_base, 85.0 / 31521.0); assert_eq!(orderbook.bids[4].quantity_quote, 85.0); assert_eq!(orderbook.bids[4].quantity_contract.unwrap(), 85.0); assert_eq!(orderbook.asks[0].price, 31529.0); assert_eq!(orderbook.asks[0].quantity_base, 12725.0 / 31529.0); assert_eq!(orderbook.asks[0].quantity_quote, 12725.0); assert_eq!(orderbook.asks[0].quantity_contract.unwrap(), 12725.0); assert_eq!(orderbook.asks[4].price, 31538.0); assert_eq!(orderbook.asks[4].quantity_base, 85.0 / 31538.0); assert_eq!(orderbook.asks[4].quantity_quote, 85.0); assert_eq!(orderbook.asks[4].quantity_contract.unwrap(), 85.0); } #[test] fn linear_swap() { let raw_msg = r#"{"type":"message","topic":"/contractMarket/level2Depth5:XBTUSDTM","subject":"level2","data":{"sequence":1643184485510,"asks":[[31608,32278],[31609,571],[31610,4456],[31611,10157],[31612,3185]],"bids":[[31607,16350],[31606.0,7183],[31605,17234],[31604,553],[31603,620]],"ts":1653992430005,"timestamp":1653992430005}}"#; let orderbook = &parse_l2_topk(EXCHANGE_NAME, MarketType::LinearSwap, raw_msg, None).unwrap()[0]; assert_eq!(orderbook.asks.len(), 5); assert_eq!(orderbook.bids.len(), 5); assert!(orderbook.snapshot); crate::utils::check_orderbook_fields( EXCHANGE_NAME, MarketType::LinearSwap, MessageType::L2TopK, "BTC/USDT".to_string(), extract_symbol(EXCHANGE_NAME, MarketType::LinearSwap, raw_msg).unwrap(), orderbook, raw_msg, ); assert_eq!( 1653992430005, extract_timestamp(EXCHANGE_NAME, MarketType::LinearSwap, raw_msg).unwrap().unwrap() ); assert_eq!(orderbook.timestamp, 1653992430005); assert_eq!(orderbook.seq_id, Some(1643184485510)); assert_eq!(orderbook.prev_seq_id, None); assert_eq!(orderbook.bids[0].price, 31607.0); assert_eq!(orderbook.bids[0].quantity_base, 16.350); assert_eq!(orderbook.bids[0].quantity_quote, round(31607.0 * 16.35)); assert_eq!(orderbook.bids[0].quantity_contract.unwrap(), 16350.0); assert_eq!(orderbook.bids[4].price, 31603.0); assert_eq!(orderbook.bids[4].quantity_base, 0.620); assert_eq!(orderbook.bids[4].quantity_quote, 31603.0 * 0.620); assert_eq!(orderbook.bids[4].quantity_contract.unwrap(), 620.0); assert_eq!(orderbook.asks[0].price, 31608.0); assert_eq!(orderbook.asks[0].quantity_base, 32.278); assert_eq!(orderbook.asks[0].quantity_quote, round(31608.0 * 32.278)); assert_eq!(orderbook.asks[0].quantity_contract.unwrap(), 32278.0); assert_eq!(orderbook.asks[4].price, 31612.0); assert_eq!(orderbook.asks[4].quantity_base, 3.185); assert_eq!(orderbook.asks[4].quantity_quote, 31612.0 * 3.185); assert_eq!(orderbook.asks[4].quantity_contract.unwrap(), 3185.0); } #[test] fn inverse_future() { let raw_msg = r#"{"type":"message","topic":"/contractMarket/level2Depth5:XBTMM22","subject":"level2","data":{"sequence":1647031214270,"asks":[[31648,1600],[31657,28],[31658,1800],[31672,1204],[31683,150]],"bids":[[31626,1628],[31625,5466],[31622,1266],[31609,1557],[31595,2943]],"ts":1653992298695,"timestamp":1653992298695}}"#; let orderbook = &parse_l2_topk(EXCHANGE_NAME, MarketType::InverseFuture, raw_msg, None).unwrap()[0]; assert_eq!(orderbook.asks.len(), 5); assert_eq!(orderbook.bids.len(), 5); assert!(orderbook.snapshot); crate::utils::check_orderbook_fields( EXCHANGE_NAME, MarketType::InverseFuture, MessageType::L2TopK, "BTC/USD".to_string(), extract_symbol(EXCHANGE_NAME, MarketType::InverseFuture, raw_msg).unwrap(), orderbook, raw_msg, ); assert_eq!( 1653992298695, extract_timestamp(EXCHANGE_NAME, MarketType::InverseFuture, raw_msg).unwrap().unwrap() ); assert_eq!(orderbook.timestamp, 1653992298695); assert_eq!(orderbook.seq_id, Some(1647031214270)); assert_eq!(orderbook.prev_seq_id, None); assert_eq!(orderbook.bids[0].price, 31626.0); assert_eq!(orderbook.bids[0].quantity_base, 1628.0 / 31626.0); assert_eq!(orderbook.bids[0].quantity_quote, 1628.0); assert_eq!(orderbook.bids[0].quantity_contract.unwrap(), 1628.0); assert_eq!(orderbook.bids[4].price, 31595.0); assert_eq!(orderbook.bids[4].quantity_base, 2943.0 / 31595.0); assert_eq!(orderbook.bids[4].quantity_quote, 2943.0); assert_eq!(orderbook.bids[4].quantity_contract.unwrap(), 2943.0); assert_eq!(orderbook.asks[0].price, 31648.0); assert_eq!(orderbook.asks[0].quantity_base, 1600.0 / 31648.0); assert_eq!(orderbook.asks[0].quantity_quote, 1600.0); assert_eq!(orderbook.asks[0].quantity_contract.unwrap(), 1600.0); assert_eq!(orderbook.asks[4].price, 31683.0); assert_eq!(orderbook.asks[4].quantity_base, 150.0 / 31683.0); assert_eq!(orderbook.asks[4].quantity_quote, 150.0); assert_eq!(orderbook.asks[4].quantity_contract.unwrap(), 150.0); } } #[cfg(test)] mod bbo { use super::EXCHANGE_NAME; use crypto_market_type::MarketType; use crypto_msg_parser::{extract_symbol, extract_timestamp, parse_bbo, round}; #[test] fn spot() { let raw_msg = r#"{"type":"message","topic":"/market/ticker:BTC-USDT","subject":"trade.ticker","data":{"bestAsk":"31785.3","bestAskSize":"1.0455757","bestBid":"31785.2","bestBidSize":"0.4645037","price":"31785.2","sequence":"1630218274617","size":"0.03133705","time":1654032320677}}"#; assert_eq!( 1654032320677, extract_timestamp(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap().unwrap() ); assert_eq!("BTC-USDT", extract_symbol(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap()); let bbo_msg = &parse_bbo(EXCHANGE_NAME, MarketType::Spot, raw_msg, None).unwrap()[0]; assert_eq!("BTC-USDT", bbo_msg.symbol); assert_eq!(1654032320677, bbo_msg.timestamp); assert_eq!(31785.3, bbo_msg.ask_price); assert_eq!(1.0455757, bbo_msg.ask_quantity_base); assert_eq!(round(31785.3 * 1.0455757), bbo_msg.ask_quantity_quote); assert_eq!(None, bbo_msg.ask_quantity_contract); assert_eq!(31785.2, bbo_msg.bid_price); assert_eq!(0.4645037, bbo_msg.bid_quantity_base); assert_eq!(round(31785.2 * 0.4645037), bbo_msg.bid_quantity_quote); assert_eq!(None, bbo_msg.bid_quantity_contract); } #[test] fn spot_2() { let raw_msg = r#"{"type":"message","topic":"/market/ticker:all","subject":"DOT-USDT","data":{"bestAsk":"10.4686","bestAskSize":"64.9647","bestBid":"10.4647","bestBidSize":"0.1416","price":"10.4686","sequence":"1619386350765","size":"0.0153","time":1653955200018}}"#; assert_eq!( 1653955200018, extract_timestamp(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap().unwrap() ); assert_eq!("DOT-USDT", extract_symbol(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap()); let bbo_msg = &parse_bbo(EXCHANGE_NAME, MarketType::Spot, raw_msg, None).unwrap()[0]; assert_eq!("DOT-USDT", bbo_msg.symbol); assert_eq!(1653955200018, bbo_msg.timestamp); assert_eq!(10.4686, bbo_msg.ask_price); assert_eq!(64.9647, bbo_msg.ask_quantity_base); assert_eq!(round(10.4686 * 64.9647), bbo_msg.ask_quantity_quote); assert_eq!(None, bbo_msg.ask_quantity_contract); assert_eq!(10.4647, bbo_msg.bid_price); assert_eq!(0.1416, bbo_msg.bid_quantity_base); assert_eq!(10.4647 * 0.1416, bbo_msg.bid_quantity_quote); assert_eq!(None, bbo_msg.bid_quantity_contract); } #[test] fn inverse_future() { let raw_msg = r#"{"type":"message","topic":"/contractMarket/tickerV2:XBTMM22","subject":"tickerV2","data":{"symbol":"XBTMM22","sequence":1647024019666,"bestBidSize":118,"bestBidPrice":"31741.0","bestAskPrice":"31776.0","ts":1654032575773272833,"bestAskSize":562}}"#; assert_eq!( 1654032575773, extract_timestamp(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap().unwrap() ); assert_eq!( "XBTMM22", extract_symbol(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap() ); } #[test] fn inverse_swap() { let raw_msg = r#"{"type":"message","topic":"/contractMarket/tickerV2:XBTUSDM","subject":"tickerV2","data":{"symbol":"XBTUSDM","sequence":1638549733058,"bestBidSize":5543,"bestBidPrice":"31741.0","bestAskPrice":"31742.0","ts":1654032770009498293,"bestAskSize":500}}"#; assert_eq!( 1654032770009, extract_timestamp(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap().unwrap() ); assert_eq!( "XBTUSDM", extract_symbol(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap() ); } #[test] fn linear_swap() { let raw_msg = r#"{"type":"message","topic":"/contractMarket/tickerV2:XBTUSDTM","subject":"tickerV2","data":{"symbol":"XBTUSDTM","sequence":1643185147205,"bestBidSize":20074,"bestBidPrice":"31766.0","bestAskPrice":"31767.0","ts":1654032807465857723,"bestAskSize":1187}}"#; assert_eq!( 1654032807465, extract_timestamp(EXCHANGE_NAME, MarketType::LinearSwap, raw_msg).unwrap().unwrap() ); assert_eq!( "XBTUSDTM", extract_symbol(EXCHANGE_NAME, MarketType::LinearSwap, raw_msg).unwrap() ); } } #[cfg(test)] mod l3_event { use super::EXCHANGE_NAME; use crypto_market_type::MarketType; use crypto_msg_parser::{extract_symbol, extract_timestamp}; #[test] fn spot() { let raw_msg = r#"{"type":"message","topic":"/spotMarket/level3:BTC-USDT","subject":"received","data":{"symbol":"BTC-USDT","orderId":"629724de1f7e6b00015310cb","sequence":1630234429271,"clientOid":"d2b351d1-e185-11ec-aceb-068cc764f03f","ts":1654072542361747612}}"#; assert_eq!( 1654072542361, extract_timestamp(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap().unwrap() ); assert_eq!("BTC-USDT", extract_symbol(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap()); } #[test] fn inverse_future() { let raw_msg = r#"{"type":"message","topic":"/contractMarket/level3v2:XBTMM22","subject":"open","data":{"symbol":"XBTMM22","sequence":1647173843748,"side":"sell","orderTime":1654073248891988536,"size":"28","orderId":"629727a023aac2000194fc87","price":"31615.0","ts":1654073248910399802}}"#; assert_eq!( 1654073248910, extract_timestamp(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap().unwrap() ); assert_eq!( "XBTMM22", extract_symbol(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap() ); } #[test] fn inverse_swap() { let raw_msg = r#"{"type":"message","topic":"/contractMarket/level3v2:XBTUSDM","subject":"open","data":{"symbol":"XBTUSDM","sequence":1639148481406,"side":"buy","orderTime":1654073289118060857,"size":"3671","orderId":"629727c9edde6b0001f422a7","price":"31570.0","ts":1654073289160921530}}"#; assert_eq!( 1654073289160, extract_timestamp(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap().unwrap() ); assert_eq!( "XBTUSDM", extract_symbol(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap() ); } #[test] fn linear_swap() { let raw_msg = r#"{"type":"message","topic":"/contractMarket/level3v2:XBTUSDTM","subject":"received","data":{"symbol":"XBTUSDTM","sequence":1655525144741,"orderId":"629727ecdd16e300018810de","clientOid":"cabifr55rj7cmsu5a850","ts":1654073324184830142}}"#; assert_eq!( 1654073324184, extract_timestamp(EXCHANGE_NAME, MarketType::LinearSwap, raw_msg).unwrap().unwrap() ); assert_eq!( "XBTUSDTM", extract_symbol(EXCHANGE_NAME, MarketType::LinearSwap, raw_msg).unwrap() ); } } #[cfg(test)] mod candlestick { use super::EXCHANGE_NAME; use crypto_market_type::MarketType; use crypto_msg_parser::{extract_symbol, extract_timestamp, parse_candlestick}; #[test] fn spot() { let raw_msg = r#"{"type":"message","topic":"/market/candles:BTC-USDT_1week","subject":"trade.candles.update","data":{"symbol":"BTC-USDT","candles":["1653523200","29543.6","31613.8","32406.7","28014.1","93044.50911291","2792095272.950902197"],"time":1654081935182826588}}"#; assert_eq!( 1654081935182, extract_timestamp(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap().unwrap() ); assert_eq!("BTC-USDT", extract_symbol(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap()); let candlestick_msg = &parse_candlestick(EXCHANGE_NAME, MarketType::Spot, raw_msg, None).unwrap()[0]; assert_eq!(1654081935182, candlestick_msg.timestamp); assert_eq!("BTC-USDT", candlestick_msg.symbol); assert_eq!(1653523200, candlestick_msg.begin_time); assert_eq!(29543.6, candlestick_msg.open); assert_eq!(32406.7, candlestick_msg.high); assert_eq!(28014.1, candlestick_msg.low); assert_eq!(31613.8, candlestick_msg.close); assert_eq!(93044.50911291, candlestick_msg.volume); assert_eq!(Some(2_792_095_272.950_902), candlestick_msg.quote_volume); assert_eq!("1week", candlestick_msg.period); } #[test] fn inverse_future() { let raw_msg = r#"{"type":"message","topic":"/contractMarket/candle:XBTMM22_10080","subject":"candle.stick","data":{"volume":1364110,"symbol":"XBTMM22","high":32320.0,"low":29274.0,"granularity":10080,"time":1653868800000,"close":31504.0,"turnover":1364110.0,"open":29435.0}}"#; assert_eq!( 1653868800000, extract_timestamp(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap().unwrap() ); assert_eq!( "XBTMM22", extract_symbol(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap() ); } #[test] fn inverse_swap() { let raw_msg = r#"{"type":"message","topic":"/contractMarket/candle:XBTUSDM_10080","subject":"candle.stick","data":{"volume":57904628,"symbol":"XBTUSDM","high":32382.0,"low":29244.0,"granularity":10080,"time":1653868800000,"close":31511.0,"turnover":57904628,"open":29397.0}}"#; assert_eq!( 1653868800000, extract_timestamp(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap().unwrap() ); assert_eq!( "XBTUSDM", extract_symbol(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap() ); } #[test] fn linear_swap() { let raw_msg = r#"{"type":"message","topic":"/contractMarket/candle:XBTUSDTM_10080","subject":"candle.stick","data":{"volume":113774348,"symbol":"XBTUSDTM","high":32410.0,"low":29294.0,"granularity":10080,"time":1653868800000,"close":31519.0,"turnover":113774348,"open":29450.0}}"#; assert_eq!( 1653868800000, extract_timestamp(EXCHANGE_NAME, MarketType::LinearSwap, raw_msg).unwrap().unwrap() ); assert_eq!( "XBTUSDTM", extract_symbol(EXCHANGE_NAME, MarketType::LinearSwap, raw_msg).unwrap() ); } } #[cfg(test)] mod ticker { use super::EXCHANGE_NAME; use crypto_market_type::MarketType; use crypto_msg_parser::{extract_symbol, extract_timestamp}; #[test] fn spot() { let raw_msg = r#"{"type":"message","topic":"/market/snapshot:BTC-USDT","subject":"trade.snapshot","data":{"sequence":"1630306486588","data":{"averagePrice":29875.32051554,"baseCurrency":"BTC","board":1,"buy":29920,"changePrice":-1689.90000000000000000000,"changeRate":-0.0534,"close":29920.1,"datetime":1654165082007,"high":31901.40000000000000000000,"lastTradedPrice":29920.1,"low":29299.90000000000000000000,"makerCoefficient":1.000000,"makerFeeRate":0.001,"marginTrade":true,"mark":0,"market":"USDS","markets":["USDS"],"open":31610.00000000000000000000,"quoteCurrency":"USDT","sell":29920.1,"sort":100,"symbol":"BTC-USDT","symbolCode":"BTC-USDT","takerCoefficient":1.000000,"takerFeeRate":0.001,"trading":true,"vol":21637.26053196000000000000,"volValue":657871182.64000021200000000000}}}"#; assert_eq!( 1654165082007, extract_timestamp(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap().unwrap() ); assert_eq!("BTC-USDT", extract_symbol(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap()); } #[test] fn inverse_future() { let raw_msg = r#"{"id":"629890fb75470d00010748c7","type":"message","topic":"/contractMarket/snapshot:XBTMM22","subject":"snapshot.24h","data":{"symbol":"XBTMM22","volume":590275,"turnover":19.41544404913293,"lastPrice":29912,"lowPrice":29332.0,"highPrice":31884.0,"priceChgPct":-0.0534,"priceChg":-1688,"ts":1654165755087785336}}"#; assert_eq!( 1654165755087, extract_timestamp(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap().unwrap() ); assert_eq!( "XBTMM22", extract_symbol(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap() ); } #[test] fn inverse_swap() { let raw_msg = r#"{"id":"6298915a75470d0001083713","type":"message","topic":"/contractMarket/snapshot:XBTUSDM","subject":"snapshot.24h","data":{"symbol":"XBTUSDM","volume":43626228,"turnover":1439.3403076772884,"lastPrice":29887,"lowPrice":29150.0,"highPrice":31890.0,"priceChgPct":-0.0535,"priceChg":-1692.0,"ts":1654165850007823190}}"#; assert_eq!( 1654165850007, extract_timestamp(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap().unwrap() ); assert_eq!( "XBTUSDM", extract_symbol(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap() ); } #[test] fn linear_swap() { let raw_msg = r#"{"id":"6298917d75470d00010885b0","type":"message","topic":"/contractMarket/snapshot:XBTUSDTM","subject":"snapshot.24h","data":{"symbol":"XBTUSDTM","volume":58142.397,"turnover":1766775651.6259518,"lastPrice":29926,"lowPrice":29288.0,"highPrice":31891.0,"priceChgPct":-0.0524,"priceChg":-1657,"ts":1654165885016336830}}"#; assert_eq!( 1654165885016, extract_timestamp(EXCHANGE_NAME, MarketType::LinearSwap, raw_msg).unwrap().unwrap() ); assert_eq!( "XBTUSDTM", extract_symbol(EXCHANGE_NAME, MarketType::LinearSwap, raw_msg).unwrap() ); } } #[cfg(test)] mod l2_snapshot { use super::EXCHANGE_NAME; use crypto_market_type::MarketType; use crypto_msg_parser::{extract_symbol, extract_timestamp}; #[test] fn spot() { let raw_msg = r#"{"code":"200000","data":{"time":1654325095225,"sequence":"1630423725254","bids":[["29701.4","1.1244206"],["29701.2","0.00006727"],["29700.1","0.49009689"]],"asks":[["29701.5","0.00023088"],["29701.6","0.48701789"],["29701.7","0.00034976"]]}}"#; assert_eq!( 1654325095225, extract_timestamp(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap().unwrap() ); assert_eq!("NONE", extract_symbol(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap()); } #[test] fn inverse_future() { let raw_msg = r#"{"code":"200000","data":{"symbol":"XBTMM22","sequence":1647109356374,"asks":[[29680.0,1400],[29688.0,1365],[29695.0,150]],"bids":[[29665.0,5463],[29664.0,1300],[29651.0,1344]],"ts":1654325996914997044}}"#; assert_eq!( 1654325996914, extract_timestamp(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap().unwrap() ); assert_eq!( "XBTMM22", extract_symbol(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap() ); } #[test] fn inverse_swap() { let raw_msg = r#"{"code":"200000","data":{"symbol":"XBTUSDM","sequence":1638901902423,"asks":[[29626.0,2521],[29627.0,16476],[29630.0,6266]],"bids":[[29625.0,15226],[29624.0,6845],[29623.0,2521]],"ts":1654326900579981822}}"#; assert_eq!( 1654326900579, extract_timestamp(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap().unwrap() ); assert_eq!( "XBTUSDM", extract_symbol(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap() ); } #[test] fn linear_swap() { let raw_msg = r#"{"code":"200000","data":{"symbol":"XBTUSDTM","sequence":1645045405918,"asks":[[29641.0,30109],[29642.0,7922],[29643.0,5820]],"bids":[[29640.0,13007],[29639.0,1072],[29638.0,169]],"ts":1654326922830525196}}"#; assert_eq!( 1654326922830, extract_timestamp(EXCHANGE_NAME, MarketType::LinearSwap, raw_msg).unwrap().unwrap() ); assert_eq!( "XBTUSDTM", extract_symbol(EXCHANGE_NAME, MarketType::LinearSwap, raw_msg).unwrap() ); } } #[cfg(test)] mod open_interest { use super::EXCHANGE_NAME; use crypto_market_type::MarketType; use crypto_msg_parser::{extract_symbol, extract_timestamp}; #[test] fn unknown() { let raw_msg = r#"{"code":"200000","data":[{"symbol":"XBTUSDTM","rootSymbol":"USDT","type":"FFWCSX","firstOpenDate":1585555200000,"expireDate":null,"settleDate":null,"baseCurrency":"XBT","quoteCurrency":"USDT","settleCurrency":"USDT","maxOrderQty":1000000,"maxPrice":1000000.0000000000,"lotSize":1,"tickSize":1.0,"indexPriceTickSize":0.01,"multiplier":0.001,"initialMargin":0.01,"maintainMargin":0.005,"maxRiskLimit":2000000,"minRiskLimit":2000000,"riskStep":1000000,"makerFeeRate":0.00020,"takerFeeRate":0.00060,"takerFixFee":0.0000000000,"makerFixFee":0.0000000000,"settlementFee":null,"isDeleverage":true,"isQuanto":true,"isInverse":false,"markMethod":"FairPrice","fairMethod":"FundingRate","fundingBaseSymbol":".XBTINT8H","fundingQuoteSymbol":".USDTINT8H","fundingRateSymbol":".XBTUSDTMFPI8H","indexSymbol":".KXBTUSDT","settlementSymbol":"","status":"Open","fundingFeeRate":-0.000013,"predictedFundingFeeRate":0.000048,"openInterest":"9876432","turnoverOf24h":751931474.32877920,"volumeOf24h":25408.11100000,"markPrice":29538.28,"indexPrice":29538.62,"lastTradePrice":29526.0000000000,"nextFundingRateTime":25242841,"maxLeverage":100,"sourceExchanges":["huobi","Okex","Binance","Kucoin","Poloniex","Hitbtc"],"premiumsSymbol1M":".XBTUSDTMPI","premiumsSymbol8H":".XBTUSDTMPI8H","fundingBaseSymbol1M":".XBTINT","fundingQuoteSymbol1M":".USDTINT","lowPrice":29275,"highPrice":29880,"priceChgPct":0.0004,"priceChg":13},{"symbol":"XBTUSDM","rootSymbol":"XBT","type":"FFWCSX","firstOpenDate":1552638575000,"expireDate":null,"settleDate":null,"baseCurrency":"XBT","quoteCurrency":"USD","settleCurrency":"XBT","maxOrderQty":10000000,"maxPrice":1000000.0000000000,"lotSize":1,"tickSize":1.0,"indexPriceTickSize":0.01,"multiplier":-1.0,"initialMargin":0.02,"maintainMargin":0.01,"maxRiskLimit":40,"minRiskLimit":40,"riskStep":20,"makerFeeRate":0.00020,"takerFeeRate":0.00060,"takerFixFee":0.0000000000,"makerFixFee":0.0000000000,"settlementFee":null,"isDeleverage":true,"isQuanto":false,"isInverse":true,"markMethod":"FairPrice","fairMethod":"FundingRate","fundingBaseSymbol":".XBTINT8H","fundingQuoteSymbol":".USDINT8H","fundingRateSymbol":".XBTUSDMFPI8H","indexSymbol":".BXBT","settlementSymbol":null,"status":"Open","fundingFeeRate":0.000100,"predictedFundingFeeRate":0.000086,"openInterest":"36857949","turnoverOf24h":480.86258578,"volumeOf24h":14215097.00000000,"markPrice":29519.05,"indexPrice":29515.53,"lastTradePrice":29486.0000000000,"nextFundingRateTime":25242830,"maxLeverage":50,"sourceExchanges":["Bitstamp","Bittrex","Coinbase","Gemini","Kraken","Liquid"],"premiumsSymbol1M":".XBTUSDMPI","premiumsSymbol8H":".XBTUSDMPI8H","fundingBaseSymbol1M":".XBTINT","fundingQuoteSymbol1M":".USDINT","lowPrice":29274,"highPrice":29864,"priceChgPct":0.0003,"priceChg":10}]}"#; assert_eq!("ALL", extract_symbol(EXCHANGE_NAME, MarketType::Unknown, raw_msg).unwrap()); assert_eq!(None, extract_timestamp(EXCHANGE_NAME, MarketType::Unknown, raw_msg).unwrap()); } }