Skip to main content

scope_cli/cli/monitor/
config.rs

1//! Configuration types, constants, and data structures for the monitor TUI.
2
3pub use crate::cli::theme::{ColorPalette, ColorSchemeExt};
4use serde::{Deserialize, Serialize};
5use std::time::Instant;
6
7// Re-export config types from scope-core so existing `crate::cli::monitor::X` paths keep working.
8pub use scope::config::{
9    AlertConfig, ColorScheme, DEFAULT_MONITOR_REFRESH_SECS, ExportConfig, LayoutPreset,
10    MonitorConfig, ScaleMode, WidgetVisibility,
11};
12
13/// Maximum data retention: 24 hours.
14/// At 5-second intervals: 24 * 60 * 12 = 17,280 points max per history.
15/// With DataPoint at 24 bytes: ~415 KB per history, ~830 KB total.
16/// Data is persisted to OS temp folder for session continuity.
17pub(crate) const MAX_DATA_AGE_SECS: f64 = 24.0 * 3600.0; // 24 hours
18
19/// Cache file prefix in temp directory.
20pub(crate) const CACHE_FILE_PREFIX: &str = "bcc_monitor_";
21
22/// Default refresh interval in seconds.
23pub(crate) const DEFAULT_REFRESH_SECS: u64 = DEFAULT_MONITOR_REFRESH_SECS;
24
25/// Minimum refresh interval in seconds.
26pub(crate) const MIN_REFRESH_SECS: u64 = 1;
27
28/// Maximum refresh interval in seconds.
29pub(crate) const MAX_REFRESH_SECS: u64 = 60;
30
31/// A data point with timestamp, value, and whether it's real (from API) or synthetic.
32#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
33pub struct DataPoint {
34    /// Unix timestamp in seconds.
35    pub timestamp: f64,
36    /// Value (price or volume).
37    pub value: f64,
38    /// True if this is real data from API, false if synthetic/estimated.
39    pub is_real: bool,
40}
41
42/// OHLC (Open-High-Low-Close) candlestick data for a time period.
43#[derive(Debug, Clone, Copy)]
44pub struct OhlcCandle {
45    /// Start timestamp of this candle period.
46    pub timestamp: f64,
47    /// Opening price.
48    pub open: f64,
49    /// Highest price during the period.
50    pub high: f64,
51    /// Lowest price during the period.
52    pub low: f64,
53    /// Closing price.
54    pub close: f64,
55    /// Whether this candle is bullish (close >= open).
56    pub is_bullish: bool,
57}
58
59impl OhlcCandle {
60    /// Creates a new candle from a single price point.
61    pub fn new(timestamp: f64, price: f64) -> Self {
62        Self {
63            timestamp,
64            open: price,
65            high: price,
66            low: price,
67            close: price,
68            is_bullish: true,
69        }
70    }
71
72    /// Updates the candle with a new price.
73    pub fn update(&mut self, price: f64) {
74        self.high = self.high.max(price);
75        self.low = self.low.min(price);
76        self.close = price;
77        self.is_bullish = self.close >= self.open;
78    }
79}
80
81/// Cached monitor data that persists between sessions.
82#[derive(Debug, Serialize, Deserialize)]
83pub(crate) struct CachedMonitorData {
84    /// Token address this cache is for.
85    pub(crate) token_address: String,
86    /// Chain identifier.
87    pub(crate) chain: String,
88    /// Price history data points.
89    pub(crate) price_history: Vec<DataPoint>,
90    /// Volume history data points.
91    pub(crate) volume_history: Vec<DataPoint>,
92    /// Timestamp when cache was saved.
93    pub(crate) saved_at: f64,
94}
95
96/// Time period for chart display (limited to 24 hours of data retention).
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98pub enum TimePeriod {
99    /// Last 1 minute
100    Min1,
101    /// Last 5 minutes
102    Min5,
103    /// Last 15 minutes
104    Min15,
105    /// Last 1 hour
106    Hour1,
107    /// Last 4 hours
108    Hour4,
109    /// Last 24 hours (1 day)
110    Day1,
111}
112
113impl TimePeriod {
114    /// Returns the duration in seconds for this period.
115    pub fn duration_secs(&self) -> i64 {
116        match self {
117            TimePeriod::Min1 => 60,
118            TimePeriod::Min5 => 5 * 60,
119            TimePeriod::Min15 => 15 * 60,
120            TimePeriod::Hour1 => 3600,
121            TimePeriod::Hour4 => 4 * 3600,
122            TimePeriod::Day1 => 24 * 3600,
123        }
124    }
125
126    /// Returns a display label for this period.
127    pub fn label(&self) -> &'static str {
128        match self {
129            TimePeriod::Min1 => "1m",
130            TimePeriod::Min5 => "5m",
131            TimePeriod::Min15 => "15m",
132            TimePeriod::Hour1 => "1h",
133            TimePeriod::Hour4 => "4h",
134            TimePeriod::Day1 => "1d",
135        }
136    }
137
138    /// Returns the zero-based index for this period (for Tabs widget).
139    pub fn index(&self) -> usize {
140        match self {
141            TimePeriod::Min1 => 0,
142            TimePeriod::Min5 => 1,
143            TimePeriod::Min15 => 2,
144            TimePeriod::Hour1 => 3,
145            TimePeriod::Hour4 => 4,
146            TimePeriod::Day1 => 5,
147        }
148    }
149
150    /// Returns the exchange API kline interval string for this period.
151    pub fn exchange_interval(&self) -> &'static str {
152        match self {
153            TimePeriod::Min1 => "1m",
154            TimePeriod::Min5 => "5m",
155            TimePeriod::Min15 => "15m",
156            TimePeriod::Hour1 => "1h",
157            TimePeriod::Hour4 => "4h",
158            TimePeriod::Day1 => "1d",
159        }
160    }
161
162    /// Cycles to the next time period.
163    pub fn next(&self) -> Self {
164        match self {
165            TimePeriod::Min1 => TimePeriod::Min5,
166            TimePeriod::Min5 => TimePeriod::Min15,
167            TimePeriod::Min15 => TimePeriod::Hour1,
168            TimePeriod::Hour1 => TimePeriod::Hour4,
169            TimePeriod::Hour4 => TimePeriod::Day1,
170            TimePeriod::Day1 => TimePeriod::Min1,
171        }
172    }
173}
174
175impl std::fmt::Display for TimePeriod {
176    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177        write!(f, "{}", self.label())
178    }
179}
180
181/// Chart display mode.
182#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
183pub enum ChartMode {
184    /// Line chart showing price over time.
185    #[default]
186    Line,
187    /// Candlestick chart showing OHLC data.
188    Candlestick,
189    /// Volume profile showing volume distribution by price level.
190    VolumeProfile,
191}
192
193impl ChartMode {
194    /// Cycles to the next chart mode.
195    pub fn next(&self) -> Self {
196        match self {
197            ChartMode::Line => ChartMode::Candlestick,
198            ChartMode::Candlestick => ChartMode::VolumeProfile,
199            ChartMode::VolumeProfile => ChartMode::Line,
200        }
201    }
202
203    /// Returns a display label for this mode.
204    pub fn label(&self) -> &'static str {
205        match self {
206            ChartMode::Line => "Line",
207            ChartMode::Candlestick => "Candle",
208            ChartMode::VolumeProfile => "VolPro",
209        }
210    }
211}
212
213/// An active (currently firing) alert with a description.
214#[derive(Debug, Clone)]
215pub struct ActiveAlert {
216    /// Human-readable message describing the alert.
217    pub message: String,
218    /// When the alert was first triggered.
219    pub triggered_at: Instant,
220}