use rust_decimal::prelude::ToPrimitive;
use super::{Panel, PanelKind};
use crate::source::{SourceId, Symbol};
use crate::state::AppState;
use crate::view::{ChartView, IndicatorField, IndicatorValue, OhlcBar, PanelView};
fn ohlc(candle: &wickra_core::Candle) -> OhlcBar {
OhlcBar {
open: candle.open,
high: candle.high,
low: candle.low,
close: candle.close,
volume: candle.volume,
timestamp: candle.timestamp,
}
}
#[derive(Debug)]
pub struct ChartPanel {
pub(crate) points: usize,
}
impl Panel for ChartPanel {
fn kind(&self) -> PanelKind {
PanelKind::Chart
}
fn view(&self, state: &AppState, focus: (SourceId, &Symbol)) -> PanelView {
let symbol = focus.1.to_string();
let chart = match state.get(&(focus.0, focus.1.clone())) {
Some(st) => ChartView {
symbol,
last: st.last.to_f64().unwrap_or(0.0),
series: st.series(self.points),
bars: st.ohlc(self.points).iter().map(ohlc).collect(),
forming: st.forming().as_ref().map(ohlc),
indicators: st
.indicators
.snapshot()
.into_iter()
.map(|mut reading| IndicatorValue {
name: reading.label,
value: reading.value,
fields: reading
.fields
.into_iter()
.map(|(name, value)| IndicatorField {
name: name.to_string(),
value,
})
.collect(),
series: reading
.series
.split_off(reading.series.len().saturating_sub(self.points)),
})
.collect(),
},
None => ChartView {
symbol,
last: 0.0,
series: Vec::new(),
bars: Vec::new(),
forming: None,
indicators: Vec::new(),
},
};
PanelView::Chart(chart)
}
}
#[cfg(test)]
mod tests {
use super::super::CHART_POINTS;
use super::*;
#[test]
fn a_market_with_no_state_yet_charts_as_empty() {
let sym = Symbol::new("BTC", "USDT");
let state = AppState::default();
let PanelView::Chart(view) = (ChartPanel {
points: CHART_POINTS,
})
.view(&state, (0, &sym)) else {
panic!("the chart panel answers with a chart")
};
assert_eq!(view.symbol, "BTC/USDT");
assert!(
view.last.abs() < f64::EPSILON,
"an unfolded market has no last price"
);
assert_eq!(view.series, Vec::<f64>::new());
assert_eq!(view.bars, Vec::new());
assert!(view.forming.is_none());
assert_eq!(view.indicators, Vec::new());
}
}