Skip to main content

data_preprocess/
models.rs

1use chrono::NaiveDateTime;
2use serde::{Deserialize, Serialize};
3
4use crate::error::{DataError, Result};
5
6/// Query parameters for tick view/query commands.
7pub struct QueryOpts {
8    pub exchange: String,
9    pub symbol: String,
10    pub from: Option<NaiveDateTime>,
11    pub to: Option<NaiveDateTime>,
12    pub limit: usize,
13    pub tail: bool,
14    pub descending: bool,
15}
16
17/// Query parameters for bar view/query commands.
18pub struct BarQueryOpts {
19    pub exchange: String,
20    pub symbol: String,
21    pub timeframe: String,
22    pub from: Option<NaiveDateTime>,
23    pub to: Option<NaiveDateTime>,
24    pub limit: usize,
25    pub tail: bool,
26    pub descending: bool,
27}
28
29/// Supported bar timeframes.
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
31pub enum Timeframe {
32    M1,
33    M3,
34    M5,
35    M15,
36    M30,
37    H1,
38    H4,
39    D1,
40    W1,
41    MN1,
42}
43
44impl Timeframe {
45    /// Parse a CLI or storage timeframe, preserving the canonical monthly `1M` label.
46    pub fn parse(s: &str) -> Result<Self> {
47        if s == "1M" {
48            return Ok(Self::MN1);
49        }
50
51        match s.to_ascii_lowercase().as_str() {
52            "1m" | "m1" => Ok(Self::M1),
53            "3m" | "m3" => Ok(Self::M3),
54            "5m" | "m5" => Ok(Self::M5),
55            "15m" | "m15" => Ok(Self::M15),
56            "30m" | "m30" => Ok(Self::M30),
57            "1h" | "h1" => Ok(Self::H1),
58            "4h" | "h4" => Ok(Self::H4),
59            "1d" | "d1" => Ok(Self::D1),
60            "1w" | "w1" => Ok(Self::W1),
61            "1mn" | "mn1" | "1m0" | "mn" => Ok(Self::MN1),
62            _ => Err(DataError::InvalidTimeframe(s.to_string())),
63        }
64    }
65
66    /// Canonical short label for storage: "1m", "3m", "5m", ...
67    pub fn as_str(&self) -> &'static str {
68        match self {
69            Self::M1 => "1m",
70            Self::M3 => "3m",
71            Self::M5 => "5m",
72            Self::M15 => "15m",
73            Self::M30 => "30m",
74            Self::H1 => "1h",
75            Self::H4 => "4h",
76            Self::D1 => "1d",
77            Self::W1 => "1w",
78            Self::MN1 => "1M",
79        }
80    }
81}
82
83impl std::fmt::Display for Timeframe {
84    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85        f.write_str(self.as_str())
86    }
87}
88
89/// A single tick (bid/ask/last at a point in time).
90#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct Tick {
92    pub exchange: String,
93    pub symbol: String,
94    pub ts: NaiveDateTime,
95    pub bid: Option<f64>,
96    pub ask: Option<f64>,
97    pub last: Option<f64>,
98    pub volume: Option<f64>,
99    pub flags: Option<i32>,
100}
101
102/// A single OHLCV bar.
103#[derive(Debug, Clone, Serialize, Deserialize)]
104pub struct Bar {
105    pub exchange: String,
106    pub symbol: String,
107    pub timeframe: Timeframe,
108    pub ts: NaiveDateTime,
109    pub open: f64,
110    pub high: f64,
111    pub low: f64,
112    pub close: f64,
113    pub tick_vol: i64,
114    pub volume: i64,
115    pub spread: i32,
116}
117
118/// Summary row returned by stats queries.
119#[derive(Debug)]
120pub struct StatRow {
121    pub exchange: String,
122    pub symbol: String,
123    pub data_type: String,
124    pub count: u64,
125    pub ts_min: NaiveDateTime,
126    pub ts_max: NaiveDateTime,
127}
128
129/// Result of an import operation.
130#[derive(Debug)]
131pub struct ImportResult {
132    pub file: String,
133    pub exchange: String,
134    pub symbol: String,
135    pub rows_parsed: usize,
136    pub rows_inserted: usize,
137    pub rows_skipped: usize,
138    pub elapsed: std::time::Duration,
139}