| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- use std::str::FromStr;
- use std::sync::Arc;
- use chrono::Utc;
- use lazy_static::lazy_static;
- use rust_decimal::{Decimal};
- use rust_decimal::prelude::FromPrimitive;
- use serde_json::{from_value, Value};
- use tokio::sync::Mutex;
- use tokio::time::Instant;
- use tracing::{error};
- use exchanges::response_base::ResponseData;
- use global::trace_stack::TraceStack;
- use crate::{Account, OrderBook, Order, Position, PositionModeEnum, SpecialOrder, Depth, Trade, Ticker, Record};
- // 处理账号信息
- pub fn handle_account_info(res_data: &ResponseData, symbol: &String) -> Account {
- format_account_info(res_data.data.as_array().unwrap().clone(), symbol)
- }
- pub fn format_account_info(data: Vec<Value>, symbol: &String) -> Account {
- let account = data.iter().find(| &item | item["accountType"] == "UNIFIED");
- match account {
- None => {
- error!("Bybit:格式化统一账户信息错误!\nformat_account_info: data={:?}", data);
- panic!("Bybit:格式化统一账户信息错误!\nformat_account_info: data={:?}", data)
- }
- Some(val) =>{
- let arr: Vec<Value> = from_value(val["coin"].clone()).unwrap();
- let upper_str = symbol.to_uppercase();
- let symbol_array: Vec<&str> = upper_str.split("_").collect();
- let balance_info = arr.iter().find(|&item| item["coin"].as_str().unwrap() == symbol_array[1]);
- match balance_info {
- None => {
- error!("Bybit:格式化usdt余额信息错误!\nformat_account_info: data={:?}", balance_info);
- panic!("Bybit:格式化usdt余额信息错误!\nformat_account_info: data={:?}", balance_info)
- }
- Some(value) => {
- let balance = Decimal::from_str(&value["walletBalance"].as_str().unwrap().to_string()).unwrap();
- Account {
- coin: symbol_array[1].to_string(),
- balance,
- available_balance: Decimal::ZERO,
- frozen_balance: Decimal::ZERO,
- stocks: Decimal::ZERO,
- available_stocks: Decimal::ZERO,
- frozen_stocks: Decimal::ZERO,
- }
- }
- }
- }
- }
- }
- // 处理position信息
- pub fn handle_position(res_data: &ResponseData, ct_val: &Decimal) -> Vec<Position> {
- res_data.data.as_array().unwrap().iter().map(|item| { format_position_item(item, ct_val) }).collect()
- }
- pub fn format_position_item(position: &Value, ct_val: &Decimal) -> Position {
- let position_idx: String = position["positionIdx"].to_string();
- let mut position_mode = match position_idx.as_str() {
- "0" => PositionModeEnum::Both,
- "1" => PositionModeEnum::Long,
- "2" => PositionModeEnum::Short,
- _ => {
- error!("bybit_swap:格式化持仓模式错误!\nformat_position_item:position={:?}", position);
- panic!("bybit_swap:格式化持仓模式错误!\nformat_position_item:position={:?}", position)
- }
- };
- let symbol_mapper = position["symbol"].as_str().unwrap().to_string();
- let currency = "USDT";
- let coin = &symbol_mapper[..symbol_mapper.find(currency).unwrap_or(0)];
- let size_str: String = from_value(position["size"].clone()).unwrap();
- let size = Decimal::from_str(size_str.as_str()).unwrap();
- let amount = size * ct_val;
- let side = position["side"].as_str().unwrap().to_string();
- let mut profit = Decimal::ZERO;
- let profit_str = position["unrealisedPnl"].as_str().unwrap_or("0");
- if profit_str != "" {
- profit = Decimal::from_str(profit_str).unwrap();
- }
- let update_time = Decimal::from_str(position["updatedTime"].as_str().unwrap()).unwrap();
- match position_mode {
- PositionModeEnum::Both => {
- position_mode = match side.as_str() {
- "Buy" => PositionModeEnum::Long,
- "Sell" => PositionModeEnum::Short,
- _ => { PositionModeEnum::Both }
- }
- }
- _ => {}
- }
- Position {
- symbol: format!{"{}_{}", coin, currency},
- margin_level: Decimal::from_str(position["leverage"].as_str().unwrap()).unwrap(),
- amount,
- frozen_amount: Decimal::ZERO,
- price: Decimal::from_str(position["entryPrice"].as_str().unwrap()).unwrap(),
- profit,
- position_mode,
- margin: Decimal::from_str(position["positionBalance"].as_str().unwrap()).unwrap(),
- update_time
- }
- }
- // 处理order信息
- pub fn handle_order(res_data: &ResponseData, ct_val: &Decimal) -> SpecialOrder {
- let res_data_json: Vec<Value> = res_data.data.as_array().unwrap().clone();
- let mut order_info = Vec::new();
- for item in res_data_json.iter() {
- order_info.push(format_order_item(item.clone(), ct_val));
- };
- SpecialOrder {
- name: res_data.label.clone(),
- order: order_info,
- }
- }
- pub fn format_order_item(order: Value, ct_val: &Decimal) -> Order {
- let status = order["orderStatus"].as_str().unwrap_or("");
- let text = order["orderLinkId"].as_str().unwrap_or("");
- let size = Decimal::from_str(order["qty"].as_str().unwrap()).unwrap();
- let right = Decimal::from_str(order["cumExecQty"].as_str().unwrap()).unwrap();
- let right_val = Decimal::from_str(order["cumExecValue"].as_str().unwrap()).unwrap();
- let price = Decimal::from_str(order["price"].as_str().unwrap()).unwrap();
- let amount = size * ct_val;
- let mut avg_price = Decimal::ZERO;
- if right != Decimal::ZERO {
- avg_price = right_val / right;
- }
- let deal_amount = right * ct_val;
- let custom_status = if status == "Filled" || status == "Cancelled" { "REMOVE".to_string() } else if status == "New" { "NEW".to_string() } else {
- "NULL".to_string()
- };
- let rst_order = Order {
- id: format!("{}", order["orderId"].as_str().unwrap()),
- custom_id: text.replace("t-my-custom-id_", "").replace("t-", ""),
- price,
- amount,
- deal_amount,
- avg_price,
- status: custom_status,
- order_type: "limit".to_string(),
- trace_stack: TraceStack::new(0, Instant::now()).on_special("132 bybit_swap_handle".to_string()),
- };
- rst_order
- }
- pub fn format_trade_items(response: &ResponseData) -> Vec<Trade> {
- let result = response.data.as_array().unwrap();
- let mut trades = vec![];
- for item in result {
- // 因为gate的量都是张数,所以要进行真实交易量处理
- let mut size = Decimal::from_str(item["v"].as_str().unwrap()).unwrap();
- let price = Decimal::from_str(item["p"].as_str().unwrap().to_string().as_str()).unwrap();
- let side = item["S"].as_str().unwrap().to_string();
- size = match side.as_str() {
- "Buy" => {
- size
- }
- "Sell" => {
- -size
- }
- _ => {
- error!("{}", item.to_string());
- panic!("Bybit trade error side(bybit_swap_handle_156)")
- }
- };
- let value = (size * price).abs();
- trades.push(Trade {
- id: item["i"].as_str().unwrap().to_string(),
- time: Decimal::from_i64(item["T"].as_i64().unwrap()).unwrap(),
- size,
- price,
- value,
- symbol: item["s"].as_str().unwrap().to_string(),
- })
- }
- return trades
- }
- pub fn handle_book_ticker(res_data: &ResponseData, mul: &Decimal) -> Depth {
- let asks = format_depth_items(res_data.data["a"].clone(), mul);
- let bids = format_depth_items(res_data.data["b"].clone(), mul);
- let t = Decimal::from_i64(res_data.reach_time).unwrap();
- let s = res_data.data["s"].as_str().unwrap().replace("USDT", "_USDT");
- Depth {
- time: t,
- symbol: s.to_string(),
- asks,
- bids,
- }
- }
- lazy_static! {
- static ref TICKER: Arc<Mutex<Ticker>> = Arc::new(Mutex::new(Ticker::new()));
- }
- pub async fn handle_ticker(res_data: &ResponseData) -> Ticker {
- let mut ticker = TICKER.lock().await;
- ticker.time = Decimal::from_i64(Utc::now().timestamp_millis()).unwrap();
- ticker.high = match res_data.data["highPrice24h"].as_str() {
- Some(str) => { Decimal::from_str(str).unwrap() }
- None => { ticker.high }
- };
- ticker.low = match res_data.data["lowPrice24h"].as_str() {
- Some(str) => { Decimal::from_str(str).unwrap() }
- None => { ticker.low }
- };
- ticker.sell = match res_data.data["ask1Price"].as_str() {
- Some(str) => { Decimal::from_str(str).unwrap() }
- None => { ticker.sell }
- };
- ticker.buy = match res_data.data["bid1Price"].as_str() {
- Some(str) => { Decimal::from_str(str).unwrap() }
- None => { ticker.buy }
- };
- ticker.last = match res_data.data["lastPrice"].as_str() {
- Some(str) => { Decimal::from_str(str).unwrap() }
- None => { ticker.last }
- };
- ticker.volume = match res_data.data["volume24h"].as_str() {
- Some(str) => { Decimal::from_str(str).unwrap() }
- None => { ticker.volume }
- };
- ticker.open_interest = match res_data.data["openInterest"].as_str() {
- Some(str) => { Decimal::from_str(str).unwrap() }
- None => { ticker.open_interest }
- };
- // let s = res_data.data["symbol"].as_str().unwrap().replace("USDT", "_USDT");
- ticker.clone()
- }
- pub fn format_depth_items(value: Value, mul: &Decimal) -> Vec<OrderBook> {
- let mut depth_items: Vec<OrderBook> = vec![];
- for val in value.as_array().unwrap() {
- let arr = val.as_array().unwrap();
- let price = Decimal::from_str(arr[0].as_str().unwrap()).unwrap();
- let size = Decimal::from_str(arr[1].as_str().unwrap()).unwrap();
- if size.is_zero() {
- continue
- }
- depth_items.push(OrderBook {
- price,
- size,
- value: price * size * mul,
- })
- }
- depth_items
- }
- pub fn handle_records(value: &Value) -> Vec<Record> {
- let mut records = vec![];
- for record_value in value.as_array().unwrap() {
- records.push(Record {
- time: Decimal::from_i64(record_value["start"].as_i64().unwrap()).unwrap(),
- open: Decimal::from_str(record_value["open"].as_str().unwrap()).unwrap(),
- high: Decimal::from_str(record_value["high"].as_str().unwrap()).unwrap(),
- low: Decimal::from_str(record_value["low"].as_str().unwrap()).unwrap(),
- close: Decimal::from_str(record_value["close"].as_str().unwrap()).unwrap(),
- volume: Decimal::from_str(record_value["volume"].as_str().unwrap()).unwrap(),
- symbol: "".to_string(),
- });
- }
- return records;
- }
|