141 lines
3.6 KiB
Rust
141 lines
3.6 KiB
Rust
use crate::{
|
|
config::{Config, ALPACA_API_BASE},
|
|
database,
|
|
};
|
|
use log::{info, warn};
|
|
use qrust::{alpaca, types};
|
|
use std::{collections::HashMap, sync::Arc};
|
|
use time::OffsetDateTime;
|
|
use tokio::join;
|
|
|
|
pub async fn check_account(config: &Arc<Config>) {
|
|
let account = alpaca::account::get(
|
|
&config.alpaca_client,
|
|
&config.alpaca_rate_limiter,
|
|
None,
|
|
&ALPACA_API_BASE,
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
assert!(
|
|
!(account.status != types::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_API_BASE, 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) = alpaca::orders::get(
|
|
&config.alpaca_client,
|
|
&config.alpaca_rate_limiter,
|
|
&types::alpaca::api::outgoing::order::Order {
|
|
status: Some(types::alpaca::api::outgoing::order::Status::All),
|
|
after: Some(after),
|
|
..Default::default()
|
|
},
|
|
None,
|
|
&ALPACA_API_BASE,
|
|
)
|
|
.await
|
|
.ok()
|
|
.filter(|message| !message.is_empty())
|
|
{
|
|
orders.extend(message);
|
|
after = orders.last().unwrap().submitted_at;
|
|
}
|
|
|
|
let orders = orders
|
|
.into_iter()
|
|
.flat_map(&types::alpaca::api::incoming::order::Order::normalize)
|
|
.collect::<Vec<_>>();
|
|
|
|
database::orders::upsert_batch(
|
|
&config.clickhouse_client,
|
|
&config.clickhouse_concurrency_limiter,
|
|
&orders,
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
info!("Rehydrated order data.");
|
|
}
|
|
|
|
pub async fn rehydrate_positions(config: &Arc<Config>) {
|
|
info!("Rehydrating position data.");
|
|
|
|
let positions_future = async {
|
|
alpaca::positions::get(
|
|
&config.alpaca_client,
|
|
&config.alpaca_rate_limiter,
|
|
None,
|
|
&ALPACA_API_BASE,
|
|
)
|
|
.await
|
|
.unwrap()
|
|
.into_iter()
|
|
.map(|position| (position.symbol.clone(), position))
|
|
.collect::<HashMap<_, _>>()
|
|
};
|
|
|
|
let assets_future = async {
|
|
database::assets::select(
|
|
&config.clickhouse_client,
|
|
&config.clickhouse_concurrency_limiter,
|
|
)
|
|
.await
|
|
.unwrap()
|
|
};
|
|
|
|
let (mut positions, assets) = join!(positions_future, assets_future);
|
|
|
|
let assets = assets
|
|
.into_iter()
|
|
.map(|mut asset| {
|
|
if let Some(position) = positions.remove(&asset.symbol) {
|
|
asset.qty = position.qty_available;
|
|
} else {
|
|
asset.qty = 0.0;
|
|
}
|
|
asset
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
database::assets::upsert_batch(
|
|
&config.clickhouse_client,
|
|
&config.clickhouse_concurrency_limiter,
|
|
&assets,
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
for position in positions.values() {
|
|
warn!(
|
|
"Position for unmonitored asset: {}, {} shares.",
|
|
position.symbol, position.qty
|
|
);
|
|
}
|
|
|
|
info!("Rehydrated position data.");
|
|
}
|