Skip to main content

nautilus_hyperliquid/common/
consts.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 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::{sync::LazyLock, time::Duration};
17
18use nautilus_model::{enums::OrderType, 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
25pub const HYPERLIQUID_WS_URL: &str = "wss://api.hyperliquid.xyz/ws";
26pub const HYPERLIQUID_INFO_URL: &str = "https://api.hyperliquid.xyz/info";
27pub const HYPERLIQUID_EXCHANGE_URL: &str = "https://api.hyperliquid.xyz/exchange";
28
29pub const HYPERLIQUID_TESTNET_WS_URL: &str = "wss://api.hyperliquid-testnet.xyz/ws";
30pub const HYPERLIQUID_TESTNET_INFO_URL: &str = "https://api.hyperliquid-testnet.xyz/info";
31pub const HYPERLIQUID_TESTNET_EXCHANGE_URL: &str = "https://api.hyperliquid-testnet.xyz/exchange";
32
33// Builder codes fee configuration for rebates
34// - https://hyperliquid.gitbook.io/hyperliquid-docs/trading/builder-codes
35// - https://hyperliquid.gitbook.io/hyperliquid-docs/trading/fees
36// Fee is specified in tenths of a basis point (0.1 bps)
37// Note: Address MUST be lowercase for msgpack serialization to match Python SDK
38pub const NAUTILUS_BUILDER_FEE_ADDRESS: &str = "0x0c8d970c462726e014ad36f6c5a63e99db48a8e7";
39pub const NAUTILUS_BUILDER_FEE_TAKER_TENTHS_BP: u32 = 10; // 1 bp = 0.01%
40pub const NAUTILUS_BUILDER_FEE_MAKER_TENTHS_BP: u32 = 5; // 0.5 bp = 0.005%
41
42/// Hyperliquid signing chain ID (0x66eee = 421614 decimal).
43pub const HYPERLIQUID_CHAIN_ID: u64 = 421614;
44
45// Error message substrings for detecting specific rejection reasons
46pub const HYPERLIQUID_POST_ONLY_WOULD_MATCH: &str =
47    "Post only order would have immediately matched";
48pub const HYPERLIQUID_BUILDER_FEE_NOT_APPROVED: &str = "Builder fee has not been approved";
49
50/// Hyperliquid supported order types.
51///
52/// # Notes
53///
54/// - All order types support trigger prices except Market and Limit.
55/// - Conditional orders follow patterns from OKX, Bybit, and BitMEX adapters.
56/// - Stop orders (StopMarket/StopLimit) are protective stops (sl).
57/// - If Touched orders (MarketIfTouched/LimitIfTouched) are profit-taking or entry orders (tp).
58/// - Post-only orders are implemented via ALO (Add Liquidity Only) time-in-force.
59pub const HYPERLIQUID_SUPPORTED_ORDER_TYPES: &[OrderType] = &[
60    OrderType::Market,          // IOC limit order
61    OrderType::Limit,           // Standard limit with GTC/IOC/ALO
62    OrderType::StopMarket,      // Protective stop with market execution
63    OrderType::StopLimit,       // Protective stop with limit price
64    OrderType::MarketIfTouched, // Profit-taking/entry with market execution
65    OrderType::LimitIfTouched,  // Profit-taking/entry with limit price
66];
67
68/// Conditional order types that use trigger orders on Hyperliquid.
69///
70/// These order types require a trigger_price and are implemented using
71/// HyperliquidExecOrderKind::Trigger with appropriate parameters.
72pub const HYPERLIQUID_CONDITIONAL_ORDER_TYPES: &[OrderType] = &[
73    OrderType::StopMarket,
74    OrderType::StopLimit,
75    OrderType::MarketIfTouched,
76    OrderType::LimitIfTouched,
77];
78
79/// Gets WebSocket URL for the specified network.
80pub fn ws_url(is_testnet: bool) -> &'static str {
81    if is_testnet {
82        HYPERLIQUID_TESTNET_WS_URL
83    } else {
84        HYPERLIQUID_WS_URL
85    }
86}
87
88/// Gets info API URL for the specified network.
89pub fn info_url(is_testnet: bool) -> &'static str {
90    if is_testnet {
91        HYPERLIQUID_TESTNET_INFO_URL
92    } else {
93        HYPERLIQUID_INFO_URL
94    }
95}
96
97/// Gets exchange API URL for the specified network.
98pub fn exchange_url(is_testnet: bool) -> &'static str {
99    if is_testnet {
100        HYPERLIQUID_TESTNET_EXCHANGE_URL
101    } else {
102        HYPERLIQUID_EXCHANGE_URL
103    }
104}
105
106// Default configuration values
107// Server closes if no message in last 60s, so ping every 30s
108pub const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(30);
109pub const RECONNECT_BASE_BACKOFF: Duration = Duration::from_millis(250);
110pub const RECONNECT_MAX_BACKOFF: Duration = Duration::from_secs(30);
111pub const HTTP_TIMEOUT: Duration = Duration::from_secs(10);
112// Max 100 inflight WS post messages per Hyperliquid docs
113pub const INFLIGHT_MAX: usize = 100;
114pub const QUEUE_MAX: usize = 1000;
115
116#[cfg(test)]
117mod tests {
118    use rstest::rstest;
119
120    use super::*;
121
122    #[rstest]
123    fn test_ws_url() {
124        assert_eq!(ws_url(false), HYPERLIQUID_WS_URL);
125        assert_eq!(ws_url(true), HYPERLIQUID_TESTNET_WS_URL);
126    }
127
128    #[rstest]
129    fn test_info_url() {
130        assert_eq!(info_url(false), HYPERLIQUID_INFO_URL);
131        assert_eq!(info_url(true), HYPERLIQUID_TESTNET_INFO_URL);
132    }
133
134    #[rstest]
135    fn test_exchange_url() {
136        assert_eq!(exchange_url(false), HYPERLIQUID_EXCHANGE_URL);
137        assert_eq!(exchange_url(true), HYPERLIQUID_TESTNET_EXCHANGE_URL);
138    }
139
140    #[rstest]
141    fn test_constants_values() {
142        assert_eq!(HEARTBEAT_INTERVAL, Duration::from_secs(30));
143        assert_eq!(RECONNECT_BASE_BACKOFF, Duration::from_millis(250));
144        assert_eq!(RECONNECT_MAX_BACKOFF, Duration::from_secs(30));
145        assert_eq!(HTTP_TIMEOUT, Duration::from_secs(10));
146        assert_eq!(INFLIGHT_MAX, 100);
147        assert_eq!(QUEUE_MAX, 1000);
148    }
149}