Skip to main content

polyester/codecs/
heatmap.rs

1//! Heatmap request helpers (Go `codecs/heatmap.go` parity).
2
3use buffa::Enumeration;
4
5use crate::errors::{Error, Result};
6use crate::proto::marketdata::v1::{HeatmapDepth, HeatmapInterval, HeatmapQuantityMode};
7
8pub fn resolve_heatmap_interval(value: &str) -> Result<HeatmapInterval> {
9    let key = heatmap_interval_channel_name(value);
10    HeatmapInterval::from_proto_name(&key)
11        .or_else(|| HeatmapInterval::from_proto_name(&key.to_ascii_uppercase()))
12        .ok_or_else(|| Error::validation(format!("unknown heatmap interval: {value}")))
13}
14
15/// Channel segment for live heatmap subscriptions (Go `IntervalAliases`).
16pub fn heatmap_interval_channel_name(value: &str) -> String {
17    match value {
18        "1s" => "INTERVAL_1S".to_owned(),
19        "1m" => "INTERVAL_1M".to_owned(),
20        "5m" => "INTERVAL_5M".to_owned(),
21        "1h" => "INTERVAL_1H".to_owned(),
22        other => other.to_owned(),
23    }
24}
25
26pub fn heatmap_depth_for_levels(depth: u32) -> HeatmapDepth {
27    match depth {
28        0..=5 => HeatmapDepth::Depth5,
29        6..=10 => HeatmapDepth::Depth10,
30        11..=20 => HeatmapDepth::Depth20,
31        21..=50 => HeatmapDepth::Depth50,
32        51..=100 => HeatmapDepth::Depth100,
33        _ => HeatmapDepth::Depth200,
34    }
35}
36
37pub fn resolve_heatmap_quantity_mode(value: &str) -> Result<HeatmapQuantityMode> {
38    let lower = value.to_ascii_lowercase();
39    let key = match lower.as_str() {
40        "close" => "CLOSE",
41        "peak" => "PEAK",
42        _ => value,
43    };
44    HeatmapQuantityMode::from_proto_name(key)
45        .or_else(|| HeatmapQuantityMode::from_proto_name(&key.to_ascii_uppercase()))
46        .ok_or_else(|| Error::validation("quantity_mode must be 'close' or 'peak'"))
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52
53    #[test]
54    fn interval_aliases() {
55        assert_eq!(
56            resolve_heatmap_interval("1m").unwrap(),
57            HeatmapInterval::Interval1m
58        );
59        assert_eq!(
60            resolve_heatmap_interval("INTERVAL_1H").unwrap(),
61            HeatmapInterval::Interval1h
62        );
63    }
64
65    #[test]
66    fn depth_buckets() {
67        assert_eq!(heatmap_depth_for_levels(5), HeatmapDepth::Depth5);
68        assert_eq!(heatmap_depth_for_levels(50), HeatmapDepth::Depth50);
69        assert_eq!(heatmap_depth_for_levels(200), HeatmapDepth::Depth200);
70    }
71}