cxmr_feeds/
event.rs

1//! Crypto-bank feeds event primitives.
2
3use cxmr_balances::points;
4use cxmr_exchanges::{Order, OrderSide, Trade};
5
6use super::{Error, OrderBook};
7
8/// Event in a currency pair market.
9#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
10pub enum Event {
11    /// Full order books update.
12    OrderBook(OrderBook),
13
14    /// Single trade.
15    Trade(Trade),
16
17    /// Insert order at rate.
18    InsertOrder(Order),
19
20    /// Reset order amount at rate.
21    ResetOrder(Order),
22
23    /// Removed order amount at rate.
24    RemoveOrder(Order),
25}
26
27impl Event {
28    pub fn is_order_book(&self) -> bool {
29        match self {
30            &Event::OrderBook(_) => true,
31            _ => false,
32        }
33    }
34}
35
36impl From<EventData> for Event {
37    fn from(row: EventData) -> Event {
38        let side = if row.is_bid {
39            OrderSide::Bid
40        } else {
41            OrderSide::Ask
42        };
43        let order = Order {
44            side: side,
45            rate: row.rate.into(),
46            amount: row.amount.into(),
47        };
48        if row.is_trade {
49            Event::Trade(Trade {
50                id: None,
51                order: order,
52                timestamp: row.ts,
53            })
54        } else {
55            Event::ResetOrder(order)
56        }
57    }
58}
59
60/// Tectonic data row.
61#[derive(Clone, Debug, Default)]
62pub struct EventData {
63    /// Event timestamp.
64    pub ts: u64,
65    /// True if event is trade.
66    /// It's order if false.
67    pub is_trade: bool,
68    /// True if event is bid.
69    /// It's ask if false.
70    pub is_bid: bool,
71    /// Order or trade price.
72    pub rate: f32,
73    /// Order or trade quantity.
74    pub amount: f32,
75}
76
77impl EventData {
78    /// Constructs data row from event.
79    pub fn from_event(event: &Event, ts: u64) -> Result<EventData, Error> {
80        let (rate, amount, is_bid, is_trade) = match event {
81            Event::Trade(trade) => Ok((
82                trade.order.rate,
83                trade.order.amount,
84                trade.order.is_bid(),
85                true,
86            )),
87            Event::RemoveOrder(order) => Ok((order.rate, -order.amount, order.is_bid(), false)),
88            Event::ResetOrder(order) => Ok((order.rate, order.amount, order.is_bid(), false)),
89            Event::InsertOrder(order) => Ok((order.rate, order.amount, order.is_bid(), false)),
90            _ => Err(Error::InvalidEventKind),
91        }?;
92        Ok(EventData {
93            ts: ts,
94            rate: rate as f32,
95            amount: amount as f32,
96            is_bid: is_bid,
97            is_trade: is_trade,
98        })
99    }
100
101    /// Serializes to CSV with timestamp in seconds.
102    pub fn to_csv(&self) -> String {
103        format!(
104            r#"{},{},{},{},{}"#,
105            self.ts / 1000,
106            if self.is_trade { 1 } else { 0 },
107            if self.is_bid { 1 } else { 0 },
108            points(self.rate as f64),
109            points(self.amount as f64)
110        )
111    }
112
113    /// Serializes to CSV with timestamp in seconds.
114    /// Prints row without `is_trade` column.
115    pub fn to_trade_csv(&self) -> String {
116        format!(
117            r#"{},{},{},{}"#,
118            self.ts / 1000,
119            if self.is_bid { 1 } else { 0 },
120            points(self.rate as f64),
121            points(self.amount as f64)
122        )
123    }
124}
125
126impl PartialEq for EventData {
127    fn eq(&self, other: &EventData) -> bool {
128        self.ts == other.ts
129    }
130}
131
132impl From<&Event> for Vec<EventData> {
133    fn from(ev: &Event) -> Vec<EventData> {
134        match ev {
135            Event::OrderBook(book) => {
136                let mut res = Vec::with_capacity(book.asks.len() + book.bids.len());
137                for order in &book.asks {
138                    res.push(EventData {
139                        ts: 0,
140                        is_bid: false,
141                        is_trade: false,
142                        rate: order.rate as f32,
143                        amount: order.amount as f32,
144                    })
145                }
146                for order in &book.bids {
147                    res.push(EventData {
148                        ts: 0,
149                        is_bid: true,
150                        is_trade: false,
151                        rate: order.rate as f32,
152                        amount: order.amount as f32,
153                    })
154                }
155                res
156            }
157            _ => vec![EventData::from_event(ev, 0).unwrap()],
158        }
159    }
160}