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) { 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."); } info!( "qrust running on {} account with {} {}, avoid transferring funds without shutting down.", *ALPACA_API_BASE, account.currency, account.cash ); } pub async fn rehydrate_orders(config: &Arc) { 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::>(); database::orders::upsert_batch( &config.clickhouse_client, &config.clickhouse_concurrency_limiter, &orders, ) .await .unwrap(); } pub async fn rehydrate_positions(config: &Arc) { 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::>() }; 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::>(); 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 ); } }