atelier_data 0.0.15

Data Artifacts and I/O for the atelier-rs engine
use crate::orderbooks::{Orderbook, OrderbookDelta};

/// Orderbook update type (exchange-agnostic)
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum OrderbookUpdateType {
    Snapshot,
    Delta,
}

/// Result of applying an update to the orderbook
#[derive(Debug, Clone)]
pub struct OrderbookUpdate {
    /// Type of update that was applied
    pub update_type: OrderbookUpdateType,
    /// Number of bid levels modified
    pub bids_modified: usize,
    /// Number of ask levels modified  
    pub asks_modified: usize,
    /// Number of levels deleted (size=0)
    pub levels_deleted: usize,
    /// Number of levels inserted (new price)
    pub levels_inserted: usize,
    /// Whether this was a reset (snapshot)
    pub was_reset: bool,
}

/// Specifies which orderbook representation to load into.
///
/// Use this to tell the reader which data structure you need:
/// - [`OrderbookTarget::Delta`] for live delta processing with `BTreeMap`
/// - [`OrderbookTarget::Snapshot`] for analysis/simulation with `Vec<Level>`
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OrderbookTarget {
    /// Load into [`OrderbookDelta`] (BTreeMap-based, for delta processing)
    Delta,
    /// Load into [`Orderbook`] (`Vec<Level>`-based, for simulation/analysis)
    Snapshot,
}

/// Result of reading an orderbook file, containing either format.
///
/// Pattern match on this to extract the concrete type you need.
#[derive(Debug)]
pub enum OrderbookTargetData {
    /// Delta-processing with `BTreeMap<Decimal, Decimal>` storage
    Delta(OrderbookDelta),
    /// Snapshot orderbook with `Vec<Level>` storage
    Snapshot(Vec<Orderbook>),
}

impl OrderbookTargetData {
    /// Returns `true` if this is a `Delta` variant.
    #[inline]
    pub fn is_delta(&self) -> bool {
        matches!(self, Self::Delta(_))
    }

    /// Returns `true` if this is a `Snapshot` variant.
    #[inline]
    pub fn is_snapshot(&self) -> bool {
        matches!(self, Self::Snapshot(_))
    }

    /// Attempt to extract the `OrderbookDelta`, consuming self.
    ///
    /// Returns `None` if this is a `Snapshot` variant.
    pub fn into_delta(self) -> Option<OrderbookDelta> {
        match self {
            Self::Delta(m) => Some(m),
            Self::Snapshot(_) => None,
        }
    }

    /// Attempt to extract the `Vec<Orderbook>`, consuming self.
    ///
    /// Returns `None` if this is a `Delta` variant.
    pub fn into_snapshot(self) -> Option<Vec<Orderbook>> {
        match self {
            Self::Delta(_) => None,
            Self::Snapshot(obs) => Some(obs),
        }
    }

    /// Get the symbol regardless of variant.
    ///
    /// For `Snapshot`, returns the symbol of the first orderbook,
    /// or an empty string if the vector is empty.
    pub fn symbol(&self) -> &str {
        match self {
            Self::Delta(m) => m.symbol(),
            Self::Snapshot(obs) => obs.first().map(|ob| ob.symbol.as_str()).unwrap_or(""),
        }
    }

    /// Number of orderbook snapshots.
    ///
    /// Returns 1 for `Delta` (single aggregated state), or the
    /// actual count for `Snapshot`.
    pub fn len(&self) -> usize {
        match self {
            Self::Delta(_) => 1,
            Self::Snapshot(obs) => obs.len(),
        }
    }

    /// Returns `true` if there are no snapshots (only possible for empty `Snapshot`).
    pub fn is_empty(&self) -> bool {
        match self {
            Self::Delta(_) => false,
            Self::Snapshot(obs) => obs.is_empty(),
        }
    }
}