Proper asset id
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
+32
-21
@@ -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 {
|
price: USD(price),
|
||||||
symbol: Symbol(symbol.to_owned()),
|
volume_24h: USD(volume_24h),
|
||||||
price: USD(price),
|
trend: ((price / previous_day_price) - <Decimal as From<i32>>::from(1))
|
||||||
volume_24h: USD(volume_24h),
|
* <Decimal as From<i32>>::from(100),
|
||||||
trend: ((price / previous_day_price) - <Decimal as From<i32>>::from(1))
|
});
|
||||||
* <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()
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user