hyperliquid_ws_post/
ws-post.rs1use std::{env, time::Duration};
19
20use nautilus_hyperliquid::{
21 common::consts::ws_url,
22 websocket::{
23 client::HyperliquidWebSocketClient,
24 messages::{ActionPayload, ActionRequest, SignatureData, TimeInForceRequest},
25 post::{Grouping, OrderBuilder},
26 },
27};
28use tracing::{info, level_filters::LevelFilter, warn};
29use tracing_subscriber::{EnvFilter, fmt};
30
31#[tokio::main]
32async fn main() -> Result<(), Box<dyn std::error::Error>> {
33 let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
35 fmt()
36 .with_env_filter(env_filter)
37 .with_max_level(LevelFilter::INFO)
38 .init();
39
40 let args: Vec<String> = env::args().collect();
41 let testnet = args.get(1).is_some_and(|s| s == "testnet");
42 let ws_url = ws_url(testnet);
43
44 info!(component = "ws_post", %ws_url, ?testnet, "connecting");
45 let mut client = HyperliquidWebSocketClient::connect(ws_url).await?;
46 info!(component = "ws_post", "websocket connected");
47
48 let book = client.info_l2_book("BTC", Duration::from_secs(2)).await?;
49 let best_bid = book
50 .levels
51 .first()
52 .and_then(|bids| bids.first())
53 .map(|l| l.px.clone())
54 .unwrap_or_default();
55 let best_ask = book
56 .levels
57 .get(1)
58 .and_then(|asks| asks.first())
59 .map(|l| l.px.clone())
60 .unwrap_or_default();
61 info!(component = "ws_post", best_bid = %best_bid, best_ask = %best_ask, "BTC top of book");
62
63 let should_send = std::env::var("HL_SEND").map(|v| v == "1").unwrap_or(false);
65 if !should_send {
66 warn!(
67 component = "ws_post",
68 "skipping action: set HL_SEND=1 to send the stubbed order"
69 );
70 return Ok(());
71 }
72
73 if best_bid.is_empty() {
74 warn!(
75 component = "ws_post",
76 "no best bid available; aborting action"
77 );
78 return Ok(());
79 }
80
81 let action: ActionRequest = OrderBuilder::new()
83 .grouping(Grouping::Na)
84 .push_limit(
85 0, true, best_bid.clone(), "0.001", false,
90 TimeInForceRequest::Alo, Some("test-cloid-1".to_string()),
92 )
93 .build();
94
95 let payload = ActionPayload {
97 action,
98 nonce: 0, signature: SignatureData {
100 r: "0x0".into(),
101 s: "0x0".into(),
102 v: "0x1b".into(),
103 },
104 vault_address: None,
105 };
106
107 match client
108 .post_action_raw(payload, Duration::from_secs(2))
109 .await
110 {
111 Ok(resp) => info!(component = "ws_post", ?resp, "action response"),
112 Err(e) => {
113 warn!(component = "ws_post", error = %e, "action failed (expected with dummy signature)")
114 }
115 }
116
117 Ok(())
118}