diff --git a/pulse-ui/src/widget/spaced.rs b/pulse-ui/src/widget/spaced.rs index 8e28ce3..4623bfa 100644 --- a/pulse-ui/src/widget/spaced.rs +++ b/pulse-ui/src/widget/spaced.rs @@ -67,3 +67,37 @@ impl Widget for SpacedColumns { } } } + +pub struct SpacedRows2(pub Vec<(LayoutItem, Box)>); + +impl Widget for SpacedRows2 { + fn render(&self, scope: &mut crate::render::RenderScope) { + let alloc = scope.rect.allocate(&crate::layout::LayoutItem::Rows { + unit: Percent(100), + items: self.0.iter().map(|v| v.0.clone()).collect(), + }); + + for i in 0..alloc.widgets.len() { + let item = &self.0[i]; + + alloc.draw(i, &*item.1); + } + } +} + +pub struct SpacedColumns2(pub Vec<(LayoutItem, Box)>); + +impl Widget for SpacedColumns2 { + fn render(&self, scope: &mut crate::render::RenderScope) { + let alloc = scope.rect.allocate(&crate::layout::LayoutItem::Columns { + unit: Percent(100), + items: self.0.iter().map(|v| v.0.clone()).collect(), + }); + + for i in 0..alloc.widgets.len() { + let item = &self.0[i]; + + alloc.draw(i, &*item.1); + } + } +} diff --git a/src/formatting.rs b/src/formatting.rs index db0936f..13b7b8f 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -1,11 +1,29 @@ use crate::types::{ - ActivePosition, EventLog, LogKind, MarketOverview, Signal, SignalKind, Status, WatchListItem, + ActivePosition, Alert, AlertLevel, EventLog, LogKind, MarketOverview, Signal, SignalKind, + Status, WatchListItem, }; pub trait Formatted { fn get_formatted(&self) -> Vec; } +impl Formatted for Alert { + fn get_formatted(&self) -> Vec { + vec![ + format!( + "{}{:?}\x1b[0m", + match &self.level { + AlertLevel::L => "\x1b[34m", + AlertLevel::M => "\x1b[33m", + AlertLevel::H => "\x1b[31m", + }, + self.level + ), + self.message.clone(), + ] + } +} + impl Formatted for EventLog { fn get_formatted(&self) -> Vec { vec![ @@ -80,7 +98,19 @@ pub struct Property(&'static str, String); impl Formatted for MarketOverview { fn get_formatted(&self) -> Vec { - vec![Property("Equity", format_f64(1.0))].get_formatted() + vec![ + Property("TREND", format!("{:?}", self.trend)), + Property("VOLATILITY", format!("{:?}", self.volatility)), + Property( + "PRESSURE", + if self.pressure.is_sign_positive() { + format!("\x1b[32m{:.3}\x1b[0m", self.pressure) + } else { + format!("\x1b[31m{:.3}\x1b[0m", self.pressure) + }, + ), + ] + .get_formatted() } } diff --git a/src/main.rs b/src/main.rs index 42bf9c3..746ad93 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,13 +13,13 @@ use pulse_ui::{ align::End, input::{Input, InputState}, outline::{Outline, VLine}, - spaced::SpacedColumns, + spaced::{SpacedColumns, SpacedColumns2, SpacedRows2}, }, }; use crate::{ formatting::{Formatted, apply_padding}, - types::{ActivePosition, EventLog, MarketOverview, Signal, Status, WatchListItem}, + types::{ActivePosition, Alert, EventLog, MarketOverview, Signal, Status, WatchListItem}, }; pub struct PulseTradeApp { @@ -33,7 +33,7 @@ pub struct PulseTradeApp { } impl App for PulseTradeApp { - async fn init(&mut self, ctx: &pulse_ui::state::Context) { + async fn init(&mut self, _ctx: &pulse_ui::state::Context) { let mut watch_list = self.watch_list.lock().await; watch_list.push(WatchListItem { @@ -110,6 +110,23 @@ impl App for PulseTradeApp { name: "pulse.init", message: "We're still not done yet ;)".to_string(), }); + + let mut market_overview = self.market_overview.lock().await; + + market_overview.alerts.push(Alert { + level: types::AlertLevel::H, + message: "BTC funding rate elevated".to_string(), + }); + + market_overview.alerts.push(Alert { + level: types::AlertLevel::M, + message: "Market volatility increasing".to_string(), + }); + + market_overview.alerts.push(Alert { + level: types::AlertLevel::L, + message: "ETH volatility returning to normal".to_string(), + }); } async fn update(&mut self, ctx: &pulse_ui::state::Context, event: Box) { @@ -192,13 +209,14 @@ impl App for PulseTradeApp { .join("\n") )), ), - ( - LayoutItem::Widget(Size::Flex(1)), + (LayoutItem::Widget(Size::Flex(1)), { + let mo = self.market_overview.lock().await; Box::new(format![ - " MARKET OVERVIEW\n{}", - apply_padding(self.market_overview.lock().await.get_formatted()).join("\n") - ]), - ), + " MARKET OVERVIEW\n{}\n\n{}", + apply_padding(mo.get_formatted()).join("\n"), + apply_padding(mo.alerts.get_formatted(),).join("\n") + ]) + }), ]), ); @@ -246,7 +264,12 @@ async fn main() { active_positions: ctx.use_state(Vec::new()), signals: ctx.use_state(Vec::new()), logs: ctx.use_state(Vec::new()), - market_overview: ctx.use_state(MarketOverview {}), + market_overview: ctx.use_state(MarketOverview { + trend: types::MarketTrend::Bullish, + volatility: types::Volatility::High, + pressure: 0.324, + alerts: Vec::new(), + }), status: ctx.use_state(Status { feed: types::Feed::Connected, exchange: "Binance".to_string(), diff --git a/src/types.rs b/src/types.rs index 27832d1..9f9f1b3 100644 --- a/src/types.rs +++ b/src/types.rs @@ -10,7 +10,27 @@ pub struct ActivePosition { pub amount: f64, } -pub struct MarketOverview {} +#[derive(Debug, Clone, Copy)] +pub enum MarketTrend { + Bullish, + Bearish, + Neutral, +} + +#[derive(Debug, Clone, Copy)] +pub enum Volatility { + Low, + Medium, + High, +} + +pub struct MarketOverview { + pub trend: MarketTrend, + pub volatility: Volatility, + pub pressure: f64, + + pub alerts: Vec, +} #[derive(Debug, Clone, Copy)] pub enum Feed { @@ -62,3 +82,16 @@ pub struct EventLog { pub name: &'static str, pub message: String, } + +#[derive(Debug, Clone, Copy)] +pub enum AlertLevel { + H, + M, + L, +} + +#[derive(Debug, Clone)] +pub struct Alert { + pub level: AlertLevel, + pub message: String, +}