1#[allow(unused_imports)]
2use progenitor_client::{encode_path, RequestBuilderExt};
3#[allow(unused_imports)]
4pub use progenitor_client::{ByteStream, Error, ResponseValue};
5#[allow(unused_imports)]
6use reqwest::header::{HeaderMap, HeaderValue};
7#[allow(clippy::all)]
9pub mod types {
10 use serde::{Deserialize, Serialize};
11 #[allow(unused_imports)]
12 use std::convert::TryFrom;
13 pub mod error {
15 pub struct ConversionError(std::borrow::Cow<'static, str>);
17 impl std::error::Error for ConversionError {}
18 impl std::fmt::Display for ConversionError {
19 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
20 std::fmt::Display::fmt(&self.0, f)
21 }
22 }
23
24 impl std::fmt::Debug for ConversionError {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
26 std::fmt::Debug::fmt(&self.0, f)
27 }
28 }
29
30 impl From<&'static str> for ConversionError {
31 fn from(value: &'static str) -> Self {
32 Self(value.into())
33 }
34 }
35
36 impl From<String> for ConversionError {
37 fn from(value: String) -> Self {
38 Self(value.into())
39 }
40 }
41 }
42
43 #[derive(Clone, Debug, Deserialize, Serialize)]
98 pub struct Candles {
99 #[serde(default, skip_serializing_if = "Vec::is_empty")]
100 pub closes: Vec<f64>,
101 #[serde(default, skip_serializing_if = "Vec::is_empty")]
102 pub highs: Vec<f64>,
103 #[serde(default, skip_serializing_if = "Vec::is_empty")]
104 pub lows: Vec<f64>,
105 #[serde(default, skip_serializing_if = "Vec::is_empty")]
106 pub opens: Vec<f64>,
107 #[serde(default, skip_serializing_if = "Vec::is_empty")]
108 pub times: Vec<i64>,
109 #[serde(default, skip_serializing_if = "Vec::is_empty")]
110 pub vols: Vec<f64>,
111 }
112
113 impl From<&Candles> for Candles {
114 fn from(value: &Candles) -> Self {
115 value.clone()
116 }
117 }
118
119 #[derive(Clone, Debug, Deserialize, Serialize)]
139 pub struct HkError {
140 #[serde(default, skip_serializing_if = "Option::is_none")]
141 pub code: Option<i32>,
142 #[serde(default, skip_serializing_if = "Option::is_none")]
143 pub message: Option<String>,
144 }
145
146 impl From<&HkError> for HkError {
147 fn from(value: &HkError) -> Self {
148 value.clone()
149 }
150 }
151
152 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
173 pub enum Resolution {
174 #[serde(rename = "m1")]
175 M1,
176 #[serde(rename = "m5")]
177 M5,
178 #[serde(rename = "m15")]
179 M15,
180 #[serde(rename = "m30")]
181 M30,
182 #[serde(rename = "h1")]
183 H1,
184 #[serde(rename = "h4")]
185 H4,
186 #[serde(rename = "d1")]
187 D1,
188 #[serde(rename = "w1")]
189 W1,
190 }
191
192 impl From<&Resolution> for Resolution {
193 fn from(value: &Resolution) -> Self {
194 value.clone()
195 }
196 }
197
198 impl ToString for Resolution {
199 fn to_string(&self) -> String {
200 match *self {
201 Self::M1 => "m1".to_string(),
202 Self::M5 => "m5".to_string(),
203 Self::M15 => "m15".to_string(),
204 Self::M30 => "m30".to_string(),
205 Self::H1 => "h1".to_string(),
206 Self::H4 => "h4".to_string(),
207 Self::D1 => "d1".to_string(),
208 Self::W1 => "w1".to_string(),
209 }
210 }
211 }
212
213 impl std::str::FromStr for Resolution {
214 type Err = self::error::ConversionError;
215 fn from_str(value: &str) -> Result<Self, self::error::ConversionError> {
216 match value {
217 "m1" => Ok(Self::M1),
218 "m5" => Ok(Self::M5),
219 "m15" => Ok(Self::M15),
220 "m30" => Ok(Self::M30),
221 "h1" => Ok(Self::H1),
222 "h4" => Ok(Self::H4),
223 "d1" => Ok(Self::D1),
224 "w1" => Ok(Self::W1),
225 _ => Err("invalid value".into()),
226 }
227 }
228 }
229
230 impl std::convert::TryFrom<&str> for Resolution {
231 type Error = self::error::ConversionError;
232 fn try_from(value: &str) -> Result<Self, self::error::ConversionError> {
233 value.parse()
234 }
235 }
236
237 impl std::convert::TryFrom<&String> for Resolution {
238 type Error = self::error::ConversionError;
239 fn try_from(value: &String) -> Result<Self, self::error::ConversionError> {
240 value.parse()
241 }
242 }
243
244 impl std::convert::TryFrom<String> for Resolution {
245 type Error = self::error::ConversionError;
246 fn try_from(value: String) -> Result<Self, self::error::ConversionError> {
247 value.parse()
248 }
249 }
250
251 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
265 pub enum SymbolTicker {
266 #[serde(rename = "MOCK:XAUUSD")]
267 MockXauusd,
268 }
269
270 impl From<&SymbolTicker> for SymbolTicker {
271 fn from(value: &SymbolTicker) -> Self {
272 value.clone()
273 }
274 }
275
276 impl ToString for SymbolTicker {
277 fn to_string(&self) -> String {
278 match *self {
279 Self::MockXauusd => "MOCK:XAUUSD".to_string(),
280 }
281 }
282 }
283
284 impl std::str::FromStr for SymbolTicker {
285 type Err = self::error::ConversionError;
286 fn from_str(value: &str) -> Result<Self, self::error::ConversionError> {
287 match value {
288 "MOCK:XAUUSD" => Ok(Self::MockXauusd),
289 _ => Err("invalid value".into()),
290 }
291 }
292 }
293
294 impl std::convert::TryFrom<&str> for SymbolTicker {
295 type Error = self::error::ConversionError;
296 fn try_from(value: &str) -> Result<Self, self::error::ConversionError> {
297 value.parse()
298 }
299 }
300
301 impl std::convert::TryFrom<&String> for SymbolTicker {
302 type Error = self::error::ConversionError;
303 fn try_from(value: &String) -> Result<Self, self::error::ConversionError> {
304 value.parse()
305 }
306 }
307
308 impl std::convert::TryFrom<String> for SymbolTicker {
309 type Error = self::error::ConversionError;
310 fn try_from(value: String) -> Result<Self, self::error::ConversionError> {
311 value.parse()
312 }
313 }
314}
315
316#[derive(Clone, Debug)]
317pub struct Client {
324 pub(crate) baseurl: String,
325 pub(crate) client: reqwest::Client,
326}
327
328impl Client {
329 pub fn new(baseurl: &str) -> Self {
335 #[cfg(not(target_arch = "wasm32"))]
336 let client = {
337 let dur = std::time::Duration::from_secs(15);
338 reqwest::ClientBuilder::new()
339 .connect_timeout(dur)
340 .timeout(dur)
341 };
342 #[cfg(target_arch = "wasm32")]
343 let client = reqwest::ClientBuilder::new();
344 Self::new_with_client(baseurl, client.build().unwrap())
345 }
346
347 pub fn new_with_client(baseurl: &str, client: reqwest::Client) -> Self {
354 Self {
355 baseurl: baseurl.to_string(),
356 client,
357 }
358 }
359
360 pub fn baseurl(&self) -> &String {
362 &self.baseurl
363 }
364
365 pub fn client(&self) -> &reqwest::Client {
367 &self.client
368 }
369
370 pub fn api_version(&self) -> &'static str {
375 "1.0.0"
376 }
377}
378
379#[allow(clippy::all)]
380impl Client {
381 pub async fn get_candles<'a>(
391 &'a self,
392 from: Option<i64>,
393 resolution: types::Resolution,
394 symbol: types::SymbolTicker,
395 to: Option<i64>,
396 ) -> Result<ResponseValue<types::Candles>, Error<types::HkError>> {
397 let url = format!("{}/candles", self.baseurl,);
398 let mut query = Vec::with_capacity(4usize);
399 if let Some(v) = &from {
400 query.push(("from", v.to_string()));
401 }
402
403 query.push(("resolution", resolution.to_string()));
404 query.push(("symbol", symbol.to_string()));
405 if let Some(v) = &to {
406 query.push(("to", v.to_string()));
407 }
408
409 #[allow(unused_mut)]
410 let mut request = self
411 .client
412 .get(url)
413 .header(
414 reqwest::header::ACCEPT,
415 reqwest::header::HeaderValue::from_static("application/json"),
416 )
417 .query(&query)
418 .build()?;
419 let result = self.client.execute(request).await;
420 let response = result?;
421 match response.status().as_u16() {
422 200u16 => ResponseValue::from_response(response).await,
423 404u16 => Err(Error::ErrorResponse(
424 ResponseValue::from_response(response).await?,
425 )),
426 500u16 => Err(Error::ErrorResponse(
427 ResponseValue::from_response(response).await?,
428 )),
429 _ => Err(Error::UnexpectedResponse(response)),
430 }
431 }
432}
433
434pub mod prelude {
436 #[allow(unused_imports)]
437 pub use super::Client;
438}