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::{
19    enums::OrderType,
20    identifiers::{ClientId, Venue},
21};
22use ustr::Ustr;
23
24use super::enums::HyperliquidEnvironment;
25
26/// Venue identifier string.
27pub const HYPERLIQUID: &str = "HYPERLIQUID";
28
29/// Static venue instance.
30pub static HYPERLIQUID_VENUE: LazyLock<Venue> =
31    LazyLock::new(|| Venue::new(Ustr::from(HYPERLIQUID)));
32
33/// Static client ID instance.
34pub static HYPERLIQUID_CLIENT_ID: LazyLock<ClientId> =
35    LazyLock::new(|| ClientId::new(Ustr::from(HYPERLIQUID)));
36
37pub const HYPERLIQUID_WS_URL: &str = "wss://api.hyperliquid.xyz/ws";
38pub const HYPERLIQUID_INFO_URL: &str = "https://api.hyperliquid.xyz/info";
39pub const HYPERLIQUID_EXCHANGE_URL: &str = "https://api.hyperliquid.xyz/exchange";
40
41pub const HYPERLIQUID_TESTNET_WS_URL: &str = "wss://api.hyperliquid-testnet.xyz/ws";
42pub const HYPERLIQUID_TESTNET_INFO_URL: &str = "https://api.hyperliquid-testnet.xyz/info";
43pub const HYPERLIQUID_TESTNET_EXCHANGE_URL: &str = "https://api.hyperliquid-testnet.xyz/exchange";
44
45pub(crate) const HYPERLIQUID_REST_WEIGHT_PER_MINUTE: u32 = 1_200;
46pub(crate) const HYPERLIQUID_WS_MESSAGES_PER_MINUTE: u32 = 2_000;
47pub(crate) const HYPERLIQUID_WS_CONNECTIONS_MAX: usize = 10;
48pub(crate) const HYPERLIQUID_WS_CONNECTIONS_PER_MINUTE: u32 = 30;
49pub(crate) const HYPERLIQUID_WS_SUBSCRIPTIONS_MAX: usize = 1_000;
50pub(crate) const HYPERLIQUID_WS_SUBSCRIPTION_USERS_MAX: usize = 10;
51pub(crate) const HYPERLIQUID_WS_POST_INFLIGHT_MAX: usize = 100;
52pub const INFLIGHT_MAX: usize = HYPERLIQUID_WS_POST_INFLIGHT_MAX;
53
54// Builder code address for order attribution (zero-fee)
55// Address MUST be lowercase for msgpack serialization
56pub const NAUTILUS_BUILDER_ADDRESS: &str = "0x0c8d970c462726e014ad36f6c5a63e99db48a8e7";
57
58/// Public docs anchor for builder fee approval.
59pub const HYPERLIQUID_BUILDER_APPROVAL_DOCS_URL: &str =
60    "https://nautilustrader.io/docs/nightly/integrations/hyperliquid/#builder-fee-approval";
61
62/// Hyperliquid signing chain ID (0x66eee = 421614 decimal).
63pub const HYPERLIQUID_CHAIN_ID: u64 = 421614;
64
65/// Key carrying the venue asset index on an instrument's `info` map.
66///
67/// Order signing needs the numeric asset index, and `InstrumentAny` is the only
68/// shape published on the message bus, so the index travels with the instrument.
69pub const ASSET_INDEX_INFO_KEY: &str = "asset_index";
70
71// Error message substrings for detecting specific rejection reasons
72pub const HYPERLIQUID_POST_ONLY_WOULD_MATCH: &str =
73    "Post only order would have immediately matched";
74pub const HYPERLIQUID_BUILDER_FEE_NOT_APPROVED: &str = "Builder fee has not been approved";
75
76/// Hyperliquid supported order types.
77///
78/// # Notes
79///
80/// - All order types support trigger prices except Market and Limit.
81/// - Conditional orders follow patterns from OKX, Bybit, and BitMEX adapters.
82/// - Stop orders (StopMarket/StopLimit) are protective stops (sl).
83/// - If Touched orders (MarketIfTouched/LimitIfTouched) are profit-taking or entry orders (tp).
84/// - Post-only orders are implemented via ALO (Add Liquidity Only) time-in-force.
85///
86/// Trailing stops (TrailingStopMarket/TrailingStopLimit) are supported by the exchange
87/// and can be parsed from incoming WS messages, but the outgoing request model does not
88/// yet serialize the trailing offset parameters. Add them once HyperliquidExchangeTriggerParams
89/// is extended with trailing offset fields.
90pub const HYPERLIQUID_SUPPORTED_ORDER_TYPES: &[OrderType] = &[
91    OrderType::Market,          // IOC limit order
92    OrderType::Limit,           // Standard limit with GTC/IOC/ALO
93    OrderType::StopMarket,      // Protective stop with market execution
94    OrderType::StopLimit,       // Protective stop with limit price
95    OrderType::MarketIfTouched, // Profit-taking/entry with market execution
96    OrderType::LimitIfTouched,  // Profit-taking/entry with limit price
97];
98
99/// Conditional order types that use trigger orders on Hyperliquid.
100///
101/// These order types require a trigger_price and are implemented using
102/// HyperliquidExchangeOrderKind::Trigger with appropriate parameters.
103pub const HYPERLIQUID_CONDITIONAL_ORDER_TYPES: &[OrderType] = &[
104    OrderType::StopMarket,
105    OrderType::StopLimit,
106    OrderType::MarketIfTouched,
107    OrderType::LimitIfTouched,
108];
109
110/// Gets WebSocket URL for the specified environment.
111pub fn ws_url(environment: HyperliquidEnvironment) -> &'static str {
112    match environment {
113        HyperliquidEnvironment::Testnet => HYPERLIQUID_TESTNET_WS_URL,
114        HyperliquidEnvironment::Mainnet => HYPERLIQUID_WS_URL,
115    }
116}
117
118/// Gets info API URL for the specified environment.
119pub fn info_url(environment: HyperliquidEnvironment) -> &'static str {
120    match environment {
121        HyperliquidEnvironment::Testnet => HYPERLIQUID_TESTNET_INFO_URL,
122        HyperliquidEnvironment::Mainnet => HYPERLIQUID_INFO_URL,
123    }
124}
125
126/// Gets exchange API URL for the specified environment.
127pub fn exchange_url(environment: HyperliquidEnvironment) -> &'static str {
128    match environment {
129        HyperliquidEnvironment::Testnet => HYPERLIQUID_TESTNET_EXCHANGE_URL,
130        HyperliquidEnvironment::Mainnet => HYPERLIQUID_EXCHANGE_URL,
131    }
132}
133
134// Default configuration values
135// Server closes if no message in last 60s, so ping every 30s
136pub const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(30);
137pub const RECONNECT_BASE_BACKOFF: Duration = Duration::from_millis(250);
138pub const RECONNECT_MAX_BACKOFF: Duration = Duration::from_secs(30);
139pub const HTTP_TIMEOUT: Duration = Duration::from_secs(10);
140pub const QUEUE_MAX: usize = 1000;
141
142#[cfg(test)]
143mod tests {
144    use rstest::rstest;
145
146    use super::*;
147
148    #[rstest]
149    fn test_ws_url() {
150        assert_eq!(ws_url(HyperliquidEnvironment::Mainnet), HYPERLIQUID_WS_URL);
151        assert_eq!(
152            ws_url(HyperliquidEnvironment::Testnet),
153            HYPERLIQUID_TESTNET_WS_URL
154        );
155    }
156
157    #[rstest]
158    fn test_info_url() {
159        assert_eq!(
160            info_url(HyperliquidEnvironment::Mainnet),
161            HYPERLIQUID_INFO_URL
162        );
163        assert_eq!(
164            info_url(HyperliquidEnvironment::Testnet),
165            HYPERLIQUID_TESTNET_INFO_URL
166        );
167    }
168
169    #[rstest]
170    fn test_exchange_url() {
171        assert_eq!(
172            exchange_url(HyperliquidEnvironment::Mainnet),
173            HYPERLIQUID_EXCHANGE_URL
174        );
175        assert_eq!(
176            exchange_url(HyperliquidEnvironment::Testnet),
177            HYPERLIQUID_TESTNET_EXCHANGE_URL
178        );
179    }
180
181    #[rstest]
182    fn test_constants_values() {
183        assert_eq!(HEARTBEAT_INTERVAL, Duration::from_secs(30));
184        assert_eq!(RECONNECT_BASE_BACKOFF, Duration::from_millis(250));
185        assert_eq!(RECONNECT_MAX_BACKOFF, Duration::from_secs(30));
186        assert_eq!(HTTP_TIMEOUT, Duration::from_secs(10));
187        assert_eq!(HYPERLIQUID_REST_WEIGHT_PER_MINUTE, 1_200);
188        assert_eq!(HYPERLIQUID_WS_MESSAGES_PER_MINUTE, 2_000);
189        assert_eq!(HYPERLIQUID_WS_CONNECTIONS_MAX, 10);
190        assert_eq!(HYPERLIQUID_WS_CONNECTIONS_PER_MINUTE, 30);
191        assert_eq!(HYPERLIQUID_WS_SUBSCRIPTIONS_MAX, 1_000);
192        assert_eq!(HYPERLIQUID_WS_SUBSCRIPTION_USERS_MAX, 10);
193        assert_eq!(HYPERLIQUID_WS_POST_INFLIGHT_MAX, 100);
194        assert_eq!(INFLIGHT_MAX, HYPERLIQUID_WS_POST_INFLIGHT_MAX);
195        assert_eq!(QUEUE_MAX, 1000);
196    }
197}