Skip to content

Commit aa4731f

Browse files
committed
temporal transformations
1 parent b13b8b8 commit aa4731f

File tree

5 files changed

+127
-0
lines changed

5 files changed

+127
-0
lines changed

atelier-data/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ path = "tests/types/synchronizers/test_market_sync.rs"
112112
name = "test_market_aggregate"
113113
path = "tests/types/snapshots/test_market_aggregate.rs"
114114

115+
[[test]]
116+
name = "test_temporal_resolutions"
117+
path = "tests/types/temporal/test_resolutions.rs"
118+
115119
# ── parquet round-trip tests ──
116120

117121
[[test]]

atelier-data/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ pub mod templates;
4646
/// Public Trades
4747
pub mod trades;
4848

49+
/// Temporal data treatments
50+
pub mod temporal;
51+
4952
/// Mappings for data sources
5053
pub mod sources;
5154

atelier-data/src/temporal/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pub mod resolutions;
2+
pub use resolutions::*;
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//! Unified temporal resolution layer.
2+
//!
3+
//! Orderbook timestamps are in nanoseconds, trade timestamps in milliseconds,
4+
//! and other datasets might be in microseconds or seconds.
5+
//! This module provides a canonical representation (nanoseconds) and conversion
6+
//! utilities to normalize heterogeneous timestamp sources.
7+
8+
use serde::{Deserialize, Serialize};
9+
10+
/// Nanoseconds per microsecond.
11+
pub const NS_PER_US: u64 = 1_000;
12+
13+
/// Nanoseconds per millisecond.
14+
pub const NS_PER_MS: u64 = 1_000_000;
15+
16+
/// Nanoseconds per second.
17+
pub const NS_PER_S: u64 = 1_000_000_000;
18+
19+
/// Supported temporal resolutions for timestamp data.
20+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
21+
pub enum TimeResolution {
22+
Nanoseconds,
23+
Microseconds,
24+
Milliseconds,
25+
Seconds,
26+
}
27+
28+
impl TimeResolution {
29+
/// Returns the number of nanoseconds per unit of this resolution.
30+
pub fn nanos_per_unit(self) -> u64 {
31+
match self {
32+
Self::Nanoseconds => 1,
33+
Self::Microseconds => NS_PER_US,
34+
Self::Milliseconds => NS_PER_MS,
35+
Self::Seconds => NS_PER_S,
36+
}
37+
}
38+
}
39+
40+
/// Convert a timestamp from the given resolution to nanoseconds.
41+
///
42+
/// # Examples
43+
///
44+
/// ```
45+
/// use atelier_data::temporal::{TimeResolution, to_nanos};
46+
///
47+
/// let ts_ms: u64 = 1_672_304_484_932;
48+
/// let ts_ns = to_nanos(ts_ms, TimeResolution::Milliseconds);
49+
/// assert_eq!(ts_ns, ts_ms * 1_000_000);
50+
/// ```
51+
pub fn to_nanos(ts: u64, resolution: TimeResolution) -> u64 {
52+
ts.saturating_mul(resolution.nanos_per_unit())
53+
}
54+
55+
/// Convert a nanosecond timestamp to the target resolution as `f64`.
56+
///
57+
/// Returns a floating-point value to preserve sub-unit precision
58+
/// (e.g. fractional microsecond or milliseconds).
59+
///
60+
/// # Examples
61+
///
62+
/// ```
63+
/// use atelier_quant::temporal::{TimeResolution, from_nanos};
64+
///
65+
/// let ts_ns: u64 = 1_500_000; // 1.5 ms
66+
/// let ts_ms = from_nanos(ts_ns, TimeResolution::Milliseconds);
67+
/// assert!((ts_ms - 1.5).abs() < 1e-10);
68+
/// ```
69+
pub fn from_nanos(ts_ns: u64, resolution: TimeResolution) -> f64 {
70+
ts_ns as f64 / resolution.nanos_per_unit() as f64
71+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#[cfg(test)]
2+
mod tests {
3+
4+
use atelier_data::temporal;
5+
6+
#[test]
7+
fn test_from_nanos_to_seconds() {
8+
let ts_ns: u64 = 1_500_000_000; // 1.5 seconds
9+
let ts_us = temporal::from_nanos(ts_ns, temporal::TimeResolution::Seconds);
10+
assert!((ts_us - 1.5).abs() < 1e-13);
11+
}
12+
13+
#[test]
14+
fn test_from_nanos_to_millis() {
15+
let ts_ns: u64 = 1_500_000; // 1.5 milliseconds
16+
let ts_ms = temporal::from_nanos(ts_ns, temporal::TimeResolution::Milliseconds);
17+
assert!((ts_ms - 1.5).abs() < 1e-10);
18+
}
19+
20+
#[test]
21+
fn test_from_nanos_to_micros() {
22+
let ts_ns: u64 = 1_500; // 1.5 microseconds
23+
let ts_us = temporal::from_nanos(ts_ns, temporal::TimeResolution::Microseconds);
24+
assert!((ts_us - 1.5).abs() < 1e-7);
25+
}
26+
27+
#[test]
28+
fn test_from_seconds_to_nanos() {
29+
let ts_s: u64 = 1_672;
30+
let ts_ns = temporal::to_nanos(ts_s, temporal::TimeResolution::Seconds);
31+
assert_eq!(ts_ns, ts_s * temporal::NS_PER_S);
32+
}
33+
34+
#[test]
35+
fn test_from_millis_to_nanos() {
36+
let ts_ms: u64 = 1_672_304;
37+
let ts_ns = temporal::to_nanos(ts_ms, temporal::TimeResolution::Milliseconds);
38+
assert_eq!(ts_ns, ts_ms * temporal::NS_PER_MS);
39+
}
40+
41+
#[test]
42+
fn test_from_micros_to_nanos() {
43+
let ts_us: u64 = 1_672_304_484;
44+
let ts_ns = temporal::to_nanos(ts_us, temporal::TimeResolution::Microseconds);
45+
assert_eq!(ts_ns, ts_us * temporal::NS_PER_US);
46+
}
47+
}

0 commit comments

Comments
 (0)