diff --git a/src/hyperliquid.rs b/src/hyperliquid.rs index 21f458a..d9d5629 100644 --- a/src/hyperliquid.rs +++ b/src/hyperliquid.rs @@ -47,7 +47,7 @@ impl HyperliquidMonitor { loop { info!("Connecting to Hyperliquid InfoClient..."); - let mut info_client = match InfoClient::new(None, Some(base_url)).await { + let mut info_client = match InfoClient::with_reconnect(None, Some(base_url)).await { Ok(client) => client, Err(e) => { error!( @@ -174,6 +174,18 @@ impl HyperliquidMonitor { Some(Message::Pong) => { debug!("Received Pong from Hyperliquid WS"); } + Some(Message::NoData) => { + // Server idle-disconnect or dropped connection. Because the + // client is created with InfoClient::with_reconnect, the SDK + // auto-reconnects (~1s) and resubscribes UserEvents on the same + // channel, so we keep reading instead of tearing down here. + // NOTE: a short application-level recv() timeout was intentionally + // NOT added: the SDK does not forward Pong frames to the + // subscription channel, so a quiet market (no fills) is + // indistinguishable from a dead connection and any finite timeout + // would cause spurious reconnects and missed-fill blind windows. + warn!("Hyperliquid WS disconnected; SDK is auto-reconnecting..."); + } Some(other) => { debug!(msg = ?other, "Received other message from Hyperliquid WS"); } diff --git a/src/notion.rs b/src/notion.rs index 9eadbc3..367b182 100644 --- a/src/notion.rs +++ b/src/notion.rs @@ -231,7 +231,10 @@ impl NotionWriter { format!("BINANCE:{}USDC.P", coin.to_uppercase()) }; let tradesnap_url = url.trim_end_matches('/'); - let http_client = reqwest::Client::new(); + let http_client = reqwest::Client::builder() + .timeout(std::time::Duration::from_secs(15)) + .build() + .unwrap_or_else(|_| reqwest::Client::new()); let mut children = Vec::new(); let mut timeframes = Vec::new();