feat: enhance HyperliquidMonitor to parse and emit all trade actions: Open, Increase, Decrease, Close

This commit is contained in:
0xcathiefish
2026-05-30 09:10:27 +00:00
parent e6dc20f845
commit df09672130
5 changed files with 84 additions and 39 deletions
+11 -3
View File
@@ -28,11 +28,19 @@ async fn main() -> Result<()> {
}
});
info!("Listening for position open events. Close with Ctrl+C...");
info!("Listening for position trade events. Close with Ctrl+C...");
while let Some(event) = rx.recv().await {
info!(
"DEMO RECEIVED POSITION OPEN: coin={}, side={}, px={}, sz={}, time={}, tid={}",
event.coin, event.side, event.px, event.sz, event.time, event.tid
"DEMO RECEIVED POSITION TRADE: coin={}, side={}, px={}, sz={}, action={:?}, start_pos={}, end_pos={}, time={}, tid={}",
event.coin,
event.side,
event.px,
event.sz,
event.action,
event.start_pos,
event.end_pos,
event.time,
event.tid
);
}
+48 -30
View File
@@ -1,4 +1,4 @@
use crate::structs::PositionOpenEvent;
use crate::structs::{PositionTradeEvent, TradeAction};
use alloy::primitives::Address;
use hyperliquid_rust_sdk::{BaseUrl, InfoClient, Message, Subscription, UserData};
use thiserror::Error;
@@ -31,7 +31,7 @@ impl HyperliquidMonitor {
/// Runs the monitor WebSocket subscription in a loop, reconnecting if disconnected.
pub async fn run(
&self,
event_tx: UnboundedSender<PositionOpenEvent>,
event_tx: UnboundedSender<PositionTradeEvent>,
) -> Result<(), HyperliquidMonitorError> {
let base_url = if self.is_testnet {
BaseUrl::Testnet
@@ -90,36 +90,54 @@ impl HyperliquidMonitor {
debug!(user_msg = ?user_msg, "Received user event message");
if let UserData::Fills(fills) = user_msg.data {
for fill in fills {
// Check if start_position is 0, which indicates a new position is opening
// Parse position details
let start_pos: f64 = fill.start_position.parse().unwrap_or(0.0);
if start_pos == 0.0 {
info!(
coin = %fill.coin,
side = %fill.side,
px = %fill.px,
sz = %fill.sz,
tid = fill.tid,
"Position opening detected!"
);
let event = PositionOpenEvent {
coin: fill.coin,
side: fill.side,
px: fill.px,
sz: fill.sz,
time: fill.time,
tid: fill.tid,
};
if let Err(e) = event_tx.send(event) {
error!(
error = ?e,
"Failed to send PositionOpenEvent through channel"
);
}
let sz: f64 = fill.sz.parse().unwrap_or(0.0);
let dir = if fill.side == "B" { 1.0 } else { -1.0 };
let change = dir * sz;
let end_pos = start_pos + change;
// Determine the trade action
let action = if start_pos == 0.0 {
TradeAction::Open
} else if end_pos == 0.0 {
TradeAction::Close
} else if start_pos.signum() != end_pos.signum() {
TradeAction::Decrease
} else if end_pos.abs() > start_pos.abs() {
TradeAction::Increase
} else {
debug!(
coin = %fill.coin,
start_pos = start_pos,
"Ignore non-opening trade fill"
TradeAction::Decrease
};
info!(
coin = %fill.coin,
side = %fill.side,
px = %fill.px,
sz = %fill.sz,
action = ?action,
start_pos = start_pos,
end_pos = end_pos,
tid = fill.tid,
"Wallet trade fill detected!"
);
let event = PositionTradeEvent {
coin: fill.coin,
side: fill.side,
px: fill.px,
sz: fill.sz,
time: fill.time,
tid: fill.tid,
action,
start_pos: fill.start_position.clone(),
end_pos: format!("{:.5}", end_pos),
};
if let Err(e) = event_tx.send(event) {
error!(
error = ?e,
"Failed to send PositionTradeEvent through channel"
);
}
}
+1 -1
View File
@@ -2,4 +2,4 @@ pub mod hyperliquid;
pub mod structs;
pub use hyperliquid::{HyperliquidMonitor, HyperliquidMonitorError};
pub use structs::PositionOpenEvent;
pub use structs::{PositionTradeEvent, TradeAction};
+4 -1
View File
@@ -48,9 +48,12 @@ async fn main() -> Result<()> {
side = %event.side,
px = %event.px,
sz = %event.sz,
action = ?event.action,
start_pos = %event.start_pos,
end_pos = %event.end_pos,
time = event.time,
tid = event.tid,
"Captured position opening event!"
"Captured position trade event!"
);
// Notion database integration will be wired here in the future
}
+20 -4
View File
@@ -1,10 +1,26 @@
/// Represents a detected position opening event on Hyperliquid.
/// The action type of a trade fill relative to the position.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TradeAction {
/// Opening a position (initial position size was 0)
Open,
/// Adding to an existing position (absolute position size increases)
Increase,
/// Reducing an existing position (absolute position size decreases but remains non-zero)
Decrease,
/// Fully closing an existing position (resulting position size becomes 0)
Close,
}
/// Represents a detected trade event on Hyperliquid affecting a wallet's position.
#[derive(Debug, Clone)]
pub struct PositionOpenEvent {
pub struct PositionTradeEvent {
pub coin: String,
pub side: String, // "B" (Buy) or "S" (Sell)
pub px: String, // Price as string
pub sz: String, // Size as string
pub sz: String, // Trade size as string
pub time: u64, // Epoch timestamp in milliseconds
pub tid: u64, // Unique trade/fill ID
pub tid: u64, // Unique trade ID
pub action: TradeAction,
pub start_pos: String, // Position size before the trade
pub end_pos: String, // Position size after the trade
}