plotters_unsable/element/
candlestick.rs

1/*!
2  The candelstick element, which showing the high/low/open/close price
3*/
4
5use std::cmp::Ordering;
6
7use crate::drawing::backend::{BackendCoord, DrawingBackend, DrawingErrorKind};
8use crate::element::{Drawable, PointCollection};
9use crate::style::ShapeStyle;
10
11/// The candelstick data point element
12pub struct CandleStick<'a, X, Y: PartialOrd> {
13    style: ShapeStyle<'a>,
14    width: u32,
15    points: [(X, Y); 4],
16}
17
18impl<'a, X: Clone, Y: PartialOrd> CandleStick<'a, X, Y> {
19    /// Create a new candlestick element, which requires the Y coordinate can be compared
20    #[allow(clippy::too_many_arguments)]
21    pub fn new<GS: Into<ShapeStyle<'a>>, LS: Into<ShapeStyle<'a>>>(
22        x: X,
23        open: Y,
24        high: Y,
25        low: Y,
26        close: Y,
27        gain_style: GS,
28        loss_style: LS,
29        width: u32,
30    ) -> Self {
31        Self {
32            style: match open.partial_cmp(&close) {
33                Some(Ordering::Less) => gain_style.into(),
34                _ => loss_style.into(),
35            },
36            width,
37            points: [
38                (x.clone(), open),
39                (x.clone(), high),
40                (x.clone(), low),
41                (x.clone(), close),
42            ],
43        }
44    }
45}
46
47impl<'b, 'a, X: 'a, Y: PartialOrd + 'a> PointCollection<'a, (X, Y)> for &'a CandleStick<'b, X, Y> {
48    type Borrow = &'a (X, Y);
49    type IntoIter = &'a [(X, Y)];
50    fn point_iter(self) -> &'a [(X, Y)] {
51        &self.points
52    }
53}
54
55impl<'a, X: 'a, Y: 'a + PartialOrd, DB: DrawingBackend> Drawable<DB> for CandleStick<'a, X, Y> {
56    fn draw<I: Iterator<Item = BackendCoord>>(
57        &self,
58        points: I,
59        backend: &mut DB,
60    ) -> Result<(), DrawingErrorKind<DB::ErrorType>> {
61        let mut points: Vec<_> = points.take(4).collect();
62        if points.len() == 4 {
63            let fill = false;
64            if points[0].1 > points[3].1 {
65                points.swap(0, 3);
66            }
67            let (l, r) = (
68                self.width as i32 / 2,
69                self.width as i32 - self.width as i32 / 2,
70            );
71
72            backend.draw_line(points[0], points[1], &Box::new(self.style.color))?;
73            backend.draw_line(points[2], points[3], &Box::new(self.style.color))?;
74
75            points[0].0 -= l;
76            points[3].0 += r;
77
78            backend.draw_rect(points[0], points[3], &Box::new(self.style.color), fill)?;
79        }
80        Ok(())
81    }
82}