lighter_rust/api/
candlestick.rs

1use crate::client::SignerClient;
2use crate::error::Result;
3use crate::models::ApiResponse;
4use chrono::{DateTime, Utc};
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct Candlestick {
9    pub symbol: String,
10    pub interval: String,
11    pub open_time: DateTime<Utc>,
12    pub close_time: DateTime<Utc>,
13    pub open: String,
14    pub high: String,
15    pub low: String,
16    pub close: String,
17    pub volume: String,
18    pub quote_volume: String,
19    pub trade_count: u32,
20}
21
22#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
23#[serde(rename_all = "lowercase")]
24pub enum CandlestickInterval {
25    #[serde(rename = "1m")]
26    OneMinute,
27    #[serde(rename = "5m")]
28    FiveMinutes,
29    #[serde(rename = "15m")]
30    FifteenMinutes,
31    #[serde(rename = "30m")]
32    ThirtyMinutes,
33    #[serde(rename = "1h")]
34    OneHour,
35    #[serde(rename = "4h")]
36    FourHours,
37    #[serde(rename = "1d")]
38    OneDay,
39    #[serde(rename = "1w")]
40    OneWeek,
41}
42
43impl CandlestickInterval {
44    pub fn as_str(&self) -> &'static str {
45        match self {
46            Self::OneMinute => "1m",
47            Self::FiveMinutes => "5m",
48            Self::FifteenMinutes => "15m",
49            Self::ThirtyMinutes => "30m",
50            Self::OneHour => "1h",
51            Self::FourHours => "4h",
52            Self::OneDay => "1d",
53            Self::OneWeek => "1w",
54        }
55    }
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct MarketStats {
60    pub symbol: String,
61    pub price_change: String,
62    pub price_change_percent: String,
63    pub last_price: String,
64    pub bid_price: String,
65    pub ask_price: String,
66    pub volume: String,
67    pub quote_volume: String,
68    pub high_price: String,
69    pub low_price: String,
70    pub open_price: String,
71    pub timestamp: DateTime<Utc>,
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct Ticker {
76    pub symbol: String,
77    pub price: String,
78    pub timestamp: DateTime<Utc>,
79}
80
81#[derive(Debug)]
82pub struct CandlestickApi {
83    client: SignerClient,
84}
85
86impl CandlestickApi {
87    pub fn new(client: SignerClient) -> Self {
88        Self { client }
89    }
90
91    pub async fn get_candlesticks(
92        &self,
93        symbol: &str,
94        interval: CandlestickInterval,
95        start_time: Option<DateTime<Utc>>,
96        end_time: Option<DateTime<Utc>>,
97        limit: Option<u32>,
98    ) -> Result<Vec<Candlestick>> {
99        let mut query_params = vec![
100            format!("symbol={}", symbol),
101            format!("interval={}", interval.as_str()),
102        ];
103
104        if let Some(start) = start_time {
105            query_params.push(format!("start_time={}", start.timestamp_millis()));
106        }
107        if let Some(end) = end_time {
108            query_params.push(format!("end_time={}", end.timestamp_millis()));
109        }
110        if let Some(limit) = limit {
111            query_params.push(format!("limit={}", limit));
112        }
113
114        let endpoint = format!("/candlesticks?{}", query_params.join("&"));
115
116        let response: ApiResponse<Vec<Candlestick>> =
117            self.client.api_client().get(&endpoint).await?;
118
119        match response.data {
120            Some(candlesticks) => Ok(candlesticks),
121            None => Err(crate::error::LighterError::Api {
122                status: 500,
123                message: response
124                    .error
125                    .unwrap_or_else(|| "Failed to fetch candlesticks".to_string()),
126            }),
127        }
128    }
129
130    pub async fn get_market_stats(&self, symbol: &str) -> Result<MarketStats> {
131        let response: ApiResponse<MarketStats> = self
132            .client
133            .api_client()
134            .get(&format!("/market/stats/{}", symbol))
135            .await?;
136
137        match response.data {
138            Some(stats) => Ok(stats),
139            None => Err(crate::error::LighterError::Api {
140                status: 404,
141                message: response
142                    .error
143                    .unwrap_or_else(|| "Market stats not found".to_string()),
144            }),
145        }
146    }
147
148    pub async fn get_all_market_stats(&self) -> Result<Vec<MarketStats>> {
149        let response: ApiResponse<Vec<MarketStats>> =
150            self.client.api_client().get("/market/stats").await?;
151
152        match response.data {
153            Some(stats) => Ok(stats),
154            None => Err(crate::error::LighterError::Api {
155                status: 500,
156                message: response
157                    .error
158                    .unwrap_or_else(|| "Failed to fetch market stats".to_string()),
159            }),
160        }
161    }
162
163    pub async fn get_ticker(&self, symbol: &str) -> Result<Ticker> {
164        let response: ApiResponse<Ticker> = self
165            .client
166            .api_client()
167            .get(&format!("/ticker/{}", symbol))
168            .await?;
169
170        match response.data {
171            Some(ticker) => Ok(ticker),
172            None => Err(crate::error::LighterError::Api {
173                status: 404,
174                message: response
175                    .error
176                    .unwrap_or_else(|| "Ticker not found".to_string()),
177            }),
178        }
179    }
180
181    pub async fn get_all_tickers(&self) -> Result<Vec<Ticker>> {
182        let response: ApiResponse<Vec<Ticker>> = self.client.api_client().get("/ticker").await?;
183
184        match response.data {
185            Some(tickers) => Ok(tickers),
186            None => Err(crate::error::LighterError::Api {
187                status: 500,
188                message: response
189                    .error
190                    .unwrap_or_else(|| "Failed to fetch tickers".to_string()),
191            }),
192        }
193    }
194
195    pub async fn get_order_book(
196        &self,
197        symbol: &str,
198        depth: Option<u32>,
199    ) -> Result<crate::models::OrderBook> {
200        let mut endpoint = format!("/orderbook/{}", symbol);
201
202        if let Some(depth) = depth {
203            endpoint = format!("{}?depth={}", endpoint, depth);
204        }
205
206        let response: ApiResponse<crate::models::OrderBook> =
207            self.client.api_client().get(&endpoint).await?;
208
209        match response.data {
210            Some(orderbook) => Ok(orderbook),
211            None => Err(crate::error::LighterError::Api {
212                status: 404,
213                message: response
214                    .error
215                    .unwrap_or_else(|| "Order book not found".to_string()),
216            }),
217        }
218    }
219}