Skip to content

Add Hyperliquid WebSocket client#2922

Merged
cjdsellers merged 5 commits into
developfrom
hyperliquid-3
Sep 4, 2025
Merged

Add Hyperliquid WebSocket client#2922
cjdsellers merged 5 commits into
developfrom
hyperliquid-3

Conversation

@v9ai

@v9ai v9ai commented Sep 2, 2025

Copy link
Copy Markdown
Member
  • Add WebSocket client, codec, and exchange modules
  • Implement message handling and subscription management

@v9ai v9ai requested a review from cjdsellers September 2, 2025 12:26
@v9ai v9ai self-assigned this Sep 2, 2025
@cjdsellers cjdsellers changed the title Add WebSocket implementation for Hyperliquid adapter Add Hyperliquid WebSocket client Sep 3, 2025
v9ai added 4 commits September 4, 2025 10:52
- Add WebSocket client, codec, and exchange modules
- Implement message handling and subscription management
- Fix clippy warnings for collapsible if statements and unnecessary casts

@cjdsellers cjdsellers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @NicolaD, good progress here!

strum = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true }
tokio-stream = { workspace = true }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's interesting tokio-stream is required, but not for the other adapters. This suggests a different pattern somewhere?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aspect Other adapters: channel + tokio::select! (no tokio-stream) This adapter: stream-first (uses tokio-stream) What it says about the dep
Direct answer Doesn’t need tokio-stream because it pulls from channels via recv().await and coordinates with select!. Needs tokio-stream when it wraps Tokio primitives as Streams or uses stream combinators. Yes—different pattern. Presence of tokio-stream signals a stream-centric composition somewhere.
Primary abstraction Futures + channels (mpsc, broadcast) + select!. Stream<Item = T> (from futures-core) + tokio_stream::wrappers + StreamExt combinators. Stream API → extra crate.
What you’ll see in code while let Some(x) = rx.recv().await { ... }, tokio::select! { ... }. ReceiverStream::new(rx).map(...).merge(...).next().await, StreamMap. Wrappers/combinators imply tokio-stream.
Fan-in / merging sources Manual select! over multiple recv() branches; verbose for many/dynamic sources. merge, SelectAll, or StreamMap to combine many streams succinctly. tokio-stream added to get these tools.
Timers / heartbeats tokio::time::interval() polled inside loops. IntervalStream + combinators (timeout, throttle, chunks_timeout). Time-aware ops come from tokio-stream features.
Public API shape Pull style: async fn next_event() -> Option<T>. Stream style: fn into_stream(self) -> impl Stream<Item = T>. Exposing a stream almost always pulls in tokio-stream.
Composability (map/filter/batch) Hand-rolled; more boilerplate/state machines. One-liners with StreamExt (map, filter, chunks_timeout, etc.). Crate enables declarative pipelines.
Backpressure semantics Channel capacity controls it; explicit. Same underlying channels; wrappers don’t change capacity. Neutral—dependency is about ergonomics.
Fairness/ordering select! has randomized fairness; ordering is imperative. Per-stream order preserved; cross-stream order depends on combinator. Must document semantics either way.
Testability Drive loops manually; custom harnesses. collect::<Vec<_>>(), take(n), etc. on streams simplify tests. Stream API often easier to test.
Cost / deps Fewer deps; minimal indirection. Adds tokio-stream; thin wrapper overhead. Small runtime impact; main cost is dep surface.
If you want to remove it Keep pull-style API; replace any wrappers with direct recv()/timeout(). N/A. Run cargo udeps; drop tokio-stream if unused.
If you want to keep it (justify) N/A. You’re wrapping mpsc/broadcast/interval as streams, merging sources, or exposing a stream API. Consider feature-gating: stream-api = ["tokio-stream"].
Reply “The other adapters use channel + select!, so they don’t need tokio-stream.” “This adapter embraces a Stream-based composition (wrapping channels/timers and/or using combinators), which is why tokio-stream is required.”

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cjdsellers cjdsellers merged commit fa4a733 into develop Sep 4, 2025
13 checks passed
@cjdsellers cjdsellers deleted the hyperliquid-3 branch September 4, 2025 22:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants