Add position types

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
2024-02-14 10:48:37 +00:00
parent 5961717520
commit 6ec71ee144
8 changed files with 114 additions and 0 deletions

View File

@@ -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";

View File

@@ -3,3 +3,4 @@ pub mod backfills;
pub mod bars;
pub mod news;
pub mod orders;
pub mod positions;

View File

View File

@@ -3,3 +3,4 @@ pub mod bar;
pub mod clock;
pub mod news;
pub mod order;
pub mod position;

View File

@@ -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<Side> 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<Position> for types::Position {
fn from(position: Position) -> Self {
Self {
symbol: position.symbol,
qty: position.qty_available,
}
}
}
pub async fn get(
config: &Arc<Config>,
backoff: Option<ExponentialBackoff>,
) -> Result<Vec<Position>, 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::<Vec<Position>>()
.await
.map_err(backoff::Error::Permanent)
},
|e, duration: Duration| {
warn!(
"Failed to get positions, will retry in {} seconds: {}",
duration.as_secs(),
e
);
},
)
.await
}

View File

@@ -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;

8
src/types/position.rs Normal file
View File

@@ -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,
}

View File

@@ -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;