From a7df23bc429b2d9764e65fc31a90dc8fdd17c66b Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Wed, 29 Jul 2026 03:03:30 +0200 Subject: [PATCH] Proper asset id --- src/engine/engine/mod.rs | 17 +++++++++--- src/engine/engine/plugin.rs | 28 +++++++++++++++++--- src/engine/fetch.rs | 53 ++++++++++++++++++++++--------------- 3 files changed, 70 insertions(+), 28 deletions(-) diff --git a/src/engine/engine/mod.rs b/src/engine/engine/mod.rs index b8e4681..48382f4 100644 --- a/src/engine/engine/mod.rs +++ b/src/engine/engine/mod.rs @@ -7,16 +7,22 @@ use crate::{ terminal::TerminalServer, }; use pulse_sdk::prelude::*; -use std::sync::Arc; +use std::{collections::HashMap, sync::Arc}; use tokio::{sync::Mutex, task::JoinHandle}; +#[derive(Clone)] +pub struct WatchList { + pub name_to_index: HashMap, + pub items: Vec, +} + #[derive(Clone)] pub struct Engine { pub terminal_server: Arc, pub strategy: Arc, pub config: Arc>, pub accounts: Arc>, - pub watch_list: Arc>>, + pub watch_list: Arc>, } impl Engine { @@ -33,7 +39,10 @@ impl Engine { strategy: strategy.initialize(engine.clone()), config, accounts, - watch_list: Arc::new(Mutex::new(Vec::new())), + watch_list: Arc::new(Mutex::new(WatchList { + name_to_index: HashMap::new(), + items: Vec::new(), + })), })) } @@ -61,7 +70,7 @@ impl Engine { .terminal_server .broadcast( pulse_sdk::terminal::TerminalServerMessage::WatchListUpdated( - watch_list, + watch_list.items, ), ) .await diff --git a/src/engine/engine/plugin.rs b/src/engine/engine/plugin.rs index ada7363..e99f983 100644 --- a/src/engine/engine/plugin.rs +++ b/src/engine/engine/plugin.rs @@ -79,7 +79,7 @@ impl StrategyEngine { Some(StrategyMessage::GetWatchList) => { self.strategy .send(&StrategyEngineMessage::WatchList( - engine.watch_list.lock().await.clone(), + engine.watch_list.lock().await.clone().items, )) .await?; } @@ -171,7 +171,7 @@ impl StrategyEngine { Some(RiskMessage::GetWatchList) => { self.risk .send(&&RiskEngineMessage::WatchList( - engine.watch_list.lock().await.clone(), + engine.watch_list.lock().await.clone().items, )) .await?; } @@ -186,9 +186,31 @@ impl StrategyEngine { let accounts = engine.accounts.lock().await; if let Some(acc) = accounts.get_active() { + let Some(asset_id) = engine + .watch_list + .lock() + .await + .name_to_index + .get(&signal.symbol) + .cloned() + else { + engine + .terminal_server + .error( + "Engine::order", + &format!( + "Invalid Symbol: {:?}, Unable to get asset id", + signal.symbol + ), + ) + .await?; + + continue; + }; + let order = BatchOrder { orders: vec![OrderRequest { - asset: 0, + asset: asset_id, is_buy: matches!(signal.kind, Direction::Buy), limit_px: signal.price.0, sz: signal.size, diff --git a/src/engine/fetch.rs b/src/engine/fetch.rs index 394618d..09bdf31 100644 --- a/src/engine/fetch.rs +++ b/src/engine/fetch.rs @@ -3,6 +3,8 @@ use pulse_sdk::prelude::*; use serde_json::Value; use std::collections::HashMap; +use crate::engine::WatchList; + fn number(value: &Value, field: &str) -> Result { let raw = value[field] .as_str() @@ -15,7 +17,7 @@ fn number(value: &Value, field: &str) -> Result { pub async fn fetch_watch_list( client: &hypersdk::hypercore::HttpClient, symbols: &[String], -) -> Result, String> { +) -> Result { let response = client .meta_and_asset_ctxs(None) .await @@ -35,6 +37,7 @@ pub async fn fetch_watch_list( let universe = response[0]["universe"] .as_array() .ok_or("metaAndAssetCtxs response is missing meta.universe")?; + let contexts = response[1] .as_array() .ok_or("metaAndAssetCtxs response contexts must be an array")?; @@ -47,12 +50,27 @@ pub async fn fetch_watch_list( )); } - let mut by_symbol = HashMap::with_capacity(universe.len()); + // Build symbol -> perp asset index + let mut name_to_index = HashMap::with_capacity(universe.len()); - for (meta, context) in universe.iter().zip(contexts) { + for (index, meta) in universe.iter().enumerate() { let symbol = meta["name"] .as_str() .ok_or("instrument metadata is missing a name")?; + + name_to_index.insert(symbol.to_owned(), index); + } + + // Now only process requested symbols + let mut items = Vec::with_capacity(symbols.len()); + + for symbol in symbols { + let index = *name_to_index + .get(symbol) + .ok_or_else(|| format!("{symbol} is not in the Hyperliquid perpetual universe"))?; + + let context = &contexts[index]; + let price = number(context, "markPx")?; let previous_day_price = number(context, "prevDayPx")?; let volume_24h = number(context, "dayNtlVlm")?; @@ -61,24 +79,17 @@ pub async fn fetch_watch_list( return Err(format!("{symbol} has an invalid previous-day price")); } - by_symbol.insert( - symbol, - MarketItem { - symbol: Symbol(symbol.to_owned()), - price: USD(price), - volume_24h: USD(volume_24h), - trend: ((price / previous_day_price) - >::from(1)) - * >::from(100), - }, - ); + items.push(MarketItem { + symbol: Symbol(symbol.clone()), + price: USD(price), + volume_24h: USD(volume_24h), + trend: ((price / previous_day_price) - >::from(1)) + * >::from(100), + }); } - symbols - .iter() - .map(|symbol| { - by_symbol - .remove(symbol.as_str()) - .ok_or_else(|| format!("{symbol} is not in the Hyperliquid perpetual universe")) - }) - .collect() + Ok(WatchList { + items, + name_to_index, + }) }