hyperliquid_ws_post/
ws-post.rs1use std::time::Duration;
19
20use nautilus_hyperliquid::{
21 common::consts::{HyperliquidNetwork, 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 = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
35 fmt()
36 .with_env_filter(env)
37 .with_max_level(LevelFilter::INFO)
38 .init();
39
40 let network = HyperliquidNetwork::from_env();
41 let ws_url = ws_url(network);
42 info!(component = "ws_post", %ws_url, ?network, "connecting");
43 let mut client = HyperliquidWebSocketClient::connect(ws_url).await?;
44 info!(component = "ws_post", "websocket connected");
45
46 let book = client.info_l2_book("BTC", Duration::from_secs(2)).await?;
47 let best_bid = book
48 .levels
49 .first()
50 .and_then(|bids| bids.first())
51 .map(|l| l.px.clone())
52 .unwrap_or_default();
53 let best_ask = book
54 .levels
55 .get(1)
56 .and_then(|asks| asks.first())
57 .map(|l| l.px.clone())
58 .unwrap_or_default();
59 info!(component = "ws_post", best_bid = %best_bid, best_ask = %best_ask, "BTC top of book");
60
61 let should_send = std::env::var("HL_SEND").map(|v| v == "1").unwrap_or(false);
63 if !should_send {
64 warn!(
65 component = "ws_post",
66 "skipping action: set HL_SEND=1 to send the stubbed order"
67 );
68 return Ok(());
69 }
70
71 if best_bid.is_empty() {
72 warn!(
73 component = "ws_post",
74 "no best bid available; aborting action"
75 );
76 return Ok(());
77 }
78
79 let action: ActionRequest = OrderBuilder::new()
81 .grouping(Grouping::Na)
82 .push_limit(
83 0, true, best_bid.clone(), "0.001", false,
88 TimeInForceRequest::Alo, Some("test-cloid-1".to_string()),
90 )
91 .build();
92
93 let payload = ActionPayload {
95 action,
96 nonce: 0, signature: SignatureData {
98 r: "0x0".into(),
99 s: "0x0".into(),
100 v: "0x1b".into(),
101 },
102 vault_address: None,
103 };
104
105 match client
106 .post_action_raw(payload, Duration::from_secs(2))
107 .await
108 {
109 Ok(resp) => info!(component = "ws_post", ?resp, "action response"),
110 Err(e) => {
111 warn!(component = "ws_post", error = %e, "action failed (expected with dummy signature)")
112 }
113 }
114
115 Ok(())
116}