From 6ec71ee144655a8f113441e0da0766767b17359f Mon Sep 17 00:00:00 2001 From: Nikolaos Karaolidis Date: Wed, 14 Feb 2024 10:48:37 +0000 Subject: [PATCH] Add position types Signed-off-by: Nikolaos Karaolidis --- src/config.rs | 1 + src/database/mod.rs | 1 + src/database/positions.rs | 0 src/types/alpaca/api/incoming/mod.rs | 1 + src/types/alpaca/api/incoming/position.rs | 95 +++++++++++++++++++ src/types/mod.rs | 2 + src/types/position.rs | 8 ++ .../docker-entrypoint-initdb.d/0000_init.sql | 6 ++ 8 files changed, 114 insertions(+) create mode 100644 src/database/positions.rs create mode 100644 src/types/alpaca/api/incoming/position.rs create mode 100644 src/types/position.rs diff --git a/src/config.rs b/src/config.rs index f06eed9..6b1e889 100644 --- a/src/config.rs +++ b/src/config.rs @@ -16,6 +16,7 @@ use tokio::sync::Mutex; pub const ALPACA_ASSET_API_URL: &str = "https://api.alpaca.markets/v2/assets"; pub const ALPACA_ORDER_API_URL: &str = "https://api.alpaca.markets/v2/orders"; +pub const ALPACA_POSITION_API_URL: &str = "https://api.alpaca.markets/v2/positions"; pub const ALPACA_CLOCK_API_URL: &str = "https://api.alpaca.markets/v2/clock"; pub const ALPACA_STOCK_DATA_URL: &str = "https://data.alpaca.markets/v2/stocks/bars"; diff --git a/src/database/mod.rs b/src/database/mod.rs index c3dc204..23d4e66 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -3,3 +3,4 @@ pub mod backfills; pub mod bars; pub mod news; pub mod orders; +pub mod positions; diff --git a/src/database/positions.rs b/src/database/positions.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/types/alpaca/api/incoming/mod.rs b/src/types/alpaca/api/incoming/mod.rs index dbeec36..e7c00c6 100644 --- a/src/types/alpaca/api/incoming/mod.rs +++ b/src/types/alpaca/api/incoming/mod.rs @@ -3,3 +3,4 @@ pub mod bar; pub mod clock; pub mod news; pub mod order; +pub mod position; diff --git a/src/types/alpaca/api/incoming/position.rs b/src/types/alpaca/api/incoming/position.rs new file mode 100644 index 0000000..f71d16d --- /dev/null +++ b/src/types/alpaca/api/incoming/position.rs @@ -0,0 +1,95 @@ +use crate::{ + config::{Config, ALPACA_POSITION_API_URL}, + types::{ + self, + alpaca::shared::{ + self, + asset::{Class, Exchange}, + }, + }, +}; +use backoff::{future::retry_notify, ExponentialBackoff}; +use log::warn; +use serde::Deserialize; +use std::{sync::Arc, time::Duration}; +use uuid::Uuid; + +#[derive(Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum Side { + Long, + Short, +} + +impl From for shared::order::Side { + fn from(side: Side) -> Self { + match side { + Side::Long => Self::Buy, + Side::Short => Self::Sell, + } + } +} + +#[derive(Deserialize)] +pub struct Position { + pub asset_id: Uuid, + pub symbol: String, + pub exchange: Exchange, + pub asset_class: Class, + pub avg_entry_price: f64, + pub qty: f64, + pub qty_available: f64, + pub side: Side, + pub market_value: f64, + pub cost_basis: f64, + pub unrealized_pl: f64, + pub unrealized_plpc: f64, + pub unrealized_intraday_pl: f64, + pub unrealized_intraday_plpc: f64, + pub current_price: f64, + pub lastday_price: f64, + pub change_today: f64, + pub asset_marginable: bool, +} + +impl From for types::Position { + fn from(position: Position) -> Self { + Self { + symbol: position.symbol, + qty: position.qty_available, + } + } +} + +pub async fn get( + config: &Arc, + backoff: Option, +) -> Result, reqwest::Error> { + retry_notify( + backoff.unwrap_or_default(), + || async { + config.alpaca_rate_limit.until_ready().await; + config + .alpaca_client + .get(ALPACA_POSITION_API_URL) + .send() + .await? + .error_for_status() + .map_err(|e| match e.status() { + Some(reqwest::StatusCode::FORBIDDEN) => backoff::Error::Permanent(e), + _ => e.into(), + })? + .json::>() + .await + .map_err(backoff::Error::Permanent) + }, + |e, duration: Duration| { + warn!( + "Failed to get positions, will retry in {} seconds: {}", + duration.as_secs(), + e + ); + }, + ) + .await +} diff --git a/src/types/mod.rs b/src/types/mod.rs index 9791365..850a554 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -4,9 +4,11 @@ pub mod backfill; pub mod bar; pub mod news; pub mod order; +pub mod position; pub use asset::{Asset, Class, Exchange}; pub use backfill::Backfill; pub use bar::Bar; pub use news::News; pub use order::Order; +pub use position::Position; diff --git a/src/types/position.rs b/src/types/position.rs new file mode 100644 index 0000000..45368eb --- /dev/null +++ b/src/types/position.rs @@ -0,0 +1,8 @@ +use clickhouse::Row; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Row)] +pub struct Position { + pub symbol: String, + pub qty: f64, +} diff --git a/support/clickhouse/docker-entrypoint-initdb.d/0000_init.sql b/support/clickhouse/docker-entrypoint-initdb.d/0000_init.sql index dfb8050..1475a49 100644 --- a/support/clickhouse/docker-entrypoint-initdb.d/0000_init.sql +++ b/support/clickhouse/docker-entrypoint-initdb.d/0000_init.sql @@ -118,3 +118,9 @@ ENGINE = ReplacingMergeTree() PARTITION BY toYYYYMM(time_submitted) PRIMARY KEY id; +CREATE TABLE IF NOT EXISTS qrust.positions ( + symbol LowCardinality(String), + qty Float64 +) +ENGINE = ReplacingMergeTree() +PRIMARY KEY symbol;