risq 0.4.1

Re-implementation of Bisq (https://github.com/bisq-network/bisq) in rust
use crate::domain::{amount::*, market::Market};

#[derive(Clone)]
pub struct Ticker {
    pub market: &'static Market,
    pub last: Option<NumberWithPrecision>,
    pub high: Option<NumberWithPrecision>,
    pub low: Option<NumberWithPrecision>,
    pub volume_left: NumberWithPrecision,
    pub volume_right: NumberWithPrecision,
    pub buy: Option<NumberWithPrecision>,
    pub sell: Option<NumberWithPrecision>,
}

#[cfg(feature = "statistics")]
pub use inner::*;
#[cfg(feature = "statistics")]
mod inner {
    use super::Ticker;
    use crate::domain::{
        amount::*,
        market::*,
        offer::{OfferDirection, OpenOffer},
        statistics::trade::*,
    };
    use std::{
        collections::HashMap,
        time::{Duration, SystemTime},
    };

    impl Ticker {
        pub fn from_trades<'a>(
            history: &TradeHistory,
            market: Option<&'static Market>,
            offers: impl Iterator<Item = &'a OpenOffer>,
        ) -> Vec<Ticker> {
            let mut tickers = HashMap::new();
            match market {
                None => {
                    for market in ALL.iter() {
                        tickers.insert(&market.pair, Self::empty(market));
                    }
                }
                Some(market) => {
                    tickers.insert(&market.pair, Self::empty(market));
                }
            }
            let mut trades = history.iter().rev();
            let mut next = match trades.next() {
                None => {
                    return Self::return_tickers(tickers, offers);
                }
                Some(next) => next,
            };

            let earliest = SystemTime::now() - Duration::from_secs(24 * 60 * 60);
            while next.timestamp >= earliest {
                if let Some(mut ticker) = tickers.get_mut(&next.market.pair) {
                    ticker.last = Some(ticker.last.unwrap_or(next.price));
                    ticker.high =
                        Some(ticker.high.map(|h| h.max(next.price)).unwrap_or(next.price));
                    ticker.low = Some(ticker.low.map(|l| l.min(next.price)).unwrap_or(next.price));
                    ticker.volume_left += next.amount;
                    ticker.volume_right += next.volume;
                }
                next = match trades.next() {
                    None => {
                        return Self::return_tickers(tickers, offers);
                    }
                    Some(next) => next,
                };
            }
            let mut missing_markets: HashMap<&String, &mut Ticker> = tickers
                .iter_mut()
                .filter_map(|(market, ticker)| {
                    if ticker.last.is_none() {
                        Some((*market, ticker))
                    } else {
                        None
                    }
                })
                .collect();
            loop {
                if missing_markets.is_empty() {
                    return Self::return_tickers(tickers, offers);
                }
                if let Some(ticker) = missing_markets.remove(&next.market.pair) {
                    ticker.last = Some(next.price);
                    ticker.high = Some(next.price);
                    ticker.low = Some(next.price);
                }

                next = match trades.next() {
                    None => {
                        return Self::return_tickers(tickers, offers);
                    }
                    Some(next) => next,
                };
            }
        }
        fn return_tickers<'a>(
            mut tickers: HashMap<&String, Ticker>,
            offers: impl Iterator<Item = &'a OpenOffer>,
        ) -> Vec<Ticker> {
            for offer in offers {
                if let Some(mut ticker) = tickers.get_mut(&offer.market.pair) {
                    match (offer.direction, ticker.buy, ticker.sell) {
                        (OfferDirection::Buy, _, None) => ticker.sell = Some(offer.display_price),
                        (OfferDirection::Sell, None, _) => ticker.buy = Some(offer.display_price),
                        (OfferDirection::Buy, _, Some(sell)) => {
                            ticker.sell = Some(sell.max(offer.display_price))
                        }
                        (OfferDirection::Sell, Some(buy), _) => {
                            ticker.buy = Some(buy.min(offer.display_price))
                        }
                    }
                }
            }
            let mut ret: Vec<Ticker> = tickers.into_iter().map(|(_, ticker)| ticker).collect();
            ret.sort_unstable_by(|a, b| a.market.pair.cmp(&b.market.pair));
            ret
        }
        fn empty(market: &'static Market) -> Self {
            Self {
                market,
                last: None,
                high: None,
                low: None,
                volume_left: ZERO,
                volume_right: ZERO,
                buy: None,
                sell: None,
            }
        }
    }
}