hyperliquid_http_private/
http_private.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;
17
18use nautilus_hyperliquid::http::{
19    client::HyperliquidHttpClient,
20    models::{
21        HyperliquidExecOrderKind, HyperliquidExecPlaceOrderRequest, HyperliquidExecTpSl,
22        HyperliquidExecTriggerParams,
23    },
24};
25use rust_decimal_macros::dec;
26use tracing::level_filters::LevelFilter;
27
28#[tokio::main]
29async fn main() -> Result<(), Box<dyn std::error::Error>> {
30    tracing_subscriber::fmt()
31        .with_max_level(LevelFilter::INFO)
32        .init();
33
34    let args: Vec<String> = env::args().collect();
35    let testnet = args.get(1).is_some_and(|s| s == "testnet");
36    let test_conditional = args.get(1).is_some_and(|s| s == "conditional")
37        || args.get(2).is_some_and(|s| s == "conditional");
38
39    tracing::info!("Starting Hyperliquid HTTP private example");
40    if testnet {
41        tracing::info!(
42            "Testnet parameter provided - ensure HYPERLIQUID_TESTNET_PK environment variable is set"
43        );
44    } else {
45        tracing::info!("Mainnet mode - ensure HYPERLIQUID_PK environment variable is set");
46    }
47    if test_conditional {
48        tracing::info!("Conditional orders test mode enabled");
49    }
50
51    // Try to create authenticated client from environment
52    let client = match HyperliquidHttpClient::from_env() {
53        Ok(client) => {
54            tracing::info!("Testnet mode: {}", client.is_testnet());
55            client
56        }
57        Err(_) => {
58            tracing::warn!(
59                "No credentials found in environment (HYPERLIQUID_PK). Skipping authenticated examples."
60            );
61            return Ok(());
62        }
63    };
64
65    // For demonstration, use a placeholder address
66    let user_address = "0x0000000000000000000000000000000000000000";
67
68    // Test conditional orders if requested
69    if test_conditional {
70        tracing::info!("=== Testing Conditional Orders ===");
71        test_conditional_orders(&client).await?;
72        return Ok(());
73    }
74
75    // Get user fills
76    match client.info_user_fills(user_address).await {
77        Ok(fills) => {
78            tracing::info!("Fetched {} fills", fills.len());
79            for (i, fill) in fills.iter().take(3).enumerate() {
80                tracing::info!("Fill {}: {} {} @ {}", i, fill.side, fill.sz, fill.px);
81            }
82        }
83        Err(e) => {
84            tracing::info!("Failed to fetch fills: {}", e);
85        }
86    }
87
88    // Get order status (example with fake order ID)
89    let example_order_id = 12345u64;
90    match client
91        .info_order_status(user_address, example_order_id)
92        .await
93    {
94        Ok(status) => {
95            tracing::info!("Order status: {:?}", status);
96        }
97        Err(e) => {
98            tracing::info!("Order status query failed (expected for demo ID): {}", e);
99        }
100    }
101
102    Ok(())
103}
104
105/// Test conditional orders (stop market, stop limit, market-if-touched, limit-if-touched).
106async fn test_conditional_orders(
107    _client: &HyperliquidHttpClient,
108) -> Result<(), Box<dyn std::error::Error>> {
109    tracing::info!("Testing conditional order types:");
110    tracing::info!("  - StopMarket (is_market=true, tpsl=Sl)");
111    tracing::info!("  - StopLimit (is_market=false, tpsl=Sl)");
112    tracing::info!("  - MarketIfTouched (is_market=true, tpsl=Tp)");
113    tracing::info!("  - LimitIfTouched (is_market=false, tpsl=Tp)");
114    tracing::info!("");
115
116    // Example: Stop Market order (BUY)
117    // Triggers when price goes ABOVE trigger_px, executes at market
118    let stop_market_buy = HyperliquidExecPlaceOrderRequest {
119        asset: 0, // BTC-USD (asset 0)
120        is_buy: true,
121        price: dec!(0), // Price is 0 for market execution after trigger
122        size: dec!(0.001),
123        reduce_only: false,
124        kind: HyperliquidExecOrderKind::Trigger {
125            trigger: HyperliquidExecTriggerParams {
126                is_market: true,
127                trigger_px: dec!(45000),       // Trigger at $45,000
128                tpsl: HyperliquidExecTpSl::Sl, // Stop Loss semantics
129            },
130        },
131        cloid: None,
132    };
133
134    // Example: Stop Limit order (SELL)
135    // Triggers when price goes BELOW trigger_px, places limit order at specified price
136    let _stop_limit_sell = HyperliquidExecPlaceOrderRequest {
137        asset: 0,
138        is_buy: false,
139        price: dec!(44900), // Limit price after trigger
140        size: dec!(0.001),
141        reduce_only: false,
142        kind: HyperliquidExecOrderKind::Trigger {
143            trigger: HyperliquidExecTriggerParams {
144                is_market: false,
145                trigger_px: dec!(45000),       // Trigger at $45,000
146                tpsl: HyperliquidExecTpSl::Sl, // Stop Loss semantics
147            },
148        },
149        cloid: None,
150    };
151
152    // Example: Market If Touched order (BUY)
153    // Triggers when price goes ABOVE trigger_px, executes at market
154    let _market_if_touched_buy = HyperliquidExecPlaceOrderRequest {
155        asset: 0,
156        is_buy: true,
157        price: dec!(0), // Price is 0 for market execution after trigger
158        size: dec!(0.001),
159        reduce_only: false,
160        kind: HyperliquidExecOrderKind::Trigger {
161            trigger: HyperliquidExecTriggerParams {
162                is_market: true,
163                trigger_px: dec!(46000),       // Trigger at $46,000
164                tpsl: HyperliquidExecTpSl::Tp, // Take Profit semantics
165            },
166        },
167        cloid: None,
168    };
169
170    // Example: Limit If Touched order (SELL)
171    // Triggers when price goes BELOW trigger_px, places limit order at specified price
172    let _limit_if_touched_sell = HyperliquidExecPlaceOrderRequest {
173        asset: 0,
174        is_buy: false,
175        price: dec!(45900), // Limit price after trigger
176        size: dec!(0.001),
177        reduce_only: false,
178        kind: HyperliquidExecOrderKind::Trigger {
179            trigger: HyperliquidExecTriggerParams {
180                is_market: false,
181                trigger_px: dec!(46000),       // Trigger at $46,000
182                tpsl: HyperliquidExecTpSl::Tp, // Take Profit semantics
183            },
184        },
185        cloid: None,
186    };
187
188    tracing::info!("Example conditional order structures created:");
189    tracing::info!("  1. Stop Market BUY @ trigger $45,000");
190    tracing::info!("  2. Stop Limit SELL @ trigger $45,000, limit $44,900");
191    tracing::info!("  3. Market If Touched BUY @ trigger $46,000");
192    tracing::info!("  4. Limit If Touched SELL @ trigger $46,000, limit $45,900");
193    tracing::info!("");
194    tracing::info!(
195        "To actually place orders, create an ExchangeAction::Order and call client.post_action()"
196    );
197    tracing::info!("");
198    tracing::info!("Example code:");
199    tracing::info!(
200        r#"
201    let action = HyperliquidExecAction::Order {{
202        orders: vec![stop_market_buy],
203        grouping: HyperliquidExecGrouping::Na,
204        builder: None,
205    }};
206    let response = client.post_action(&action).await?;
207    "#
208    );
209
210    // Display the JSON serialization to show the exact API format
211    let example_json = serde_json::to_string_pretty(&stop_market_buy)?;
212    tracing::info!("Stop Market order JSON format:");
213    tracing::info!("{}", example_json);
214
215    Ok(())
216}