1use std::collections::HashMap;
2
3use crate::model::candle::Candle;
4use crate::model::order::OrderSide;
5use crate::model::signal::Signal;
6use crate::model::tick::Tick;
7use crate::order_manager::{OrderHistorySnapshot, OrderHistoryStats, OrderUpdate};
8use crate::risk_module::RateBudgetSnapshot;
9
10#[derive(Debug, Clone)]
11pub enum WsConnectionStatus {
12 Connected,
13 Disconnected,
14 Reconnecting { attempt: u32, delay_ms: u64 },
15}
16
17#[derive(Debug, Clone)]
18pub enum AppEvent {
19 MarketTick(Tick),
20 StrategySignal {
21 signal: Signal,
22 symbol: String,
23 source_tag: String,
24 price: Option<f64>,
25 timestamp_ms: u64,
26 },
27 StrategyState {
28 fast_sma: Option<f64>,
29 slow_sma: Option<f64>,
30 },
31 OrderUpdate(OrderUpdate),
32 WsStatus(WsConnectionStatus),
33 HistoricalCandles {
34 candles: Vec<Candle>,
35 interval_ms: u64,
36 interval: String,
37 },
38 BalanceUpdate(HashMap<String, f64>),
39 OrderHistoryUpdate(OrderHistorySnapshot),
40 StrategyStatsUpdate {
41 strategy_stats: HashMap<String, OrderHistoryStats>,
42 },
43 PredictorMetricsUpdate {
44 symbol: String,
45 market: String,
46 predictor: String,
47 horizon: String,
48 r2: Option<f64>,
49 hit_rate: Option<f64>,
50 mae: Option<f64>,
51 sample_count: u64,
52 updated_at_ms: u64,
53 },
54 ExitPolicyUpdate {
55 symbol: String,
56 source_tag: String,
57 stop_price: Option<f64>,
58 expected_holding_ms: Option<u64>,
59 protective_stop_ok: Option<bool>,
60 },
61 PortfolioStateUpdate {
62 snapshot: PortfolioStateSnapshot,
63 },
64 RiskRateSnapshot {
65 global: RateBudgetSnapshot,
66 orders: RateBudgetSnapshot,
67 account: RateBudgetSnapshot,
68 market_data: RateBudgetSnapshot,
69 },
70 CloseAllRequested {
71 job_id: u64,
72 total: usize,
73 symbols: Vec<String>,
74 },
75 CloseAllProgress {
76 job_id: u64,
77 symbol: String,
78 completed: usize,
79 total: usize,
80 failed: usize,
81 reason: Option<String>,
82 },
83 CloseAllFinished {
84 job_id: u64,
85 completed: usize,
86 total: usize,
87 failed: usize,
88 },
89 TickDropped,
90 LogRecord(LogRecord),
91 LogMessage(String),
92 Error(String),
93}
94
95#[derive(Debug, Clone, Default)]
96pub struct PredictorMetricEntry {
97 pub symbol: String,
98 pub market: String,
99 pub predictor: String,
100 pub horizon: String,
101 pub r2: Option<f64>,
102 pub hit_rate: Option<f64>,
103 pub mae: Option<f64>,
104 pub sample_count: u64,
105 pub updated_at_ms: u64,
106}
107
108#[derive(Debug, Clone, Default)]
109pub struct ExitPolicyEntry {
110 pub stop_price: Option<f64>,
111 pub expected_holding_ms: Option<u64>,
112 pub protective_stop_ok: Option<bool>,
113 pub updated_at_ms: u64,
114}
115
116#[derive(Debug, Clone, Default)]
117pub struct AssetPnlEntry {
118 pub is_futures: bool,
119 pub side: Option<OrderSide>,
120 pub position_qty: f64,
121 pub entry_price: f64,
122 pub realized_pnl_usdt: f64,
123 pub unrealized_pnl_usdt: f64,
124}
125
126#[derive(Debug, Clone, Default)]
127pub struct PortfolioStateSnapshot {
128 pub by_symbol: HashMap<String, AssetPnlEntry>,
129 pub total_realized_pnl_usdt: f64,
130 pub total_unrealized_pnl_usdt: f64,
131 pub open_orders_count: usize,
132 pub reserved_cash_usdt: f64,
133 pub gross_exposure_usdt: f64,
134 pub net_exposure_usdt: f64,
135}
136
137#[derive(Debug, Clone, Copy, PartialEq, Eq)]
138pub enum LogLevel {
139 Debug,
140 Info,
141 Warn,
142 Error,
143}
144
145#[derive(Debug, Clone, Copy, PartialEq, Eq)]
146pub enum LogDomain {
147 Ws,
148 Strategy,
149 Risk,
150 Order,
151 Portfolio,
152 Ui,
153 System,
154}
155
156#[derive(Debug, Clone)]
157pub struct LogRecord {
158 pub ts_ms: u64,
159 pub level: LogLevel,
160 pub domain: LogDomain,
161 pub event: &'static str,
162 pub symbol: Option<String>,
163 pub strategy_tag: Option<String>,
164 pub trace_id: Option<String>,
165 pub msg: String,
166}
167
168impl LogRecord {
169 pub fn new(
170 level: LogLevel,
171 domain: LogDomain,
172 event: &'static str,
173 msg: impl Into<String>,
174 ) -> Self {
175 Self {
176 ts_ms: chrono::Utc::now().timestamp_millis() as u64,
177 level,
178 domain,
179 event,
180 symbol: None,
181 strategy_tag: None,
182 trace_id: None,
183 msg: msg.into(),
184 }
185 }
186}