1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use crate::{
    chart_data::ChartData, chart_renderer::ChartRenderer, info_bar::InfoBar,
    volume_pane::VolumePane, y_axis::YAxis,
};
use std::cell::RefCell;
use std::rc::Rc;

#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
pub struct Candle {
    pub open: f64,
    pub high: f64,
    pub low: f64,
    pub close: f64,
    pub volume: Option<f64>,
    pub timestamp: Option<i64>,
}

pub(crate) enum CandleType {
    Bearish,
    Bullish,
}

impl Candle {
    #[allow(dead_code)]
    pub fn new(
        open: f64,
        high: f64,
        low: f64,
        close: f64,
        volume: Option<f64>,
        timestamp: Option<i64>,
    ) -> Candle {
        Candle {
            open,
            high,
            low,
            close,
            volume,
            timestamp,
        }
    }

    pub(crate) fn get_type(&self) -> CandleType {
        match self.open < self.close {
            true => CandleType::Bullish,
            false => CandleType::Bearish,
        }
    }
}

pub struct Chart {
    pub(crate) renderer: ChartRenderer,
    pub(crate) y_axis: YAxis,
    pub(crate) chart_data: Rc<RefCell<ChartData>>,
    pub(crate) info_bar: InfoBar,
    pub(crate) volume_pane: VolumePane,
}

impl Chart {
    pub fn new(candles: &[Candle]) -> Self {
        let renderer = ChartRenderer::new();
        let chart_data = Rc::new(RefCell::new(ChartData::new(candles.to_vec())));
        let y_axis = YAxis::new(chart_data.clone());
        let info_bar = InfoBar::new("APPLE".to_string(), chart_data.clone());

        let volume_pane = VolumePane::new(
            chart_data.clone(),
            (chart_data.borrow().terminal_size.1 / 6) as i64,
        );

        chart_data.borrow_mut().compute_height(&volume_pane);

        Chart {
            renderer,
            y_axis,
            chart_data,
            info_bar,
            volume_pane,
        }
    }

    /// Draws the chart by outputting multiples strings in the terminal.
    pub fn draw(&self) {
        self.renderer.render(self);
    }

    /// Set the name of the chart in the info bar.
    pub fn set_name(&mut self, name: String) {
        self.info_bar.name = name;
    }

    /// Set the color of the bearish candle
    /// The default color is  (234, 74, 90).
    pub fn set_bear_color(&mut self, r: u8, g: u8, b: u8) {
        self.renderer.bearish_color = (r, g, b);
    }

    /// Set the color of the bullish candle
    /// The default color is  (52, 208, 88).
    pub fn set_bull_color(&mut self, r: u8, g: u8, b: u8) {
        self.renderer.bullish_color = (r, g, b);
    }

    /// Sets the color of the volume when the candle is bearish.
    /// The default color is  (234, 74, 90).
    pub fn set_vol_bear_color(&mut self, r: u8, g: u8, b: u8) {
        self.volume_pane.bearish_color = (r, g, b);
    }

    /// Sets the color of the volume when the candle is bullish.
    /// The default color is  (52, 208, 88).
    pub fn set_vol_bull_color(&mut self, r: u8, g: u8, b: u8) {
        self.volume_pane.bullish_color = (r, g, b);
    }

    /// Hide or show the volume pane.
    pub fn set_volume_pane_enabled(&mut self, enabled: bool) {
        self.volume_pane.enabled = enabled;
    }

    /// Set the character for drawing the volume bars.
    pub fn set_volume_pane_unicode_fill(&mut self, unicode_fill: char) {
        self.volume_pane.unicode_fill = unicode_fill;
    }

    /// Set the volume pane height.
    /// Default is 1/6 of the terminal height.
    pub fn set_volume_pane_height(&mut self, height: i64) {
        self.volume_pane.height = height;
    }
}