Skip to main content

flowsurface_data/
chart.rs

1pub mod comparison;
2pub mod heatmap;
3pub mod indicator;
4pub mod kline;
5
6use exchange::Timeframe;
7use serde::{Deserialize, Serialize};
8
9use super::aggr::{
10    self,
11    ticks::TickAggr,
12    time::{DataPoint, TimeSeries},
13};
14pub use kline::KlineChartKind;
15
16pub enum PlotData<D: DataPoint> {
17    TimeBased(TimeSeries<D>),
18    TickBased(TickAggr),
19}
20
21impl<D: DataPoint> PlotData<D> {
22    pub fn latest_y_midpoint(&self, calculate_target_y: impl Fn(exchange::Kline) -> f32) -> f32 {
23        match self {
24            PlotData::TimeBased(timeseries) => timeseries
25                .latest_kline()
26                .map_or(0.0, |kline| calculate_target_y(*kline)),
27            PlotData::TickBased(tick_aggr) => tick_aggr
28                .latest_dp()
29                .map_or(0.0, |(dp, _)| calculate_target_y(dp.kline)),
30        }
31    }
32
33    pub fn visible_price_range(
34        &self,
35        start_interval: u64,
36        end_interval: u64,
37    ) -> Option<(f32, f32)> {
38        match self {
39            PlotData::TimeBased(timeseries) => {
40                timeseries.min_max_price_in_range(start_interval, end_interval)
41            }
42            PlotData::TickBased(tick_aggr) => {
43                tick_aggr.min_max_price_in_range(start_interval as usize, end_interval as usize)
44            }
45        }
46    }
47}
48
49#[derive(Debug, Clone, Deserialize, Serialize, Default)]
50pub struct ViewConfig {
51    pub splits: Vec<f32>,
52    pub autoscale: Option<Autoscale>,
53}
54
55#[derive(Debug, Clone, Copy, Deserialize, Serialize, Default, PartialEq)]
56pub enum Autoscale {
57    #[default]
58    CenterLatest,
59    FitToVisible,
60}
61
62/// Defines how chart data is aggregated and displayed along the x-axis.
63#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
64pub enum Basis {
65    /// Time-based aggregation where each datapoint represents a fixed time interval.
66    Time(exchange::Timeframe),
67
68    /// Trade-based aggregation where each datapoint represents a fixed number of trades.
69    ///
70    /// The u16 value represents the number of trades per aggregation unit.
71    Tick(aggr::TickCount),
72}
73
74impl Basis {
75    pub fn is_time(&self) -> bool {
76        matches!(self, Basis::Time(_))
77    }
78
79    pub fn default_kline_time(
80        ticker_info: Option<exchange::TickerInfo>,
81        fallback: Timeframe,
82    ) -> Self {
83        let interval = ticker_info.map_or(fallback, |info| {
84            let exchange = info.exchange();
85
86            if exchange.supports_kline_timeframe(fallback) {
87                fallback
88            } else {
89                Timeframe::KLINE
90                    .iter()
91                    .copied()
92                    .find(|tf| exchange.supports_kline_timeframe(*tf))
93                    .unwrap_or(fallback)
94            }
95        });
96
97        interval.into()
98    }
99
100    pub fn default_heatmap_time(ticker_info: Option<exchange::TickerInfo>) -> Self {
101        let fallback = Timeframe::MS500;
102
103        let interval = ticker_info.map_or(fallback, |info| {
104            let ex = info.exchange();
105            Timeframe::HEATMAP
106                .iter()
107                .copied()
108                .find(|tf| ex.supports_heatmap_timeframe(*tf))
109                .unwrap_or(fallback)
110        });
111
112        interval.into()
113    }
114}
115
116impl std::fmt::Display for Basis {
117    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118        match self {
119            Basis::Time(timeframe) => write!(f, "{timeframe}"),
120            Basis::Tick(count) => write!(f, "{count}"),
121        }
122    }
123}
124
125impl From<exchange::Timeframe> for Basis {
126    fn from(timeframe: exchange::Timeframe) -> Self {
127        Self::Time(timeframe)
128    }
129}
130
131#[derive(Debug, Clone, Deserialize, Serialize)]
132pub enum Study {
133    Heatmap(Vec<heatmap::HeatmapStudy>),
134    Footprint(Vec<kline::FootprintStudy>),
135}