From e0230425ec922ce0457759ba73cba00d4e5541ac Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Wed, 29 Jul 2026 02:28:54 +0200 Subject: [PATCH] Order execution and using decimal --- pulse-sdk/src/general.rs | 4 ++- pulse-sdk/src/terminal.rs | 4 +-- pulse-sdk/src/units.rs | 10 ++++--- src/engine/engine/mod.rs | 5 ++-- src/engine/engine/plugin.rs | 54 +++++++++++++++++++++++++++++++++++-- src/engine/fetch.rs | 10 ++++--- src/terminal/formatting.rs | 2 +- 7 files changed, 72 insertions(+), 17 deletions(-) diff --git a/pulse-sdk/src/general.rs b/pulse-sdk/src/general.rs index a2fffad..93e312f 100644 --- a/pulse-sdk/src/general.rs +++ b/pulse-sdk/src/general.rs @@ -1,3 +1,5 @@ +use hypersdk::Decimal; + use crate::units::{Direction, Symbol, USD}; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] @@ -20,7 +22,7 @@ pub struct Signal { pub symbol: String, pub kind: Direction, pub confidence: f32, - pub size: f64, + pub size: Decimal, pub price: USD, pub take_profit: USD, pub stop_loss: USD, diff --git a/pulse-sdk/src/terminal.rs b/pulse-sdk/src/terminal.rs index a07e9f1..625af89 100644 --- a/pulse-sdk/src/terminal.rs +++ b/pulse-sdk/src/terminal.rs @@ -3,7 +3,7 @@ use crate::{ plugin::{RiskManifest, StrategyManifest}, units::{Symbol, USD, Volatility}, }; -use hypersdk::hypercore::CandleInterval; +use hypersdk::{Decimal, hypercore::CandleInterval}; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub enum TerminalServerMessage { @@ -39,7 +39,7 @@ pub enum TerminalClientMessage { pub struct MarketItem { pub symbol: Symbol, pub price: USD, - pub trend: f64, + pub trend: Decimal, pub volume_24h: USD, } diff --git a/pulse-sdk/src/units.rs b/pulse-sdk/src/units.rs index e2758e9..9dd091f 100644 --- a/pulse-sdk/src/units.rs +++ b/pulse-sdk/src/units.rs @@ -1,8 +1,10 @@ +use hypersdk::Decimal; + #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct Symbol(pub String); #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)] -pub struct USD(pub f64); +pub struct USD(pub Decimal); #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub enum Direction { @@ -25,10 +27,10 @@ impl std::fmt::Display for Symbol { impl std::fmt::Display for USD { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if self.0 > 0.0 { - write!(f, "\x1b[32m${}\x1b[0m", format_f64(self.0)) + if self.0.is_sign_positive() { + write!(f, "\x1b[32m${}\x1b[0m", format_f64(self.0.as_f64())) } else { - write!(f, "\x1b[31m${}\x1b[0m", format_f64(self.0)) + write!(f, "\x1b[31m${}\x1b[0m", format_f64(self.0.as_f64())) } } } diff --git a/src/engine/engine/mod.rs b/src/engine/engine/mod.rs index bb1d46f..b8e4681 100644 --- a/src/engine/engine/mod.rs +++ b/src/engine/engine/mod.rs @@ -102,9 +102,8 @@ impl Engine { entry_price: USD(position .position .entry_px - .map(|px| px.as_f64()) - .unwrap_or(0.0)), - profit: USD(position.position.unrealized_pnl.as_f64()), + .unwrap_or_default()), + profit: USD(position.position.unrealized_pnl), }) .collect(), ), diff --git a/src/engine/engine/plugin.rs b/src/engine/engine/plugin.rs index 5c69307..ada7363 100644 --- a/src/engine/engine/plugin.rs +++ b/src/engine/engine/plugin.rs @@ -1,4 +1,7 @@ -use hypersdk::hypercore::{self, CandleInterval, Subscription, WebSocket}; +use hypersdk::hypercore::{ + self, BatchOrder, CandleInterval, OrderRequest, OrderTypePlacement, Subscription, TimeInForce, + WebSocket, +}; use pulse_sdk::prelude::*; use std::{ collections::HashSet, @@ -178,7 +181,54 @@ impl StrategyEngine { engine.terminal_server.log_raw(log).await?; } - Some(RiskMessage::Signal(signal)) => {} + Some(RiskMessage::Signal(RiskSignal::Approve(signal))) => { + let client = hypercore::mainnet(); + let accounts = engine.accounts.lock().await; + + if let Some(acc) = accounts.get_active() { + let order = BatchOrder { + orders: vec![OrderRequest { + asset: 0, + is_buy: matches!(signal.kind, Direction::Buy), + limit_px: signal.price.0, + sz: signal.size, + reduce_only: false, + order_type: OrderTypePlacement::Limit { + tif: TimeInForce::Gtc, + }, + cloid: Default::default(), + }], + grouping: hypercore::OrderGrouping::Na, + builder: None, + }; + + let nonce = chrono::Utc::now().timestamp_millis() as u64; + + match client + .place(&acc.private_key.0, order, nonce, None, None) + .await + { + Ok(_) => {} + Err(e) => { + engine + .terminal_server + .error("Engine::order", &e.to_string()) + .await?; + } + } + } else { + engine + .terminal_server + .error("Engine::order", "Unable to get active account") + .await?; + } + } + + Some(RiskMessage::Signal(RiskSignal::Reject { + signal, + rejection_confidence, + reason, + })) => {} } } } diff --git a/src/engine/fetch.rs b/src/engine/fetch.rs index d8be404..394618d 100644 --- a/src/engine/fetch.rs +++ b/src/engine/fetch.rs @@ -1,13 +1,14 @@ +use hypersdk::Decimal; use pulse_sdk::prelude::*; use serde_json::Value; use std::collections::HashMap; -fn number(value: &Value, field: &str) -> Result { +fn number(value: &Value, field: &str) -> Result { let raw = value[field] .as_str() .ok_or_else(|| format!("asset context field {field} must be a string"))?; - raw.parse::() + raw.parse::() .map_err(|error| format!("could not parse asset context field {field} ({raw}): {error}")) } @@ -56,7 +57,7 @@ pub async fn fetch_watch_list( let previous_day_price = number(context, "prevDayPx")?; let volume_24h = number(context, "dayNtlVlm")?; - if previous_day_price <= 0.0 { + if previous_day_price.is_zero() || previous_day_price.is_sign_negative() { return Err(format!("{symbol} has an invalid previous-day price")); } @@ -66,7 +67,8 @@ pub async fn fetch_watch_list( symbol: Symbol(symbol.to_owned()), price: USD(price), volume_24h: USD(volume_24h), - trend: ((price / previous_day_price) - 1.0) * 100.0, + trend: ((price / previous_day_price) - >::from(1)) + * >::from(100), }, ); } diff --git a/src/terminal/formatting.rs b/src/terminal/formatting.rs index 50211d6..58d4439 100644 --- a/src/terminal/formatting.rs +++ b/src/terminal/formatting.rs @@ -85,7 +85,7 @@ impl Formatted for MarketItem { } else { "\x1b[31m▼\x1b[0m" }, - format_f64(self.trend.abs()) + format_f64(self.trend.as_f64().abs()) ), ] }