Skip to main content

sandbox_quant/ui/
app_state_v2.rs

1use std::collections::HashMap;
2
3use super::AppState;
4
5#[derive(Debug, Clone, Default)]
6pub struct PortfolioSummary {
7    pub total_equity_usdt: Option<f64>,
8    pub total_realized_pnl_usdt: f64,
9    pub total_unrealized_pnl_usdt: f64,
10    pub ws_connected: bool,
11}
12
13#[derive(Debug, Clone, Default)]
14pub struct AssetEntry {
15    pub symbol: String,
16    pub last_price: Option<f64>,
17    pub position_qty: f64,
18    pub realized_pnl_usdt: f64,
19    pub unrealized_pnl_usdt: f64,
20}
21
22#[derive(Debug, Clone, Default)]
23pub struct StrategyEntry {
24    pub strategy_id: String,
25    pub trade_count: u32,
26    pub win_count: u32,
27    pub lose_count: u32,
28    pub realized_pnl_usdt: f64,
29}
30
31#[derive(Debug, Clone, Default)]
32pub struct MatrixCell {
33    pub symbol: String,
34    pub strategy_id: String,
35    pub trade_count: u32,
36    pub realized_pnl_usdt: f64,
37}
38
39#[derive(Debug, Clone, Default)]
40pub struct FocusState {
41    pub symbol: Option<String>,
42    pub strategy_id: Option<String>,
43}
44
45#[derive(Debug, Clone, Default)]
46pub struct AppStateV2 {
47    pub portfolio: PortfolioSummary,
48    pub assets: Vec<AssetEntry>,
49    pub strategies: Vec<StrategyEntry>,
50    pub matrix: Vec<MatrixCell>,
51    pub focus: FocusState,
52}
53
54impl AppStateV2 {
55    pub fn new() -> Self {
56        Self::default()
57    }
58
59    pub fn from_legacy(state: &AppState) -> Self {
60        let mut strategy_rows = Vec::new();
61        let mut matrix_rows = Vec::new();
62        for (strategy_id, stats) in &state.strategy_stats {
63            strategy_rows.push(StrategyEntry {
64                strategy_id: strategy_id.clone(),
65                trade_count: stats.trade_count,
66                win_count: stats.win_count,
67                lose_count: stats.lose_count,
68                realized_pnl_usdt: stats.realized_pnl,
69            });
70            matrix_rows.push(MatrixCell {
71                symbol: state.symbol.clone(),
72                strategy_id: strategy_id.clone(),
73                trade_count: stats.trade_count,
74                realized_pnl_usdt: stats.realized_pnl,
75            });
76        }
77        strategy_rows.sort_by(|a, b| a.strategy_id.cmp(&b.strategy_id));
78        matrix_rows.sort_by(|a, b| {
79            a.symbol
80                .cmp(&b.symbol)
81                .then_with(|| a.strategy_id.cmp(&b.strategy_id))
82        });
83
84        let asset_row = AssetEntry {
85            symbol: state.symbol.clone(),
86            last_price: state.last_price(),
87            position_qty: state.position.qty,
88            realized_pnl_usdt: state.history_realized_pnl,
89            unrealized_pnl_usdt: state.position.unrealized_pnl,
90        };
91
92        Self {
93            portfolio: PortfolioSummary {
94                total_equity_usdt: state.current_equity_usdt,
95                total_realized_pnl_usdt: state.history_realized_pnl,
96                total_unrealized_pnl_usdt: state.position.unrealized_pnl,
97                ws_connected: state.ws_connected,
98            },
99            assets: vec![asset_row],
100            strategies: strategy_rows,
101            matrix: matrix_rows,
102            focus: FocusState {
103                symbol: Some(state.symbol.clone()),
104                strategy_id: Some(state.strategy_label.clone()),
105            },
106        }
107    }
108
109    pub fn strategy_lookup(&self) -> HashMap<String, StrategyEntry> {
110        self.strategies
111            .iter()
112            .cloned()
113            .map(|s| (s.strategy_id.clone(), s))
114            .collect()
115    }
116}