mirror of
https://github.com/floor-licker/polyfill-rs.git
synced 2026-08-18 23:18:08 +00:00
feat(ws): align auth and event_type parsing
This commit is contained in:
+58
-49
@@ -26,7 +26,7 @@ use polyfill_rs::{
|
||||
types::*,
|
||||
|
||||
// Utility functions
|
||||
utils::{address, crypto, math, rate_limit, retry, time, url},
|
||||
utils::{address, math, rate_limit, retry, time, url},
|
||||
|
||||
// Configuration
|
||||
ClientConfig,
|
||||
@@ -43,7 +43,7 @@ use rust_decimal::Decimal;
|
||||
use rust_decimal_macros::dec;
|
||||
use std::time::Duration;
|
||||
use tokio::time::sleep;
|
||||
use tracing::{debug, error, info};
|
||||
use tracing::{error, info};
|
||||
|
||||
/// Demo showcasing polyfill-rs functionality
|
||||
#[allow(dead_code)]
|
||||
@@ -592,10 +592,9 @@ impl PolyfillDemo {
|
||||
initial_dump: Some(true),
|
||||
custom_feature_enabled: None,
|
||||
auth: Some(WssAuth {
|
||||
address: "0x1234567890123456789012345678901234567890".to_string(),
|
||||
signature: "mock_signature".to_string(),
|
||||
timestamp: time::now_secs(),
|
||||
nonce: crypto::generate_nonce().to_string(),
|
||||
api_key: "test-api-key".to_string(),
|
||||
secret: "test-secret".to_string(),
|
||||
passphrase: "test-passphrase".to_string(),
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -603,33 +602,33 @@ impl PolyfillDemo {
|
||||
|
||||
// Simulate receiving stream messages
|
||||
let messages = vec![
|
||||
StreamMessage::Heartbeat {
|
||||
timestamp: chrono::Utc::now(),
|
||||
},
|
||||
StreamMessage::BookUpdate {
|
||||
data: OrderDelta {
|
||||
token_id: "12345".to_string(),
|
||||
timestamp: chrono::Utc::now(),
|
||||
side: Side::BUY,
|
||||
StreamMessage::Book(BookUpdate {
|
||||
asset_id: "12345".to_string(),
|
||||
market: "market1".to_string(),
|
||||
timestamp: time::now_millis(),
|
||||
bids: vec![OrderSummary {
|
||||
price: dec!(0.75),
|
||||
size: dec!(100.0),
|
||||
sequence: 1,
|
||||
},
|
||||
},
|
||||
StreamMessage::Trade {
|
||||
data: FillEvent {
|
||||
id: "fill1".to_string(),
|
||||
order_id: "order1".to_string(),
|
||||
token_id: "12345".to_string(),
|
||||
side: Side::BUY,
|
||||
price: dec!(0.75),
|
||||
}],
|
||||
asks: vec![OrderSummary {
|
||||
price: dec!(0.76),
|
||||
size: dec!(50.0),
|
||||
timestamp: chrono::Utc::now(),
|
||||
maker_address: alloy_primitives::Address::ZERO,
|
||||
taker_address: alloy_primitives::Address::ZERO,
|
||||
fee: dec!(0.375),
|
||||
},
|
||||
},
|
||||
}],
|
||||
hash: None,
|
||||
}),
|
||||
StreamMessage::Trade(TradeMessage {
|
||||
id: "fill1".to_string(),
|
||||
market: "market1".to_string(),
|
||||
asset_id: "12345".to_string(),
|
||||
side: Side::BUY,
|
||||
size: dec!(50.0),
|
||||
price: dec!(0.75),
|
||||
status: Some("MATCHED".to_string()),
|
||||
msg_type: None,
|
||||
last_update: None,
|
||||
matchtime: None,
|
||||
timestamp: None,
|
||||
}),
|
||||
];
|
||||
|
||||
for message in messages {
|
||||
@@ -638,31 +637,41 @@ impl PolyfillDemo {
|
||||
|
||||
// Process message based on type
|
||||
match &message {
|
||||
StreamMessage::BookUpdate { data } => {
|
||||
info!(" Processing book update for token: {}", data.token_id);
|
||||
if let Err(e) = self.book_manager.apply_delta(data.clone()) {
|
||||
error!(" Failed to apply book update: {}", e);
|
||||
self.stats.errors += 1;
|
||||
StreamMessage::Book(book) => {
|
||||
info!(" Processing book update for asset: {}", book.asset_id);
|
||||
// This is a demo: apply snapshot levels as deltas.
|
||||
for level in &book.bids {
|
||||
let _ = self.book_manager.apply_delta(OrderDelta {
|
||||
token_id: book.asset_id.clone(),
|
||||
timestamp: chrono::Utc::now(),
|
||||
side: Side::BUY,
|
||||
price: level.price,
|
||||
size: level.size,
|
||||
sequence: book.timestamp,
|
||||
});
|
||||
}
|
||||
},
|
||||
StreamMessage::Trade { data } => {
|
||||
for level in &book.asks {
|
||||
let _ = self.book_manager.apply_delta(OrderDelta {
|
||||
token_id: book.asset_id.clone(),
|
||||
timestamp: chrono::Utc::now(),
|
||||
side: Side::SELL,
|
||||
price: level.price,
|
||||
size: level.size,
|
||||
sequence: book.timestamp,
|
||||
});
|
||||
}
|
||||
}
|
||||
StreamMessage::Trade(trade) => {
|
||||
info!(
|
||||
" Processing trade: {} {} @ {}",
|
||||
data.side.as_str(),
|
||||
data.size,
|
||||
data.price
|
||||
trade.side.as_str(),
|
||||
trade.size,
|
||||
trade.price
|
||||
);
|
||||
if let Err(e) = self.fill_processor.process_fill(data.clone()) {
|
||||
error!(" Failed to process fill: {}", e);
|
||||
self.stats.errors += 1;
|
||||
}
|
||||
},
|
||||
StreamMessage::Heartbeat { timestamp } => {
|
||||
debug!(" Received heartbeat at: {}", timestamp);
|
||||
},
|
||||
}
|
||||
_ => {
|
||||
info!(" Unhandled message type");
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+79
-41
@@ -101,43 +101,86 @@ impl SnipeStrategy {
|
||||
/// Process a market data update
|
||||
pub fn process_update(&mut self, message: StreamMessage) -> Result<()> {
|
||||
match message {
|
||||
StreamMessage::BookUpdate { data } => {
|
||||
if data.token_id == self.token_id {
|
||||
self.process_book_update(data)?;
|
||||
StreamMessage::Book(book) => {
|
||||
if book.asset_id == self.token_id {
|
||||
self.process_book_update(book)?;
|
||||
}
|
||||
},
|
||||
StreamMessage::Trade { data } => {
|
||||
if data.token_id == self.token_id {
|
||||
self.process_trade(data)?;
|
||||
StreamMessage::Trade(trade) => {
|
||||
if trade.asset_id == self.token_id {
|
||||
self.process_trade(trade)?;
|
||||
}
|
||||
},
|
||||
StreamMessage::Heartbeat { timestamp: _ } => {
|
||||
self.check_stale_quotes()?;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
// Opportunistically check for staleness on any incoming update.
|
||||
self.check_stale_quotes()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Process order book update
|
||||
fn process_book_update(&mut self, delta: OrderDelta) -> Result<()> {
|
||||
fn process_book_update(&mut self, book: BookUpdate) -> Result<()> {
|
||||
// Ensure book exists
|
||||
self.book_manager.get_or_create_book(&self.token_id)?;
|
||||
|
||||
// Update local order book
|
||||
self.book_manager.apply_delta(delta.clone())?;
|
||||
// Clear the existing book and rebuild from the snapshot.
|
||||
if let Ok(current) = self.book_manager.get_book(&self.token_id) {
|
||||
for level in ¤t.bids {
|
||||
let _ = self.book_manager.apply_delta(OrderDelta {
|
||||
token_id: self.token_id.clone(),
|
||||
timestamp: chrono::Utc::now(),
|
||||
side: Side::BUY,
|
||||
price: level.price,
|
||||
size: Decimal::ZERO,
|
||||
sequence: book.timestamp,
|
||||
});
|
||||
}
|
||||
|
||||
// Get current book state
|
||||
let book = self.book_manager.get_book(&self.token_id)?;
|
||||
for level in ¤t.asks {
|
||||
let _ = self.book_manager.apply_delta(OrderDelta {
|
||||
token_id: self.token_id.clone(),
|
||||
timestamp: chrono::Utc::now(),
|
||||
side: Side::SELL,
|
||||
price: level.price,
|
||||
size: Decimal::ZERO,
|
||||
sequence: book.timestamp,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Update best prices
|
||||
if let Some(best_bid) = book.bids.first() {
|
||||
self.last_best_bid = Some(best_bid.price);
|
||||
let ts = chrono::DateTime::from_timestamp(
|
||||
(book.timestamp / 1000) as i64,
|
||||
((book.timestamp % 1000) * 1_000_000) as u32,
|
||||
)
|
||||
.unwrap_or_else(chrono::Utc::now);
|
||||
|
||||
for level in &book.bids {
|
||||
let _ = self.book_manager.apply_delta(OrderDelta {
|
||||
token_id: self.token_id.clone(),
|
||||
timestamp: ts,
|
||||
side: Side::BUY,
|
||||
price: level.price,
|
||||
size: level.size,
|
||||
sequence: book.timestamp,
|
||||
});
|
||||
}
|
||||
if let Some(best_ask) = book.asks.first() {
|
||||
self.last_best_ask = Some(best_ask.price);
|
||||
|
||||
for level in &book.asks {
|
||||
let _ = self.book_manager.apply_delta(OrderDelta {
|
||||
token_id: self.token_id.clone(),
|
||||
timestamp: ts,
|
||||
side: Side::SELL,
|
||||
price: level.price,
|
||||
size: level.size,
|
||||
sequence: book.timestamp,
|
||||
});
|
||||
}
|
||||
|
||||
// Update best prices directly from the snapshot
|
||||
self.last_best_bid = book.bids.first().map(|l| l.price);
|
||||
self.last_best_ask = book.asks.first().map(|l| l.price);
|
||||
|
||||
self.last_update = time::now_secs();
|
||||
|
||||
// Check for trading opportunities
|
||||
@@ -147,17 +190,17 @@ impl SnipeStrategy {
|
||||
}
|
||||
|
||||
/// Process trade update
|
||||
fn process_trade(&mut self, fill: FillEvent) -> Result<()> {
|
||||
fn process_trade(&mut self, trade: TradeMessage) -> Result<()> {
|
||||
info!(
|
||||
"Trade: {} {} @ {} (size: {})",
|
||||
fill.side.as_str(),
|
||||
fill.token_id,
|
||||
fill.price,
|
||||
fill.size
|
||||
trade.side.as_str(),
|
||||
trade.asset_id,
|
||||
trade.price,
|
||||
trade.size
|
||||
);
|
||||
|
||||
// Update statistics
|
||||
self.stats.total_volume += fill.size;
|
||||
self.stats.total_volume += trade.size;
|
||||
|
||||
// Calculate P&L if this was our trade
|
||||
// (In a real implementation, you'd track your own orders)
|
||||
@@ -327,24 +370,19 @@ impl MockMarketData {
|
||||
let price_change = random_factor * Decimal::from(2) * self.volatility;
|
||||
let new_price = self.base_price * (Decimal::from(1) + price_change);
|
||||
|
||||
// Generate order book update
|
||||
let side = if rand::random::<bool>() {
|
||||
Side::BUY
|
||||
} else {
|
||||
Side::SELL
|
||||
};
|
||||
// Generate a simple orderbook snapshot update
|
||||
let size = Decimal::from(rand::random::<u64>() % 1000 + 100);
|
||||
let bid = new_price - dec!(0.01);
|
||||
let ask = new_price + dec!(0.01);
|
||||
|
||||
StreamMessage::BookUpdate {
|
||||
data: OrderDelta {
|
||||
token_id: self.token_id.clone(),
|
||||
timestamp: chrono::Utc::now(),
|
||||
side,
|
||||
price: new_price,
|
||||
size,
|
||||
sequence: self.sequence,
|
||||
},
|
||||
}
|
||||
StreamMessage::Book(BookUpdate {
|
||||
asset_id: self.token_id.clone(),
|
||||
market: "0xmock".to_string(),
|
||||
timestamp: time::now_millis(),
|
||||
bids: vec![OrderSummary { price: bid, size }],
|
||||
asks: vec![OrderSummary { price: ask, size }],
|
||||
hash: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user