Skip to content

Commit

Permalink
implement parse_l2_snapshot for bitget
Browse files Browse the repository at this point in the history
  • Loading branch information
AdoreWisdom committed Jul 7, 2024
1 parent d785f48 commit 991395f
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 11 deletions.
62 changes: 62 additions & 0 deletions crypto-msg-parser/src/exchanges/bitget/bitget_mix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use crypto_msg_type::MessageType;

use crypto_message::{CandlestickMsg, Order, OrderBookMsg, TradeMsg, TradeSide};

use crate::exchanges::utils::calc_quantity_and_volume;

use super::EXCHANGE_NAME;
use serde::{Deserialize, Serialize};
use serde_json::Value;
Expand Down Expand Up @@ -35,6 +37,24 @@ struct RawOrderBook {
extra: HashMap<String, Value>,
}

#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
struct RestMsg<T: Sized> {
code: String,
msg: String,
requestTime: i64,
data: T,
#[serde(flatten)]
extra: HashMap<String, Value>,
}

#[derive(Serialize, Deserialize)]
struct RawL2SnapshotData {
asks: Vec<[String; 2]>,
bids: Vec<[String; 2]>,
timestamp: String,
}

fn extract_market_type_and_symbol(arg: &Arg) -> (MarketType, String) {
match arg.instType.as_str() {
"sp" => (MarketType::Spot, format!("{}_SPBL", arg.instId)),
Expand Down Expand Up @@ -191,6 +211,48 @@ pub(super) fn parse_l2(msg: &str) -> Result<Vec<OrderBookMsg>, SimpleError> {
Ok(orderbooks)
}

/// docs:
/// * https://bitgetlimited.github.io/apidoc/en/spot/
/// * https://api.bitget.com/api/spot/v1/market/depth?symbol=BTCUSDT_SPBL&type=step0
/// * https://bitgetlimited.github.io/apidoc/en/mix/#restapi
/// * https://api.bitget.com/api/mix/v1/market/depth?symbol=BTCUSDT_UMCBL&limit=100
pub(super) fn parse_l2_snapshot(
market_type: MarketType,
msg: &str,
symbol: Option<&str>,
) -> Result<Vec<OrderBookMsg>, SimpleError> {
let rs_msg = serde_json::from_str::<RestMsg<RawL2SnapshotData>>(msg)
.map_err(|_e| SimpleError::new(format!("Failed to parse JSON string {msg}")))?;

let symbol = symbol.unwrap_or_default();
let pair = crypto_pair::normalize_pair(symbol, EXCHANGE_NAME).unwrap();

let parse_order = |raw_order: &[String; 2]| -> Order {
let price = raw_order[0].parse::<f64>().unwrap();
let quantity = raw_order[1].parse::<f64>().unwrap();
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 orderbook_msg = OrderBookMsg {
exchange: EXCHANGE_NAME.to_string(),
market_type,
symbol: symbol.to_string(),
pair: pair.clone(),
msg_type: MessageType::L2Snapshot,
timestamp: rs_msg.data.timestamp.parse::<i64>().unwrap(),
seq_id: None,
prev_seq_id: None,
asks: rs_msg.data.asks.iter().map(parse_order).collect(),
bids: rs_msg.data.bids.iter().map(parse_order).collect(),
snapshot: true,
json: serde_json::to_string(&rs_msg).unwrap(),
};

Ok(vec![orderbook_msg])
}

/// docs:
/// * https://bitgetlimited.github.io/apidoc/en/spot/#candlesticks-channel
/// * https://bitgetlimited.github.io/apidoc/en/mix/#candlesticks-channel
Expand Down
10 changes: 9 additions & 1 deletion crypto-msg-parser/src/exchanges/bitget/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ pub(crate) fn get_msg_type(msg: &str) -> MessageType {
}
}
} else {
MessageType::Other
MessageType::L2Snapshot
}
}

Expand Down Expand Up @@ -185,3 +185,11 @@ pub(crate) fn parse_candlestick(
Err(SimpleError::new(format!("Unsupported Candlestick message {msg}")))
}
}

pub(crate) fn parse_l2_snapshot(
market_type: MarketType,
msg: &str,
symbol: Option<&str>,
) -> Result<Vec<OrderBookMsg>, SimpleError> {
bitget_mix::parse_l2_snapshot(market_type, msg, symbol)
}
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),
"bitget" => exchanges::bitget::parse_l2_snapshot(market_type, msg, symbol),
_ => Err(SimpleError::new(format!("Unknown exchange {exchange}"))),
};
match ret {
Expand Down
159 changes: 149 additions & 10 deletions crypto-msg-parser/tests/bitget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -685,42 +685,181 @@ 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#"{"code":"00000","msg":"success","requestTime":1654232411024,"data":{"asks":[["30413.33","0.0141"],["30413.37","0.0112"],["30413.41","0.0179"]],"bids":[["30413.27","0.0296"],["30413.23","0.0484"],["30413.19","0.1272"]],"timestamp":"1654232411024"}}"#;

let raw_msg = r#"{"code":"00000","msg":"success","requestTime":1677628818447,"data":{"asks":[["23141.01","0.0763"],["23141.16","0.1244"],["23141.4","0.0120"]],"bids":[["23139.91","0.7697"],["23139.76","1.2558"],["23139.56","0.0120"]],"timestamp":"1677628818450"}}"#;
// let raw_msg =
// r#"{"code":"00000","msg":"success","requestTime":1654232411024,"data":{"asks"
// :[["30413.33","0.0141"],["30413.37","0.0112"],["30413.41","0.0179"]],"bids":
// [["30413.27","0.0296"],["30413.23","0.0484"],["30413.19","0.1272"]],"
// timestamp":"1654232411024"}}"#;
let symbol = Some("BTCUSDT_SPBL");
assert_eq!("NONE", extract_symbol(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap());
assert_eq!(
1654232411024,
1677628818450,
extract_timestamp(EXCHANGE_NAME, MarketType::Spot, raw_msg).unwrap().unwrap()
);

let orderbook =
&parse_l2_snapshot(EXCHANGE_NAME, MarketType::Spot, raw_msg, symbol, None).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(),
symbol.unwrap().to_string(),
orderbook,
raw_msg,
);

assert_eq!(orderbook.timestamp, 1677628818450);
assert_eq!(orderbook.seq_id, None);
assert_eq!(orderbook.prev_seq_id, None);
//[["23139.91","0.7697"],["23139.76","1.2558"],["23139.56","0.0120"]] descending
assert_eq!(orderbook.bids[0].price, 23139.91);
assert_eq!(orderbook.bids[0].quantity_base, 0.7697);
assert_eq!(orderbook.bids[0].quantity_quote, round(23139.91 * 0.7697));
assert_eq!(orderbook.bids[0].quantity_contract, None);

assert_eq!(orderbook.bids[2].price, 23139.56);
assert_eq!(orderbook.bids[2].quantity_base, 0.0120);
assert_eq!(orderbook.bids[2].quantity_quote, round(23139.56 * 0.0120));
assert_eq!(orderbook.bids[2].quantity_contract, None);
//[["23141.01","0.0763"],["23141.16","0.1244"],["23141.4","0.0120"]] ascending
assert_eq!(orderbook.asks[0].price, 23141.01);
assert_eq!(orderbook.asks[0].quantity_base, 0.0763);
assert_eq!(orderbook.asks[0].quantity_quote, round(23141.01 * 0.0763));
assert_eq!(orderbook.asks[0].quantity_contract, None);

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

#[test]
fn inverse_swap() {
let raw_msg = r#"{"code":"00000","msg":"success","requestTime":1654234201087,"data":{"asks":[["30521","1.661"],["30521.5","2.312"],["30522","0.139"]],"bids":[["3.052E+4","0.256"],["30519.5","0.482"],["30519","0.079"]],"timestamp":"1654234201087"}}"#;

//let raw_msg =
// r#"{"code":"00000","msg":"success","requestTime":1654234201087,"data":{"asks"
// :[["30521","1.661"],["30521.5","2.312"],["30522","0.139"]],"bids":[["3.
// 052E+4","0.256"],["30519.5","0.482"],["30519","0.079"]],"timestamp":"
// 1654234201087"}}"#;
let raw_msg = r#"{"code":"00000","msg":"success","requestTime":0,"data":{"asks":[["23146","3.666"],["23146.5","5.562"],["2.315E+4","0.5"]],"bids":[["23145","0.107"],["23143","0.363"],["23141.5","0.518"]],"timestamp":"1677628802358"}}"#;
let symbol = Some("BTCUSD_DMCBL");
assert_eq!(
"NONE",
extract_symbol(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap()
);
assert_eq!(
1654234201087,
1677628802358,
extract_timestamp(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg).unwrap().unwrap()
);
let orderbook =
&parse_l2_snapshot(EXCHANGE_NAME, MarketType::InverseSwap, raw_msg, symbol, None)
.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::InverseSwap,
MessageType::L2Snapshot,
"BTC/USD".to_string(),
symbol.unwrap().to_string(),
orderbook,
raw_msg,
);

assert_eq!(orderbook.timestamp, 1677628802358);
assert_eq!(orderbook.seq_id, None);
assert_eq!(orderbook.prev_seq_id, None);
//"bids":[["23145","0.107"],["23143","0.363"],["23141.5","0.518"]] descending
assert_eq!(orderbook.bids[0].price, 23145.0);
assert_eq!(orderbook.bids[0].quantity_base, 0.107 * 1.0 / 23145.0);
assert_eq!(orderbook.bids[0].quantity_quote, 0.107);
assert_eq!(orderbook.bids[0].quantity_contract, Some(0.107));

assert_eq!(orderbook.bids[2].price, 23141.5);
assert_eq!(orderbook.bids[2].quantity_base, 0.518 / 23141.5);
assert_eq!(orderbook.bids[2].quantity_quote, 0.518);
assert_eq!(orderbook.bids[2].quantity_contract, Some(0.518));
//"asks":[["23146","3.666"],["23146.5","5.562"],["2.315E+4","0.5"]] ascending
assert_eq!(orderbook.asks[0].price, 23146.0);
assert_eq!(orderbook.asks[0].quantity_base, 3.666 / 23146.0);
assert_eq!(orderbook.asks[0].quantity_quote, 3.666);
assert_eq!(orderbook.asks[0].quantity_contract, Some(3.666));

assert_eq!(orderbook.asks[2].price, 2.315E+4);
assert_eq!(orderbook.asks[2].quantity_base, 0.5 / 2.315E+4);
assert_eq!(orderbook.asks[2].quantity_quote, 0.5);
assert_eq!(orderbook.asks[2].quantity_contract, Some(0.5));
}

#[test]
fn linear_swap() {
let raw_msg = r#"{"code":"00000","msg":"success","requestTime":1654234229348,"data":{"asks":[["30544","13.904"],["30544.5","2.678"],["30545","4.923"]],"bids":[["30543","0.756"],["30542.5","0.749"],["30542","15.438"]],"timestamp":"1654234229348"}}"#;

//let raw_msg =
// r#"{"code":"00000","msg":"success","requestTime":1654234229348,"data":{"asks"
// :[["30544","13.904"],["30544.5","2.678"],["30545","4.923"]],"bids":[["30543",
// "0.756"],["30542.5","0.749"],["30542","15.438"]],"timestamp":"1654234229348"
// }}"#;
let raw_msg = r#"{"code":"00000","msg":"success","requestTime":0,"data":{"asks":[["23133.5","28.326"],["23134","3.027"],["23134.5","128.392"]],"bids":[["23133","16.282"],["23132.5","0.074"],["23132","0.039"]],"timestamp":"1677628852476"}}"#;
let symbol = Some("BTCUSDT_UMCBL");
assert_eq!("NONE", extract_symbol(EXCHANGE_NAME, MarketType::LinearSwap, raw_msg).unwrap());
assert_eq!(
1654234229348,
1677628852476,
extract_timestamp(EXCHANGE_NAME, MarketType::LinearSwap, raw_msg).unwrap().unwrap()
);
let orderbook =
&parse_l2_snapshot(EXCHANGE_NAME, MarketType::LinearSwap, raw_msg, symbol, None)
.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::LinearSwap,
MessageType::L2Snapshot,
"BTC/USDT".to_string(),
symbol.unwrap().to_string(),
orderbook,
raw_msg,
);

assert_eq!(orderbook.timestamp, 1677628852476);
assert_eq!(orderbook.seq_id, None);
assert_eq!(orderbook.prev_seq_id, None);
//"bids":[["23133","16.282"],["23132.5","0.074"],["23132","0.039"]] descending
assert_eq!(orderbook.bids[0].price, 23133.0);
assert_eq!(orderbook.bids[0].quantity_base, 16.282 * 0.001);
assert_eq!(orderbook.bids[0].quantity_quote, round(16.282 * 0.001 * 23133.0));
assert_eq!(orderbook.bids[0].quantity_contract, Some(16.282));

assert_eq!(orderbook.bids[2].price, 23132.0);
assert_eq!(orderbook.bids[2].quantity_base, 0.039 * 0.001);
assert_eq!(orderbook.bids[2].quantity_quote, round(0.039 * 0.001 * 23132.0));
assert_eq!(orderbook.bids[2].quantity_contract, Some(0.039));
//"asks":[["23133.5","28.326"],["23134","3.027"],["23134.5","128.392"]] ascending
assert_eq!(orderbook.asks[0].price, 23133.5);
assert_eq!(orderbook.asks[0].quantity_base, 28.326 * 0.001);
assert_eq!(orderbook.asks[0].quantity_quote, round(28.326 * 0.001 * 23133.5));
assert_eq!(orderbook.asks[0].quantity_contract, Some(28.326));

assert_eq!(orderbook.asks[2].price, 23134.5);
assert_eq!(orderbook.asks[2].quantity_base, 128.392 * 0.001);
assert_eq!(orderbook.asks[2].quantity_quote, round(128.392 * 0.001 * 23134.5));
assert_eq!(orderbook.asks[2].quantity_contract, Some(128.392));
}
}

Expand Down

0 comments on commit 991395f

Please sign in to comment.