mirror of
https://github.com/exchanges-lab/tradesync.git
synced 2026-08-05 05:06:07 +08:00
feat: implement Level and Order Type calculation and integrate with Notion writer
This commit is contained in:
@@ -93,6 +93,7 @@ impl HyperliquidMonitor {
|
|||||||
accumulated_px_sz: f64,
|
accumulated_px_sz: f64,
|
||||||
time: u64,
|
time: u64,
|
||||||
tid: u64,
|
tid: u64,
|
||||||
|
crossed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut active_opening_orders =
|
let mut active_opening_orders =
|
||||||
@@ -122,6 +123,7 @@ impl HyperliquidMonitor {
|
|||||||
accumulated_px_sz: px * sz,
|
accumulated_px_sz: px * sz,
|
||||||
time: fill.time,
|
time: fill.time,
|
||||||
tid: fill.tid,
|
tid: fill.tid,
|
||||||
|
crossed: fill.crossed,
|
||||||
});
|
});
|
||||||
info!(
|
info!(
|
||||||
coin = %fill.coin,
|
coin = %fill.coin,
|
||||||
@@ -206,6 +208,7 @@ impl HyperliquidMonitor {
|
|||||||
action: TradeAction::Open,
|
action: TradeAction::Open,
|
||||||
start_pos: "0.0".to_string(),
|
start_pos: "0.0".to_string(),
|
||||||
end_pos: format!("{:.5}", state.accumulated_sz),
|
end_pos: format!("{:.5}", state.accumulated_sz),
|
||||||
|
crossed: state.crossed,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = event_tx.send(event) {
|
if let Err(e) = event_tx.send(event) {
|
||||||
|
|||||||
+23
-9
@@ -53,15 +53,6 @@ async fn main() -> Result<()> {
|
|||||||
|
|
||||||
info!("Service running. Monitoring position openings and writing to Notion...");
|
info!("Service running. Monitoring position openings and writing to Notion...");
|
||||||
while let Some(event) = rx.recv().await {
|
while let Some(event) = rx.recv().await {
|
||||||
info!(
|
|
||||||
coin = %event.coin,
|
|
||||||
side = %event.side,
|
|
||||||
px = %event.px,
|
|
||||||
sz = %event.sz,
|
|
||||||
oid = event.oid,
|
|
||||||
"Captured position open event, writing to Notion..."
|
|
||||||
);
|
|
||||||
|
|
||||||
// Format Date/Time to "YYYY/MM/DD HH:MM"
|
// Format Date/Time to "YYYY/MM/DD HH:MM"
|
||||||
let dt = Utc
|
let dt = Utc
|
||||||
.timestamp_millis_opt(event.time as i64)
|
.timestamp_millis_opt(event.time as i64)
|
||||||
@@ -79,6 +70,27 @@ async fn main() -> Result<()> {
|
|||||||
"Short".to_string()
|
"Short".to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Parse quantity and price to calculate USD value
|
||||||
|
let sz: f64 = event.sz.parse().unwrap_or(0.0);
|
||||||
|
let px: f64 = event.px.parse().unwrap_or(0.0);
|
||||||
|
let usd_val = sz * px;
|
||||||
|
|
||||||
|
// Calculate Level and Determine Order Type using NotionRowData helpers
|
||||||
|
let level_str = NotionRowData::calculate_level(usd_val);
|
||||||
|
let order_type_str = NotionRowData::determine_order_type(event.crossed);
|
||||||
|
|
||||||
|
info!(
|
||||||
|
coin = %event.coin,
|
||||||
|
side = %event.side,
|
||||||
|
px = %event.px,
|
||||||
|
sz = %event.sz,
|
||||||
|
usd_val = %format!("{:.2}", usd_val),
|
||||||
|
level = %level_str,
|
||||||
|
order_type = %order_type_str,
|
||||||
|
oid = event.oid,
|
||||||
|
"Captured position open event, writing to Notion..."
|
||||||
|
);
|
||||||
|
|
||||||
// Construct Notion Row Data
|
// Construct Notion Row Data
|
||||||
let row = NotionRowData {
|
let row = NotionRowData {
|
||||||
symbol: symbol_formatted,
|
symbol: symbol_formatted,
|
||||||
@@ -90,6 +102,8 @@ async fn main() -> Result<()> {
|
|||||||
time: event.time,
|
time: event.time,
|
||||||
order_id: event.oid,
|
order_id: event.oid,
|
||||||
check: false,
|
check: false,
|
||||||
|
level: level_str,
|
||||||
|
order_type: order_type_str,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Write row to Notion
|
// Write row to Notion
|
||||||
|
|||||||
@@ -161,6 +161,32 @@ impl NotionWriter {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 9. Level (Select column)
|
||||||
|
properties.insert(
|
||||||
|
"Level".to_string(),
|
||||||
|
PageProperty::Select {
|
||||||
|
id: None,
|
||||||
|
select: Some(SelectPropertyValue {
|
||||||
|
id: None,
|
||||||
|
name: Some(data.level),
|
||||||
|
color: None,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// 10. Order Type (Select column)
|
||||||
|
properties.insert(
|
||||||
|
"Order Type".to_string(),
|
||||||
|
PageProperty::Select {
|
||||||
|
id: None,
|
||||||
|
select: Some(SelectPropertyValue {
|
||||||
|
id: None,
|
||||||
|
name: Some(data.order_type),
|
||||||
|
color: None,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// Build CreateAPageRequest and create page
|
// Build CreateAPageRequest and create page
|
||||||
let request = CreateAPageRequestBuilder::default()
|
let request = CreateAPageRequestBuilder::default()
|
||||||
.parent(Parent::DatabaseId {
|
.parent(Parent::DatabaseId {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ pub struct PositionTradeEvent {
|
|||||||
pub action: TradeAction,
|
pub action: TradeAction,
|
||||||
pub start_pos: String, // Position size before the trade
|
pub start_pos: String, // Position size before the trade
|
||||||
pub end_pos: String, // Position size after the trade
|
pub end_pos: String, // Position size after the trade
|
||||||
|
pub crossed: bool, // True if taker (market), false if maker (limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents the structured row data formatted for Notion database insertion.
|
/// Represents the structured row data formatted for Notion database insertion.
|
||||||
@@ -38,4 +39,73 @@ pub struct NotionRowData {
|
|||||||
pub time: u64, // Unix timestamp in milliseconds
|
pub time: u64, // Unix timestamp in milliseconds
|
||||||
pub order_id: u64, // Hyperliquid order ID (oid)
|
pub order_id: u64, // Hyperliquid order ID (oid)
|
||||||
pub check: bool, // false
|
pub check: bool, // false
|
||||||
|
pub level: String, // e.g. "0", "1", "2"
|
||||||
|
pub order_type: String, // "MARKET" or "LIMIT"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NotionRowData {
|
||||||
|
/// Calculate Level from USD value:
|
||||||
|
/// level 0 = 2.5$, level 1 = 5$, level 2 = 10$, and so on (scaling logarithmically by factor of 2)
|
||||||
|
pub fn calculate_level(usd_val: f64) -> String {
|
||||||
|
if usd_val <= 0.0 {
|
||||||
|
"0".to_string()
|
||||||
|
} else {
|
||||||
|
let ratio = usd_val / 2.5;
|
||||||
|
let lvl = ratio.log2().round() as i32;
|
||||||
|
let lvl = lvl.max(0);
|
||||||
|
lvl.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determine Order Type from the crossed flag:
|
||||||
|
/// crossed == true represents MARKET (taker), crossed == false represents LIMIT (maker)
|
||||||
|
pub fn determine_order_type(crossed: bool) -> String {
|
||||||
|
if crossed {
|
||||||
|
"MARKET".to_string()
|
||||||
|
} else {
|
||||||
|
"LIMIT".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_calculate_level() {
|
||||||
|
// Test base cases specified by user
|
||||||
|
assert_eq!(NotionRowData::calculate_level(2.5), "0");
|
||||||
|
assert_eq!(NotionRowData::calculate_level(5.0), "1");
|
||||||
|
assert_eq!(NotionRowData::calculate_level(10.0), "2");
|
||||||
|
|
||||||
|
// Test boundary limits (midpoints between levels)
|
||||||
|
// Midpoint of 2.5 and 5.0 is geometric mean sqrt(2.5 * 5.0) = ~3.535
|
||||||
|
// log2(3.5 / 2.5) = log2(1.4) = ~0.485 (rounds to 0) -> level 0
|
||||||
|
// log2(3.6 / 2.5) = log2(1.44) = ~0.526 (rounds to 1) -> level 1
|
||||||
|
assert_eq!(NotionRowData::calculate_level(3.5), "0");
|
||||||
|
assert_eq!(NotionRowData::calculate_level(3.6), "1");
|
||||||
|
|
||||||
|
// Midpoint of 5.0 and 10.0 is geometric mean sqrt(5 * 10) = ~7.07
|
||||||
|
// log2(7.0 / 2.5) = log2(2.8) = ~1.485 (rounds to 1) -> level 1
|
||||||
|
// log2(7.1 / 2.5) = log2(2.84) = ~1.506 (rounds to 2) -> level 2
|
||||||
|
assert_eq!(NotionRowData::calculate_level(7.0), "1");
|
||||||
|
assert_eq!(NotionRowData::calculate_level(7.1), "2");
|
||||||
|
|
||||||
|
// Test edge cases
|
||||||
|
assert_eq!(NotionRowData::calculate_level(0.0), "0");
|
||||||
|
assert_eq!(NotionRowData::calculate_level(-5.5), "0");
|
||||||
|
assert_eq!(NotionRowData::calculate_level(1.0), "0");
|
||||||
|
|
||||||
|
// Test higher levels
|
||||||
|
assert_eq!(NotionRowData::calculate_level(20.0), "3");
|
||||||
|
assert_eq!(NotionRowData::calculate_level(40.0), "4");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_determine_order_type() {
|
||||||
|
assert_eq!(NotionRowData::determine_order_type(true), "MARKET");
|
||||||
|
assert_eq!(NotionRowData::determine_order_type(false), "LIMIT");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user