cxmr_ta_core/
data_item.rs

1use crate::errors::*;
2use crate::traits::{Close, High, Low, Open, Volume};
3
4/// Data item is used as an input for indicators.
5///
6/// # Example
7///
8/// ```
9/// use ta::DataItem;
10/// use ta::{Open, High, Low, Close, Volume};
11///
12/// let item = DataItem::builder()
13///     .open(20.0)
14///     .high(25.0)
15///     .low(15.0)
16///     .close(21.0)
17///     .volume(7500.0)
18///     .build()
19///     .unwrap();
20///
21/// assert_eq!(item.open(), 20.0);
22/// assert_eq!(item.high(), 25.0);
23/// assert_eq!(item.low(), 15.0);
24/// assert_eq!(item.close(), 21.0);
25/// assert_eq!(item.volume(), 7500.0);
26/// ```
27///
28#[derive(Debug, Clone)]
29pub struct DataItem {
30    open: f64,
31    high: f64,
32    low: f64,
33    close: f64,
34    volume: f64,
35}
36
37impl DataItem {
38    pub fn builder() -> DataItemBuilder {
39        DataItemBuilder::new()
40    }
41}
42
43impl Open for DataItem {
44    fn open(&self) -> f64 {
45        self.open
46    }
47}
48
49impl High for DataItem {
50    fn high(&self) -> f64 {
51        self.high
52    }
53}
54
55impl Low for DataItem {
56    fn low(&self) -> f64 {
57        self.low
58    }
59}
60
61impl Close for DataItem {
62    fn close(&self) -> f64 {
63        self.close
64    }
65}
66
67impl Volume for DataItem {
68    fn volume(&self) -> f64 {
69        self.volume
70    }
71}
72
73pub struct DataItemBuilder {
74    open: Option<f64>,
75    high: Option<f64>,
76    low: Option<f64>,
77    close: Option<f64>,
78    volume: Option<f64>,
79}
80
81impl DataItemBuilder {
82    pub fn new() -> Self {
83        Self {
84            open: None,
85            high: None,
86            low: None,
87            close: None,
88            volume: None,
89        }
90    }
91
92    pub fn open(mut self, val: f64) -> Self {
93        self.open = Some(val);
94        self
95    }
96
97    pub fn high(mut self, val: f64) -> Self {
98        self.high = Some(val);
99        self
100    }
101
102    pub fn low(mut self, val: f64) -> Self {
103        self.low = Some(val);
104        self
105    }
106
107    pub fn close(mut self, val: f64) -> Self {
108        self.close = Some(val);
109        self
110    }
111
112    pub fn volume(mut self, val: f64) -> Self {
113        self.volume = Some(val);
114        self
115    }
116
117    pub fn build(self) -> Result<DataItem> {
118        if let (Some(open), Some(high), Some(low), Some(close), Some(volume)) =
119            (self.open, self.high, self.low, self.close, self.volume)
120        {
121            // validate
122            if low <= open
123                && low <= close
124                && low <= high
125                && high >= open
126                && high >= close
127                && volume >= 0.0
128                && low >= 0.0
129            {
130                let item = DataItem {
131                    open,
132                    high,
133                    low,
134                    close,
135                    volume,
136                };
137                Ok(item)
138            } else {
139                Err(Error::from_kind(ErrorKind::DataItemInvalid))
140            }
141        } else {
142            Err(Error::from_kind(ErrorKind::DataItemIncomplete))
143        }
144    }
145}
146
147#[cfg(test)]
148mod tests {
149    use super::*;
150
151    #[test]
152    fn test_builder() {
153        fn assert_valid((open, high, low, close, volume): (f64, f64, f64, f64, f64)) {
154            let result = DataItem::builder()
155                .open(open)
156                .high(high)
157                .low(low)
158                .close(close)
159                .volume(volume)
160                .build();
161            assert!(result.is_ok());
162        }
163
164        fn assert_invalid((open, high, low, close, volume): (f64, f64, f64, f64, f64)) {
165            let result = DataItem::builder()
166                .open(open)
167                .high(high)
168                .low(low)
169                .close(close)
170                .volume(volume)
171                .build();
172            assert!(result.is_err());
173        }
174
175        let valid_records = vec![
176            // open, high, low , close, volume
177            (20.0, 25.0, 15.0, 21.0, 7500.0),
178            (10.0, 10.0, 10.0, 10.0, 10.0),
179            (0.0, 0.0, 0.0, 0.0, 0.0),
180        ];
181        for record in valid_records {
182            assert_valid(record)
183        }
184
185        let invalid_records = vec![
186            // open, high, low , close, volume
187            (-1.0, 25.0, 15.0, 21.0, 7500.0),
188            (20.0, -1.0, 15.0, 21.0, 7500.0),
189            (20.0, 25.0, -1.0, 21.0, 7500.0),
190            (20.0, 25.0, 15.0, -1.0, 7500.0),
191            (20.0, 25.0, 15.0, 21.0, -1.0),
192            (14.9, 25.0, 15.0, 21.0, 7500.0),
193            (25.1, 25.0, 15.0, 21.0, 7500.0),
194            (20.0, 25.0, 15.0, 14.9, 7500.0),
195            (20.0, 25.0, 15.0, 25.1, 7500.0),
196            (20.0, 15.0, 25.0, 21.0, 7500.0),
197        ];
198        for record in invalid_records {
199            assert_invalid(record)
200        }
201    }
202}