Add paper URL support
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
use crate::types::alpaca::shared::Source;
|
use crate::types::alpaca::shared::{Mode, Source};
|
||||||
use governor::{DefaultDirectRateLimiter, Quota, RateLimiter};
|
use governor::{DefaultDirectRateLimiter, Quota, RateLimiter};
|
||||||
|
use lazy_static::lazy_static;
|
||||||
use reqwest::{
|
use reqwest::{
|
||||||
header::{HeaderMap, HeaderName, HeaderValue},
|
header::{HeaderMap, HeaderName, HeaderValue},
|
||||||
Client,
|
Client,
|
||||||
@@ -14,20 +15,35 @@ use rust_bert::{
|
|||||||
use std::{env, num::NonZeroU32, path::PathBuf, sync::Arc};
|
use std::{env, num::NonZeroU32, path::PathBuf, sync::Arc};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
pub const ALPACA_ACCOUNT_API_URL: &str = "https://api.alpaca.markets/v2/account";
|
lazy_static! {
|
||||||
pub const ALPACA_ASSET_API_URL: &str = "https://api.alpaca.markets/v2/assets";
|
pub static ref ALPACA_MODE: Mode = env::var("ALPACA_MODE")
|
||||||
pub const ALPACA_ORDER_API_URL: &str = "https://api.alpaca.markets/v2/orders";
|
.expect("ALPACA_MODE must be set.")
|
||||||
pub const ALPACA_POSITION_API_URL: &str = "https://api.alpaca.markets/v2/positions";
|
.parse()
|
||||||
pub const ALPACA_CLOCK_API_URL: &str = "https://api.alpaca.markets/v2/clock";
|
.expect("ALPACA_MODE must be 'live' or 'paper'");
|
||||||
|
static ref ALPACA_URL_SUBDOMAIN: String = match *ALPACA_MODE {
|
||||||
|
Mode::Live => String::from("api"),
|
||||||
|
Mode::Paper => String::from("paper-api"),
|
||||||
|
};
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub static ref ALPACA_API_URL: String = format!(
|
||||||
|
"https://{subdomain}.alpaca.markets/v2",
|
||||||
|
subdomain = *ALPACA_URL_SUBDOMAIN
|
||||||
|
);
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub static ref ALPACA_WEBSOCKET_URL: String = format!(
|
||||||
|
"wss://{subdomain}.alpaca.markets/stream",
|
||||||
|
subdomain = *ALPACA_URL_SUBDOMAIN
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
pub const ALPACA_STOCK_DATA_URL: &str = "https://data.alpaca.markets/v2/stocks/bars";
|
pub const ALPACA_STOCK_DATA_API_URL: &str = "https://data.alpaca.markets/v2/stocks/bars";
|
||||||
pub const ALPACA_CRYPTO_DATA_URL: &str = "https://data.alpaca.markets/v1beta3/crypto/us/bars";
|
pub const ALPACA_CRYPTO_DATA_API_URL: &str = "https://data.alpaca.markets/v1beta3/crypto/us/bars";
|
||||||
pub const ALPACA_NEWS_DATA_URL: &str = "https://data.alpaca.markets/v1beta1/news";
|
pub const ALPACA_NEWS_DATA_API_URL: &str = "https://data.alpaca.markets/v1beta1/news";
|
||||||
|
|
||||||
pub const ALPACA_TRADING_WEBSOCKET_URL: &str = "wss://api.alpaca.markets/stream";
|
pub const ALPACA_STOCK_DATA_WEBSOCKET_URL: &str = "wss://stream.data.alpaca.markets/v2";
|
||||||
pub const ALPACA_STOCK_WEBSOCKET_URL: &str = "wss://stream.data.alpaca.markets/v2";
|
pub const ALPACA_CRYPTO_DATA_WEBSOCKET_URL: &str =
|
||||||
pub const ALPACA_CRYPTO_WEBSOCKET_URL: &str = "wss://stream.data.alpaca.markets/v1beta3/crypto/us";
|
"wss://stream.data.alpaca.markets/v1beta3/crypto/us";
|
||||||
pub const ALPACA_NEWS_WEBSOCKET_URL: &str = "wss://stream.data.alpaca.markets/v1beta1/news";
|
pub const ALPACA_NEWS_DATA_WEBSOCKET_URL: &str = "wss://stream.data.alpaca.markets/v1beta1/news";
|
||||||
|
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub alpaca_api_key: String,
|
pub alpaca_api_key: String,
|
||||||
|
103
src/init.rs
Normal file
103
src/init.rs
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
use crate::{
|
||||||
|
config::{Config, ALPACA_MODE},
|
||||||
|
database,
|
||||||
|
types::alpaca::{self, api, shared::Sort},
|
||||||
|
};
|
||||||
|
use log::{info, warn};
|
||||||
|
use std::{collections::HashSet, sync::Arc};
|
||||||
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
|
pub async fn check_account(config: &Arc<Config>) {
|
||||||
|
let account = alpaca::api::incoming::account::get(config, None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
!(account.status != alpaca::api::incoming::account::Status::Active),
|
||||||
|
"Account status is not active: {:?}.",
|
||||||
|
account.status
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!account.trade_suspend_by_user,
|
||||||
|
"Account trading is suspended by user."
|
||||||
|
);
|
||||||
|
assert!(!account.trading_blocked, "Account trading is blocked.");
|
||||||
|
assert!(!account.blocked, "Account is blocked.");
|
||||||
|
|
||||||
|
if account.cash == 0.0 {
|
||||||
|
warn!("Account cash is zero, qrust will not be able to trade.");
|
||||||
|
}
|
||||||
|
|
||||||
|
warn!(
|
||||||
|
"qrust active on {} account with {} {}, avoid transferring funds without shutting down.",
|
||||||
|
*ALPACA_MODE, account.currency, account.cash
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn rehydrate_orders(config: &Arc<Config>) {
|
||||||
|
info!("Rehydrating order data.");
|
||||||
|
|
||||||
|
let mut orders = vec![];
|
||||||
|
let mut after = OffsetDateTime::UNIX_EPOCH;
|
||||||
|
|
||||||
|
while let Some(message) = api::incoming::order::get(
|
||||||
|
config,
|
||||||
|
&api::outgoing::order::Order {
|
||||||
|
status: Some(api::outgoing::order::Status::All),
|
||||||
|
limit: Some(500),
|
||||||
|
after: Some(after),
|
||||||
|
until: None,
|
||||||
|
direction: Some(Sort::Asc),
|
||||||
|
nested: Some(true),
|
||||||
|
symbols: None,
|
||||||
|
side: None,
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.ok()
|
||||||
|
.filter(|message| !message.is_empty())
|
||||||
|
{
|
||||||
|
orders.extend(message);
|
||||||
|
after = orders.last().unwrap().submitted_at;
|
||||||
|
}
|
||||||
|
|
||||||
|
let orders = orders
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(&api::incoming::order::Order::normalize)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
database::orders::upsert_batch(&config.clickhouse_client, &orders)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
info!("Rehydrated order data.");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn check_positions(config: &Arc<Config>) {
|
||||||
|
let positions_future = async {
|
||||||
|
alpaca::api::incoming::position::get(config, None)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let assets_future = async {
|
||||||
|
database::assets::select(&config.clickhouse_client)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.map(|asset| asset.symbol)
|
||||||
|
.collect::<HashSet<_>>()
|
||||||
|
};
|
||||||
|
|
||||||
|
let (positions, assets) = tokio::join!(positions_future, assets_future);
|
||||||
|
|
||||||
|
for position in positions {
|
||||||
|
if !assets.contains(&position.symbol) {
|
||||||
|
warn!(
|
||||||
|
"Position for unmonitored asset: {}, {} shares.",
|
||||||
|
position.symbol, position.qty
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod database;
|
mod database;
|
||||||
|
mod init;
|
||||||
mod routes;
|
mod routes;
|
||||||
mod threads;
|
mod threads;
|
||||||
mod types;
|
mod types;
|
||||||
@@ -33,8 +34,9 @@ async fn main() {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
threads::trading::check_account(&config).await;
|
init::check_account(&config).await;
|
||||||
threads::trading::check_positions(&config).await;
|
init::rehydrate_orders(&config).await;
|
||||||
|
init::check_positions(&config).await;
|
||||||
|
|
||||||
spawn(threads::trading::run(config.clone()));
|
spawn(threads::trading::run(config.clone()));
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
use super::ThreadType;
|
use super::ThreadType;
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{Config, ALPACA_CRYPTO_DATA_URL, ALPACA_STOCK_DATA_URL},
|
config::{Config, ALPACA_CRYPTO_DATA_API_URL, ALPACA_STOCK_DATA_API_URL},
|
||||||
database,
|
database,
|
||||||
types::{
|
types::{
|
||||||
alpaca::{
|
alpaca::{
|
||||||
@@ -77,6 +77,7 @@ pub async fn run(handler: Arc<Box<dyn Handler>>, mut receiver: mpsc::Receiver<Me
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
let message = receiver.recv().await.unwrap();
|
let message = receiver.recv().await.unwrap();
|
||||||
|
|
||||||
spawn(handle_backfill_message(
|
spawn(handle_backfill_message(
|
||||||
handler.clone(),
|
handler.clone(),
|
||||||
backfill_jobs.clone(),
|
backfill_jobs.clone(),
|
||||||
@@ -414,12 +415,12 @@ pub fn create_handler(thread_type: ThreadType, config: Arc<Config>) -> Box<dyn H
|
|||||||
match thread_type {
|
match thread_type {
|
||||||
ThreadType::Bars(Class::UsEquity) => Box::new(BarHandler {
|
ThreadType::Bars(Class::UsEquity) => Box::new(BarHandler {
|
||||||
config,
|
config,
|
||||||
data_url: ALPACA_STOCK_DATA_URL,
|
data_url: ALPACA_STOCK_DATA_API_URL,
|
||||||
api_query_constructor: us_equity_query_constructor,
|
api_query_constructor: us_equity_query_constructor,
|
||||||
}),
|
}),
|
||||||
ThreadType::Bars(Class::Crypto) => Box::new(BarHandler {
|
ThreadType::Bars(Class::Crypto) => Box::new(BarHandler {
|
||||||
config,
|
config,
|
||||||
data_url: ALPACA_CRYPTO_DATA_URL,
|
data_url: ALPACA_CRYPTO_DATA_API_URL,
|
||||||
api_query_constructor: crypto_query_constructor,
|
api_query_constructor: crypto_query_constructor,
|
||||||
}),
|
}),
|
||||||
ThreadType::News => Box::new(NewsHandler { config }),
|
ThreadType::News => Box::new(NewsHandler { config }),
|
||||||
|
@@ -4,7 +4,8 @@ mod websocket;
|
|||||||
use super::clock;
|
use super::clock;
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{
|
config::{
|
||||||
Config, ALPACA_CRYPTO_WEBSOCKET_URL, ALPACA_NEWS_WEBSOCKET_URL, ALPACA_STOCK_WEBSOCKET_URL,
|
Config, ALPACA_CRYPTO_DATA_WEBSOCKET_URL, ALPACA_NEWS_DATA_WEBSOCKET_URL,
|
||||||
|
ALPACA_STOCK_DATA_WEBSOCKET_URL,
|
||||||
},
|
},
|
||||||
create_send_await, database,
|
create_send_await, database,
|
||||||
types::{alpaca, Asset, Class},
|
types::{alpaca, Asset, Class},
|
||||||
@@ -99,10 +100,13 @@ async fn init_thread(
|
|||||||
) {
|
) {
|
||||||
let websocket_url = match thread_type {
|
let websocket_url = match thread_type {
|
||||||
ThreadType::Bars(Class::UsEquity) => {
|
ThreadType::Bars(Class::UsEquity) => {
|
||||||
format!("{}/{}", ALPACA_STOCK_WEBSOCKET_URL, &config.alpaca_source)
|
format!(
|
||||||
|
"{}/{}",
|
||||||
|
ALPACA_STOCK_DATA_WEBSOCKET_URL, &config.alpaca_source
|
||||||
|
)
|
||||||
}
|
}
|
||||||
ThreadType::Bars(Class::Crypto) => ALPACA_CRYPTO_WEBSOCKET_URL.into(),
|
ThreadType::Bars(Class::Crypto) => ALPACA_CRYPTO_DATA_WEBSOCKET_URL.into(),
|
||||||
ThreadType::News => ALPACA_NEWS_WEBSOCKET_URL.into(),
|
ThreadType::News => ALPACA_NEWS_DATA_WEBSOCKET_URL.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (websocket, _) = connect_async(websocket_url).await.unwrap();
|
let (websocket, _) = connect_async(websocket_url).await.unwrap();
|
||||||
|
@@ -186,9 +186,7 @@ async fn handle_websocket_message(
|
|||||||
) {
|
) {
|
||||||
match message {
|
match message {
|
||||||
tungstenite::Message::Text(message) => {
|
tungstenite::Message::Text(message) => {
|
||||||
let message = from_str::<Vec<websocket::data::incoming::Message>>(&message);
|
if let Ok(message) = from_str::<Vec<websocket::data::incoming::Message>>(&message) {
|
||||||
|
|
||||||
if let Ok(message) = message {
|
|
||||||
for message in message {
|
for message in message {
|
||||||
let handler = handler.clone();
|
let handler = handler.clone();
|
||||||
let pending = pending.clone();
|
let pending = pending.clone();
|
||||||
|
@@ -1,78 +1,21 @@
|
|||||||
mod rehydrate;
|
|
||||||
mod websocket;
|
mod websocket;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{Config, ALPACA_TRADING_WEBSOCKET_URL},
|
config::{Config, ALPACA_WEBSOCKET_URL},
|
||||||
database,
|
|
||||||
types::alpaca,
|
types::alpaca,
|
||||||
};
|
};
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use log::warn;
|
use std::sync::Arc;
|
||||||
use rehydrate::rehydrate;
|
|
||||||
use std::{collections::HashSet, sync::Arc};
|
|
||||||
use tokio::spawn;
|
use tokio::spawn;
|
||||||
use tokio_tungstenite::connect_async;
|
use tokio_tungstenite::connect_async;
|
||||||
|
|
||||||
pub async fn run(config: Arc<Config>) {
|
pub async fn run(config: Arc<Config>) {
|
||||||
let (websocket, _) = connect_async(ALPACA_TRADING_WEBSOCKET_URL).await.unwrap();
|
let (websocket, _) = connect_async(&*ALPACA_WEBSOCKET_URL).await.unwrap();
|
||||||
let (mut websocket_sink, mut websocket_stream) = websocket.split();
|
let (mut websocket_sink, mut websocket_stream) = websocket.split();
|
||||||
|
|
||||||
alpaca::websocket::trading::authenticate(&config, &mut websocket_sink, &mut websocket_stream)
|
alpaca::websocket::trading::authenticate(&config, &mut websocket_sink, &mut websocket_stream)
|
||||||
.await;
|
.await;
|
||||||
alpaca::websocket::trading::subscribe(&mut websocket_sink, &mut websocket_stream).await;
|
alpaca::websocket::trading::subscribe(&mut websocket_sink, &mut websocket_stream).await;
|
||||||
|
|
||||||
rehydrate(&config).await;
|
|
||||||
spawn(websocket::run(config, websocket_stream, websocket_sink));
|
spawn(websocket::run(config, websocket_stream, websocket_sink));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn check_account(config: &Arc<Config>) {
|
|
||||||
let account = alpaca::api::incoming::account::get(config, None)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
!(account.status != alpaca::api::incoming::account::Status::Active),
|
|
||||||
"Account status is not active: {:?}.",
|
|
||||||
account.status
|
|
||||||
);
|
|
||||||
assert!(
|
|
||||||
!account.trade_suspend_by_user,
|
|
||||||
"Account trading is suspended by user."
|
|
||||||
);
|
|
||||||
assert!(!account.trading_blocked, "Account trading is blocked.");
|
|
||||||
assert!(!account.blocked, "Account is blocked.");
|
|
||||||
|
|
||||||
if account.cash == 0.0 {
|
|
||||||
warn!("Account cash is zero, qrust will not be able to trade.");
|
|
||||||
}
|
|
||||||
|
|
||||||
warn!(
|
|
||||||
"qrust active with {}{}, avoid transferring funds without shutting down.",
|
|
||||||
account.cash, account.currency
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn check_positions(config: &Arc<Config>) {
|
|
||||||
let positions_future = async {
|
|
||||||
alpaca::api::incoming::position::get(config, None)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
let assets_future = async {
|
|
||||||
database::assets::select(&config.clickhouse_client)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.into_iter()
|
|
||||||
.map(|asset| asset.symbol)
|
|
||||||
.collect::<HashSet<_>>()
|
|
||||||
};
|
|
||||||
|
|
||||||
let (positions, assets) = tokio::join!(positions_future, assets_future);
|
|
||||||
|
|
||||||
for position in positions {
|
|
||||||
if !assets.contains(&position.symbol) {
|
|
||||||
warn!("Position for unmonitored asset: {:?}", position.symbol);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -1,48 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
config::Config,
|
|
||||||
database,
|
|
||||||
types::alpaca::{api, shared::Sort},
|
|
||||||
};
|
|
||||||
use log::info;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use time::OffsetDateTime;
|
|
||||||
|
|
||||||
pub async fn rehydrate(config: &Arc<Config>) {
|
|
||||||
info!("Rehydrating trading data.");
|
|
||||||
|
|
||||||
let mut orders = vec![];
|
|
||||||
let mut after = OffsetDateTime::UNIX_EPOCH;
|
|
||||||
|
|
||||||
while let Some(message) = api::incoming::order::get(
|
|
||||||
config,
|
|
||||||
&api::outgoing::order::Order {
|
|
||||||
status: Some(api::outgoing::order::Status::All),
|
|
||||||
limit: Some(500),
|
|
||||||
after: Some(after),
|
|
||||||
until: None,
|
|
||||||
direction: Some(Sort::Asc),
|
|
||||||
nested: Some(true),
|
|
||||||
symbols: None,
|
|
||||||
side: None,
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.ok()
|
|
||||||
.filter(|message| !message.is_empty())
|
|
||||||
{
|
|
||||||
orders.extend(message);
|
|
||||||
after = orders.last().unwrap().submitted_at;
|
|
||||||
}
|
|
||||||
|
|
||||||
let orders = orders
|
|
||||||
.into_iter()
|
|
||||||
.flat_map(&api::incoming::order::Order::normalize)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
database::orders::upsert_batch(&config.clickhouse_client, &orders)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
info!("Rehydrated trading data.");
|
|
||||||
}
|
|
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::config::{Config, ALPACA_API_URL};
|
||||||
use backoff::{future::retry_notify, ExponentialBackoff};
|
use backoff::{future::retry_notify, ExponentialBackoff};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use reqwest::Error;
|
use reqwest::Error;
|
||||||
@@ -9,8 +10,6 @@ use std::{sync::Arc, time::Duration};
|
|||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::config::{Config, ALPACA_ACCOUNT_API_URL};
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, PartialEq, Eq)]
|
#[derive(Deserialize, Debug, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
pub enum Status {
|
pub enum Status {
|
||||||
@@ -90,7 +89,7 @@ pub async fn get(
|
|||||||
config.alpaca_rate_limit.until_ready().await;
|
config.alpaca_rate_limit.until_ready().await;
|
||||||
config
|
config
|
||||||
.alpaca_client
|
.alpaca_client
|
||||||
.get(ALPACA_ACCOUNT_API_URL)
|
.get(&format!("{}/account", *ALPACA_API_URL))
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()
|
.error_for_status()
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
use super::position::Position;
|
use super::position::Position;
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{Config, ALPACA_ASSET_API_URL},
|
config::{Config, ALPACA_API_URL},
|
||||||
types::{
|
types::{
|
||||||
self,
|
self,
|
||||||
alpaca::shared::asset::{Class, Exchange, Status},
|
alpaca::shared::asset::{Class, Exchange, Status},
|
||||||
@@ -57,7 +57,7 @@ pub async fn get_by_symbol(
|
|||||||
config.alpaca_rate_limit.until_ready().await;
|
config.alpaca_rate_limit.until_ready().await;
|
||||||
config
|
config
|
||||||
.alpaca_client
|
.alpaca_client
|
||||||
.get(&format!("{ALPACA_ASSET_API_URL}/{symbol}"))
|
.get(&format!("{}/assets/{}", *ALPACA_API_URL, symbol))
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()
|
.error_for_status()
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
use crate::config::{Config, ALPACA_CLOCK_API_URL};
|
use crate::config::{Config, ALPACA_API_URL};
|
||||||
use backoff::{future::retry_notify, ExponentialBackoff};
|
use backoff::{future::retry_notify, ExponentialBackoff};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use reqwest::Error;
|
use reqwest::Error;
|
||||||
@@ -27,7 +27,7 @@ pub async fn get(
|
|||||||
config.alpaca_rate_limit.until_ready().await;
|
config.alpaca_rate_limit.until_ready().await;
|
||||||
config
|
config
|
||||||
.alpaca_client
|
.alpaca_client
|
||||||
.get(ALPACA_CLOCK_API_URL)
|
.get(&format!("{}/clock", *ALPACA_API_URL))
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()
|
.error_for_status()
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
config::{Config, ALPACA_NEWS_DATA_URL},
|
config::{Config, ALPACA_NEWS_DATA_API_URL},
|
||||||
types::{
|
types::{
|
||||||
self,
|
self,
|
||||||
alpaca::{api::outgoing, shared::news::normalize_html_content},
|
alpaca::{api::outgoing, shared::news::normalize_html_content},
|
||||||
@@ -83,7 +83,7 @@ pub async fn get_historical(
|
|||||||
config.alpaca_rate_limit.until_ready().await;
|
config.alpaca_rate_limit.until_ready().await;
|
||||||
config
|
config
|
||||||
.alpaca_client
|
.alpaca_client
|
||||||
.get(ALPACA_NEWS_DATA_URL)
|
.get(ALPACA_NEWS_DATA_API_URL)
|
||||||
.query(query)
|
.query(query)
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
config::{Config, ALPACA_ORDER_API_URL},
|
config::{Config, ALPACA_API_URL},
|
||||||
types::alpaca::{api::outgoing, shared},
|
types::alpaca::{api::outgoing, shared},
|
||||||
};
|
};
|
||||||
use backoff::{future::retry_notify, ExponentialBackoff};
|
use backoff::{future::retry_notify, ExponentialBackoff};
|
||||||
@@ -20,7 +20,7 @@ pub async fn get(
|
|||||||
config.alpaca_rate_limit.until_ready().await;
|
config.alpaca_rate_limit.until_ready().await;
|
||||||
config
|
config
|
||||||
.alpaca_client
|
.alpaca_client
|
||||||
.get(ALPACA_ORDER_API_URL)
|
.get(&format!("{}/orders", *ALPACA_API_URL))
|
||||||
.query(query)
|
.query(query)
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
config::{Config, ALPACA_POSITION_API_URL},
|
config::{Config, ALPACA_API_URL},
|
||||||
types::alpaca::shared::{
|
types::alpaca::shared::{
|
||||||
self,
|
self,
|
||||||
asset::{Class, Exchange},
|
asset::{Class, Exchange},
|
||||||
@@ -74,7 +74,7 @@ pub async fn get(
|
|||||||
config.alpaca_rate_limit.until_ready().await;
|
config.alpaca_rate_limit.until_ready().await;
|
||||||
config
|
config
|
||||||
.alpaca_client
|
.alpaca_client
|
||||||
.get(ALPACA_POSITION_API_URL)
|
.get(&format!("{}/positions", *ALPACA_API_URL))
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()
|
.error_for_status()
|
||||||
@@ -108,7 +108,7 @@ pub async fn get_by_symbol(
|
|||||||
config.alpaca_rate_limit.until_ready().await;
|
config.alpaca_rate_limit.until_ready().await;
|
||||||
let response = config
|
let response = config
|
||||||
.alpaca_client
|
.alpaca_client
|
||||||
.get(&format!("{ALPACA_POSITION_API_URL}/{symbol}"))
|
.get(&format!("{}/positions/{}", *ALPACA_API_URL, symbol))
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
pub mod asset;
|
pub mod asset;
|
||||||
|
pub mod mode;
|
||||||
pub mod news;
|
pub mod news;
|
||||||
pub mod order;
|
pub mod order;
|
||||||
pub mod sort;
|
pub mod sort;
|
||||||
pub mod source;
|
pub mod source;
|
||||||
|
|
||||||
|
pub use mode::Mode;
|
||||||
pub use sort::Sort;
|
pub use sort::Sort;
|
||||||
pub use source::Source;
|
pub use source::Source;
|
||||||
|
33
src/types/alpaca/shared/mode.rs
Normal file
33
src/types/alpaca/shared/mode.rs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{
|
||||||
|
fmt::{Display, Formatter},
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum Mode {
|
||||||
|
Live,
|
||||||
|
Paper,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Mode {
|
||||||
|
type Err = String;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"live" => Ok(Self::Live),
|
||||||
|
"paper" => Ok(Self::Paper),
|
||||||
|
_ => Err(format!("Unknown mode: {s}")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Mode {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||||
|
match self {
|
||||||
|
Self::Live => write!(f, "live"),
|
||||||
|
Self::Paper => write!(f, "paper"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user