futu_gateway_core/bridge/
mod.rs1use std::sync::Arc;
26
27use arc_swap::ArcSwap;
28use futu_backend::auth;
29use futu_backend::conn::BackendConn;
30use futu_core::error::Result;
31
32mod account;
37mod auth_runtime;
38mod background_runtime;
39mod broker_conn;
40mod broker_runtime;
41pub mod cache_bundle;
42mod channel_runtime_shadow;
43mod client_identity_runtime;
44mod client_sig_reactive;
45mod client_sig_timer;
46pub mod clock;
47mod dispatcher;
48mod gateway_parts;
49mod init_phase1;
51mod init_phase2;
52mod init_phase3;
53mod init_phase4_reconnect;
54mod market_startup;
55mod mkt_id_refresh;
56mod platform_pool;
57mod push_basic_qot;
58mod push_broker_queue;
59mod push_builder;
60mod push_callback;
61mod push_crypto_notify;
62mod push_dedup;
63pub mod push_delivery;
64mod push_health;
65mod push_kline;
66mod push_msg_center;
67mod push_orderbook;
68pub mod push_parser;
69mod push_rt;
70mod push_runtime;
71mod push_ticker;
72mod push_trade_notify;
73mod qot_login_health;
74mod qot_right_push;
75mod qot_right_refresh;
76mod qot_runtime;
77mod reference_data_download;
78mod reference_runtime;
79mod reload;
80mod stock_list_sync;
81mod subscription_runtime;
82mod trade_dispatch;
83mod trade_write_runtime;
84mod types;
85pub mod utils;
86pub use types::*;
87
88#[cfg(test)]
89mod runtime_construction_tests;
90
91pub fn load_backend(
98 shared: &SharedBackend,
99) -> Option<std::sync::Arc<futu_backend::conn::BackendConn>> {
100 let guard = shared.load();
101 guard.as_ref().clone()
102}
103
104pub use auth_runtime::AuthRuntime;
106pub use background_runtime::{BackgroundRuntime, BackgroundTaskTracker};
107use broker_conn::{
108 BrokerChannelSetup, broker_tcp_login, establish_single_broker, keep_subscribe_quotes,
109 resubscribe_quotes, sync_current_quote_desired_set,
110};
111pub use broker_runtime::{BROKER_RECONNECT_MAX_CONSECUTIVE_FAILURES, BrokerRuntime};
112pub use cache_bundle::CacheBundle;
113pub use client_identity_runtime::ClientIdentityRuntime;
114pub use clock::ServerClockRuntime;
115use gateway_parts::GatewayBridgeParts;
116use push_builder::{build_order_fill_update_push, build_order_update_push, resolve_push_header};
117pub use push_health::{
118 PushHealth, PushHealthSnapshot, QotLoginHealth, QotLoginHealthSnapshot, SharedPushHealth,
119 SharedQotLoginHealth,
120};
121pub use push_runtime::PushRuntime;
122pub use qot_runtime::QotRuntime;
123pub use reference_runtime::ReferenceRuntime;
124pub use subscription_runtime::SubscriptionRuntime;
125use trade_dispatch::{dispatch_trade_data_queries, retry_with_exp_backoff};
126pub use trade_write_runtime::TradeWriteRuntime;
127use utils::split_host_port;
128pub use utils::{
131 derive_exch_type_with_fallback, listing_date_to_str, market_code_to_exch_type_with_security,
132 market_code_to_qot_market_with_security,
133};
134
135pub type SharedBackend = Arc<ArcSwap<Option<Arc<BackendConn>>>>;
139
140pub struct GatewayBridge {
142 caches: CacheBundle,
145 push_runtime: PushRuntime,
147 broker_runtime: BrokerRuntime,
149 qot_runtime: QotRuntime,
151 subscription_runtime: SubscriptionRuntime,
153 auth_runtime: AuthRuntime,
155 background_runtime: BackgroundRuntime,
158 server_clock: ServerClockRuntime,
161 reference_runtime: ReferenceRuntime,
163 client_identity: ClientIdentityRuntime,
165 trade_write_runtime: TradeWriteRuntime,
167}
168
169impl GatewayBridge {
170 pub fn new() -> Self {
171 let parts = GatewayBridgeParts::new_default();
172 Self {
173 caches: parts.caches,
174 push_runtime: parts.push_runtime,
175 broker_runtime: parts.broker_runtime,
176 qot_runtime: parts.qot_runtime,
177 subscription_runtime: parts.subscription_runtime,
178 auth_runtime: parts.auth_runtime,
179 background_runtime: parts.background_runtime,
180 server_clock: parts.server_clock,
181 reference_runtime: parts.reference_runtime,
182 client_identity: parts.client_identity,
183 trade_write_runtime: parts.trade_write_runtime,
184 }
185 }
186
187 pub fn caches(&self) -> &CacheBundle {
190 &self.caches
191 }
192
193 pub fn trade_write_runtime(&self) -> &TradeWriteRuntime {
195 &self.trade_write_runtime
196 }
197
198 pub fn push_runtime(&self) -> &PushRuntime {
201 &self.push_runtime
202 }
203
204 pub fn auth_runtime(&self) -> &AuthRuntime {
206 &self.auth_runtime
207 }
208
209 pub fn subscription_runtime(&self) -> &SubscriptionRuntime {
211 &self.subscription_runtime
212 }
213
214 pub fn broker_runtime(&self) -> &BrokerRuntime {
216 &self.broker_runtime
217 }
218
219 pub(super) fn auth_runtime_mut(&mut self) -> &mut AuthRuntime {
220 &mut self.auth_runtime
221 }
222
223 pub fn client_identity(&self) -> &ClientIdentityRuntime {
225 &self.client_identity
226 }
227
228 pub(super) fn client_identity_mut(&mut self) -> &mut ClientIdentityRuntime {
229 &mut self.client_identity
230 }
231
232 pub fn reference_runtime(&self) -> &ReferenceRuntime {
234 &self.reference_runtime
235 }
236
237 pub fn qot_runtime(&self) -> &QotRuntime {
239 &self.qot_runtime
240 }
241
242 pub fn server_clock(&self) -> &ServerClockRuntime {
245 &self.server_clock
246 }
247
248 pub fn background_runtime(&self) -> &BackgroundRuntime {
251 &self.background_runtime
252 }
253
254 pub fn all_broker_backends(&self) -> Vec<(u32, Arc<BackendConn>)> {
259 self.broker_runtime().all_broker_backends()
260 }
261
262 pub fn single_crypto_account_broker(&self) -> Option<u32> {
275 futu_backend::crypto_trade::single_crypto_account_broker(&self.caches().trd_cache)
276 }
277
278 pub async fn initialize(
291 &mut self,
292 config: &GatewayConfig,
293 verify_cb: Option<auth::VerifyCodeCallback>,
294 ) -> Result<tokio::sync::mpsc::Receiver<PushEvent>> {
295 let proactive_env = std::env::var("FUTU_CLIENT_SIG_PROACTIVE_REFRESH").ok();
296 let reactive_env = std::env::var("FUTU_CLIENT_SIG_REACTIVE_REFRESH").ok();
297 self.auth_runtime_mut()
298 .set_client_sig_refresh_status(ClientSigRefreshStatus {
299 client_sig_proactive_refresh_enabled:
300 client_sig_timer::effective_proactive_refresh_enabled(
301 config.client_sig_proactive_refresh,
302 proactive_env.as_deref(),
303 ),
304 client_sig_reactive_refresh_enabled:
305 client_sig_reactive::effective_reactive_refresh_enabled(
306 config.client_sig_reactive_refresh,
307 reactive_env.as_deref(),
308 ),
309 });
310
311 let phase1 = match self.init_phase1_http_auth(config, verify_cb).await? {
313 init_phase1::Phase1Outcome::SetupOnlyDone(rx) => return Ok(rx),
314 init_phase1::Phase1Outcome::Continue(p1) => p1,
315 };
316
317 let phase2 = self
319 .init_phase2_tcp_connect(config, phase1.auth_result)
320 .await?;
321 let init_phase2::Phase2Result {
322 backend,
323 login_result,
324 push_rx,
325 push_cb,
326 auth_result_for_reconnect,
327 } = phase2;
328
329 self.init_phase3_workers(&backend).await?;
331
332 let heartbeat_interval =
334 std::time::Duration::from_secs(login_result.keep_alive_interval as u64);
335 tracing::info!(interval_secs = ?heartbeat_interval, "starting backend heartbeat");
336 let heartbeat_handle =
337 futu_backend::heartbeat::start_heartbeat(Arc::clone(&backend), heartbeat_interval);
338
339 init_phase4_reconnect::spawn_heartbeat_and_reconnect_monitor(
340 init_phase4_reconnect::ReconnectMonitorDeps {
341 config: config.clone(),
342 auth: auth_result_for_reconnect,
343 caches: self.caches().clone(),
344 broker_runtime: self.broker_runtime().clone(),
345 subscriptions: self.subscription_runtime().manager(),
346 push_runtime: self.push_runtime().clone(),
347 main_broker_cache: self.qot_runtime().main_broker_cache(),
348 push_cb,
349 commconfig: self.auth_runtime().commconfig(),
350 auth_refresher: self.auth_runtime().auth_refresher(),
351 account: config.account.clone(),
352 initial_heartbeat: heartbeat_handle,
353 background_runtime: self.background_runtime().clone(),
354 },
355 );
356
357 tracing::info!("gateway initialization complete");
366 Ok(push_rx)
367 }
368 }
374
375impl Default for GatewayBridge {
376 fn default() -> Self {
377 Self::new()
378 }
379}