sandbox_quant/
strategy_session.rs1use std::collections::HashSet;
2use std::path::{Path, PathBuf};
3
4use anyhow::{Context, Result};
5use serde::{Deserialize, Serialize};
6
7use crate::strategy_catalog::StrategyCatalog;
8
9#[derive(Debug, Clone)]
10pub struct LoadedStrategySession {
11 pub catalog: StrategyCatalog,
12 pub selected_source_tag: Option<String>,
13 pub enabled_source_tags: HashSet<String>,
14}
15
16#[derive(Debug, Serialize, Deserialize)]
17struct PersistedStrategySession {
18 selected_source_tag: String,
19 profiles: Vec<crate::strategy_catalog::StrategyProfile>,
20 #[serde(default)]
21 enabled_source_tags: Vec<String>,
22}
23
24fn strategy_session_path() -> PathBuf {
25 std::env::var("SQ_STRATEGY_SESSION_PATH")
26 .map(PathBuf::from)
27 .unwrap_or_else(|_| PathBuf::from("data/strategy_session.json"))
28}
29
30pub fn load_strategy_session(
31 default_symbol: &str,
32 config_fast: usize,
33 config_slow: usize,
34 min_ticks_between_signals: u64,
35) -> Result<Option<LoadedStrategySession>> {
36 let path = strategy_session_path();
37 load_strategy_session_from_path(
38 &path,
39 default_symbol,
40 config_fast,
41 config_slow,
42 min_ticks_between_signals,
43 )
44}
45
46pub fn load_strategy_session_from_path(
47 path: &Path,
48 default_symbol: &str,
49 config_fast: usize,
50 config_slow: usize,
51 min_ticks_between_signals: u64,
52) -> Result<Option<LoadedStrategySession>> {
53 if !path.exists() {
54 return Ok(None);
55 }
56
57 let payload = std::fs::read_to_string(&path)
58 .with_context(|| format!("failed to read {}", path.display()))?;
59 let persisted: PersistedStrategySession =
60 serde_json::from_str(&payload).context("failed to parse persisted strategy session json")?;
61
62 Ok(Some(LoadedStrategySession {
63 catalog: StrategyCatalog::from_profiles(
64 persisted.profiles,
65 default_symbol,
66 config_fast,
67 config_slow,
68 min_ticks_between_signals,
69 ),
70 selected_source_tag: Some(persisted.selected_source_tag),
71 enabled_source_tags: persisted.enabled_source_tags.into_iter().collect(),
72 }))
73}
74
75pub fn persist_strategy_session(
76 catalog: &StrategyCatalog,
77 selected_source_tag: &str,
78 enabled_source_tags: &HashSet<String>,
79) -> Result<()> {
80 let path = strategy_session_path();
81 persist_strategy_session_to_path(&path, catalog, selected_source_tag, enabled_source_tags)
82}
83
84pub fn persist_strategy_session_to_path(
85 path: &Path,
86 catalog: &StrategyCatalog,
87 selected_source_tag: &str,
88 enabled_source_tags: &HashSet<String>,
89) -> Result<()> {
90 if let Some(parent) = path.parent() {
91 std::fs::create_dir_all(parent)
92 .with_context(|| format!("failed to create {}", parent.display()))?;
93 }
94
95 let payload = PersistedStrategySession {
96 selected_source_tag: selected_source_tag.to_string(),
97 profiles: catalog.profiles().to_vec(),
98 enabled_source_tags: enabled_source_tags.iter().cloned().collect(),
99 };
100 let json = serde_json::to_string_pretty(&payload)
101 .context("failed to serialize persisted strategy session json")?;
102 std::fs::write(&path, json).with_context(|| format!("failed to write {}", path.display()))?;
103 Ok(())
104}