Browse Source

Move Quote inside projection actor

debug-collab-settlement
Mariusz Klochowicz 3 years ago
parent
commit
5fde06500b
No known key found for this signature in database GPG Key ID: 470C865699C8D4D
  1. 133
      daemon/src/projection.rs
  2. 5
      daemon/src/routes_taker.rs
  3. 96
      daemon/src/to_sse_event.rs

133
daemon/src/projection.rs

@ -1,8 +1,10 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::bitmex_price_feed::Quote; use crate::model::{TakerId, Timestamp};
use crate::model::TakerId; use crate::{bitmex_price_feed, model};
use crate::{Cfd, Order, UpdateCfdProposals}; use crate::{Cfd, Order, UpdateCfdProposals};
use rust_decimal::Decimal;
use serde::Serialize;
use tokio::sync::watch; use tokio::sync::watch;
use xtra_productivity::xtra_productivity; use xtra_productivity::xtra_productivity;
@ -11,19 +13,19 @@ pub struct Actor {
} }
pub struct Feeds { pub struct Feeds {
pub cfds: watch::Receiver<Vec<Cfd>>,
pub order: watch::Receiver<Option<Order>>, pub order: watch::Receiver<Option<Order>>,
pub cfds: watch::Receiver<Vec<Cfd>>,
pub quote: watch::Receiver<Quote>, pub quote: watch::Receiver<Quote>,
pub settlements: watch::Receiver<UpdateCfdProposals>, pub settlements: watch::Receiver<UpdateCfdProposals>,
pub connected_takers: watch::Receiver<Vec<TakerId>>, pub connected_takers: watch::Receiver<Vec<TakerId>>,
} }
impl Actor { 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_cfds, rx_cfds) = watch::channel(init_cfds);
let (tx_order, rx_order) = watch::channel(None); let (tx_order, rx_order) = watch::channel(None);
let (tx_update_cfd_feed, rx_update_cfd_feed) = watch::channel(HashMap::new()); 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()); 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>>) { fn handle(&mut self, msg: Update<Option<Order>>) {
let _ = self.tx.order.send(msg.0); let _ = self.tx.order.send(msg.0);
} }
fn handle(&mut self, msg: Update<Quote>) { fn handle(&mut self, msg: Update<bitmex_price_feed::Quote>) {
let _ = self.tx.quote.send(msg.0); let _ = self.tx.quote.send(msg.0.into());
} }
fn handle(&mut self, msg: Update<UpdateCfdProposals>) { fn handle(&mut self, msg: Update<UpdateCfdProposals>) {
let _ = self.tx.settlements.send(msg.0); let _ = self.tx.settlements.send(msg.0);
@ -80,3 +82,120 @@ impl Actor {
} }
impl xtra::Actor for 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")]);
}
}

5
daemon/src/routes_taker.rs

@ -5,7 +5,7 @@ use daemon::model::{Leverage, Price, Usd, WalletInfo};
use daemon::projection::Feeds; use daemon::projection::Feeds;
use daemon::routes::EmbeddedFileExt; use daemon::routes::EmbeddedFileExt;
use daemon::to_sse_event::{CfdAction, CfdsWithAuxData, ToSseEvent}; use daemon::to_sse_event::{CfdAction, CfdsWithAuxData, ToSseEvent};
use daemon::{taker_cfd, wallet}; use daemon::{bitmex_price_feed, taker_cfd, wallet};
use http_api_problem::{HttpApiProblem, StatusCode}; use http_api_problem::{HttpApiProblem, StatusCode};
use rocket::http::{ContentType, Status}; use rocket::http::{ContentType, Status};
use rocket::response::stream::EventStream; use rocket::response::stream::EventStream;
@ -152,7 +152,8 @@ pub async fn post_cfd_action(
} }
CfdAction::Commit => cfd_action_channel.send(Commit { order_id: id }), CfdAction::Commit => cfd_action_channel.send(Commit { order_id: id }),
CfdAction::Settle => { CfdAction::Settle => {
let current_price = feeds.quote.borrow().for_taker(); let quote: bitmex_price_feed::Quote = feeds.quote.borrow().clone().into();
let current_price = quote.for_taker();
cfd_action_channel.send(ProposeSettlement { cfd_action_channel.send(ProposeSettlement {
order_id: id, order_id: id,
current_price, current_price,

96
daemon/src/to_sse_event.rs

@ -3,6 +3,7 @@ use crate::model::cfd::{
Dlc, OrderId, Payout, Role, SettlementKind, UpdateCfdProposal, UpdateCfdProposals, Dlc, OrderId, Payout, Role, SettlementKind, UpdateCfdProposal, UpdateCfdProposals,
}; };
use crate::model::{Leverage, Position, TakerId, Timestamp, TradingPair}; use crate::model::{Leverage, Position, TakerId, Timestamp, TradingPair};
use crate::projection::{Price, Quote, Usd};
use crate::{bitmex_price_feed, model}; use crate::{bitmex_price_feed, model};
use bdk::bitcoin::{Amount, Network, SignedAmount, Txid}; use bdk::bitcoin::{Amount, Network, SignedAmount, Txid};
use rocket::request::FromParam; use rocket::request::FromParam;
@ -13,64 +14,6 @@ use std::convert::TryInto;
use time::OffsetDateTime; use time::OffsetDateTime;
use tokio::sync::watch; use tokio::sync::watch;
#[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)
}
}
#[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)
}
}
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, Serialize)] #[derive(Debug, Clone, Serialize)]
pub struct Cfd { pub struct Cfd {
pub order_id: OrderId, pub order_id: OrderId,
@ -250,12 +193,12 @@ pub struct CfdsWithAuxData {
impl CfdsWithAuxData { impl CfdsWithAuxData {
pub fn new( pub fn new(
rx_cfds: &watch::Receiver<Vec<model::cfd::Cfd>>, rx_cfds: &watch::Receiver<Vec<model::cfd::Cfd>>,
rx_quote: &watch::Receiver<bitmex_price_feed::Quote>, rx_quote: &watch::Receiver<Quote>,
rx_updates: &watch::Receiver<UpdateCfdProposals>, rx_updates: &watch::Receiver<UpdateCfdProposals>,
role: Role, role: Role,
network: Network, network: Network,
) -> Self { ) -> Self {
let quote = rx_quote.borrow().clone(); let quote: bitmex_price_feed::Quote = rx_quote.borrow().clone().into();
let current_price = match role { let current_price = match role {
Role::Maker => quote.for_maker(), Role::Maker => quote.for_maker(),
Role::Taker => quote.for_taker(), Role::Taker => quote.for_taker(),
@ -487,21 +430,9 @@ fn to_tx_url_list(state: model::cfd::CfdState, network: Network) -> Vec<TxUrl> {
} }
} }
#[derive(Debug, Clone, Serialize)] impl ToSseEvent for Quote {
pub struct Quote {
bid: Price,
ask: Price,
last_updated_at: Timestamp,
}
impl ToSseEvent for bitmex_price_feed::Quote {
fn to_sse_event(&self) -> Event { fn to_sse_event(&self) -> Event {
let quote = Quote { Event::json(self).event("quote")
bid: self.bid.into(),
ask: self.ask.into(),
last_updated_at: self.timestamp,
};
Event::json(&quote).event("quote")
} }
} }
@ -537,9 +468,6 @@ fn available_actions(state: CfdState, role: Role) -> Vec<CfdAction> {
mod tests { mod tests {
use super::*; use super::*;
use rust_decimal_macros::dec;
use serde_test::{assert_ser_tokens, Token};
#[test] #[test]
fn state_snapshot_test() { fn state_snapshot_test() {
// Make sure to update the UI after changing this test! // Make sure to update the UI after changing this test!
@ -567,18 +495,4 @@ mod tests {
let json = serde_json::to_string(&CfdState::SetupFailed).unwrap(); let json = serde_json::to_string(&CfdState::SetupFailed).unwrap();
assert_eq!(json, "\"SetupFailed\""); assert_eq!(json, "\"SetupFailed\"");
} }
#[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")]);
}
} }

Loading…
Cancel
Save