From 9c367b380751c3b1d2aace660ab9659996307ed2 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Sat, 25 Jul 2026 17:09:35 +0200 Subject: [PATCH 1/7] Refactor, and added strategy with risk --- Cargo.lock | 1 + pulse-wire/Cargo.toml | 1 + pulse-wire/src/general.rs | 18 ++++++------------ pulse-wire/src/lib.rs | 4 ++++ pulse-wire/src/strategy.rs | 18 ++++++++++++++++++ pulse-wire/src/terminal.rs | 7 +++++++ pulse-wire/src/units.rs | 30 ++++++++++++++++++++++++++++++ src/engine/engine.rs | 11 +++-------- src/engine/fetch.rs | 8 ++------ src/engine/terminal.rs | 6 ++---- src/terminal/formatting.rs | 7 +------ src/terminal/main.rs | 5 +---- src/terminal/terminal.rs | 3 ++- 13 files changed, 78 insertions(+), 41 deletions(-) create mode 100644 pulse-wire/src/strategy.rs diff --git a/Cargo.lock b/Cargo.lock index fc34e9b..8495645 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3907,6 +3907,7 @@ name = "pulse-wire" version = "0.1.0-alpha.0" dependencies = [ "pulse-macros", + "serde", ] [[package]] diff --git a/pulse-wire/Cargo.toml b/pulse-wire/Cargo.toml index 449b94f..10ed64f 100644 --- a/pulse-wire/Cargo.toml +++ b/pulse-wire/Cargo.toml @@ -5,3 +5,4 @@ edition = "2024" [dependencies] pulse-macros = { workspace = true } +serde = { version = "1.0.229", features = ["serde_derive"] } diff --git a/pulse-wire/src/general.rs b/pulse-wire/src/general.rs index 7f286a4..78ca796 100644 --- a/pulse-wire/src/general.rs +++ b/pulse-wire/src/general.rs @@ -2,7 +2,7 @@ use pulse_macros::pwp; use crate::{ PulseWire, - units::{Symbol, USD}, + units::{Direction, Symbol, USD}, }; #[pwp] @@ -20,9 +20,11 @@ pub enum Volatility { } #[pwp] -pub enum Direction { - Buy, - Sell, +pub enum LogKind { + Info, + Warn, + Err, + Debug, } #[pwp] @@ -36,14 +38,6 @@ pub struct Signal { pub stop_loss: USD, } -#[pwp] -pub enum LogKind { - Info, - Warn, - Err, - Debug, -} - #[pwp] pub struct EventLog { pub kind: LogKind, diff --git a/pulse-wire/src/lib.rs b/pulse-wire/src/lib.rs index fba6f5d..689db77 100644 --- a/pulse-wire/src/lib.rs +++ b/pulse-wire/src/lib.rs @@ -2,11 +2,15 @@ use std::path::PathBuf; pub mod general; +pub mod strategy; pub mod terminal; pub mod units; pub mod prelude { + pub use crate::PulseWire; pub use crate::general::*; + pub use crate::server_path; + pub use crate::strategy::*; pub use crate::terminal::*; pub use crate::units::*; } diff --git a/pulse-wire/src/strategy.rs b/pulse-wire/src/strategy.rs new file mode 100644 index 0000000..bab45e5 --- /dev/null +++ b/pulse-wire/src/strategy.rs @@ -0,0 +1,18 @@ +use crate::{PulseWire, units::TimeFrame}; +use pulse_macros::pwp; + +#[pwp] +#[derive(serde::Deserialize, serde::Serialize)] +pub struct Strategy { + name: String, + timeframes: Vec, + description: String, + author: String, +} + +#[pwp] +#[derive(serde::Deserialize, serde::Serialize)] +pub struct Risk { + name: String, + max_loss: u8, +} diff --git a/pulse-wire/src/terminal.rs b/pulse-wire/src/terminal.rs index 79d901b..6851c2d 100644 --- a/pulse-wire/src/terminal.rs +++ b/pulse-wire/src/terminal.rs @@ -1,6 +1,7 @@ use crate::{ PulseWire, general::{EventLog, MarketTrend, Position, Signal, Volatility}, + strategy::{Risk, Strategy}, units::{Symbol, USD}, }; use pulse_macros::pwp; @@ -100,6 +101,12 @@ pub enum Feed { Failed, } +#[pwp] +pub struct StrategyView { + strategy: Strategy, + risk: Risk, +} + impl std::fmt::Display for AlertLevel { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/pulse-wire/src/units.rs b/pulse-wire/src/units.rs index b43000a..a59c668 100644 --- a/pulse-wire/src/units.rs +++ b/pulse-wire/src/units.rs @@ -1,3 +1,5 @@ +use pulse_macros::pwp; + use crate::PulseWire; #[derive(Debug, Clone)] @@ -6,6 +8,34 @@ pub struct Symbol(pub String); #[derive(Debug, Clone, Copy)] pub struct USD(pub f64); +#[pwp] +#[derive(Copy, serde::Deserialize, serde::Serialize)] +pub enum TimeFrame { + M1, + M3, + M5, + M15, + M30, + + H1, + H2, + H4, + H8, + H12, + + D1, + D3, + + W1, + Month1, +} + +#[pwp] +pub enum Direction { + Buy, + Sell, +} + impl std::fmt::Display for Symbol { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "\x1b[35m{}\x1b[0m", self.0) diff --git a/src/engine/engine.rs b/src/engine/engine.rs index 9b3dfe7..438b06c 100644 --- a/src/engine/engine.rs +++ b/src/engine/engine.rs @@ -1,15 +1,10 @@ -use std::sync::Arc; - -use pulse_wire::{ - general::Position, - units::{Symbol, USD}, -}; -use tokio::{sync::Mutex, task::JoinHandle}; - use crate::{ store::{accounts::AccountList, config::Config}, terminal::TerminalServer, }; +use pulse_wire::prelude::*; +use std::sync::Arc; +use tokio::{sync::Mutex, task::JoinHandle}; #[derive(Debug, Clone)] pub struct Engine { diff --git a/src/engine/fetch.rs b/src/engine/fetch.rs index d09be91..28a3733 100644 --- a/src/engine/fetch.rs +++ b/src/engine/fetch.rs @@ -1,10 +1,6 @@ -use std::collections::HashMap; - -use pulse_wire::{ - terminal::MarketItem, - units::{Symbol, USD}, -}; +use pulse_wire::prelude::*; use serde_json::Value; +use std::collections::HashMap; fn number(value: &Value, field: &str) -> Result { let raw = value[field] diff --git a/src/engine/terminal.rs b/src/engine/terminal.rs index 72666c2..7de2c44 100644 --- a/src/engine/terminal.rs +++ b/src/engine/terminal.rs @@ -1,9 +1,9 @@ +use crate::engine::Engine; +use pulse_wire::prelude::*; use std::{ collections::HashMap, sync::{Arc, Weak}, }; - -use pulse_wire::{PulseWire, general::{EventLog, LogKind}, terminal::TerminalClientMessage}; use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, net::{ @@ -13,8 +13,6 @@ use tokio::{ sync::Mutex, }; -use crate::engine::Engine; - #[derive(Debug)] pub struct TerminalServer { clients: Mutex>, diff --git a/src/terminal/formatting.rs b/src/terminal/formatting.rs index 6a21e0a..a408d88 100644 --- a/src/terminal/formatting.rs +++ b/src/terminal/formatting.rs @@ -1,9 +1,4 @@ -use pulse_wire::terminal::{ - Alert, AlertLevel, InspectItem, InspectTarget, MarketItem, MarketOverview, Status, -}; - -use pulse_wire::general::{Direction, EventLog, LogKind, Position, Signal}; -use pulse_wire::units::format_f64; +use pulse_wire::prelude::*; pub trait Formatted { fn get_formatted(&self) -> Vec; diff --git a/src/terminal/main.rs b/src/terminal/main.rs index fa224d0..6e7e9d5 100644 --- a/src/terminal/main.rs +++ b/src/terminal/main.rs @@ -21,10 +21,7 @@ use pulse_ui::{ use crate::formatting::{Formatted, apply_padding}; -use pulse_wire::{ - general::{EventLog, Position, Signal}, - terminal::{InspectTarget, MarketItem, MarketOverview, Status}, -}; +use pulse_wire::prelude::*; pub struct PulseTradeApp { sock: Option, diff --git a/src/terminal/terminal.rs b/src/terminal/terminal.rs index 763cae7..98fac0c 100644 --- a/src/terminal/terminal.rs +++ b/src/terminal/terminal.rs @@ -1,4 +1,5 @@ -use pulse_wire::{PulseWire, server_path, terminal::TerminalServerMessage}; +use pulse_wire::prelude::*; + use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, net::{ From acb81b4f27db5f487f76d75c7c6336eecfb15bd5 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Sat, 25 Jul 2026 17:32:19 +0200 Subject: [PATCH 2/7] Improve timeframe and other stuff --- Cargo.toml | 3 +- pulse-wire/Cargo.toml | 2 +- pulse-wire/src/general.rs | 25 --------------- pulse-wire/src/terminal.rs | 4 +-- pulse-wire/src/units.rs | 64 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 69 insertions(+), 29 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f70095e..8780172 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ hypersdk = "0.2.14" serde_json = "1" rand = "0.8.7" toml = "1.1.3" -serde = { version = "1.0.229", features = ["serde_derive"] } +serde = { workspace = true } [workspace] members = ["pulse-macros", "pulse-ui", "pulse-wire"] @@ -33,6 +33,7 @@ pulse-wire = { path = "pulse-wire", version = "0.1.0-alpha.0" } tokio = "1.52.3" crossterm = "0.29.0" +serde = { version = "1.0.229", features = ["serde_derive"] } [[bin]] name = "pulse-trader" diff --git a/pulse-wire/Cargo.toml b/pulse-wire/Cargo.toml index 10ed64f..81b1d0c 100644 --- a/pulse-wire/Cargo.toml +++ b/pulse-wire/Cargo.toml @@ -5,4 +5,4 @@ edition = "2024" [dependencies] pulse-macros = { workspace = true } -serde = { version = "1.0.229", features = ["serde_derive"] } +serde = { workspace = true } diff --git a/pulse-wire/src/general.rs b/pulse-wire/src/general.rs index 78ca796..353cb24 100644 --- a/pulse-wire/src/general.rs +++ b/pulse-wire/src/general.rs @@ -12,13 +12,6 @@ pub enum MarketTrend { Neutral, } -#[pwp] -pub enum Volatility { - Low, - Medium, - High, -} - #[pwp] pub enum LogKind { Info, @@ -53,15 +46,6 @@ pub struct Position { pub profit: USD, } -impl std::fmt::Display for Direction { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Buy => write!(f, "BUY"), - Self::Sell => write!(f, "SELL"), - } - } -} - impl std::fmt::Display for MarketTrend { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -72,15 +56,6 @@ impl std::fmt::Display for MarketTrend { } } -impl std::fmt::Display for Volatility { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Low => write!(f, "Low"), - Self::Medium => write!(f, "Medium"), - Self::High => write!(f, "High"), - } - } -} impl std::fmt::Display for LogKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/pulse-wire/src/terminal.rs b/pulse-wire/src/terminal.rs index 6851c2d..c137889 100644 --- a/pulse-wire/src/terminal.rs +++ b/pulse-wire/src/terminal.rs @@ -1,8 +1,8 @@ use crate::{ PulseWire, - general::{EventLog, MarketTrend, Position, Signal, Volatility}, + general::{EventLog, MarketTrend, Position, Signal}, strategy::{Risk, Strategy}, - units::{Symbol, USD}, + units::{Symbol, USD, Volatility}, }; use pulse_macros::pwp; diff --git a/pulse-wire/src/units.rs b/pulse-wire/src/units.rs index a59c668..06e84ae 100644 --- a/pulse-wire/src/units.rs +++ b/pulse-wire/src/units.rs @@ -11,22 +11,36 @@ pub struct USD(pub f64); #[pwp] #[derive(Copy, serde::Deserialize, serde::Serialize)] pub enum TimeFrame { + #[serde(rename = "1m")] M1, + #[serde(rename = "3m")] M3, + #[serde(rename = "5m")] M5, + #[serde(rename = "15m")] M15, + #[serde(rename = "30m")] M30, + #[serde(rename = "1h")] H1, + #[serde(rename = "2h")] H2, + #[serde(rename = "4h")] H4, + #[serde(rename = "8h")] H8, + #[serde(rename = "12h")] H12, + #[serde(rename = "1d")] D1, + #[serde(rename = "3d")] D3, + #[serde(rename = "1w")] W1, + #[serde(rename = "1M")] Month1, } @@ -36,6 +50,13 @@ pub enum Direction { Sell, } +#[pwp] +pub enum Volatility { + Low, + Medium, + High, +} + impl std::fmt::Display for Symbol { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "\x1b[35m{}\x1b[0m", self.0) @@ -126,3 +147,46 @@ pub fn format_f64(value: f64) -> String { formatted } + +impl TimeFrame { + pub fn as_str(&self) -> &'static str { + match self { + Self::M1 => "1m", + Self::M3 => "3m", + Self::M5 => "5m", + Self::M15 => "15m", + Self::M30 => "30m", + + Self::H1 => "1h", + Self::H2 => "2h", + Self::H4 => "4h", + Self::H8 => "8h", + Self::H12 => "12h", + + Self::D1 => "1d", + Self::D3 => "3d", + + Self::W1 => "1w", + Self::Month1 => "1M", + } + } +} + +impl std::fmt::Display for Direction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Buy => write!(f, "BUY"), + Self::Sell => write!(f, "SELL"), + } + } +} + +impl std::fmt::Display for Volatility { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Low => write!(f, "Low"), + Self::Medium => write!(f, "Medium"), + Self::High => write!(f, "High"), + } + } +} From 083e0bdd8b9170c9bb049dc43c18225e6dd2fcc4 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Sat, 25 Jul 2026 17:45:41 +0200 Subject: [PATCH 3/7] Manfiest and Strategy data --- pulse-wire/src/strategy.rs | 13 ++++++++++--- pulse-wire/src/terminal.rs | 31 ++++++++++++++++++++----------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/pulse-wire/src/strategy.rs b/pulse-wire/src/strategy.rs index bab45e5..757ac70 100644 --- a/pulse-wire/src/strategy.rs +++ b/pulse-wire/src/strategy.rs @@ -3,16 +3,23 @@ use pulse_macros::pwp; #[pwp] #[derive(serde::Deserialize, serde::Serialize)] -pub struct Strategy { +pub struct StrategyManifest { name: String, - timeframes: Vec, description: String, author: String, + version: String, + + timeframes: Vec, } #[pwp] #[derive(serde::Deserialize, serde::Serialize)] -pub struct Risk { +pub struct RiskManifest { name: String, + description: String, + author: String, + version: String, + max_loss: u8, + cooldown: TimeFrame, } diff --git a/pulse-wire/src/terminal.rs b/pulse-wire/src/terminal.rs index c137889..ab83339 100644 --- a/pulse-wire/src/terminal.rs +++ b/pulse-wire/src/terminal.rs @@ -1,8 +1,8 @@ use crate::{ PulseWire, general::{EventLog, MarketTrend, Position, Signal}, - strategy::{Risk, Strategy}, - units::{Symbol, USD, Volatility}, + strategy::{RiskManifest, StrategyManifest}, + units::{Symbol, TimeFrame, USD, Volatility}, }; use pulse_macros::pwp; @@ -87,24 +87,33 @@ pub enum InspectItem { #[pwp] pub struct Status { - feed: Feed, + feed: Mode, exchange: String, dex: String, latency: u16, } #[pwp] -pub enum Feed { - Connected, - Disconnected, - Connecting, - Failed, +pub enum Mode { + Auto, + Manual, } #[pwp] -pub struct StrategyView { - strategy: Strategy, - risk: Risk, +pub enum ItemState { + Running, + Off, + Error, +} + +#[pwp] +pub struct Strategy { + strategy: StrategyManifest, + risk: RiskManifest, + + mode: Mode, + state: ItemState, + cooldown: TimeFrame, } impl std::fmt::Display for AlertLevel { From 009f8b76b33268dbf8368aba00219a9f6a8728f0 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Sat, 25 Jul 2026 17:50:20 +0200 Subject: [PATCH 4/7] replaced market overview --- pulse-wire/src/terminal.rs | 4 ++-- src/terminal/formatting.rs | 8 +++++++- src/terminal/main.rs | 12 +++--------- src/terminal/terminal.rs | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pulse-wire/src/terminal.rs b/pulse-wire/src/terminal.rs index ab83339..16e422b 100644 --- a/pulse-wire/src/terminal.rs +++ b/pulse-wire/src/terminal.rs @@ -14,8 +14,8 @@ pub enum TerminalServerMessage { // Positions PositionsUpdated(Vec), - // Overview - OverviewUpdated(MarketOverview), + // Strategy + StrategyUpdated(Strategy), // Signals SignalsUpdated(Vec), diff --git a/src/terminal/formatting.rs b/src/terminal/formatting.rs index a408d88..66190b3 100644 --- a/src/terminal/formatting.rs +++ b/src/terminal/formatting.rs @@ -151,7 +151,7 @@ impl Formatted for Status { impl Formatted for Property { fn get_formatted(&self) -> Vec { - vec![format!("\x1b[91m{}\x1b[0m", self.0), self.1.clone()] + vec![self.0.to_string(), self.1.clone()] } } @@ -195,6 +195,12 @@ impl Formatted for Vec { } } +impl Formatted for Strategy { + fn get_formatted(&self) -> Vec { + vec![format!("1"), format!("2")] + } +} + pub fn apply_padding(mut items: Vec) -> Vec { for item in &mut items { item.insert(0, ' '); diff --git a/src/terminal/main.rs b/src/terminal/main.rs index 6e7e9d5..1a3c8e4 100644 --- a/src/terminal/main.rs +++ b/src/terminal/main.rs @@ -29,7 +29,7 @@ pub struct PulseTradeApp { command: State, scroll: State>, - market_overview: State>, + strategy: State>, status: State>, watch_list: State>, @@ -134,13 +134,7 @@ impl App for PulseTradeApp { ( LayoutItem::Widget(Size::Flex(1)), Box::new( - advanced_option_draw( - &self.scroll, - 2, - "MARKET OVERVIEW", - &self.market_overview, - ) - .await, + advanced_option_draw(&self.scroll, 2, "STRATEGY", &self.strategy).await, ), ), ]), @@ -187,7 +181,7 @@ async fn main() -> tokio::io::Result<()> { signals: ctx.use_state(Vec::new()), logs: ctx.use_state(Vec::new()), inspect: ctx.use_state(InspectTarget::None), - market_overview: ctx.use_state(None), + strategy: ctx.use_state(None), status: ctx.use_state(None), }) }) diff --git a/src/terminal/terminal.rs b/src/terminal/terminal.rs index 98fac0c..468a20d 100644 --- a/src/terminal/terminal.rs +++ b/src/terminal/terminal.rs @@ -48,7 +48,7 @@ impl TerminalClient { let active_positions = app.active_positions.clone(); let logs = app.logs.clone(); let signals = app.signals.clone(); - let market_overview = app.market_overview.clone(); + let market_overview = app.strategy.clone(); let status = app.status.clone(); let inspect = app.inspect.clone(); @@ -78,7 +78,7 @@ impl TerminalClient { *active_positions.lock().await = v; } - TerminalServerMessage::OverviewUpdated(v) => { + TerminalServerMessage::StrategyUpdated(v) => { *market_overview.lock().await = Some(v); } From 72901540258ba56b2e342d5f02d76bb6e7f1000e Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Sat, 25 Jul 2026 18:20:22 +0200 Subject: [PATCH 5/7] Strategy formatting --- pulse-wire/src/terminal.rs | 19 +++++++++++ src/terminal/formatting.rs | 66 ++++++++++++++++++++++++++++++-------- src/terminal/main.rs | 27 +++++++++++++++- 3 files changed, 97 insertions(+), 15 deletions(-) diff --git a/pulse-wire/src/terminal.rs b/pulse-wire/src/terminal.rs index 16e422b..5b26c2c 100644 --- a/pulse-wire/src/terminal.rs +++ b/pulse-wire/src/terminal.rs @@ -125,3 +125,22 @@ impl std::fmt::Display for AlertLevel { } } } + +impl std::fmt::Display for Mode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Auto => write!(f, "\x1b[96mAUTO\x1b[0m"), + Self::Manual => write!(f, "\x1b[97mMANUAL\x1b[0m"), + } + } +} + +impl std::fmt::Display for ItemState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Running => write!(f, "\x1b[92mRUNNING\x1b[0m"), + Self::Off => write!(f, "\x1b[90mOFF\x1b[0m"), + Self::Error => write!(f, "\x1b[91mERROR\x1b[0m"), + } + } +} diff --git a/src/terminal/formatting.rs b/src/terminal/formatting.rs index 66190b3..0978493 100644 --- a/src/terminal/formatting.rs +++ b/src/terminal/formatting.rs @@ -110,16 +110,16 @@ impl Formatted for Position { } } -struct Property(&'static str, String); +struct Pair<'a>(&'a str, &'a str); impl Formatted for MarketOverview { fn get_formatted(&self) -> Vec { let mut o = vec![ - Property("TREND", format!("{}", self.trend)), - Property("VOLATILITY", format!("{}", self.volatility)), - Property( + Pair("TREND", &format!("{}", self.trend)), + Pair("VOLATILITY", &format!("{}", self.volatility)), + Pair( "PRESSURE", - if self.pressure.is_sign_positive() { + &if self.pressure.is_sign_positive() { format!("\x1b[32m{:.3}\x1b[0m", self.pressure) } else { format!("\x1b[31m{:.3}\x1b[0m", self.pressure) @@ -140,21 +140,44 @@ impl Formatted for MarketOverview { impl Formatted for Status { fn get_formatted(&self) -> Vec { vec![ - Property("Feed", format!("{:?}", self.feed)), - Property("Exchange", self.exchange.clone()), - Property("DEX", self.dex.clone()), - Property("Latency", format!("{} ms", self.latency)), + Pair("Feed", &format!("{:?}", self.feed)), + Pair("Exchange", &self.exchange.clone()), + Pair("DEX", &self.dex.clone()), + Pair("Latency", &format!("{} ms", self.latency)), ] .get_formatted() } } -impl Formatted for Property { +impl<'a> Formatted for Pair<'a> { fn get_formatted(&self) -> Vec { - vec![self.0.to_string(), self.1.clone()] + vec![self.0.to_string(), self.1.to_string()] } } +fn visible_len(s: &str) -> usize { + let mut len = 0; + let mut chars = s.chars().peekable(); + + while let Some(c) = chars.next() { + if c == '\x1b' { + if chars.peek() == Some(&'[') { + chars.next(); + + while let Some(c) = chars.next() { + if c.is_ascii_alphabetic() { + break; + } + } + } + } else { + len += c.len_utf8(); + } + } + + len +} + impl Formatted for Vec { fn get_formatted(&self) -> Vec { let mut output = Vec::new(); @@ -171,7 +194,7 @@ impl Formatted for Vec { for row in &rows { for (i, col) in row.iter().enumerate() { - widths[i] = widths[i].max(col.len()); + widths[i] = widths[i].max(visible_len(col)); } } @@ -181,7 +204,8 @@ impl Formatted for Vec { for (col_idx, col) in row.iter().enumerate() { let width = widths[col_idx]; - row_out.push_str(&format!("{: Formatted for Vec { impl Formatted for Strategy { fn get_formatted(&self) -> Vec { - vec![format!("1"), format!("2")] + vec![ + Pair("", ""), + Pair("\x1b[2mName\x1b[0m", "\x1b[2mRisk\x1b[0m"), + Pair(&self.strategy.name.to_string(), &self.risk.name.to_string()), + Pair("", ""), + Pair("\x1b[2mMode\x1b[0m", "\x1b[2mState\x1b[0m"), + Pair(&self.mode.to_string(), &self.state.to_string()), + Pair("", ""), + Pair("\x1b[2mCooldown\x1b[0m", "\x1b[2mTimeframes\x1b[0m"), + Pair( + &format!("{:?} ({:?} rec)", self.cooldown, self.risk.cooldown), + &format!("{:?}", self.strategy.timeframes), + ), + ] + .get_formatted() } } diff --git a/src/terminal/main.rs b/src/terminal/main.rs index 1a3c8e4..5555e58 100644 --- a/src/terminal/main.rs +++ b/src/terminal/main.rs @@ -181,7 +181,32 @@ async fn main() -> tokio::io::Result<()> { signals: ctx.use_state(Vec::new()), logs: ctx.use_state(Vec::new()), inspect: ctx.use_state(InspectTarget::None), - strategy: ctx.use_state(None), + strategy: ctx.use_state(Some(Strategy { + strategy: StrategyManifest { + name: "Liquidity Sweep".to_string(), + description: + "Detects liquidity grabs around key support and resistance levels." + .to_string(), + author: "Klesty Selimaj".to_string(), + version: "1.0.0".to_string(), + + timeframes: vec![TimeFrame::M5, TimeFrame::M15, TimeFrame::H1], + }, + + risk: RiskManifest { + name: "Aggressive".to_string(), + description: "High-risk profile with larger position sizing.".to_string(), + author: "Klesty Selimaj".to_string(), + version: "1.0.0".to_string(), + + max_loss: 5, + cooldown: TimeFrame::M15, + }, + + mode: Mode::Auto, + state: ItemState::Running, + cooldown: TimeFrame::M15, + })), status: ctx.use_state(None), }) }) From 5ec0415e520269af5d300148dd40f6b08154b46a Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Sat, 25 Jul 2026 18:29:10 +0200 Subject: [PATCH 6/7] Improved strategy formatting --- src/terminal/formatting.rs | 53 ++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/src/terminal/formatting.rs b/src/terminal/formatting.rs index 0978493..43a6ffc 100644 --- a/src/terminal/formatting.rs +++ b/src/terminal/formatting.rs @@ -112,6 +112,8 @@ impl Formatted for Position { struct Pair<'a>(&'a str, &'a str); +struct Triple<'a>(&'a str, &'a str, &'a str); + impl Formatted for MarketOverview { fn get_formatted(&self) -> Vec { let mut o = vec![ @@ -155,6 +157,12 @@ impl<'a> Formatted for Pair<'a> { } } +impl<'a> Formatted for Triple<'a> { + fn get_formatted(&self) -> Vec { + vec![self.0.to_string(), self.1.to_string(), self.2.to_string()] + } +} + fn visible_len(s: &str) -> usize { let mut len = 0; let mut chars = s.chars().peekable(); @@ -222,17 +230,40 @@ impl Formatted for Vec { impl Formatted for Strategy { fn get_formatted(&self) -> Vec { vec![ - Pair("", ""), - Pair("\x1b[2mName\x1b[0m", "\x1b[2mRisk\x1b[0m"), - Pair(&self.strategy.name.to_string(), &self.risk.name.to_string()), - Pair("", ""), - Pair("\x1b[2mMode\x1b[0m", "\x1b[2mState\x1b[0m"), - Pair(&self.mode.to_string(), &self.state.to_string()), - Pair("", ""), - Pair("\x1b[2mCooldown\x1b[0m", "\x1b[2mTimeframes\x1b[0m"), - Pair( - &format!("{:?} ({:?} rec)", self.cooldown, self.risk.cooldown), - &format!("{:?}", self.strategy.timeframes), + Triple( + "\x1b[2mName\x1b[0m", + "\x1b[2mRisk\x1b[0m", + "\x1b[2mStrat Ver\x1b[0m", + ), + Triple( + &format!("\x1b[97m{}\x1b[0m", self.strategy.name), + &format!("\x1b[93m{}\x1b[0m", self.risk.name), + &format!("\x1b[90m{}\x1b[0m", self.strategy.version), + ), + Triple("", "", ""), + Triple( + "\x1b[2mMode\x1b[0m", + "\x1b[2mState\x1b[0m", + "\x1b[2mRisk Ver\x1b[0m", + ), + Triple( + &self.mode.to_string(), + &self.state.to_string(), + &format!("\x1b[90m{}\x1b[0m", self.risk.version), + ), + Triple("", "", ""), + Triple( + "\x1b[2mCooldown\x1b[0m", + "\x1b[2mTimeframes\x1b[0m", + "\x1b[2mMax loss\x1b[0m", + ), + Triple( + &format!( + "\x1b[96m{:?}\x1b[0m (\x1b[90m{:?} rec\x1b[0m)", + self.cooldown, self.risk.cooldown + ), + &format!("\x1b[96m{:?}\x1b[0m", self.strategy.timeframes), + &format!("\x1b[93m{}%\x1b[0m", self.risk.max_loss), ), ] .get_formatted() From 0337e484cd43c4aa53fe46312ebc7f9dd6f1f21d Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Sat, 25 Jul 2026 18:40:17 +0200 Subject: [PATCH 7/7] Swap signals with strategy --- src/terminal/main.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/terminal/main.rs b/src/terminal/main.rs index 5555e58..da08d43 100644 --- a/src/terminal/main.rs +++ b/src/terminal/main.rs @@ -133,9 +133,7 @@ impl App for PulseTradeApp { ), ( LayoutItem::Widget(Size::Flex(1)), - Box::new( - advanced_option_draw(&self.scroll, 2, "STRATEGY", &self.strategy).await, - ), + Box::new(advanced_draw(&self.scroll, 2, "SIGNALS", &self.signals).await), ), ]), ); @@ -145,7 +143,9 @@ impl App for PulseTradeApp { SpacedColumns(vec![ ( LayoutItem::Widget(Size::Flex(1)), - Box::new(advanced_draw(&self.scroll, 3, "SIGNALS", &self.signals).await), + Box::new( + advanced_option_draw(&self.scroll, 3, "STRATEGY", &self.strategy).await, + ), ), ( LayoutItem::Widget(Size::Flex(1)),