Proper asset id

This commit is contained in:
2026-07-29 03:03:30 +02:00
parent e0230425ec
commit a7df23bc42
3 changed files with 70 additions and 28 deletions
+13 -4
View File
@@ -7,16 +7,22 @@ use crate::{
terminal::TerminalServer, terminal::TerminalServer,
}; };
use pulse_sdk::prelude::*; use pulse_sdk::prelude::*;
use std::sync::Arc; use std::{collections::HashMap, sync::Arc};
use tokio::{sync::Mutex, task::JoinHandle}; use tokio::{sync::Mutex, task::JoinHandle};
#[derive(Clone)]
pub struct WatchList {
pub name_to_index: HashMap<String, usize>,
pub items: Vec<MarketItem>,
}
#[derive(Clone)] #[derive(Clone)]
pub struct Engine { pub struct Engine {
pub terminal_server: Arc<TerminalServer>, pub terminal_server: Arc<TerminalServer>,
pub strategy: Arc<StrategyEngine>, pub strategy: Arc<StrategyEngine>,
pub config: Arc<Mutex<Config>>, pub config: Arc<Mutex<Config>>,
pub accounts: Arc<Mutex<AccountList>>, pub accounts: Arc<Mutex<AccountList>>,
pub watch_list: Arc<Mutex<Vec<MarketItem>>>, pub watch_list: Arc<Mutex<WatchList>>,
} }
impl Engine { impl Engine {
@@ -33,7 +39,10 @@ impl Engine {
strategy: strategy.initialize(engine.clone()), strategy: strategy.initialize(engine.clone()),
config, config,
accounts, 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 .terminal_server
.broadcast( .broadcast(
pulse_sdk::terminal::TerminalServerMessage::WatchListUpdated( pulse_sdk::terminal::TerminalServerMessage::WatchListUpdated(
watch_list, watch_list.items,
), ),
) )
.await .await
+25 -3
View File
@@ -79,7 +79,7 @@ impl StrategyEngine {
Some(StrategyMessage::GetWatchList) => { Some(StrategyMessage::GetWatchList) => {
self.strategy self.strategy
.send(&StrategyEngineMessage::WatchList( .send(&StrategyEngineMessage::WatchList(
engine.watch_list.lock().await.clone(), engine.watch_list.lock().await.clone().items,
)) ))
.await?; .await?;
} }
@@ -171,7 +171,7 @@ impl StrategyEngine {
Some(RiskMessage::GetWatchList) => { Some(RiskMessage::GetWatchList) => {
self.risk self.risk
.send(&&RiskEngineMessage::WatchList( .send(&&RiskEngineMessage::WatchList(
engine.watch_list.lock().await.clone(), engine.watch_list.lock().await.clone().items,
)) ))
.await?; .await?;
} }
@@ -186,9 +186,31 @@ impl StrategyEngine {
let accounts = engine.accounts.lock().await; let accounts = engine.accounts.lock().await;
if let Some(acc) = accounts.get_active() { 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 { let order = BatchOrder {
orders: vec![OrderRequest { orders: vec![OrderRequest {
asset: 0, asset: asset_id,
is_buy: matches!(signal.kind, Direction::Buy), is_buy: matches!(signal.kind, Direction::Buy),
limit_px: signal.price.0, limit_px: signal.price.0,
sz: signal.size, sz: signal.size,
+27 -16
View File
@@ -3,6 +3,8 @@ use pulse_sdk::prelude::*;
use serde_json::Value; use serde_json::Value;
use std::collections::HashMap; use std::collections::HashMap;
use crate::engine::WatchList;
fn number(value: &Value, field: &str) -> Result<Decimal, String> { fn number(value: &Value, field: &str) -> Result<Decimal, String> {
let raw = value[field] let raw = value[field]
.as_str() .as_str()
@@ -15,7 +17,7 @@ fn number(value: &Value, field: &str) -> Result<Decimal, String> {
pub async fn fetch_watch_list( pub async fn fetch_watch_list(
client: &hypersdk::hypercore::HttpClient, client: &hypersdk::hypercore::HttpClient,
symbols: &[String], symbols: &[String],
) -> Result<Vec<MarketItem>, String> { ) -> Result<WatchList, String> {
let response = client let response = client
.meta_and_asset_ctxs(None) .meta_and_asset_ctxs(None)
.await .await
@@ -35,6 +37,7 @@ pub async fn fetch_watch_list(
let universe = response[0]["universe"] let universe = response[0]["universe"]
.as_array() .as_array()
.ok_or("metaAndAssetCtxs response is missing meta.universe")?; .ok_or("metaAndAssetCtxs response is missing meta.universe")?;
let contexts = response[1] let contexts = response[1]
.as_array() .as_array()
.ok_or("metaAndAssetCtxs response contexts must be an 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"] let symbol = meta["name"]
.as_str() .as_str()
.ok_or("instrument metadata is missing a name")?; .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 price = number(context, "markPx")?;
let previous_day_price = number(context, "prevDayPx")?; let previous_day_price = number(context, "prevDayPx")?;
let volume_24h = number(context, "dayNtlVlm")?; 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")); return Err(format!("{symbol} has an invalid previous-day price"));
} }
by_symbol.insert( items.push(MarketItem {
symbol, symbol: Symbol(symbol.clone()),
MarketItem {
symbol: Symbol(symbol.to_owned()),
price: USD(price), price: USD(price),
volume_24h: USD(volume_24h), volume_24h: USD(volume_24h),
trend: ((price / previous_day_price) - <Decimal as From<i32>>::from(1)) trend: ((price / previous_day_price) - <Decimal as From<i32>>::from(1))
* <Decimal as From<i32>>::from(100), * <Decimal as From<i32>>::from(100),
}, });
);
} }
symbols Ok(WatchList {
.iter() items,
.map(|symbol| { name_to_index,
by_symbol
.remove(symbol.as_str())
.ok_or_else(|| format!("{symbol} is not in the Hyperliquid perpetual universe"))
}) })
.collect()
} }