11
Cargo.lock
generated
11
Cargo.lock
generated
@@ -1482,6 +1482,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_threads"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "number_prefix"
|
||||
version = "0.4.0"
|
||||
@@ -2394,7 +2403,9 @@ checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"libc",
|
||||
"num-conv",
|
||||
"num_threads",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"time-core",
|
||||
|
@@ -46,9 +46,11 @@ uuid = { version = "1.6.1", features = [
|
||||
] }
|
||||
time = { version = "0.3.31", features = [
|
||||
"serde",
|
||||
"serde-well-known",
|
||||
"serde-human-readable",
|
||||
"formatting",
|
||||
"macros",
|
||||
"serde-well-known",
|
||||
"local-offset",
|
||||
] }
|
||||
backoff = { version = "0.4.0", features = [
|
||||
"tokio",
|
||||
|
55
src/types/alpaca/api/incoming/calendar.rs
Normal file
55
src/types/alpaca/api/incoming/calendar.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
use crate::{config::ALPACA_API_URL, types::alpaca::api::outgoing, utils::de};
|
||||
use backoff::{future::retry_notify, ExponentialBackoff};
|
||||
use governor::DefaultDirectRateLimiter;
|
||||
use log::warn;
|
||||
use reqwest::{Client, Error};
|
||||
use serde::Deserialize;
|
||||
use std::time::Duration;
|
||||
use time::{Date, Time};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Calendar {
|
||||
pub date: Date,
|
||||
#[serde(deserialize_with = "de::human_time_hh_mm")]
|
||||
pub open: Time,
|
||||
#[serde(deserialize_with = "de::human_time_hh_mm")]
|
||||
pub close: Time,
|
||||
pub settlement_date: Date,
|
||||
}
|
||||
|
||||
pub async fn get(
|
||||
alpaca_client: &Client,
|
||||
alpaca_rate_limiter: &DefaultDirectRateLimiter,
|
||||
query: &outgoing::calendar::Calendar,
|
||||
backoff: Option<ExponentialBackoff>,
|
||||
) -> Result<Vec<Calendar>, Error> {
|
||||
retry_notify(
|
||||
backoff.unwrap_or_default(),
|
||||
|| async {
|
||||
alpaca_rate_limiter.until_ready().await;
|
||||
alpaca_client
|
||||
.get(&format!("{}/calendar", *ALPACA_API_URL))
|
||||
.query(query)
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()
|
||||
.map_err(|e| match e.status() {
|
||||
Some(reqwest::StatusCode::BAD_REQUEST | reqwest::StatusCode::FORBIDDEN) => {
|
||||
backoff::Error::Permanent(e)
|
||||
}
|
||||
_ => e.into(),
|
||||
})?
|
||||
.json::<Vec<Calendar>>()
|
||||
.await
|
||||
.map_err(backoff::Error::Permanent)
|
||||
},
|
||||
|e, duration: Duration| {
|
||||
warn!(
|
||||
"Failed to get calendar, will retry in {} seconds: {}",
|
||||
duration.as_secs(),
|
||||
e
|
||||
);
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
pub mod account;
|
||||
pub mod asset;
|
||||
pub mod bar;
|
||||
pub mod calendar;
|
||||
pub mod clock;
|
||||
pub mod news;
|
||||
pub mod order;
|
||||
|
20
src/types/alpaca/api/outgoing/calendar.rs
Normal file
20
src/types/alpaca/api/outgoing/calendar.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use serde::Serialize;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
#[allow(dead_code)]
|
||||
pub enum DateType {
|
||||
Trading,
|
||||
Settlement,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Calendar {
|
||||
#[serde(with = "time::serde::rfc3339")]
|
||||
pub start: OffsetDateTime,
|
||||
#[serde(with = "time::serde::rfc3339")]
|
||||
pub end: OffsetDateTime,
|
||||
#[serde(rename = "date")]
|
||||
pub date_type: DateType,
|
||||
}
|
@@ -1,3 +1,4 @@
|
||||
pub mod bar;
|
||||
pub mod calendar;
|
||||
pub mod news;
|
||||
pub mod order;
|
||||
|
@@ -5,9 +5,11 @@ use serde::{
|
||||
Deserializer,
|
||||
};
|
||||
use std::fmt;
|
||||
use time::{format_description::OwnedFormatItem, macros::format_description, Time};
|
||||
|
||||
lazy_static! {
|
||||
static ref RE_SLASH: Regex = Regex::new(r"^(.+)(BTC|USD.?)$").unwrap();
|
||||
static ref FMT_HH_MM: OwnedFormatItem = format_description!("[hour]:[minute]").into();
|
||||
}
|
||||
|
||||
fn add_slash(pair: &str) -> String {
|
||||
@@ -75,3 +77,27 @@ where
|
||||
|
||||
deserializer.deserialize_seq(VecStringVisitor)
|
||||
}
|
||||
|
||||
pub fn human_time_hh_mm<'de, D>(deserializer: D) -> Result<Time, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct TimeVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for TimeVisitor {
|
||||
type Value = time::Time;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a string in the format HH:MM")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, time: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Time::parse(time, &FMT_HH_MM).map_err(|e| de::Error::custom(e.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_str(TimeVisitor)
|
||||
}
|
||||
|
Reference in New Issue
Block a user