Config presets
This commit is contained in:
@@ -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,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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()?;
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user