Config presets

This commit is contained in:
2026-07-31 17:02:38 +02:00
parent b76f694aee
commit f9c67ce923
6 changed files with 141 additions and 43 deletions
+1
View File
@@ -63,5 +63,6 @@ pub enum InspectItem {
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct EngineConfig { pub struct EngineConfig {
pub preset: String,
pub strategy: StrategyManifest, pub strategy: StrategyManifest,
} }
+87 -31
View File
@@ -1,4 +1,4 @@
use crate::{engine::Engine, store::config::Config}; use crate::{engine::Engine, store::config::ConfigManager};
use pulse_sdk::prelude::*; use pulse_sdk::prelude::*;
use toml::Value; use toml::Value;
@@ -21,7 +21,7 @@ impl Engine {
match args[0] { match args[0] {
"reload" => { "reload" => {
*self.config.lock().await = Config::new().await?; *self.config.lock().await = ConfigManager::new().await?;
self.terminal_server self.terminal_server
.info("engine::config", "successfully reloaded") .info("engine::config", "successfully reloaded")
.await?; .await?;
@@ -50,17 +50,11 @@ impl Engine {
if args.len() == 3 { if args.len() == 3 {
match args[1] { match args[1] {
"watchlist" | "watch" | "wl" => { "watchlist" | "watch" | "wl" => {
set_cfg!(v, self.config.lock().await.watchlist = v); set_cfg!(v, self.config.lock().await.get_mut()?.watchlist = v);
self.terminal_server self.terminal_server
.info("config::set", "watchlist set successfully, use `config save` to persist changes") .info("config::set", "watchlist set successfully, use `config save` to persist changes")
.await?; .await?;
self.terminal_server
.broadcast(TerminalServerMessage::ConfigUpdated(
self.get_config_status().await,
))
.await?;
} }
"strategy" | "strat" | "sg" => { "strategy" | "strat" | "sg" => {
@@ -81,30 +75,12 @@ impl Engine {
} }
self.strategy_engine.reload(id.as_str()).await?; self.strategy_engine.reload(id.as_str()).await?;
self.config.lock().await.strategy = id; self.config.lock().await.get_mut()?.strategy = id;
}); });
self.terminal_server self.terminal_server
.info("config::set", "strategy set successfully, use `config save` to persist changes") .info("config::set", "strategy set successfully, use `config save` to persist changes")
.await?; .await?;
self.terminal_server
.broadcast(TerminalServerMessage::ConfigUpdated(
self.get_config_status().await,
))
.await?;
}
"cooldown" | "cool" | "cd" => {
set_cfg!(cooldown, {
self.config.lock().await.cooldown = cooldown;
});
self.terminal_server
.broadcast(TerminalServerMessage::ConfigUpdated(
self.get_config_status().await,
))
.await?;
} }
_ => { _ => {
@@ -113,6 +89,12 @@ impl Engine {
.await?; .await?;
} }
} }
self.terminal_server
.broadcast(TerminalServerMessage::ConfigUpdated(
self.get_config_status().await,
))
.await?;
} else { } else {
self.invalid_command_usage("engine::config").await?; self.invalid_command_usage("engine::config").await?;
} }
@@ -169,7 +151,7 @@ impl Engine {
.terminal_server .terminal_server
.error( .error(
"engine::account", "engine::account",
&format!("Account not found ({new_active})"), &format!("account not found ({new_active})"),
) )
.await; .await;
} }
@@ -179,7 +161,7 @@ impl Engine {
self.terminal_server self.terminal_server
.info( .info(
"engine::account", "engine::account",
&format!("Account set to {new_active} successfully!"), &format!("account set to {new_active} successfully!"),
) )
.await?; .await?;
} }
@@ -190,6 +172,80 @@ impl Engine {
} }
} }
"preset" => {
if args.len() == 0 {
return self.invalid_command_usage("engine::preset").await;
}
match args[0] {
"list" | "ls" => {
self.terminal_server
.info("engine::preset", "PRESET LIST")
.await?;
let config = self.config.lock().await;
for (name, cfg) in &config.configs {
let description = if let Some(desc) = &cfg.description {
format!("- {desc}")
} else {
String::new()
};
self.terminal_server
.info(
"engine::preset",
&if name == &config.preset {
format!("{name} (active) {description}")
} else {
format!("{name} {description}")
},
)
.await?;
}
}
"use" | "set" => {
if args.len() < 2 {
return self.invalid_command_usage("engine::preset").await;
}
let new_active = args[1];
let mut config = self.config.lock().await;
if !config.configs.contains_key(new_active) {
return self
.terminal_server
.error(
"engine::preset",
&format!("preset not found ({new_active})"),
)
.await;
}
config.preset = new_active.to_string();
self.terminal_server
.info(
"engine::preset",
&format!("preset set to {new_active} successfully!"),
)
.await?;
self.terminal_server
.broadcast(TerminalServerMessage::ConfigUpdated(
self.get_config_status().await,
))
.await?;
}
_ => {
return self.invalid_command_usage("engine::preset").await;
}
}
}
"strategy" | "strat" | "sg" => { "strategy" | "strat" | "sg" => {
let Some(strategy_command) = args.drain(0..=0).next() else { let Some(strategy_command) = args.drain(0..=0).next() else {
return self.invalid_command_usage("strategy").await; return self.invalid_command_usage("strategy").await;
@@ -211,7 +267,7 @@ impl Engine {
_ => { _ => {
self.terminal_server self.terminal_server
.error("engine::cmd", &format!("Command '{}' not found", command)) .error("engine::cmd", &format!("command '{}' not found", command))
.await?; .await?;
} }
} }
+5 -4
View File
@@ -5,7 +5,7 @@ pub mod terminal;
use crate::{ use crate::{
engine::{strategy::StrategyEngine, terminal::TerminalServer}, engine::{strategy::StrategyEngine, terminal::TerminalServer},
store::{accounts::AccountList, config::Config}, store::{accounts::AccountList, config::ConfigManager},
}; };
use hypersdk::hypercore::ws::ConnectionStream; use hypersdk::hypercore::ws::ConnectionStream;
use pulse_sdk::prelude::*; use pulse_sdk::prelude::*;
@@ -26,7 +26,7 @@ pub struct Engine {
pub ws_stream: Arc<Mutex<ConnectionStream>>, pub ws_stream: Arc<Mutex<ConnectionStream>>,
// data // data
pub config: Arc<Mutex<Config>>, pub config: Arc<Mutex<ConfigManager>>,
pub accounts: Arc<Mutex<AccountList>>, pub accounts: Arc<Mutex<AccountList>>,
// live data / status // live data / status
@@ -37,11 +37,11 @@ pub struct Engine {
impl Engine { impl Engine {
pub async fn new() -> tokio::io::Result<Arc<Self>> { pub async fn new() -> tokio::io::Result<Arc<Self>> {
let config = Config::new().await?; let config = ConfigManager::new().await?;
let (ws_handle, ws_stream) = hypersdk::hypercore::mainnet_ws().split(); let (ws_handle, ws_stream) = hypersdk::hypercore::mainnet_ws().split();
let strategy = StrategyEngine::new(&config.strategy, ws_handle).await?; let strategy = StrategyEngine::new(&config.get_ref()?.strategy, ws_handle).await?;
let accounts = Arc::new(Mutex::new(AccountList::new().await?)); let accounts = Arc::new(Mutex::new(AccountList::new().await?));
let config = Arc::new(Mutex::new(config)); let config = Arc::new(Mutex::new(config));
@@ -91,6 +91,7 @@ impl Engine {
pub async fn get_config_status(&self) -> EngineConfig { pub async fn get_config_status(&self) -> EngineConfig {
EngineConfig { EngineConfig {
preset: self.config.lock().await.preset.clone(),
strategy: self.strategy_engine.strategy.lock().await.manifest.clone(), strategy: self.strategy_engine.strategy.lock().await.manifest.clone(),
} }
} }
+5 -2
View File
@@ -194,8 +194,11 @@ impl TerminalServer {
loop { loop {
refresh.tick().await; refresh.tick().await;
match crate::fetch::fetch_watch_list(&client, &engine.config.lock().await.watchlist) match crate::fetch::fetch_watch_list(
.await &client,
&engine.config.lock().await.get_ref()?.watchlist,
)
.await
{ {
Ok(watch_list) => { Ok(watch_list) => {
*engine.watch_list.lock().await = watch_list.clone(); *engine.watch_list.lock().await = watch_list.clone();
+41 -4
View File
@@ -1,23 +1,60 @@
use hypersdk::hypercore::CandleInterval; use std::collections::HashMap;
#[derive(Debug, serde::Serialize, serde::Deserialize)] #[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct Config { pub struct Config {
pub description: Option<String>,
pub watchlist: Vec<String>, pub watchlist: Vec<String>,
pub strategy: String, pub strategy: String,
pub cooldown: CandleInterval, }
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct ConfigManager {
pub preset: String,
#[serde(flatten)]
pub configs: HashMap<String, Config>,
} }
impl Default for Config { impl Default for Config {
fn default() -> Self { fn default() -> Self {
Self { Self {
description: None,
watchlist: vec!["BTC".to_string(), "SOL".to_string(), "ETH".to_string()], watchlist: vec!["BTC".to_string(), "SOL".to_string(), "ETH".to_string()],
strategy: String::new(), strategy: String::new(),
cooldown: CandleInterval::ThirtyMinutes,
} }
} }
} }
impl Config { impl Default for ConfigManager {
fn default() -> Self {
Self {
preset: "Main".to_string(),
configs: HashMap::from([("Main".to_string(), Config::default())]),
}
}
}
impl ConfigManager {
pub fn get_ref(&self) -> tokio::io::Result<&Config> {
self.configs.get(&self.preset).ok_or_else(|| {
tokio::io::Error::new(
std::io::ErrorKind::NotFound,
format!("Failed to get preset '{}'", self.preset),
)
})
}
pub fn get_mut(&mut self) -> tokio::io::Result<&mut Config> {
self.configs.get_mut(&self.preset).ok_or_else(|| {
tokio::io::Error::new(
std::io::ErrorKind::NotFound,
format!("Failed to get preset '{}'", self.preset),
)
})
}
}
impl ConfigManager {
pub async fn new() -> tokio::io::Result<Self> { pub async fn new() -> tokio::io::Result<Self> {
let path = crate::store::pulse_config_file()?; let path = crate::store::pulse_config_file()?;
+2 -2
View File
@@ -217,11 +217,11 @@ impl Formatted for EngineConfig {
fn get_formatted(&self) -> Vec<String> { fn get_formatted(&self) -> Vec<String> {
vec![ vec![
Triple( Triple(
"\x1b[2mPreset\x1b[0m",
"\x1b[2mStrategy\x1b[0m", "\x1b[2mStrategy\x1b[0m",
"\x1b[2m..\x1b[0m", "\x1b[2m..\x1b[0m",
"\x1b[2m..\x1b[0m",
), ),
Triple(&self.strategy.name, "..", ".."), Triple(&self.preset, &self.strategy.name, ".."),
] ]
.get_formatted() .get_formatted()
} }