Skip to main content

sandbox_quant/app/
bootstrap.rs

1use std::env;
2use std::sync::Arc;
3
4use crate::error::exchange_error::ExchangeError;
5use crate::exchange::binance::auth::BinanceAuth;
6use crate::exchange::binance::client::{BinanceExchange, BinanceHttpTransport, BinanceTransport};
7use crate::exchange::binance::demo::BinanceDemoHttpTransport;
8use crate::exchange::facade::ExchangeFacade;
9use crate::execution::service::ExecutionService;
10use crate::market_data::price_store::PriceStore;
11use crate::market_data::service::MarketDataService;
12use crate::portfolio::store::PortfolioStateStore;
13use crate::portfolio::sync::PortfolioSyncService;
14use crate::storage::event_log::EventLog;
15
16#[derive(Debug)]
17pub struct AppBootstrap<E: ExchangeFacade> {
18    pub exchange: E,
19    pub portfolio_store: PortfolioStateStore,
20    pub price_store: PriceStore,
21    pub event_log: EventLog,
22    pub execution: ExecutionService,
23    pub portfolio_sync: PortfolioSyncService,
24    pub market_data: MarketDataService,
25}
26
27impl<E: ExchangeFacade> AppBootstrap<E> {
28    pub fn new(exchange: E, portfolio_store: PortfolioStateStore) -> Self {
29        Self {
30            exchange,
31            portfolio_store,
32            price_store: PriceStore::default(),
33            event_log: EventLog::default(),
34            execution: ExecutionService::default(),
35            portfolio_sync: PortfolioSyncService,
36            market_data: MarketDataService,
37        }
38    }
39}
40
41impl AppBootstrap<BinanceExchange> {
42    /// Builds the real Binance-backed app bootstrap from environment variables.
43    ///
44    /// Required:
45    /// - `BINANCE_API_KEY`
46    /// - `BINANCE_SECRET_KEY`
47    ///
48    /// Optional:
49    /// - `BINANCE_SPOT_BASE_URL`
50    /// - `BINANCE_FUTURES_BASE_URL`
51    /// - `BINANCE_MODE`
52    pub fn from_env(portfolio_store: PortfolioStateStore) -> Result<Self, ExchangeError> {
53        let config = BinanceEnvConfig::from_env()?;
54        Ok(Self::new(BinanceExchange::new(config.build_transport()), portfolio_store))
55    }
56
57    pub fn switch_mode(&mut self, mode: BinanceMode) -> Result<(), ExchangeError> {
58        let mut config = BinanceEnvConfig::from_env()?;
59        config.mode = mode;
60        config.spot_base_url = None;
61        config.futures_base_url = None;
62        self.exchange = BinanceExchange::new(config.build_transport());
63        Ok(())
64    }
65}
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub enum BinanceMode {
69    Real,
70    Demo,
71}
72
73#[derive(Debug, Clone, PartialEq, Eq)]
74pub struct BinanceEnvConfig {
75    pub api_key: String,
76    pub secret_key: String,
77    pub mode: BinanceMode,
78    pub spot_base_url: Option<String>,
79    pub futures_base_url: Option<String>,
80}
81
82impl BinanceEnvConfig {
83    pub fn from_env() -> Result<Self, ExchangeError> {
84        let api_key = env::var("BINANCE_API_KEY")
85            .map_err(|_| ExchangeError::MissingConfiguration("BINANCE_API_KEY"))?;
86        let secret_key = env::var("BINANCE_SECRET_KEY")
87            .map_err(|_| ExchangeError::MissingConfiguration("BINANCE_SECRET_KEY"))?;
88        let mode = match env::var("BINANCE_MODE")
89            .unwrap_or_else(|_| "demo".to_string())
90            .to_ascii_lowercase()
91            .as_str()
92        {
93            "demo" => BinanceMode::Demo,
94            _ => BinanceMode::Real,
95        };
96        Ok(Self {
97            api_key,
98            secret_key,
99            mode,
100            spot_base_url: env::var("BINANCE_SPOT_BASE_URL").ok(),
101            futures_base_url: env::var("BINANCE_FUTURES_BASE_URL").ok(),
102        })
103    }
104
105    pub fn build_transport(&self) -> Arc<dyn BinanceTransport> {
106        let auth = BinanceAuth::new(self.api_key.clone(), self.secret_key.clone());
107        match (&self.spot_base_url, &self.futures_base_url) {
108            (Some(spot), Some(futures)) => {
109                Arc::new(BinanceHttpTransport::with_base_urls(auth, spot.clone(), futures.clone()))
110            }
111            _ => match self.mode {
112                BinanceMode::Real => Arc::new(BinanceHttpTransport::new(auth)),
113                BinanceMode::Demo => Arc::new(BinanceDemoHttpTransport::new(auth)),
114            },
115        }
116    }
117}