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}