1 /*!
2   The candlestick element, which showing the high/low/open/close price
3 */
4 
5 use std::cmp::Ordering;
6 
7 use crate::element::{Drawable, PointCollection};
8 use crate::style::ShapeStyle;
9 use plotters_backend::{BackendCoord, DrawingBackend, DrawingErrorKind};
10 
11 /// The candlestick data point element
12 pub struct CandleStick<X, Y: PartialOrd> {
13     style: ShapeStyle,
14     width: u32,
15     points: [(X, Y); 4],
16 }
17 
18 impl<X: Clone, Y: PartialOrd> CandleStick<X, Y> {
19     /// Create a new candlestick element, which requires the Y coordinate can be compared
20     ///
21     /// - `x`: The x coordinate
22     /// - `open`: The open value
23     /// - `high`: The high value
24     /// - `low`: The low value
25     /// - `close`: The close value
26     /// - `gain_style`: The style for gain
27     /// - `loss_style`: The style for loss
28     /// - `width`: The width
29     /// - **returns** The newly created candlestick element
30     ///
31     /// ```rust
32     /// use chrono::prelude::*;
33     /// use plotters::prelude::*;
34     ///
35     /// let candlestick = CandleStick::new(Local::now(), 130.0600, 131.3700, 128.8300, 129.1500, &GREEN, &RED, 15);
36     /// ```
37     #[allow(clippy::too_many_arguments)]
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, ) -> Self38     pub fn new<GS: Into<ShapeStyle>, LS: Into<ShapeStyle>>(
39         x: X,
40         open: Y,
41         high: Y,
42         low: Y,
43         close: Y,
44         gain_style: GS,
45         loss_style: LS,
46         width: u32,
47     ) -> Self {
48         Self {
49             style: match open.partial_cmp(&close) {
50                 Some(Ordering::Less) => gain_style.into(),
51                 _ => loss_style.into(),
52             },
53             width,
54             points: [
55                 (x.clone(), open),
56                 (x.clone(), high),
57                 (x.clone(), low),
58                 (x, close),
59             ],
60         }
61     }
62 }
63 
64 impl<'a, X: 'a, Y: PartialOrd + 'a> PointCollection<'a, (X, Y)> for &'a CandleStick<X, Y> {
65     type Point = &'a (X, Y);
66     type IntoIter = &'a [(X, Y)];
point_iter(self) -> &'a [(X, Y)]67     fn point_iter(self) -> &'a [(X, Y)] {
68         &self.points
69     }
70 }
71 
72 impl<X, Y: PartialOrd, DB: DrawingBackend> Drawable<DB> for CandleStick<X, Y> {
draw<I: Iterator<Item = BackendCoord>>( &self, points: I, backend: &mut DB, _: (u32, u32), ) -> Result<(), DrawingErrorKind<DB::ErrorType>>73     fn draw<I: Iterator<Item = BackendCoord>>(
74         &self,
75         points: I,
76         backend: &mut DB,
77         _: (u32, u32),
78     ) -> Result<(), DrawingErrorKind<DB::ErrorType>> {
79         let mut points: Vec<_> = points.take(4).collect();
80         if points.len() == 4 {
81             let fill = self.style.filled;
82             if points[0].1 > points[3].1 {
83                 points.swap(0, 3);
84             }
85             let (l, r) = (
86                 self.width as i32 / 2,
87                 self.width as i32 - self.width as i32 / 2,
88             );
89 
90             backend.draw_line(points[0], points[1], &self.style)?;
91             backend.draw_line(points[2], points[3], &self.style)?;
92 
93             points[0].0 -= l;
94             points[3].0 += r;
95 
96             backend.draw_rect(points[0], points[3], &self.style, fill)?;
97         }
98         Ok(())
99     }
100 }
101