use crate::model; use crate::model::cfd::CfdOfferId; use crate::model::{Leverage, Position, TradingPair, Usd}; use bdk::bitcoin::Amount; use rocket::response::stream::Event; use serde::Serialize; use std::time::UNIX_EPOCH; #[derive(Debug, Clone, Serialize)] pub struct Cfd { pub offer_id: CfdOfferId, pub initial_price: Usd, pub leverage: Leverage, pub trading_pair: TradingPair, pub position: Position, pub liquidation_price: Usd, pub quantity_usd: Usd, #[serde(with = "::bdk::bitcoin::util::amount::serde::as_btc")] pub margin: Amount, #[serde(with = "::bdk::bitcoin::util::amount::serde::as_btc")] pub profit_btc: Amount, pub profit_usd: Usd, pub state: String, pub state_transition_unix_timestamp: u64, } #[derive(Debug, Clone, Serialize)] pub struct CfdOffer { pub id: CfdOfferId, pub trading_pair: TradingPair, pub position: Position, pub price: Usd, pub min_quantity: Usd, pub max_quantity: Usd, pub leverage: Leverage, pub liquidation_price: Usd, pub creation_unix_timestamp: u64, pub term_in_secs: u64, } pub trait ToSseEvent { fn to_sse_event(&self) -> Event; } impl ToSseEvent for Vec { // TODO: This conversion can fail, we might want to change the API fn to_sse_event(&self) -> Event { let cfds = self .iter() .map(|cfd| { // TODO: Get the actual current price here let current_price = Usd::ZERO; let (profit_btc, profit_usd) = cfd.calc_profit(current_price).unwrap(); Cfd { offer_id: cfd.offer_id, initial_price: cfd.initial_price, leverage: cfd.leverage, trading_pair: cfd.trading_pair.clone(), position: cfd.position.clone(), liquidation_price: cfd.liquidation_price, quantity_usd: cfd.quantity_usd, profit_btc, profit_usd, state: cfd.state.to_string(), state_transition_unix_timestamp: cfd .state .get_transition_timestamp() .duration_since(UNIX_EPOCH) .expect("timestamp to be convertable to duration since epoch") .as_secs(), // TODO: Depending on the state the margin might be set (i.e. in Open we save it // in the DB internally) and does not have to be calculated margin: cfd.calc_margin().unwrap(), } }) .collect::>(); Event::json(&cfds).event("cfds") } } impl ToSseEvent for Option { fn to_sse_event(&self) -> Event { let offer = self.clone().map(|offer| CfdOffer { id: offer.id, trading_pair: offer.trading_pair, position: offer.position, price: offer.price, min_quantity: offer.min_quantity, max_quantity: offer.max_quantity, leverage: offer.leverage, liquidation_price: offer.liquidation_price, creation_unix_timestamp: offer .creation_timestamp .duration_since(UNIX_EPOCH) .expect("timestamp to be convertible to duration since epoch") .as_secs(), term_in_secs: offer.term.as_secs(), }); Event::json(&offer).event("offer") } } impl ToSseEvent for Amount { fn to_sse_event(&self) -> Event { Event::json(&self.as_btc()).event("balance") } }