sandbox_quant/ui/
app_state_v2.rs1use 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}