Add order types
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -2573,6 +2573,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a"
|
checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@@ -39,7 +39,9 @@ clickhouse = { version = "0.11.6", features = [
|
|||||||
"time",
|
"time",
|
||||||
"uuid",
|
"uuid",
|
||||||
] }
|
] }
|
||||||
uuid = "1.6.1"
|
uuid = { version = "1.6.1", features = [
|
||||||
|
"serde",
|
||||||
|
] }
|
||||||
time = { version = "0.3.31", features = [
|
time = { version = "0.3.31", features = [
|
||||||
"serde",
|
"serde",
|
||||||
"formatting",
|
"formatting",
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
use crate::types::alpaca::Source;
|
use crate::types::alpaca::shared::Source;
|
||||||
use governor::{DefaultDirectRateLimiter, Quota, RateLimiter};
|
use governor::{DefaultDirectRateLimiter, Quota, RateLimiter};
|
||||||
use reqwest::{
|
use reqwest::{
|
||||||
header::{HeaderMap, HeaderName, HeaderValue},
|
header::{HeaderMap, HeaderName, HeaderValue},
|
||||||
@@ -15,7 +15,9 @@ use std::{env, num::NonZeroU32, path::PathBuf, sync::Arc};
|
|||||||
use tokio::sync::Mutex;
|
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_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";
|
||||||
pub const ALPACA_CRYPTO_DATA_URL: &str = "https://data.alpaca.markets/v1beta3/crypto/us/bars";
|
pub const ALPACA_CRYPTO_DATA_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_URL: &str = "https://data.alpaca.markets/v1beta1/news";
|
||||||
|
@@ -2,3 +2,4 @@ pub mod assets;
|
|||||||
pub mod backfills;
|
pub mod backfills;
|
||||||
pub mod bars;
|
pub mod bars;
|
||||||
pub mod news;
|
pub mod news;
|
||||||
|
pub mod orders;
|
||||||
|
0
src/database/orders.rs
Normal file
0
src/database/orders.rs
Normal file
@@ -6,7 +6,7 @@ use crate::{
|
|||||||
alpaca::{
|
alpaca::{
|
||||||
self,
|
self,
|
||||||
api::{self, outgoing::Sort},
|
api::{self, outgoing::Sort},
|
||||||
Source,
|
shared::Source,
|
||||||
},
|
},
|
||||||
news::Prediction,
|
news::Prediction,
|
||||||
Backfill, Bar, Class, News,
|
Backfill, Bar, Class, News,
|
||||||
@@ -248,7 +248,7 @@ impl Handler for BarHandler {
|
|||||||
async fn backfill(&self, symbol: String, fetch_from: OffsetDateTime, fetch_to: OffsetDateTime) {
|
async fn backfill(&self, symbol: String, fetch_from: OffsetDateTime, fetch_to: OffsetDateTime) {
|
||||||
info!("Backfilling bars for {}.", symbol);
|
info!("Backfilling bars for {}.", symbol);
|
||||||
|
|
||||||
let mut bars = Vec::new();
|
let mut bars = vec![];
|
||||||
let mut next_page_token = None;
|
let mut next_page_token = None;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@@ -348,7 +348,7 @@ impl Handler for NewsHandler {
|
|||||||
async fn backfill(&self, symbol: String, fetch_from: OffsetDateTime, fetch_to: OffsetDateTime) {
|
async fn backfill(&self, symbol: String, fetch_from: OffsetDateTime, fetch_to: OffsetDateTime) {
|
||||||
info!("Backfilling news for {}.", symbol);
|
info!("Backfilling news for {}.", symbol);
|
||||||
|
|
||||||
let mut news = Vec::new();
|
let mut news = vec![];
|
||||||
let mut next_page_token = None;
|
let mut next_page_token = None;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
@@ -8,7 +8,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
create_send_await, database,
|
create_send_await, database,
|
||||||
types::{alpaca, Asset, Class},
|
types::{alpaca, Asset, Class},
|
||||||
utils::{authenticate, backoff, cleanup},
|
utils::{backoff, cleanup},
|
||||||
};
|
};
|
||||||
use futures_util::{future::join_all, StreamExt};
|
use futures_util::{future::join_all, StreamExt};
|
||||||
use itertools::{Either, Itertools};
|
use itertools::{Either, Itertools};
|
||||||
@@ -19,7 +19,7 @@ use tokio::{
|
|||||||
};
|
};
|
||||||
use tokio_tungstenite::connect_async;
|
use tokio_tungstenite::connect_async;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
Add,
|
Add,
|
||||||
Remove,
|
Remove,
|
||||||
@@ -45,7 +45,7 @@ impl Message {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum ThreadType {
|
pub enum ThreadType {
|
||||||
Bars(Class),
|
Bars(Class),
|
||||||
News,
|
News,
|
||||||
@@ -107,7 +107,8 @@ async fn init_thread(
|
|||||||
|
|
||||||
let (websocket, _) = connect_async(websocket_url).await.unwrap();
|
let (websocket, _) = connect_async(websocket_url).await.unwrap();
|
||||||
let (mut websocket_sink, mut websocket_stream) = websocket.split();
|
let (mut websocket_sink, mut websocket_stream) = websocket.split();
|
||||||
authenticate(&config, &mut websocket_sink, &mut websocket_stream).await;
|
alpaca::websocket::data::authenticate(&config, &mut websocket_sink, &mut websocket_stream)
|
||||||
|
.await;
|
||||||
|
|
||||||
let (backfill_sender, backfill_receiver) = mpsc::channel(100);
|
let (backfill_sender, backfill_receiver) = mpsc::channel(100);
|
||||||
spawn(backfill::run(
|
spawn(backfill::run(
|
||||||
@@ -160,14 +161,14 @@ async fn handle_message(
|
|||||||
create_send_await!(
|
create_send_await!(
|
||||||
bars_us_equity_websocket_sender,
|
bars_us_equity_websocket_sender,
|
||||||
websocket::Message::new,
|
websocket::Message::new,
|
||||||
message.action.clone().into(),
|
message.action.into(),
|
||||||
us_equity_symbols.clone()
|
us_equity_symbols.clone()
|
||||||
);
|
);
|
||||||
|
|
||||||
create_send_await!(
|
create_send_await!(
|
||||||
bars_us_equity_backfill_sender,
|
bars_us_equity_backfill_sender,
|
||||||
backfill::Message::new,
|
backfill::Message::new,
|
||||||
message.action.clone().into(),
|
message.action.into(),
|
||||||
us_equity_symbols
|
us_equity_symbols
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -180,14 +181,14 @@ async fn handle_message(
|
|||||||
create_send_await!(
|
create_send_await!(
|
||||||
bars_crypto_websocket_sender,
|
bars_crypto_websocket_sender,
|
||||||
websocket::Message::new,
|
websocket::Message::new,
|
||||||
message.action.clone().into(),
|
message.action.into(),
|
||||||
crypto_symbols.clone()
|
crypto_symbols.clone()
|
||||||
);
|
);
|
||||||
|
|
||||||
create_send_await!(
|
create_send_await!(
|
||||||
bars_crypto_backfill_sender,
|
bars_crypto_backfill_sender,
|
||||||
backfill::Message::new,
|
backfill::Message::new,
|
||||||
message.action.clone().into(),
|
message.action.into(),
|
||||||
crypto_symbols
|
crypto_symbols
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -196,14 +197,14 @@ async fn handle_message(
|
|||||||
create_send_await!(
|
create_send_await!(
|
||||||
news_websocket_sender,
|
news_websocket_sender,
|
||||||
websocket::Message::new,
|
websocket::Message::new,
|
||||||
message.action.clone().into(),
|
message.action.into(),
|
||||||
symbols.clone()
|
symbols.clone()
|
||||||
);
|
);
|
||||||
|
|
||||||
create_send_await!(
|
create_send_await!(
|
||||||
news_backfill_sender,
|
news_backfill_sender,
|
||||||
backfill::Message::new,
|
backfill::Message::new,
|
||||||
message.action.clone().into(),
|
message.action.into(),
|
||||||
symbols.clone()
|
symbols.clone()
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -66,11 +66,11 @@ pub trait Handler: Send + Sync {
|
|||||||
fn create_subscription_message(
|
fn create_subscription_message(
|
||||||
&self,
|
&self,
|
||||||
symbols: Vec<String>,
|
symbols: Vec<String>,
|
||||||
) -> websocket::outgoing::subscribe::Message;
|
) -> websocket::data::outgoing::subscribe::Message;
|
||||||
async fn handle_parsed_websocket_message(
|
async fn handle_parsed_websocket_message(
|
||||||
&self,
|
&self,
|
||||||
pending: Arc<RwLock<Pending>>,
|
pending: Arc<RwLock<Pending>>,
|
||||||
message: websocket::incoming::Message,
|
message: websocket::data::incoming::Message,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ async fn handle_message(
|
|||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.send(tungstenite::Message::Text(
|
.send(tungstenite::Message::Text(
|
||||||
to_string(&websocket::outgoing::Message::Subscribe(
|
to_string(&websocket::data::outgoing::Message::Subscribe(
|
||||||
handler.create_subscription_message(message.symbols),
|
handler.create_subscription_message(message.symbols),
|
||||||
))
|
))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
@@ -168,7 +168,7 @@ async fn handle_message(
|
|||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.send(tungstenite::Message::Text(
|
.send(tungstenite::Message::Text(
|
||||||
to_string(&websocket::outgoing::Message::Unsubscribe(
|
to_string(&websocket::data::outgoing::Message::Unsubscribe(
|
||||||
handler.create_subscription_message(message.symbols.clone()),
|
handler.create_subscription_message(message.symbols.clone()),
|
||||||
))
|
))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
@@ -191,7 +191,7 @@ async fn handle_websocket_message(
|
|||||||
) {
|
) {
|
||||||
match message {
|
match message {
|
||||||
tungstenite::Message::Text(message) => {
|
tungstenite::Message::Text(message) => {
|
||||||
let message = from_str::<Vec<websocket::incoming::Message>>(&message);
|
let message = from_str::<Vec<websocket::data::incoming::Message>>(&message);
|
||||||
|
|
||||||
if let Ok(message) = message {
|
if let Ok(message) = message {
|
||||||
for message in message {
|
for message in message {
|
||||||
@@ -222,7 +222,8 @@ async fn handle_websocket_message(
|
|||||||
|
|
||||||
struct BarsHandler {
|
struct BarsHandler {
|
||||||
config: Arc<Config>,
|
config: Arc<Config>,
|
||||||
subscription_message_constructor: fn(Vec<String>) -> websocket::outgoing::subscribe::Message,
|
subscription_message_constructor:
|
||||||
|
fn(Vec<String>) -> websocket::data::outgoing::subscribe::Message,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -230,19 +231,20 @@ impl Handler for BarsHandler {
|
|||||||
fn create_subscription_message(
|
fn create_subscription_message(
|
||||||
&self,
|
&self,
|
||||||
symbols: Vec<String>,
|
symbols: Vec<String>,
|
||||||
) -> websocket::outgoing::subscribe::Message {
|
) -> websocket::data::outgoing::subscribe::Message {
|
||||||
(self.subscription_message_constructor)(symbols)
|
(self.subscription_message_constructor)(symbols)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_parsed_websocket_message(
|
async fn handle_parsed_websocket_message(
|
||||||
&self,
|
&self,
|
||||||
pending: Arc<RwLock<Pending>>,
|
pending: Arc<RwLock<Pending>>,
|
||||||
message: websocket::incoming::Message,
|
message: websocket::data::incoming::Message,
|
||||||
) {
|
) {
|
||||||
match message {
|
match message {
|
||||||
websocket::incoming::Message::Subscription(message) => {
|
websocket::data::incoming::Message::Subscription(message) => {
|
||||||
let websocket::incoming::subscription::Message::Market { bars: symbols, .. } =
|
let websocket::data::incoming::subscription::Message::Market {
|
||||||
message
|
bars: symbols, ..
|
||||||
|
} = message
|
||||||
else {
|
else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
@@ -283,8 +285,8 @@ impl Handler for BarsHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
websocket::incoming::Message::Bar(message)
|
websocket::data::incoming::Message::Bar(message)
|
||||||
| websocket::incoming::Message::UpdatedBar(message) => {
|
| websocket::data::incoming::Message::UpdatedBar(message) => {
|
||||||
let bar = Bar::from(message);
|
let bar = Bar::from(message);
|
||||||
debug!("Received bar for {}: {}.", bar.symbol, bar.time);
|
debug!("Received bar for {}: {}.", bar.symbol, bar.time);
|
||||||
|
|
||||||
@@ -292,15 +294,15 @@ impl Handler for BarsHandler {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
websocket::incoming::Message::Status(message) => {
|
websocket::data::incoming::Message::Status(message) => {
|
||||||
debug!(
|
debug!(
|
||||||
"Received status message for {}: {}.",
|
"Received status message for {}: {}.",
|
||||||
message.symbol, message.status_message
|
message.symbol, message.status_message
|
||||||
);
|
);
|
||||||
|
|
||||||
match message.status {
|
match message.status {
|
||||||
websocket::incoming::status::Status::TradingHalt
|
websocket::data::incoming::status::Status::TradingHalt
|
||||||
| websocket::incoming::status::Status::VolatilityTradingPause => {
|
| websocket::data::incoming::status::Status::VolatilityTradingPause => {
|
||||||
database::assets::update_status_where_symbol(
|
database::assets::update_status_where_symbol(
|
||||||
&self.config.clickhouse_client,
|
&self.config.clickhouse_client,
|
||||||
&message.symbol,
|
&message.symbol,
|
||||||
@@ -309,8 +311,8 @@ impl Handler for BarsHandler {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
websocket::incoming::status::Status::Resume
|
websocket::data::incoming::status::Status::Resume
|
||||||
| websocket::incoming::status::Status::TradingResumption => {
|
| websocket::data::incoming::status::Status::TradingResumption => {
|
||||||
database::assets::update_status_where_symbol(
|
database::assets::update_status_where_symbol(
|
||||||
&self.config.clickhouse_client,
|
&self.config.clickhouse_client,
|
||||||
&message.symbol,
|
&message.symbol,
|
||||||
@@ -322,7 +324,7 @@ impl Handler for BarsHandler {
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
websocket::incoming::Message::Error(message) => {
|
websocket::data::incoming::Message::Error(message) => {
|
||||||
error!("Received error message: {}.", message.message);
|
error!("Received error message: {}.", message.message);
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
@@ -339,18 +341,19 @@ impl Handler for NewsHandler {
|
|||||||
fn create_subscription_message(
|
fn create_subscription_message(
|
||||||
&self,
|
&self,
|
||||||
symbols: Vec<String>,
|
symbols: Vec<String>,
|
||||||
) -> websocket::outgoing::subscribe::Message {
|
) -> websocket::data::outgoing::subscribe::Message {
|
||||||
websocket::outgoing::subscribe::Message::new_news(symbols)
|
websocket::data::outgoing::subscribe::Message::new_news(symbols)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_parsed_websocket_message(
|
async fn handle_parsed_websocket_message(
|
||||||
&self,
|
&self,
|
||||||
pending: Arc<RwLock<Pending>>,
|
pending: Arc<RwLock<Pending>>,
|
||||||
message: websocket::incoming::Message,
|
message: websocket::data::incoming::Message,
|
||||||
) {
|
) {
|
||||||
match message {
|
match message {
|
||||||
websocket::incoming::Message::Subscription(message) => {
|
websocket::data::incoming::Message::Subscription(message) => {
|
||||||
let websocket::incoming::subscription::Message::News { news: symbols } = message
|
let websocket::data::incoming::subscription::Message::News { news: symbols } =
|
||||||
|
message
|
||||||
else {
|
else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
@@ -396,7 +399,7 @@ impl Handler for NewsHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
websocket::incoming::Message::News(message) => {
|
websocket::data::incoming::Message::News(message) => {
|
||||||
let news = News::from(message);
|
let news = News::from(message);
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
@@ -426,7 +429,7 @@ impl Handler for NewsHandler {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
websocket::incoming::Message::Error(message) => {
|
websocket::data::incoming::Message::Error(message) => {
|
||||||
error!("Received error message: {}.", message.message);
|
error!("Received error message: {}.", message.message);
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
@@ -439,12 +442,12 @@ pub fn create_handler(thread_type: ThreadType, config: Arc<Config>) -> Box<dyn H
|
|||||||
ThreadType::Bars(Class::UsEquity) => Box::new(BarsHandler {
|
ThreadType::Bars(Class::UsEquity) => Box::new(BarsHandler {
|
||||||
config,
|
config,
|
||||||
subscription_message_constructor:
|
subscription_message_constructor:
|
||||||
websocket::outgoing::subscribe::Message::new_market_us_equity,
|
websocket::data::outgoing::subscribe::Message::new_market_us_equity,
|
||||||
}),
|
}),
|
||||||
ThreadType::Bars(Class::Crypto) => Box::new(BarsHandler {
|
ThreadType::Bars(Class::Crypto) => Box::new(BarsHandler {
|
||||||
config,
|
config,
|
||||||
subscription_message_constructor:
|
subscription_message_constructor:
|
||||||
websocket::outgoing::subscribe::Message::new_market_crypto,
|
websocket::data::outgoing::subscribe::Message::new_market_crypto,
|
||||||
}),
|
}),
|
||||||
ThreadType::News => Box::new(NewsHandler { config }),
|
ThreadType::News => Box::new(NewsHandler { config }),
|
||||||
}
|
}
|
||||||
|
@@ -1,2 +1,3 @@
|
|||||||
pub mod clock;
|
pub mod clock;
|
||||||
pub mod data;
|
pub mod data;
|
||||||
|
pub mod trading;
|
||||||
|
0
src/threads/trading/mod.rs
Normal file
0
src/threads/trading/mod.rs
Normal file
@@ -1,68 +1,21 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
config::{Config, ALPACA_ASSET_API_URL},
|
config::{Config, ALPACA_ASSET_API_URL},
|
||||||
impl_from_enum, types,
|
types::{
|
||||||
|
self,
|
||||||
|
alpaca::shared::asset::{Class, Exchange, Status},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use backoff::{future::retry_notify, ExponentialBackoff};
|
use backoff::{future::retry_notify, ExponentialBackoff};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use reqwest::Error;
|
use reqwest::Error;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::{sync::Arc, time::Duration};
|
use std::{sync::Arc, time::Duration};
|
||||||
|
use uuid::Uuid;
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize)]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub enum Class {
|
|
||||||
UsEquity,
|
|
||||||
Crypto,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_from_enum!(types::Class, Class, UsEquity, Crypto);
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize)]
|
|
||||||
#[serde(rename_all = "UPPERCASE")]
|
|
||||||
pub enum Exchange {
|
|
||||||
Amex,
|
|
||||||
Arca,
|
|
||||||
Bats,
|
|
||||||
Nyse,
|
|
||||||
Nasdaq,
|
|
||||||
Nysearca,
|
|
||||||
Otc,
|
|
||||||
Crypto,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_from_enum!(
|
|
||||||
types::Exchange,
|
|
||||||
Exchange,
|
|
||||||
Amex,
|
|
||||||
Arca,
|
|
||||||
Bats,
|
|
||||||
Nyse,
|
|
||||||
Nasdaq,
|
|
||||||
Nysearca,
|
|
||||||
Otc,
|
|
||||||
Crypto
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub enum Status {
|
|
||||||
Active,
|
|
||||||
Inactive,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Status> for bool {
|
|
||||||
fn from(status: Status) -> Self {
|
|
||||||
match status {
|
|
||||||
Status::Active => true,
|
|
||||||
Status::Inactive => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::struct_excessive_bools)]
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Asset {
|
pub struct Asset {
|
||||||
pub id: String,
|
pub id: Uuid,
|
||||||
pub class: Class,
|
pub class: Class,
|
||||||
pub exchange: Exchange,
|
pub exchange: Exchange,
|
||||||
pub symbol: String,
|
pub symbol: String,
|
||||||
@@ -78,12 +31,12 @@ pub struct Asset {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl From<Asset> for types::Asset {
|
impl From<Asset> for types::Asset {
|
||||||
fn from(item: Asset) -> Self {
|
fn from(asset: Asset) -> Self {
|
||||||
Self {
|
Self {
|
||||||
symbol: item.symbol,
|
symbol: asset.symbol,
|
||||||
class: item.class.into(),
|
class: asset.class.into(),
|
||||||
exchange: item.exchange.into(),
|
exchange: asset.exchange.into(),
|
||||||
status: item.status.into(),
|
status: asset.status.into(),
|
||||||
time_added: time::OffsetDateTime::now_utc(),
|
time_added: time::OffsetDateTime::now_utc(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,7 @@ use serde::Deserialize;
|
|||||||
use std::{collections::HashMap, sync::Arc, time::Duration};
|
use std::{collections::HashMap, sync::Arc, time::Duration};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Bar {
|
pub struct Bar {
|
||||||
#[serde(rename = "t")]
|
#[serde(rename = "t")]
|
||||||
#[serde(with = "time::serde::rfc3339")]
|
#[serde(with = "time::serde::rfc3339")]
|
||||||
@@ -46,7 +46,7 @@ impl From<(Bar, String)> for types::Bar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
pub bars: HashMap<String, Vec<Bar>>,
|
pub bars: HashMap<String, Vec<Bar>>,
|
||||||
pub next_page_token: Option<String>,
|
pub next_page_token: Option<String>,
|
||||||
|
@@ -6,7 +6,7 @@ use serde::Deserialize;
|
|||||||
use std::{sync::Arc, time::Duration};
|
use std::{sync::Arc, time::Duration};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Clock {
|
pub struct Clock {
|
||||||
#[serde(with = "time::serde::rfc3339")]
|
#[serde(with = "time::serde::rfc3339")]
|
||||||
pub timestamp: OffsetDateTime,
|
pub timestamp: OffsetDateTime,
|
||||||
|
@@ -2,3 +2,4 @@ pub mod asset;
|
|||||||
pub mod bar;
|
pub mod bar;
|
||||||
pub mod clock;
|
pub mod clock;
|
||||||
pub mod news;
|
pub mod news;
|
||||||
|
pub mod order;
|
||||||
|
@@ -10,21 +10,21 @@ use serde::Deserialize;
|
|||||||
use std::{sync::Arc, time::Duration};
|
use std::{sync::Arc, time::Duration};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum ImageSize {
|
pub enum ImageSize {
|
||||||
Thumb,
|
Thumb,
|
||||||
Small,
|
Small,
|
||||||
Large,
|
Large,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Image {
|
pub struct Image {
|
||||||
pub size: ImageSize,
|
pub size: ImageSize,
|
||||||
pub url: String,
|
pub url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct News {
|
pub struct News {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
#[serde(with = "time::serde::rfc3339")]
|
#[serde(with = "time::serde::rfc3339")]
|
||||||
@@ -66,7 +66,7 @@ impl From<News> for types::News {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
pub news: Vec<News>,
|
pub news: Vec<News>,
|
||||||
pub next_page_token: Option<String>,
|
pub next_page_token: Option<String>,
|
||||||
|
45
src/types/alpaca/api/incoming/order.rs
Normal file
45
src/types/alpaca/api/incoming/order.rs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
use crate::{
|
||||||
|
config::{Config, ALPACA_ORDER_API_URL},
|
||||||
|
types::alpaca::{api::outgoing, shared},
|
||||||
|
};
|
||||||
|
use backoff::{future::retry_notify, ExponentialBackoff};
|
||||||
|
use log::warn;
|
||||||
|
use reqwest::Error;
|
||||||
|
use std::{sync::Arc, time::Duration};
|
||||||
|
|
||||||
|
pub use shared::order::Order;
|
||||||
|
|
||||||
|
pub async fn get(
|
||||||
|
config: &Arc<Config>,
|
||||||
|
query: &outgoing::order::Order,
|
||||||
|
backoff: Option<ExponentialBackoff>,
|
||||||
|
) -> Result<Vec<Order>, Error> {
|
||||||
|
retry_notify(
|
||||||
|
backoff.unwrap_or_default(),
|
||||||
|
|| async {
|
||||||
|
config.alpaca_rate_limit.until_ready().await;
|
||||||
|
config
|
||||||
|
.alpaca_client
|
||||||
|
.get(ALPACA_ORDER_API_URL)
|
||||||
|
.query(query)
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.error_for_status()
|
||||||
|
.map_err(|e| match e.status() {
|
||||||
|
Some(reqwest::StatusCode::FORBIDDEN) => backoff::Error::Permanent(e),
|
||||||
|
_ => e.into(),
|
||||||
|
})?
|
||||||
|
.json::<Vec<Order>>()
|
||||||
|
.await
|
||||||
|
.map_err(backoff::Error::Permanent)
|
||||||
|
},
|
||||||
|
|e, duration: Duration| {
|
||||||
|
warn!(
|
||||||
|
"Failed to get orders, will retry in {} seconds: {}",
|
||||||
|
duration.as_secs(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
@@ -1,5 +1,5 @@
|
|||||||
use super::{serialize_symbols, Sort};
|
use super::{serialize_symbols, Sort};
|
||||||
use crate::types::alpaca::Source;
|
use crate::types::alpaca::shared::Source;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
@@ -1,10 +1,11 @@
|
|||||||
pub mod bar;
|
pub mod bar;
|
||||||
pub mod news;
|
pub mod news;
|
||||||
|
pub mod order;
|
||||||
|
|
||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer};
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "snake_case")]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub enum Sort {
|
pub enum Sort {
|
||||||
Asc,
|
Asc,
|
||||||
@@ -18,3 +19,16 @@ where
|
|||||||
let string = symbols.join(",");
|
let string = symbols.join(",");
|
||||||
serializer.serialize_str(&string)
|
serializer.serialize_str(&string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn serialize_symbols_option<S>(
|
||||||
|
symbols: &Option<Vec<String>>,
|
||||||
|
serializer: S,
|
||||||
|
) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
match symbols {
|
||||||
|
Some(symbols) => serialize_symbols(symbols, serializer),
|
||||||
|
None => serializer.serialize_none(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
35
src/types/alpaca/api/outgoing/order.rs
Normal file
35
src/types/alpaca/api/outgoing/order.rs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
use super::{serialize_symbols_option, Sort};
|
||||||
|
use crate::types::alpaca::shared::order::Side;
|
||||||
|
use serde::Serialize;
|
||||||
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum Status {
|
||||||
|
Open,
|
||||||
|
Closed,
|
||||||
|
All,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct Order {
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub status: Option<Status>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub limit: Option<i64>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[serde(with = "time::serde::rfc3339::option")]
|
||||||
|
pub after: Option<OffsetDateTime>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[serde(with = "time::serde::rfc3339::option")]
|
||||||
|
pub until: Option<OffsetDateTime>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub direction: Option<Sort>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub nested: Option<bool>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[serde(serialize_with = "serialize_symbols_option")]
|
||||||
|
pub symbols: Option<Vec<String>>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub side: Option<Side>,
|
||||||
|
}
|
@@ -1,5 +1,3 @@
|
|||||||
pub mod api;
|
pub mod api;
|
||||||
pub mod source;
|
pub mod shared;
|
||||||
pub mod websocket;
|
pub mod websocket;
|
||||||
|
|
||||||
pub use source::Source;
|
|
||||||
|
53
src/types/alpaca/shared/asset.rs
Normal file
53
src/types/alpaca/shared/asset.rs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
use crate::{impl_from_enum, types};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum Class {
|
||||||
|
UsEquity,
|
||||||
|
Crypto,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_from_enum!(types::Class, Class, UsEquity, Crypto);
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(rename_all = "UPPERCASE")]
|
||||||
|
pub enum Exchange {
|
||||||
|
Amex,
|
||||||
|
Arca,
|
||||||
|
Bats,
|
||||||
|
Nyse,
|
||||||
|
Nasdaq,
|
||||||
|
Nysearca,
|
||||||
|
Otc,
|
||||||
|
Crypto,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_from_enum!(
|
||||||
|
types::Exchange,
|
||||||
|
Exchange,
|
||||||
|
Amex,
|
||||||
|
Arca,
|
||||||
|
Bats,
|
||||||
|
Nyse,
|
||||||
|
Nasdaq,
|
||||||
|
Nysearca,
|
||||||
|
Otc,
|
||||||
|
Crypto
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum Status {
|
||||||
|
Active,
|
||||||
|
Inactive,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Status> for bool {
|
||||||
|
fn from(status: Status) -> Self {
|
||||||
|
match status {
|
||||||
|
Status::Active => true,
|
||||||
|
Status::Inactive => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
src/types/alpaca/shared/mod.rs
Normal file
5
src/types/alpaca/shared/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
pub mod asset;
|
||||||
|
pub mod order;
|
||||||
|
pub mod source;
|
||||||
|
|
||||||
|
pub use source::Source;
|
214
src/types/alpaca/shared/order.rs
Normal file
214
src/types/alpaca/shared/order.rs
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
use crate::{impl_from_enum, types};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use time::OffsetDateTime;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum Class {
|
||||||
|
#[serde(alias = "")]
|
||||||
|
Simple,
|
||||||
|
Bracket,
|
||||||
|
Oco,
|
||||||
|
Oto,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_from_enum!(types::order::Class, Class, Simple, Bracket, Oco, Oto);
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum Type {
|
||||||
|
Market,
|
||||||
|
Limit,
|
||||||
|
Stop,
|
||||||
|
StopLimit,
|
||||||
|
TrailingStop,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_from_enum!(
|
||||||
|
types::order::Type,
|
||||||
|
Type,
|
||||||
|
Market,
|
||||||
|
Limit,
|
||||||
|
Stop,
|
||||||
|
StopLimit,
|
||||||
|
TrailingStop
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum Side {
|
||||||
|
Buy,
|
||||||
|
Sell,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_from_enum!(types::order::Side, Side, Buy, Sell);
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum TimeInForce {
|
||||||
|
Day,
|
||||||
|
Gtc,
|
||||||
|
Opg,
|
||||||
|
Cls,
|
||||||
|
Ioc,
|
||||||
|
Fok,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_from_enum!(
|
||||||
|
types::order::TimeInForce,
|
||||||
|
TimeInForce,
|
||||||
|
Day,
|
||||||
|
Gtc,
|
||||||
|
Opg,
|
||||||
|
Cls,
|
||||||
|
Ioc,
|
||||||
|
Fok
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum Status {
|
||||||
|
New,
|
||||||
|
PartiallyFilled,
|
||||||
|
Filled,
|
||||||
|
DoneForDay,
|
||||||
|
Canceled,
|
||||||
|
Expired,
|
||||||
|
Replaced,
|
||||||
|
PendingCancel,
|
||||||
|
PendingReplace,
|
||||||
|
Accepted,
|
||||||
|
PendingNew,
|
||||||
|
AcceptedForBidding,
|
||||||
|
Stopped,
|
||||||
|
Rejected,
|
||||||
|
Suspended,
|
||||||
|
Calculated,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_from_enum!(
|
||||||
|
types::order::Status,
|
||||||
|
Status,
|
||||||
|
New,
|
||||||
|
PartiallyFilled,
|
||||||
|
Filled,
|
||||||
|
DoneForDay,
|
||||||
|
Canceled,
|
||||||
|
Expired,
|
||||||
|
Replaced,
|
||||||
|
PendingCancel,
|
||||||
|
PendingReplace,
|
||||||
|
Accepted,
|
||||||
|
PendingNew,
|
||||||
|
AcceptedForBidding,
|
||||||
|
Stopped,
|
||||||
|
Rejected,
|
||||||
|
Suspended,
|
||||||
|
Calculated
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone, Debug, PartialEq)]
|
||||||
|
#[allow(clippy::struct_field_names)]
|
||||||
|
pub struct Order {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub client_order_id: Uuid,
|
||||||
|
#[serde(with = "time::serde::rfc3339")]
|
||||||
|
pub created_at: OffsetDateTime,
|
||||||
|
#[serde(with = "time::serde::rfc3339::option")]
|
||||||
|
pub updated_at: Option<OffsetDateTime>,
|
||||||
|
#[serde(with = "time::serde::rfc3339")]
|
||||||
|
pub submitted_at: OffsetDateTime,
|
||||||
|
#[serde(with = "time::serde::rfc3339::option")]
|
||||||
|
pub filled_at: Option<OffsetDateTime>,
|
||||||
|
#[serde(with = "time::serde::rfc3339::option")]
|
||||||
|
pub expired_at: Option<OffsetDateTime>,
|
||||||
|
#[serde(with = "time::serde::rfc3339::option")]
|
||||||
|
pub cancel_requested_at: Option<OffsetDateTime>,
|
||||||
|
#[serde(with = "time::serde::rfc3339::option")]
|
||||||
|
pub canceled_at: Option<OffsetDateTime>,
|
||||||
|
#[serde(with = "time::serde::rfc3339::option")]
|
||||||
|
pub failed_at: Option<OffsetDateTime>,
|
||||||
|
#[serde(with = "time::serde::rfc3339::option")]
|
||||||
|
pub replaced_at: Option<OffsetDateTime>,
|
||||||
|
pub replaced_by: Option<Uuid>,
|
||||||
|
pub replaces: Option<Uuid>,
|
||||||
|
pub asset_id: Uuid,
|
||||||
|
pub symbol: String,
|
||||||
|
pub asset_class: super::asset::Class,
|
||||||
|
pub notional: Option<f64>,
|
||||||
|
pub qty: Option<f64>,
|
||||||
|
pub filled_qty: f64,
|
||||||
|
pub filled_avg_price: Option<f64>,
|
||||||
|
pub order_class: Class,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub order_type: Type,
|
||||||
|
pub side: Side,
|
||||||
|
pub time_in_force: TimeInForce,
|
||||||
|
pub limit_price: Option<f64>,
|
||||||
|
pub stop_price: Option<f64>,
|
||||||
|
pub status: Status,
|
||||||
|
pub extended_hours: bool,
|
||||||
|
pub legs: Option<Vec<Order>>,
|
||||||
|
pub trail_percent: Option<f64>,
|
||||||
|
pub trail_price: Option<f64>,
|
||||||
|
pub hwm: Option<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Order> for types::Order {
|
||||||
|
fn from(order: Order) -> Self {
|
||||||
|
Self {
|
||||||
|
id: order.id,
|
||||||
|
client_order_id: order.client_order_id,
|
||||||
|
time_submitted: order.submitted_at,
|
||||||
|
time_created: order.created_at,
|
||||||
|
time_updated: order.updated_at.unwrap_or(OffsetDateTime::UNIX_EPOCH),
|
||||||
|
time_filled: order.filled_at.unwrap_or(OffsetDateTime::UNIX_EPOCH),
|
||||||
|
time_expired: order.expired_at.unwrap_or(OffsetDateTime::UNIX_EPOCH),
|
||||||
|
time_cancel_requested: order
|
||||||
|
.cancel_requested_at
|
||||||
|
.unwrap_or(OffsetDateTime::UNIX_EPOCH),
|
||||||
|
time_canceled: order.canceled_at.unwrap_or(OffsetDateTime::UNIX_EPOCH),
|
||||||
|
time_failed: order.failed_at.unwrap_or(OffsetDateTime::UNIX_EPOCH),
|
||||||
|
time_replaced: order.replaced_at.unwrap_or(OffsetDateTime::UNIX_EPOCH),
|
||||||
|
replaced_by: order.replaced_by.unwrap_or_default(),
|
||||||
|
replaces: order.replaces.unwrap_or_default(),
|
||||||
|
symbol: order.symbol,
|
||||||
|
order_class: order.order_class.into(),
|
||||||
|
order_type: order.order_type.into(),
|
||||||
|
side: order.side.into(),
|
||||||
|
time_in_force: order.time_in_force.into(),
|
||||||
|
notional: order.notional.unwrap_or_default(),
|
||||||
|
qty: order.qty.unwrap_or_default(),
|
||||||
|
filled_qty: order.filled_qty,
|
||||||
|
filled_avg_price: order.filled_avg_price.unwrap_or_default(),
|
||||||
|
status: order.status.into(),
|
||||||
|
extended_hours: order.extended_hours,
|
||||||
|
limit_price: order.limit_price.unwrap_or_default(),
|
||||||
|
stop_price: order.stop_price.unwrap_or_default(),
|
||||||
|
trail_percent: order.trail_percent.unwrap_or_default(),
|
||||||
|
trail_price: order.trail_price.unwrap_or_default(),
|
||||||
|
hwm: order.hwm.unwrap_or_default(),
|
||||||
|
legs: order
|
||||||
|
.legs
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into_iter()
|
||||||
|
.map(|order| order.id)
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Order {
|
||||||
|
pub fn normalize(self) -> Vec<types::Order> {
|
||||||
|
let mut orders = vec![self.clone().into()];
|
||||||
|
|
||||||
|
if let Some(legs) = self.legs {
|
||||||
|
for leg in legs {
|
||||||
|
orders.extend(leg.normalize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
orders
|
||||||
|
}
|
||||||
|
}
|
@@ -5,7 +5,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum Source {
|
pub enum Source {
|
||||||
Iex,
|
Iex,
|
||||||
Sip,
|
Sip,
|
@@ -2,7 +2,7 @@ use crate::types;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
#[derive(Deserialize, Debug, PartialEq)]
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
#[serde(rename = "t")]
|
#[serde(rename = "t")]
|
||||||
#[serde(with = "time::serde::rfc3339")]
|
#[serde(with = "time::serde::rfc3339")]
|
@@ -1,7 +1,6 @@
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
|
#[derive(Deserialize, Debug, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
pub code: u16,
|
pub code: u16,
|
||||||
#[serde(rename = "msg")]
|
#[serde(rename = "msg")]
|
@@ -7,7 +7,7 @@ pub mod success;
|
|||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
#[derive(Deserialize, Debug, PartialEq)]
|
||||||
#[serde(tag = "T")]
|
#[serde(tag = "T")]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
#[serde(rename = "success")]
|
#[serde(rename = "success")]
|
@@ -5,7 +5,7 @@ use crate::{
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
|
#[derive(Deserialize, Debug, PartialEq, Eq)]
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
#[serde(with = "time::serde::rfc3339")]
|
#[serde(with = "time::serde::rfc3339")]
|
@@ -1,7 +1,7 @@
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
|
#[derive(Deserialize, Debug, PartialEq, Eq)]
|
||||||
pub enum Status {
|
pub enum Status {
|
||||||
#[serde(rename = "2")]
|
#[serde(rename = "2")]
|
||||||
#[serde(alias = "H")]
|
#[serde(alias = "H")]
|
||||||
@@ -36,7 +36,7 @@ pub enum Status {
|
|||||||
VolatilityTradingPause,
|
VolatilityTradingPause,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
|
#[derive(Deserialize, Debug, PartialEq, Eq)]
|
||||||
#[serde(tag = "rc", content = "rm")]
|
#[serde(tag = "rc", content = "rm")]
|
||||||
pub enum Reason {
|
pub enum Reason {
|
||||||
#[serde(rename = "D")]
|
#[serde(rename = "D")]
|
||||||
@@ -125,7 +125,7 @@ pub enum Reason {
|
|||||||
MarketWideCircuitBreakerResumption,
|
MarketWideCircuitBreakerResumption,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
|
#[derive(Deserialize, Debug, PartialEq, Eq)]
|
||||||
pub enum Tape {
|
pub enum Tape {
|
||||||
A,
|
A,
|
||||||
B,
|
B,
|
||||||
@@ -133,7 +133,7 @@ pub enum Tape {
|
|||||||
O,
|
O,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
|
#[derive(Deserialize, Debug, PartialEq, Eq)]
|
||||||
#[allow(clippy::struct_field_names)]
|
#[allow(clippy::struct_field_names)]
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
#[serde(rename = "t")]
|
#[serde(rename = "t")]
|
@@ -1,6 +1,6 @@
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
|
#[derive(Deserialize, Debug, PartialEq, Eq)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
@@ -15,6 +15,7 @@ pub enum Message {
|
|||||||
lulds: Option<Vec<String>>,
|
lulds: Option<Vec<String>>,
|
||||||
cancel_errors: Option<Vec<String>>,
|
cancel_errors: Option<Vec<String>>,
|
||||||
},
|
},
|
||||||
#[serde(rename_all = "camelCase")]
|
News {
|
||||||
News { news: Vec<String> },
|
news: Vec<String>,
|
||||||
|
},
|
||||||
}
|
}
|
@@ -1,8 +1,8 @@
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize)]
|
#[derive(Deserialize, Debug, PartialEq, Eq)]
|
||||||
#[serde(tag = "msg")]
|
#[serde(tag = "msg")]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
Connected,
|
Connected,
|
||||||
Authenticated,
|
Authenticated,
|
@@ -1,3 +1,6 @@
|
|||||||
|
pub mod incoming;
|
||||||
|
pub mod outgoing;
|
||||||
|
|
||||||
use crate::{config::Config, types::alpaca::websocket};
|
use crate::{config::Config, types::alpaca::websocket};
|
||||||
use core::panic;
|
use core::panic;
|
||||||
use futures_util::{
|
use futures_util::{
|
||||||
@@ -16,18 +19,18 @@ pub async fn authenticate(
|
|||||||
) {
|
) {
|
||||||
match stream.next().await.unwrap().unwrap() {
|
match stream.next().await.unwrap().unwrap() {
|
||||||
Message::Text(data)
|
Message::Text(data)
|
||||||
if from_str::<Vec<websocket::incoming::Message>>(&data)
|
if from_str::<Vec<websocket::data::incoming::Message>>(&data)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.first()
|
.first()
|
||||||
== Some(&websocket::incoming::Message::Success(
|
== Some(&websocket::data::incoming::Message::Success(
|
||||||
websocket::incoming::success::Message::Connected,
|
websocket::data::incoming::success::Message::Connected,
|
||||||
)) => {}
|
)) => {}
|
||||||
_ => panic!("Failed to connect to Alpaca websocket."),
|
_ => panic!("Failed to connect to Alpaca websocket."),
|
||||||
}
|
}
|
||||||
|
|
||||||
sink.send(Message::Text(
|
sink.send(Message::Text(
|
||||||
to_string(&websocket::outgoing::Message::Auth(
|
to_string(&websocket::data::outgoing::Message::Auth(
|
||||||
websocket::outgoing::auth::Message {
|
websocket::auth::Message {
|
||||||
key: config.alpaca_api_key.clone(),
|
key: config.alpaca_api_key.clone(),
|
||||||
secret: config.alpaca_api_secret.clone(),
|
secret: config.alpaca_api_secret.clone(),
|
||||||
},
|
},
|
||||||
@@ -39,11 +42,11 @@ pub async fn authenticate(
|
|||||||
|
|
||||||
match stream.next().await.unwrap().unwrap() {
|
match stream.next().await.unwrap().unwrap() {
|
||||||
Message::Text(data)
|
Message::Text(data)
|
||||||
if from_str::<Vec<websocket::incoming::Message>>(&data)
|
if from_str::<Vec<websocket::data::incoming::Message>>(&data)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.first()
|
.first()
|
||||||
== Some(&websocket::incoming::Message::Success(
|
== Some(&websocket::data::incoming::Message::Success(
|
||||||
websocket::incoming::success::Message::Authenticated,
|
websocket::data::incoming::success::Message::Authenticated,
|
||||||
)) => {}
|
)) => {}
|
||||||
_ => panic!("Failed to authenticate with Alpaca websocket."),
|
_ => panic!("Failed to authenticate with Alpaca websocket."),
|
||||||
};
|
};
|
@@ -1,11 +1,11 @@
|
|||||||
pub mod auth;
|
|
||||||
pub mod subscribe;
|
pub mod subscribe;
|
||||||
|
|
||||||
|
use crate::types::alpaca::websocket::auth;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
#[serde(tag = "action")]
|
#[serde(tag = "action")]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
Auth(auth::Message),
|
Auth(auth::Message),
|
||||||
Subscribe(subscribe::Message),
|
Subscribe(subscribe::Message),
|
@@ -21,7 +21,6 @@ pub enum Market {
|
|||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
Market(Market),
|
Market(Market),
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
News {
|
News {
|
||||||
news: Vec<String>,
|
news: Vec<String>,
|
||||||
},
|
},
|
@@ -1,2 +1,3 @@
|
|||||||
pub mod incoming;
|
pub mod auth;
|
||||||
pub mod outgoing;
|
pub mod data;
|
||||||
|
pub mod trading;
|
||||||
|
22
src/types/alpaca/websocket/trading/incoming/auth.rs
Normal file
22
src/types/alpaca/websocket/trading/incoming/auth.rs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, PartialEq, Eq)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum Status {
|
||||||
|
Authorized,
|
||||||
|
Unauthorized,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Action {
|
||||||
|
#[serde(rename = "authenticate")]
|
||||||
|
Auth,
|
||||||
|
#[serde(rename = "listen")]
|
||||||
|
Subscribe,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, PartialEq, Eq)]
|
||||||
|
pub struct Message {
|
||||||
|
pub status: Status,
|
||||||
|
pub action: Action,
|
||||||
|
}
|
16
src/types/alpaca/websocket/trading/incoming/mod.rs
Normal file
16
src/types/alpaca/websocket/trading/incoming/mod.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
pub mod auth;
|
||||||
|
pub mod order;
|
||||||
|
pub mod subscription;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, PartialEq)]
|
||||||
|
#[serde(tag = "stream", content = "data")]
|
||||||
|
pub enum Message {
|
||||||
|
#[serde(rename = "authorization")]
|
||||||
|
Auth(auth::Message),
|
||||||
|
#[serde(rename = "listening")]
|
||||||
|
Subscription(subscription::Message),
|
||||||
|
#[serde(rename = "trade_updates")]
|
||||||
|
Order(order::Message),
|
||||||
|
}
|
86
src/types/alpaca/websocket/trading/incoming/order.rs
Normal file
86
src/types/alpaca/websocket/trading/incoming/order.rs
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
use crate::types::alpaca::shared;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use time::OffsetDateTime;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
pub use shared::order::Order;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, PartialEq)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
#[serde(tag = "event")]
|
||||||
|
pub enum Message {
|
||||||
|
New {
|
||||||
|
execution_id: Uuid,
|
||||||
|
order: Order,
|
||||||
|
},
|
||||||
|
Fill {
|
||||||
|
execution_id: Uuid,
|
||||||
|
order: Order,
|
||||||
|
timestamp: OffsetDateTime,
|
||||||
|
position_qty: f64,
|
||||||
|
price: f64,
|
||||||
|
},
|
||||||
|
PartialFill {
|
||||||
|
execution_id: Uuid,
|
||||||
|
order: Order,
|
||||||
|
timestamp: OffsetDateTime,
|
||||||
|
position_qty: f64,
|
||||||
|
price: f64,
|
||||||
|
},
|
||||||
|
Canceled {
|
||||||
|
execution_id: Uuid,
|
||||||
|
order: Order,
|
||||||
|
timestamp: OffsetDateTime,
|
||||||
|
},
|
||||||
|
Expired {
|
||||||
|
execution_id: Uuid,
|
||||||
|
order: Order,
|
||||||
|
timestamp: OffsetDateTime,
|
||||||
|
},
|
||||||
|
DoneForDay {
|
||||||
|
execution_id: Uuid,
|
||||||
|
order: Order,
|
||||||
|
},
|
||||||
|
Replaced {
|
||||||
|
execution_id: Uuid,
|
||||||
|
order: Order,
|
||||||
|
timestamp: OffsetDateTime,
|
||||||
|
},
|
||||||
|
Rejected {
|
||||||
|
execution_id: Uuid,
|
||||||
|
order: Order,
|
||||||
|
timestamp: OffsetDateTime,
|
||||||
|
},
|
||||||
|
PendingNew {
|
||||||
|
execution_id: Uuid,
|
||||||
|
order: Order,
|
||||||
|
},
|
||||||
|
Stopped {
|
||||||
|
execution_id: Uuid,
|
||||||
|
order: Order,
|
||||||
|
},
|
||||||
|
PendingCancel {
|
||||||
|
execution_id: Uuid,
|
||||||
|
order: Order,
|
||||||
|
},
|
||||||
|
PendingReplace {
|
||||||
|
execution_id: Uuid,
|
||||||
|
order: Order,
|
||||||
|
},
|
||||||
|
Calculated {
|
||||||
|
execution_id: Uuid,
|
||||||
|
order: Order,
|
||||||
|
},
|
||||||
|
Suspended {
|
||||||
|
execution_id: Uuid,
|
||||||
|
order: Order,
|
||||||
|
},
|
||||||
|
OrderReplaceRejected {
|
||||||
|
execution_id: Uuid,
|
||||||
|
order: Order,
|
||||||
|
},
|
||||||
|
OrderCancelRejected {
|
||||||
|
execution_id: Uuid,
|
||||||
|
order: Order,
|
||||||
|
},
|
||||||
|
}
|
@@ -0,0 +1,6 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, PartialEq, Eq)]
|
||||||
|
pub struct Message {
|
||||||
|
pub streams: Vec<String>,
|
||||||
|
}
|
51
src/types/alpaca/websocket/trading/mod.rs
Normal file
51
src/types/alpaca/websocket/trading/mod.rs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
pub mod incoming;
|
||||||
|
pub mod outgoing;
|
||||||
|
|
||||||
|
use crate::{config::Config, types::alpaca::websocket};
|
||||||
|
use core::panic;
|
||||||
|
use futures_util::{
|
||||||
|
stream::{SplitSink, SplitStream},
|
||||||
|
SinkExt, StreamExt,
|
||||||
|
};
|
||||||
|
use serde_json::{from_str, to_string};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::net::TcpStream;
|
||||||
|
use tokio_tungstenite::{tungstenite::Message, MaybeTlsStream, WebSocketStream};
|
||||||
|
|
||||||
|
pub async fn authenticate(
|
||||||
|
config: &Arc<Config>,
|
||||||
|
sink: &mut SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, Message>,
|
||||||
|
stream: &mut SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>,
|
||||||
|
) {
|
||||||
|
sink.send(Message::Text(
|
||||||
|
to_string(&websocket::trading::outgoing::Message::Auth(
|
||||||
|
websocket::auth::Message {
|
||||||
|
key: config.alpaca_api_key.clone(),
|
||||||
|
secret: config.alpaca_api_secret.clone(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.unwrap(),
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match stream.next().await.unwrap().unwrap() {
|
||||||
|
Message::Binary(data) => {
|
||||||
|
let data = String::from_utf8(data).unwrap();
|
||||||
|
|
||||||
|
if from_str::<Vec<websocket::trading::incoming::Message>>(&data)
|
||||||
|
.unwrap()
|
||||||
|
.first()
|
||||||
|
!= Some(&websocket::trading::incoming::Message::Auth(
|
||||||
|
websocket::trading::incoming::auth::Message {
|
||||||
|
status: websocket::trading::incoming::auth::Status::Authorized,
|
||||||
|
action: websocket::trading::incoming::auth::Action::Auth,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
{
|
||||||
|
panic!("Failed to authenticate with Alpaca websocket.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => panic!("Failed to authenticate with Alpaca websocket."),
|
||||||
|
};
|
||||||
|
}
|
15
src/types/alpaca/websocket/trading/outgoing/mod.rs
Normal file
15
src/types/alpaca/websocket/trading/outgoing/mod.rs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
pub mod subscribe;
|
||||||
|
|
||||||
|
use crate::types::alpaca::websocket::auth;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(tag = "action")]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum Message {
|
||||||
|
Auth(auth::Message),
|
||||||
|
#[serde(rename = "listen")]
|
||||||
|
Subscribe {
|
||||||
|
data: subscribe::Message,
|
||||||
|
},
|
||||||
|
}
|
6
src/types/alpaca/websocket/trading/outgoing/subscribe.rs
Normal file
6
src/types/alpaca/websocket/trading/outgoing/subscribe.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct Message {
|
||||||
|
streams: Vec<String>,
|
||||||
|
}
|
@@ -3,8 +3,10 @@ pub mod asset;
|
|||||||
pub mod backfill;
|
pub mod backfill;
|
||||||
pub mod bar;
|
pub mod bar;
|
||||||
pub mod news;
|
pub mod news;
|
||||||
|
pub mod order;
|
||||||
|
|
||||||
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;
|
||||||
|
107
src/types/order.rs
Normal file
107
src/types/order.rs
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
use clickhouse::Row;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||||
|
use time::OffsetDateTime;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
|
||||||
|
#[repr(i8)]
|
||||||
|
pub enum Class {
|
||||||
|
Simple = 1,
|
||||||
|
Bracket = 2,
|
||||||
|
Oco = 3,
|
||||||
|
Oto = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
|
||||||
|
#[repr(i8)]
|
||||||
|
pub enum Type {
|
||||||
|
Market = 1,
|
||||||
|
Limit = 2,
|
||||||
|
Stop = 3,
|
||||||
|
StopLimit = 4,
|
||||||
|
TrailingStop = 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
|
||||||
|
#[repr(i8)]
|
||||||
|
pub enum Side {
|
||||||
|
Buy = 1,
|
||||||
|
Sell = -1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
|
||||||
|
#[repr(i8)]
|
||||||
|
pub enum TimeInForce {
|
||||||
|
Day = 1,
|
||||||
|
Gtc = 2,
|
||||||
|
Opg = 3,
|
||||||
|
Cls = 4,
|
||||||
|
Ioc = 5,
|
||||||
|
Fok = 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
|
||||||
|
#[repr(i8)]
|
||||||
|
pub enum Status {
|
||||||
|
New = 1,
|
||||||
|
PartiallyFilled = 2,
|
||||||
|
Filled = 3,
|
||||||
|
DoneForDay = 4,
|
||||||
|
Canceled = 5,
|
||||||
|
Expired = 6,
|
||||||
|
Replaced = 7,
|
||||||
|
PendingCancel = 8,
|
||||||
|
PendingReplace = 9,
|
||||||
|
Accepted = 10,
|
||||||
|
PendingNew = 11,
|
||||||
|
AcceptedForBidding = 12,
|
||||||
|
Stopped = 13,
|
||||||
|
Rejected = 14,
|
||||||
|
Suspended = 15,
|
||||||
|
Calculated = 16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Row)]
|
||||||
|
#[allow(clippy::struct_field_names)]
|
||||||
|
pub struct Order {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub client_order_id: Uuid,
|
||||||
|
#[serde(with = "clickhouse::serde::time::datetime")]
|
||||||
|
pub time_submitted: OffsetDateTime,
|
||||||
|
#[serde(with = "clickhouse::serde::time::datetime")]
|
||||||
|
pub time_created: OffsetDateTime,
|
||||||
|
#[serde(with = "clickhouse::serde::time::datetime")]
|
||||||
|
pub time_updated: OffsetDateTime,
|
||||||
|
#[serde(with = "clickhouse::serde::time::datetime")]
|
||||||
|
pub time_filled: OffsetDateTime,
|
||||||
|
#[serde(with = "clickhouse::serde::time::datetime")]
|
||||||
|
pub time_expired: OffsetDateTime,
|
||||||
|
#[serde(with = "clickhouse::serde::time::datetime")]
|
||||||
|
pub time_cancel_requested: OffsetDateTime,
|
||||||
|
#[serde(with = "clickhouse::serde::time::datetime")]
|
||||||
|
pub time_canceled: OffsetDateTime,
|
||||||
|
#[serde(with = "clickhouse::serde::time::datetime")]
|
||||||
|
pub time_failed: OffsetDateTime,
|
||||||
|
#[serde(with = "clickhouse::serde::time::datetime")]
|
||||||
|
pub time_replaced: OffsetDateTime,
|
||||||
|
pub replaced_by: Uuid,
|
||||||
|
pub replaces: Uuid,
|
||||||
|
pub symbol: String,
|
||||||
|
pub order_class: Class,
|
||||||
|
pub order_type: Type,
|
||||||
|
pub side: Side,
|
||||||
|
pub time_in_force: TimeInForce,
|
||||||
|
pub extended_hours: bool,
|
||||||
|
pub notional: f64,
|
||||||
|
pub qty: f64,
|
||||||
|
pub filled_qty: f64,
|
||||||
|
pub filled_avg_price: f64,
|
||||||
|
pub status: Status,
|
||||||
|
pub limit_price: f64,
|
||||||
|
pub stop_price: f64,
|
||||||
|
pub trail_percent: f64,
|
||||||
|
pub trail_price: f64,
|
||||||
|
pub hwm: f64,
|
||||||
|
pub legs: Vec<Uuid>,
|
||||||
|
}
|
@@ -3,9 +3,7 @@ pub mod cleanup;
|
|||||||
pub mod macros;
|
pub mod macros;
|
||||||
pub mod news;
|
pub mod news;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
pub mod websocket;
|
|
||||||
|
|
||||||
pub use cleanup::cleanup;
|
pub use cleanup::cleanup;
|
||||||
pub use news::{add_slash_to_pair, normalize_news_content, remove_slash_from_pair};
|
pub use news::{add_slash_to_pair, normalize_news_content, remove_slash_from_pair};
|
||||||
pub use time::{duration_until, last_minute, FIFTEEN_MINUTES, ONE_MINUTE, ONE_SECOND};
|
pub use time::{duration_until, last_minute, FIFTEEN_MINUTES, ONE_MINUTE, ONE_SECOND};
|
||||||
pub use websocket::authenticate;
|
|
||||||
|
@@ -64,3 +64,57 @@ CREATE TABLE IF NOT EXISTS qrust.backfills_news (
|
|||||||
)
|
)
|
||||||
ENGINE = ReplacingMergeTree()
|
ENGINE = ReplacingMergeTree()
|
||||||
PRIMARY KEY symbol;
|
PRIMARY KEY symbol;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS qrust.orders (
|
||||||
|
id UUID,
|
||||||
|
client_order_id UUID,
|
||||||
|
time_submitted DateTime,
|
||||||
|
time_created DateTime,
|
||||||
|
time_updated DateTime,
|
||||||
|
time_filled DateTime,
|
||||||
|
time_expired DateTime,
|
||||||
|
time_cancel_requested DateTime,
|
||||||
|
time_canceled DateTime,
|
||||||
|
time_failed DateTime,
|
||||||
|
time_replaced DateTime,
|
||||||
|
replaced_by UUID,
|
||||||
|
replaces UUID,
|
||||||
|
symbol LowCardinality(String),
|
||||||
|
order_class Enum('simple' = 1, 'bracket' = 2, 'oco' = 3, 'oto' = 4),
|
||||||
|
order_type Enum('market' = 1, 'limit' = 2, 'stop' = 3, 'stop_limit' = 4, 'trailing_stop' = 5),
|
||||||
|
side Enum('buy' = 1, 'sell' = -1),
|
||||||
|
time_in_force Enum('day' = 1, 'gtc' = 2, 'opg' = 3, 'cls' = 4, 'ioc' = 5, 'fok' = 6),
|
||||||
|
extended_hours Boolean,
|
||||||
|
notional Float64,
|
||||||
|
qty Float64,
|
||||||
|
filled_qty Float64,
|
||||||
|
filled_avg_price Float64,
|
||||||
|
status Enum(
|
||||||
|
'new' = 1,
|
||||||
|
'partially_filled' = 2,
|
||||||
|
'filled' = 3,
|
||||||
|
'done_for_day' = 4,
|
||||||
|
'canceled' = 5,
|
||||||
|
'expired' = 6,
|
||||||
|
'replaced' = 7,
|
||||||
|
'pending_cancel' = 8,
|
||||||
|
'pending_replace' = 9,
|
||||||
|
'accepted' = 10,
|
||||||
|
'pending_new' = 11,
|
||||||
|
'accepted_for_bidding' = 12,
|
||||||
|
'stopped' = 13,
|
||||||
|
'rejected' = 14,
|
||||||
|
'suspended' = 15,
|
||||||
|
'calculated' = 16
|
||||||
|
),
|
||||||
|
limit_price Float64,
|
||||||
|
stop_price Float64,
|
||||||
|
trail_percent Float64,
|
||||||
|
trail_price Float64,
|
||||||
|
hwm Float64,
|
||||||
|
legs Array(UUID)
|
||||||
|
)
|
||||||
|
ENGINE = ReplacingMergeTree()
|
||||||
|
PARTITION BY toYYYYMM(time_submitted)
|
||||||
|
PRIMARY KEY id;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user