ux_indicators/
data_item.rs

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