Add position types
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
@@ -16,6 +16,7 @@ use tokio::sync::Mutex;
|
|||||||
|
|
||||||
pub const ALPACA_ASSET_API_URL: &str = "https://api.alpaca.markets/v2/assets";
|
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_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_CLOCK_API_URL: &str = "https://api.alpaca.markets/v2/clock";
|
||||||
|
|
||||||
pub const ALPACA_STOCK_DATA_URL: &str = "https://data.alpaca.markets/v2/stocks/bars";
|
pub const ALPACA_STOCK_DATA_URL: &str = "https://data.alpaca.markets/v2/stocks/bars";
|
||||||
|
@@ -3,3 +3,4 @@ pub mod backfills;
|
|||||||
pub mod bars;
|
pub mod bars;
|
||||||
pub mod news;
|
pub mod news;
|
||||||
pub mod orders;
|
pub mod orders;
|
||||||
|
pub mod positions;
|
||||||
|
0
src/database/positions.rs
Normal file
0
src/database/positions.rs
Normal file
@@ -3,3 +3,4 @@ pub mod bar;
|
|||||||
pub mod clock;
|
pub mod clock;
|
||||||
pub mod news;
|
pub mod news;
|
||||||
pub mod order;
|
pub mod order;
|
||||||
|
pub mod position;
|
||||||
|
95
src/types/alpaca/api/incoming/position.rs
Normal file
95
src/types/alpaca/api/incoming/position.rs
Normal 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
|
||||||
|
}
|
@@ -4,9 +4,11 @@ pub mod backfill;
|
|||||||
pub mod bar;
|
pub mod bar;
|
||||||
pub mod news;
|
pub mod news;
|
||||||
pub mod order;
|
pub mod order;
|
||||||
|
pub mod position;
|
||||||
|
|
||||||
pub use asset::{Asset, Class, Exchange};
|
pub use asset::{Asset, Class, Exchange};
|
||||||
pub use backfill::Backfill;
|
pub use backfill::Backfill;
|
||||||
pub use bar::Bar;
|
pub use bar::Bar;
|
||||||
pub use news::News;
|
pub use news::News;
|
||||||
pub use order::Order;
|
pub use order::Order;
|
||||||
|
pub use position::Position;
|
||||||
|
8
src/types/position.rs
Normal file
8
src/types/position.rs
Normal 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,
|
||||||
|
}
|
@@ -118,3 +118,9 @@ ENGINE = ReplacingMergeTree()
|
|||||||
PARTITION BY toYYYYMM(time_submitted)
|
PARTITION BY toYYYYMM(time_submitted)
|
||||||
PRIMARY KEY id;
|
PRIMARY KEY id;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS qrust.positions (
|
||||||
|
symbol LowCardinality(String),
|
||||||
|
qty Float64
|
||||||
|
)
|
||||||
|
ENGINE = ReplacingMergeTree()
|
||||||
|
PRIMARY KEY symbol;
|
||||||
|
Reference in New Issue
Block a user