Skip to main content

flowsurface_data/chart/kline/indicators/core/
input.rs

1use exchange::UnixMs;
2use std::collections::BTreeMap;
3
4/// Provider-timestamped, finite Open Interest values owned by `KlineSeries`.
5///
6/// The concrete model deliberately has no provider or field identity. Open
7/// Interest is the only fetch-backed input in the Kline indicator runtime, so
8/// a sorted map is enough to support viewport merging and as-of bar lookup.
9#[derive(Debug, Clone, Default, PartialEq)]
10pub struct OpenInterestSeries {
11    pub values: BTreeMap<UnixMs, f64>,
12}
13
14impl OpenInterestSeries {
15    pub fn from_points(points: impl IntoIterator<Item = (UnixMs, f64)>) -> Self {
16        let mut series = Self::default();
17        series.extend(points);
18        series
19    }
20
21    pub fn extend(&mut self, points: impl IntoIterator<Item = (UnixMs, f64)>) {
22        for (time, value) in points {
23            if value.is_finite() {
24                self.values.insert(time, value);
25            }
26        }
27    }
28
29    pub fn merge(&mut self, other: &Self) {
30        self.extend(other.values.iter().map(|(&time, &value)| (time, value)));
31    }
32
33    pub fn value_at(&self, timestamp: UnixMs) -> Option<f64> {
34        self.values.get(&timestamp).copied()
35    }
36
37    pub fn latest_at_or_before(&self, timestamp: UnixMs) -> Option<f64> {
38        self.values
39            .range(..=timestamp)
40            .next_back()
41            .map(|(_, value)| *value)
42    }
43
44    pub fn covered_range(&self) -> Option<(UnixMs, UnixMs)> {
45        Some((
46            *self.values.first_key_value()?.0,
47            *self.values.last_key_value()?.0,
48        ))
49    }
50
51    pub fn is_empty(&self) -> bool {
52        self.values.is_empty()
53    }
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
57pub enum IndicatorInputSource {
58    Klines,
59    DirectionalVolume,
60    Trades,
61    OpenInterest,
62}
63
64impl IndicatorInputSource {
65    pub const fn id(self) -> &'static str {
66        match self {
67            Self::Klines => "klines",
68            Self::DirectionalVolume => "directional_volume",
69            Self::Trades => "trades",
70            Self::OpenInterest => "open_interest",
71        }
72    }
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub struct IndicatorInputRequirement {
77    pub source: IndicatorInputSource,
78    required: bool,
79}
80
81impl IndicatorInputRequirement {
82    pub const fn required(source: IndicatorInputSource) -> Self {
83        Self {
84            source,
85            required: true,
86        }
87    }
88
89    pub const fn optional(source: IndicatorInputSource) -> Self {
90        Self {
91            source,
92            required: false,
93        }
94    }
95
96    pub const fn is_required(self) -> bool {
97        self.required
98    }
99
100    pub const fn id(self) -> &'static str {
101        self.source.id()
102    }
103}