diff options
Diffstat (limited to 'src/element/candlestick.rs')
-rw-r--r-- | src/element/candlestick.rs | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/src/element/candlestick.rs b/src/element/candlestick.rs new file mode 100644 index 0000000..b026425 --- /dev/null +++ b/src/element/candlestick.rs @@ -0,0 +1,100 @@ +/*! + The candlestick element, which showing the high/low/open/close price +*/ + +use std::cmp::Ordering; + +use crate::drawing::backend::{BackendCoord, DrawingBackend, DrawingErrorKind}; +use crate::element::{Drawable, PointCollection}; +use crate::style::ShapeStyle; + +/// The candlestick data point element +pub struct CandleStick<X, Y: PartialOrd> { + style: ShapeStyle, + width: u32, + points: [(X, Y); 4], +} + +impl<X: Clone, Y: PartialOrd> CandleStick<X, Y> { + /// Create a new candlestick element, which requires the Y coordinate can be compared + /// + /// - `x`: The x coordinate + /// - `open`: The open value + /// - `high`: The high value + /// - `low`: The low value + /// - `close`: The close value + /// - `gain_style`: The style for gain + /// - `loss_style`: The style for loss + /// - `width`: The width + /// - **returns** The newly created candlestick element + /// + /// ```rust + /// use chrono::prelude::*; + /// use plotters::prelude::*; + /// + /// let candlestick = CandleStick::new(Local::now(), 130.0600, 131.3700, 128.8300, 129.1500, &GREEN, &RED, 15); + /// ``` + #[allow(clippy::too_many_arguments)] + pub fn new<GS: Into<ShapeStyle>, LS: Into<ShapeStyle>>( + x: X, + open: Y, + high: Y, + low: Y, + close: Y, + gain_style: GS, + loss_style: LS, + width: u32, + ) -> Self { + Self { + style: match open.partial_cmp(&close) { + Some(Ordering::Less) => gain_style.into(), + _ => loss_style.into(), + }, + width, + points: [ + (x.clone(), open), + (x.clone(), high), + (x.clone(), low), + (x, close), + ], + } + } +} + +impl<'a, X: 'a, Y: PartialOrd + 'a> PointCollection<'a, (X, Y)> for &'a CandleStick<X, Y> { + type Borrow = &'a (X, Y); + type IntoIter = &'a [(X, Y)]; + fn point_iter(self) -> &'a [(X, Y)] { + &self.points + } +} + +impl<X, Y: PartialOrd, DB: DrawingBackend> Drawable<DB> for CandleStick<X, Y> { + fn draw<I: Iterator<Item = BackendCoord>>( + &self, + points: I, + backend: &mut DB, + _: (u32, u32), + ) -> Result<(), DrawingErrorKind<DB::ErrorType>> { + let mut points: Vec<_> = points.take(4).collect(); + if points.len() == 4 { + let fill = false; + if points[0].1 > points[3].1 { + points.swap(0, 3); + } + let (l, r) = ( + self.width as i32 / 2, + self.width as i32 - self.width as i32 / 2, + ); + + backend.draw_line(points[0], points[1], &self.style)?; + backend.draw_line(points[2], points[3], &self.style)?; + + points[0].0 -= l; + points[3].0 += r; + + backend.draw_rect(points[0], points[3], &self.style, fill)?; + } + Ok(()) + } +} |