atelier_quant 0.0.12

Quantitative Finance Tools & Models for the atelier-rs engine
Documentation
//! Timestamp validation and gap detection.
//!
//! Pre-flight checks for timestamp sequences before they enter the
//! estimation pipeline.

use serde::{Deserialize, Serialize};

/// A gap between consecutive timestamps that exceeds the threshold.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GapInfo {
    /// Index of the *first* event in the gap (i.e. `timestamps[index]`
    /// to `timestamps[index + 1]`).
    pub index: usize,
    /// Gap width in nanoseconds.
    pub gap_ns: u64,
}

/// Result of monotonicity validation.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum MonotonicityResult {
    /// All timestamps are strictly increasing.
    StrictlyMonotonic,
    /// A violation was found at the given index.
    Violation { index: usize, prev: u64, curr: u64 },
}

impl MonotonicityResult {
    /// Returns `true` if the sequence is strictly monotonic.
    pub fn is_ok(&self) -> bool {
        matches!(self, Self::StrictlyMonotonic)
    }
}

/// Check whether a timestamp sequence is strictly monotonically
/// increasing.
///
/// Returns at the first violation (if any).
pub fn check_monotonicity(timestamps_ns: &[u64]) -> MonotonicityResult {
    for i in 1..timestamps_ns.len() {
        if timestamps_ns[i] <= timestamps_ns[i - 1] {
            return MonotonicityResult::Violation {
                index: i,
                prev: timestamps_ns[i - 1],
                curr: timestamps_ns[i],
            };
        }
    }
    MonotonicityResult::StrictlyMonotonic
}

/// Detect all gaps between consecutive timestamps that exceed a
/// threshold.
///
/// # Arguments
///
/// * `timestamps_ns` — sorted nanosecond timestamps.
/// * `threshold_ns` — minimum gap width to report.
///
/// # Returns
///
/// A `Vec<GapInfo>` of all oversized gaps, in order.
pub fn detect_gaps(timestamps_ns: &[u64], threshold_ns: u64) -> Vec<GapInfo> {
    let mut gaps = Vec::new();
    for i in 1..timestamps_ns.len() {
        let gap = timestamps_ns[i].saturating_sub(timestamps_ns[i - 1]);
        if gap > threshold_ns {
            gaps.push(GapInfo {
                index: i - 1,
                gap_ns: gap,
            });
        }
    }
    gaps
}