|
|
@ -1,8 +1,10 @@ |
|
|
|
use std::collections::HashMap; |
|
|
|
|
|
|
|
use crate::bitmex_price_feed::Quote; |
|
|
|
use crate::model::TakerId; |
|
|
|
use crate::model::{TakerId, Timestamp}; |
|
|
|
use crate::{bitmex_price_feed, model}; |
|
|
|
use crate::{Cfd, Order, UpdateCfdProposals}; |
|
|
|
use rust_decimal::Decimal; |
|
|
|
use serde::Serialize; |
|
|
|
use tokio::sync::watch; |
|
|
|
use xtra_productivity::xtra_productivity; |
|
|
|
|
|
|
@ -11,19 +13,19 @@ pub struct Actor { |
|
|
|
} |
|
|
|
|
|
|
|
pub struct Feeds { |
|
|
|
pub cfds: watch::Receiver<Vec<Cfd>>, |
|
|
|
pub order: watch::Receiver<Option<Order>>, |
|
|
|
pub cfds: watch::Receiver<Vec<Cfd>>, |
|
|
|
pub quote: watch::Receiver<Quote>, |
|
|
|
pub settlements: watch::Receiver<UpdateCfdProposals>, |
|
|
|
pub connected_takers: watch::Receiver<Vec<TakerId>>, |
|
|
|
} |
|
|
|
|
|
|
|
impl Actor { |
|
|
|
pub fn new(init_cfds: Vec<Cfd>, init_quote: Quote) -> (Self, Feeds) { |
|
|
|
pub fn new(init_cfds: Vec<Cfd>, init_quote: bitmex_price_feed::Quote) -> (Self, Feeds) { |
|
|
|
let (tx_cfds, rx_cfds) = watch::channel(init_cfds); |
|
|
|
let (tx_order, rx_order) = watch::channel(None); |
|
|
|
let (tx_update_cfd_feed, rx_update_cfd_feed) = watch::channel(HashMap::new()); |
|
|
|
let (tx_quote, rx_quote) = watch::channel(init_quote); |
|
|
|
let (tx_quote, rx_quote) = watch::channel(init_quote.into()); |
|
|
|
let (tx_connected_takers, rx_connected_takers) = watch::channel(Vec::new()); |
|
|
|
|
|
|
|
( |
|
|
@ -68,8 +70,8 @@ impl Actor { |
|
|
|
fn handle(&mut self, msg: Update<Option<Order>>) { |
|
|
|
let _ = self.tx.order.send(msg.0); |
|
|
|
} |
|
|
|
fn handle(&mut self, msg: Update<Quote>) { |
|
|
|
let _ = self.tx.quote.send(msg.0); |
|
|
|
fn handle(&mut self, msg: Update<bitmex_price_feed::Quote>) { |
|
|
|
let _ = self.tx.quote.send(msg.0.into()); |
|
|
|
} |
|
|
|
fn handle(&mut self, msg: Update<UpdateCfdProposals>) { |
|
|
|
let _ = self.tx.settlements.send(msg.0); |
|
|
@ -80,3 +82,120 @@ impl Actor { |
|
|
|
} |
|
|
|
|
|
|
|
impl xtra::Actor for Actor {} |
|
|
|
|
|
|
|
/// Types
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)] |
|
|
|
pub struct Usd { |
|
|
|
inner: model::Usd, |
|
|
|
} |
|
|
|
|
|
|
|
impl Usd { |
|
|
|
fn new(usd: model::Usd) -> Self { |
|
|
|
Self { |
|
|
|
inner: model::Usd::new(usd.into_decimal().round_dp(2)), |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
impl From<model::Usd> for Usd { |
|
|
|
fn from(usd: model::Usd) -> Self { |
|
|
|
Self::new(usd) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
impl Serialize for Usd { |
|
|
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |
|
|
|
where |
|
|
|
S: serde::Serializer, |
|
|
|
{ |
|
|
|
<Decimal as Serialize>::serialize(&self.inner.into_decimal(), serializer) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
impl Serialize for Price { |
|
|
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |
|
|
|
where |
|
|
|
S: serde::Serializer, |
|
|
|
{ |
|
|
|
<Decimal as Serialize>::serialize(&self.inner.into_decimal(), serializer) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[derive(Debug, Clone)] |
|
|
|
pub struct Price { |
|
|
|
inner: model::Price, |
|
|
|
} |
|
|
|
|
|
|
|
impl Price { |
|
|
|
fn new(price: model::Price) -> Self { |
|
|
|
Self { |
|
|
|
inner: model::Price::new(price.into_decimal().round_dp(2)).expect( |
|
|
|
"rounding a valid price to 2 decimal places should still result in a valid price", |
|
|
|
), |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
impl From<model::Price> for Price { |
|
|
|
fn from(price: model::Price) -> Self { |
|
|
|
Self::new(price) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// TODO: Remove this after CfdsWithAuxData is removed
|
|
|
|
impl From<Price> for model::Price { |
|
|
|
fn from(price: Price) -> Self { |
|
|
|
price.inner |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize)] |
|
|
|
pub struct Quote { |
|
|
|
bid: Price, |
|
|
|
ask: Price, |
|
|
|
last_updated_at: Timestamp, |
|
|
|
} |
|
|
|
|
|
|
|
impl From<bitmex_price_feed::Quote> for Quote { |
|
|
|
fn from(quote: bitmex_price_feed::Quote) -> Self { |
|
|
|
Quote { |
|
|
|
bid: quote.bid.into(), |
|
|
|
ask: quote.ask.into(), |
|
|
|
last_updated_at: quote.timestamp, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// TODO: Remove this after CfdsWithAuxData is removed
|
|
|
|
impl From<Quote> for bitmex_price_feed::Quote { |
|
|
|
fn from(quote: Quote) -> Self { |
|
|
|
Self { |
|
|
|
timestamp: quote.last_updated_at, |
|
|
|
bid: quote.bid.into(), |
|
|
|
ask: quote.ask.into(), |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[cfg(test)] |
|
|
|
mod tests { |
|
|
|
use super::*; |
|
|
|
|
|
|
|
use rust_decimal_macros::dec; |
|
|
|
use serde_test::{assert_ser_tokens, Token}; |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn usd_serializes_with_only_cents() { |
|
|
|
let usd = Usd::new(model::Usd::new(dec!(1000.12345))); |
|
|
|
|
|
|
|
assert_ser_tokens(&usd, &[Token::Str("1000.12")]); |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn price_serializes_with_only_cents() { |
|
|
|
let price = Price::new(model::Price::new(dec!(1000.12345)).unwrap()); |
|
|
|
|
|
|
|
assert_ser_tokens(&price, &[Token::Str("1000.12")]); |
|
|
|
} |
|
|
|
} |
|
|
|