Skip to content

Commit

Permalink
implement parse_l2_snapshot for bitfinex
Browse files Browse the repository at this point in the history
  • Loading branch information
AdoreWisdom committed Jul 7, 2024
1 parent 059fe6c commit b6f2de6
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 4 deletions.
66 changes: 66 additions & 0 deletions crypto-msg-parser/src/exchanges/bitfinex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,72 @@ pub(crate) fn parse_l2(
Ok(vec![orderbook])
}



// See https://docs.bitfinex.com/reference/rest-public-book
// See https://api-pub.bitfinex.com/v2/book/{symbol}/{precision}
// request example https://api-pub.bitfinex.com/v2/book/tBTCUSD/P0?len=100
//[[68361,2,0.17328582],[68360,1,0.65244918],[68357,1,0.07]]
//price ,count ,amount
// See https://binance-docs.github.io/apidocs/spot/en/#order-book


pub(crate) fn parse_l2_snapshot(
market_type: MarketType,
msg: &str,
symbol:Option<&str>,
received_at: Option<i64>
) -> Result<Vec<OrderBookMsg>, SimpleError> {
let rs_msg = serde_json::from_str::<Vec<[Value;3]>>(msg)
.map_err(|_e| SimpleError::new(format!("Failed to deserialize {msg} to Vec<[Value;3]]>")))?;

let s=symbol.unwrap_or("").to_string();
let pair = crypto_pair::normalize_pair(&s, EXCHANGE_NAME)
.ok_or_else(|| SimpleError::new(format!("Failed to normalize {s} from {msg}")))?;

let snapshot = true;
let parse_order = |x: &[Value; 3]| -> Order {
let price = x[0].as_f64().unwrap();
// delete price level if count = 0
let quantity = x[2].as_f64().unwrap().abs();

let (quantity_base, quantity_quote, quantity_contract) =
calc_quantity_and_volume(EXCHANGE_NAME, market_type, &pair, price, quantity);

Order { price, quantity_base, quantity_quote, quantity_contract }
};

let mut asks=Vec::new();
let mut bids=Vec::new();

for raw_order in rs_msg.iter() {
let order = parse_order(raw_order);
if raw_order[2].as_f64().unwrap() > 0.0 {
bids.push(order);
} else {
asks.push(order);
}
}

let orderbook = OrderBookMsg {
exchange: EXCHANGE_NAME.to_string(),
market_type,
symbol: s,
pair: pair.clone(),
msg_type: MessageType::L2Snapshot,
timestamp:received_at.unwrap(),
seq_id: None,
prev_seq_id: None,
asks: asks.clone(),
bids: bids.clone(),
snapshot,
json: msg.to_string(),
};

Ok(vec![orderbook])
}


fn parse_one_candle(
market_type: MarketType,
symbol: &str,
Expand Down
1 change: 1 addition & 0 deletions crypto-msg-parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ pub fn parse_l2_snapshot(
) -> Result<Vec<OrderBookMsg>, SimpleError> {
let ret = match exchange {
"binance" => exchanges::binance::parse_l2_snapshot(market_type, msg, symbol, received_at),
"bitfinex" => exchanges::bitfinex::parse_l2_snapshot(market_type, msg, symbol,received_at),
_ => Err(SimpleError::new(format!("Unknown exchange {exchange}"))),
};
match ret {
Expand Down
117 changes: 113 additions & 4 deletions crypto-msg-parser/tests/bitfinex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -500,24 +500,133 @@ mod ticker {
mod l2_snapshot {
use super::EXCHANGE_NAME;
use crypto_market_type::MarketType;
use crypto_msg_parser::{extract_symbol, extract_timestamp};
use crypto_msg_parser::{extract_symbol, extract_timestamp, parse_l2_snapshot, round};
use crypto_msg_type::MessageType;

#[test]
fn spot() {
let raw_msg = r#"[[30428,1,0.01],[30426,1,0.1],[30424,1,0.2954],[30423,1,0.3333],[30422,3,0.72231346],[30420,2,0.3349],[30416,2,0.29700845],[30415,3,0.482257],[30414,1,0.4],[30413,1,0.15439084]]"#;


/* data sample excerpt
1677628803683 1677628803683 [[23144,3,0.87789],[23142,3,0.27991505],[23141,4,0.48547974],
[23325,5,-0.00297898],[23326,1,-0.00060965],[23327,2,-0.00119078]]
*/

//let raw_msg = r#"[[30428,1,0.01],[30426,1,0.1],[30424,1,0.2954],[30423,1,0.3333],[30422,3,0.72231346],[30420,2,0.3349],[30416,2,0.29700845],[30415,3,0.482257],[30414,1,0.4],[30413,1,0.15439084]]"#;
let raw_msg = r#"[[23144,3,0.87789],[23142,3,0.27991505],[23141,4,0.48547974],[23325,5,-0.00297898],[23326,1,-0.00060965],[23327,2,-0.00119078]]"#;
let received_at=Some(1677628803683) ;
assert_eq!("NONE", extract_symbol(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap());
assert_eq!(None, extract_timestamp(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap());

let orderbook = &parse_l2_snapshot(
EXCHANGE_NAME,
MarketType::Spot,
raw_msg,
Some("tBTCUSD"),
received_at,
)
.unwrap()[0];

assert_eq!(orderbook.asks.len(), 3);
assert_eq!(orderbook.bids.len(), 3);
assert!(orderbook.snapshot);

crate::utils::check_orderbook_fields(
EXCHANGE_NAME,
MarketType::Spot,
MessageType::L2Snapshot,
"BTC/USD".to_string(),
"tBTCUSD".to_string(),
orderbook,
raw_msg,
);

assert_eq!(orderbook.timestamp, received_at.unwrap());
assert_eq!(orderbook.seq_id, None);
assert_eq!(orderbook.prev_seq_id, None);
//"bids":[23144,3,0.87789],[23142,3,0.27991505],[23141,4,0.48547974] descending


assert_eq!(orderbook.bids[0].price, 23144.0);
assert_eq!(orderbook.bids[0].quantity_base, 0.87789);
assert_eq!(orderbook.bids[0].quantity_quote, 23144.0 * 0.87789);
assert_eq!(orderbook.bids[0].quantity_contract, None);

assert_eq!(orderbook.bids[2].price, 23141.0);
assert_eq!(orderbook.bids[2].quantity_base, 0.48547974);
assert_eq!(orderbook.bids[2].quantity_quote, 23141.0 * 0.48547974);
assert_eq!(orderbook.bids[2].quantity_contract, None);
//"asks":[[23325,5,-0.00297898],[23326,1,-0.00060965],[23327,2,-0.00119078]] ascending
assert_eq!(orderbook.asks[0].price, 23325.0);
assert_eq!(orderbook.asks[0].quantity_base, 0.00297898);
assert_eq!(orderbook.asks[0].quantity_quote, 23325.0 * 0.00297898);
assert_eq!(orderbook.asks[0].quantity_contract, None);

assert_eq!(orderbook.asks[2].price, 23327.0);
assert_eq!(orderbook.asks[2].quantity_base, 0.00119078);
assert_eq!(orderbook.asks[2].quantity_quote, 23327.0 * 0.00119078);
assert_eq!(orderbook.asks[2].quantity_contract, None);
}

#[test]
fn linear_swap() {
let raw_msg = r#"[[28293,1,0.0350506],[28291,1,0.0526735],[28289,2,0.1037385],[28287,1,0.1059222],[28285,1,0.1324028],[28284,1,0.1765371],[28282,1,0.2206713],[28280,1,0.2427385],[28277,1,0.2648056]]"#;

/*
1677628819111 1677628819111 [[23143,5,0.4721613],[23142,3,0.22947044],[23141,2,0.6234],[23906,1,-0.005],[23920,1,-0.0026],[23923,1,-0.02]]
*/
//let raw_msg = r#"[[28293,1,0.0350506],[28291,1,0.0526735],[28289,2,0.1037385],[28287,1,0.1059222],[28285,1,0.1324028],[28284,1,0.1765371],[28282,1,0.2206713],[28280,1,0.2427385],[28277,1,0.2648056]]"#;
let raw_msg = r#"[[23143,5,0.4721613],[23142,3,0.22947044],[23141,2,0.6234],[23906,1,-0.005],[23920,1,-0.0026],[23923,1,-0.02]]"#;
let received_at=Some(1677628819111) ;
assert_eq!("NONE", extract_symbol(EXCHANGE_NAME, MarketType::LinearSwap, raw_msg).unwrap());
assert_eq!(
None,
extract_timestamp(EXCHANGE_NAME, MarketType::LinearSwap, raw_msg).unwrap()
);

let orderbook = &parse_l2_snapshot(
EXCHANGE_NAME,
MarketType::Spot,
raw_msg,
Some("tBTCF0:USTF0"),
received_at
)
.unwrap()[0];

assert_eq!(orderbook.asks.len(), 3);
assert_eq!(orderbook.bids.len(), 3);
assert!(orderbook.snapshot);

crate::utils::check_orderbook_fields(
EXCHANGE_NAME,
MarketType::Spot,
MessageType::L2Snapshot,
"BTC/USDT".to_string(),
"tBTCF0:USTF0".to_string(),
orderbook,
raw_msg,
);

assert_eq!(orderbook.timestamp, received_at.unwrap());
assert_eq!(orderbook.seq_id, None);
assert_eq!(orderbook.prev_seq_id, None);
//"bids":[23143,5,0.4721613],[23142,3,0.22947044],[23141,2,0.6234] descending
assert_eq!(orderbook.bids[2].price, 23141.0);
assert_eq!(orderbook.bids[2].quantity_base, 0.6234);
assert_eq!(orderbook.bids[2].quantity_quote, round(23141.0 * 0.6234));
assert_eq!(orderbook.bids[2].quantity_contract, None);

assert_eq!(orderbook.bids[0].price, 23143.0);
assert_eq!(orderbook.bids[0].quantity_base, 0.4721613);
assert_eq!(orderbook.bids[0].quantity_quote, round(23143.0 * 0.4721613));
assert_eq!(orderbook.bids[0].quantity_contract, None);
//"asks":[[23906,1,-0.005],[23920,1,-0.0026],[23923,1,-0.02]] ascending
assert_eq!(orderbook.asks[0].price, 23906.0);
assert_eq!(orderbook.asks[0].quantity_base, 0.005);
assert_eq!(orderbook.asks[0].quantity_quote, round(23906.0 * 0.005));
assert_eq!(orderbook.asks[0].quantity_contract, None);

assert_eq!(orderbook.asks[2].price, 23923.0);
assert_eq!(orderbook.asks[2].quantity_base, 0.02);
assert_eq!(orderbook.asks[2].quantity_quote, round(23923.0 * 0.02));
assert_eq!(orderbook.asks[2].quantity_contract, None);
}
}

0 comments on commit b6f2de6

Please sign in to comment.