nautilus_hyperliquid/common/
consts.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use std::{env, sync::LazyLock, time::Duration};
17
18use nautilus_model::identifiers::Venue;
19use ustr::Ustr;
20
21pub const HYPERLIQUID: &str = "HYPERLIQUID";
22pub static HYPERLIQUID_VENUE: LazyLock<Venue> =
23    LazyLock::new(|| Venue::new(Ustr::from(HYPERLIQUID)));
24
25/// Represents the network configuration for Hyperliquid.
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum HyperliquidNetwork {
28    Mainnet,
29    Testnet,
30}
31
32impl HyperliquidNetwork {
33    /// Loads network from environment variable `HYPERLIQUID_NET`.
34    ///
35    /// Defaults to `Mainnet` if not set or invalid.
36    pub fn from_env() -> Self {
37        match env::var("HYPERLIQUID_NET")
38            .unwrap_or_else(|_| "mainnet".to_string())
39            .to_lowercase()
40            .as_str()
41        {
42            "testnet" | "test" => HyperliquidNetwork::Testnet,
43            _ => HyperliquidNetwork::Mainnet,
44        }
45    }
46}
47
48// Mainnet URLs
49pub const HYPERLIQUID_WS_URL: &str = "wss://api.hyperliquid.xyz/ws";
50pub const HYPERLIQUID_INFO_URL: &str = "https://api.hyperliquid.xyz/info";
51pub const HYPERLIQUID_EXCHANGE_URL: &str = "https://api.hyperliquid.xyz/exchange";
52
53// Testnet URLs
54pub const HYPERLIQUID_TESTNET_WS_URL: &str = "wss://api.hyperliquid-testnet.xyz/ws";
55pub const HYPERLIQUID_TESTNET_INFO_URL: &str = "https://api.hyperliquid-testnet.xyz/info";
56pub const HYPERLIQUID_TESTNET_EXCHANGE_URL: &str = "https://api.hyperliquid-testnet.xyz/exchange";
57
58/// Gets WebSocket URL for the specified network.
59pub fn ws_url(network: HyperliquidNetwork) -> &'static str {
60    match network {
61        HyperliquidNetwork::Mainnet => HYPERLIQUID_WS_URL,
62        HyperliquidNetwork::Testnet => HYPERLIQUID_TESTNET_WS_URL,
63    }
64}
65
66/// Gets info API URL for the specified network.
67pub fn info_url(network: HyperliquidNetwork) -> &'static str {
68    match network {
69        HyperliquidNetwork::Mainnet => HYPERLIQUID_INFO_URL,
70        HyperliquidNetwork::Testnet => HYPERLIQUID_TESTNET_INFO_URL,
71    }
72}
73
74/// Gets exchange API URL for the specified network.
75pub fn exchange_url(network: HyperliquidNetwork) -> &'static str {
76    match network {
77        HyperliquidNetwork::Mainnet => HYPERLIQUID_EXCHANGE_URL,
78        HyperliquidNetwork::Testnet => HYPERLIQUID_TESTNET_EXCHANGE_URL,
79    }
80}
81
82// Default configuration values
83// Server closes if no message in last 60s, so ping every 30s
84pub const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(30);
85pub const RECONNECT_BASE_BACKOFF: Duration = Duration::from_millis(250);
86pub const RECONNECT_MAX_BACKOFF: Duration = Duration::from_secs(30);
87pub const HTTP_TIMEOUT: Duration = Duration::from_secs(10);
88// Max 100 inflight WS post messages per Hyperliquid docs
89pub const INFLIGHT_MAX: usize = 100;
90pub const QUEUE_MAX: usize = 1000;
91
92////////////////////////////////////////////////////////////////////////////////
93// Tests
94////////////////////////////////////////////////////////////////////////////////
95
96#[cfg(test)]
97mod tests {
98    use rstest::rstest;
99
100    use super::*;
101
102    #[rstest]
103    fn test_network_variants() {
104        assert_eq!(HyperliquidNetwork::Mainnet, HyperliquidNetwork::Mainnet);
105        assert_eq!(HyperliquidNetwork::Testnet, HyperliquidNetwork::Testnet);
106        assert_ne!(HyperliquidNetwork::Mainnet, HyperliquidNetwork::Testnet);
107    }
108
109    #[rstest]
110    fn test_network_from_env_handles_default() {
111        let network = HyperliquidNetwork::from_env();
112
113        assert!(matches!(
114            network,
115            HyperliquidNetwork::Mainnet | HyperliquidNetwork::Testnet
116        ));
117    }
118
119    #[rstest]
120    fn test_ws_url() {
121        assert_eq!(ws_url(HyperliquidNetwork::Mainnet), HYPERLIQUID_WS_URL);
122        assert_eq!(
123            ws_url(HyperliquidNetwork::Testnet),
124            HYPERLIQUID_TESTNET_WS_URL
125        );
126    }
127
128    #[rstest]
129    fn test_info_url() {
130        assert_eq!(info_url(HyperliquidNetwork::Mainnet), HYPERLIQUID_INFO_URL);
131        assert_eq!(
132            info_url(HyperliquidNetwork::Testnet),
133            HYPERLIQUID_TESTNET_INFO_URL
134        );
135    }
136
137    #[rstest]
138    fn test_exchange_url() {
139        assert_eq!(
140            exchange_url(HyperliquidNetwork::Mainnet),
141            HYPERLIQUID_EXCHANGE_URL
142        );
143        assert_eq!(
144            exchange_url(HyperliquidNetwork::Testnet),
145            HYPERLIQUID_TESTNET_EXCHANGE_URL
146        );
147    }
148
149    #[rstest]
150    fn test_constants_values() {
151        assert_eq!(HEARTBEAT_INTERVAL, Duration::from_secs(30));
152        assert_eq!(RECONNECT_BASE_BACKOFF, Duration::from_millis(250));
153        assert_eq!(RECONNECT_MAX_BACKOFF, Duration::from_secs(30));
154        assert_eq!(HTTP_TIMEOUT, Duration::from_secs(10));
155        assert_eq!(INFLIGHT_MAX, 100);
156        assert_eq!(QUEUE_MAX, 1000);
157    }
158}