Skip to main content

sandbox_quant/
strategy_session.rs

1use 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 = serde_json::from_str(&payload)
60        .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}