From c18dceb29167e4b7193d9c03d4b1ad170ae7317c Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Sun, 2 Aug 2026 05:32:11 +0200 Subject: [PATCH] Fix risk --- pulse-sdk/src/general.rs | 15 ++++++++++++++- pulse-sdk/src/terminal.rs | 4 ++-- src/engine/engine/execution.rs | 28 +++++++++++++--------------- src/engine/engine/risk.rs | 34 ++++++++++++++++++++-------------- src/engine/store/config.rs | 2 ++ src/terminal/formatting.rs | 14 +++++++++----- 6 files changed, 60 insertions(+), 37 deletions(-) diff --git a/pulse-sdk/src/general.rs b/pulse-sdk/src/general.rs index 670ff9c..0de0dfe 100644 --- a/pulse-sdk/src/general.rs +++ b/pulse-sdk/src/general.rs @@ -1,4 +1,7 @@ -use hypersdk::{dec, hypercore::Side}; +use hypersdk::{ + dec, + hypercore::{Cloid, Side}, +}; use rust_decimal::Decimal; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] @@ -44,7 +47,17 @@ pub struct Signal { pub confidence: u8, pub entry_price: Decimal, pub take_profit: Decimal, +} + +#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)] +pub struct EngineOrder { + pub risk_equity: Decimal, + pub size: Decimal, pub stop_loss: Decimal, + + pub entry: Cloid, + pub take_profit: Cloid, + pub stop_loss_id: Cloid, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] diff --git a/pulse-sdk/src/terminal.rs b/pulse-sdk/src/terminal.rs index 8a0151f..20cbe6d 100644 --- a/pulse-sdk/src/terminal.rs +++ b/pulse-sdk/src/terminal.rs @@ -1,10 +1,10 @@ use crate::{ - general::{EngineStatus, EventLog, Signal}, + general::{EngineOrder, EngineStatus, EventLog, Signal}, strategy::StrategyManifest, }; use hypersdk::Decimal; -pub type SignalStatus = Result<(Signal, Option), (Signal, Option)>; +pub type SignalStatus = Result<(Signal, Option), (Signal, Option)>; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub enum TerminalServerMessage { diff --git a/src/engine/engine/execution.rs b/src/engine/engine/execution.rs index 4be479d..69e4569 100644 --- a/src/engine/engine/execution.rs +++ b/src/engine/engine/execution.rs @@ -1,9 +1,6 @@ -use hypersdk::{ - Decimal, - hypercore::{ - self, BatchOrder, NonceHandler, OrderRequest, OrderResponseStatus, OrderTypePlacement, - Side, TimeInForce, - }, +use hypersdk::hypercore::{ + self, BatchOrder, NonceHandler, OrderRequest, OrderResponseStatus, OrderTypePlacement, Side, + TimeInForce, }; use pulse_sdk::prelude::*; @@ -12,7 +9,7 @@ use crate::engine::Engine; impl Engine { pub async fn execute_signal( &self, - size: &mut Option, + size: &mut Option, signal: &Signal, ) -> anyhow::Result<()> { let client = hypercore::mainnet(); @@ -38,7 +35,7 @@ impl Engine { let engine_order = self.risk_engine.create_order(signal).await?; - *size = Some(engine_order.size); + *size = Some(engine_order); let order = BatchOrder { orders: vec![ @@ -55,7 +52,7 @@ impl Engine { }, OrderRequest { asset: asset_id, - is_buy: matches!(signal.side, Side::Bid), + is_buy: !matches!(signal.side, Side::Bid), limit_px: signal.entry_price, sz: engine_order.size, reduce_only: true, @@ -68,19 +65,19 @@ impl Engine { }, OrderRequest { asset: asset_id, - is_buy: matches!(signal.side, Side::Bid), + is_buy: !matches!(signal.side, Side::Bid), limit_px: signal.entry_price, sz: engine_order.size, reduce_only: true, order_type: OrderTypePlacement::Trigger { is_market: true, - trigger_px: signal.stop_loss, + trigger_px: engine_order.stop_loss, tpsl: hypercore::TpSl::Sl, }, - cloid: engine_order.stop_loss, + cloid: engine_order.stop_loss_id, }, ], - grouping: hypercore::OrderGrouping::Na, + grouping: hypercore::OrderGrouping::NormalTpsl, builder: None, }; @@ -95,9 +92,10 @@ impl Engine { return Err(anyhow::anyhow!( "order rejected by HyperLiquid: {}", o.into_iter() - .filter_map(|res| { + .enumerate() + .filter_map(|(i, res)| { if let OrderResponseStatus::Error(e) = res { - Some(e) + Some(format!("{i}={e}")) } else { None } diff --git a/src/engine/engine/risk.rs b/src/engine/engine/risk.rs index fd3ce95..d217d16 100644 --- a/src/engine/engine/risk.rs +++ b/src/engine/engine/risk.rs @@ -4,7 +4,7 @@ use futures::StreamExt; use hypersdk::{ Decimal, hypercore::{ - self, Cloid, + self, Cloid, Side, ws::{ConnectionHandle, ConnectionStream, Event}, }, }; @@ -13,16 +13,6 @@ use tokio::sync::Mutex; use crate::{engine::Engine, store::config::RiskConfig}; -#[derive(Debug, Clone)] -pub struct EngineOrder { - pub risk_equity: Decimal, - pub size: Decimal, - - pub entry: Cloid, - pub take_profit: Cloid, - pub stop_loss: Cloid, -} - pub struct RiskState { pub starting_equity: Decimal, pub pnl: Decimal, @@ -85,7 +75,7 @@ impl RiskEngine { let mut rm = Vec::new(); for (i, order_ids) in orders.iter().enumerate() { - if order_ids.stop_loss == cloid { + if order_ids.stop_loss_id == cloid { if order.status.is_filled() { rm.push(i); } @@ -154,12 +144,28 @@ impl RiskEngine { .risk_per_trade .get(state.starting_equity); + let size = self + .get_risk_config() + .await? + .size_per_trade + .get(state.starting_equity); + + let sl_distance = risk / (size / signal.entry_price); + Ok(EngineOrder { entry: Cloid::random(), take_profit: Cloid::random(), - stop_loss: Cloid::random(), + stop_loss_id: Cloid::random(), + + size: size.round_dp(3), risk_equity: risk, - size: (risk / (signal.entry_price - signal.stop_loss).abs()).round_dp(3), + + stop_loss: if matches!(signal.side, Side::Ask) { + signal.entry_price + sl_distance + } else { + signal.entry_price - sl_distance + } + .round_dp(3), }) } diff --git a/src/engine/store/config.rs b/src/engine/store/config.rs index c9c7be7..cc9d21b 100644 --- a/src/engine/store/config.rs +++ b/src/engine/store/config.rs @@ -4,6 +4,7 @@ use std::collections::HashMap; #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)] pub struct RiskConfig { + pub size_per_trade: Allocation, pub risk_per_trade: Allocation, pub max_open_positions: u32, pub max_daily_loss: Allocation, @@ -28,6 +29,7 @@ pub struct ConfigManager { impl Default for RiskConfig { fn default() -> Self { Self { + size_per_trade: Allocation::Percent(20.into()), risk_per_trade: Allocation::Percent(10.into()), max_daily_loss: Allocation::Percent(20.into()), max_open_positions: 1, diff --git a/src/terminal/formatting.rs b/src/terminal/formatting.rs index 3ade1b2..de475d5 100644 --- a/src/terminal/formatting.rs +++ b/src/terminal/formatting.rs @@ -55,8 +55,8 @@ impl Formatted for EventLog { impl Formatted for SignalStatus { fn get_formatted(&self) -> Vec { match self { - Ok((signal, size)) | Err((signal, size)) => { - vec![ + Ok((signal, order)) | Err((signal, order)) => { + let mut base = vec![ if matches!(self, Ok(_)) { format!("\x1b[34mOK\x1b[0m") } else { @@ -67,12 +67,16 @@ impl Formatted for SignalStatus { } else { format!("\x1b[31mSELL\x1b[0m") }, - size.map(|v| format_f64(v.as_f64())).unwrap_or_default(), format_symbol(&signal.symbol), format_usd(signal.entry_price.as_f64()), format_usd(signal.take_profit.as_f64()), - format_usd_reverse(signal.stop_loss.as_f64()), - ] + ]; + + if let Some(order) = order { + base.extend(vec![format_usd_reverse(order.stop_loss.as_f64())]); + } + + base } } }