"""Thread-safe shared state for UI and trading loop.""" import threading from dataclasses import dataclass, field from typing import Optional from datetime import datetime, timezone @dataclass class PositionState: """Current position information.""" trade_id: str = "" symbol: str = "" side: str = "" entry_price: float = 0.0 current_price: float = 0.0 size: float = 0.0 size_usdt: float = 0.0 unrealized_pnl: float = 0.0 unrealized_pnl_pct: float = 0.0 stop_loss_price: float = 0.0 take_profit_price: float = 0.0 @dataclass class StrategyState: """Current strategy signal state.""" z_score: float = 0.0 probability: float = 0.0 funding_rate: float = 0.0 last_action: str = "hold" last_reason: str = "" last_signal_time: str = "" @dataclass class AccountState: """Account balance information.""" balance: float = 0.0 available: float = 0.0 leverage: int = 1 class SharedState: """ Thread-safe shared state between trading loop and UI. All access to state fields should go through the getter/setter methods which use a lock for thread safety. """ def __init__(self): self._lock = threading.Lock() self._position: Optional[PositionState] = None self._strategy = StrategyState() self._account = AccountState() self._is_running = True self._last_cycle_time: Optional[str] = None self._mode = "DEMO" self._eth_symbol = "ETH/USDT:USDT" self._btc_symbol = "BTC/USDT:USDT" # Position methods def get_position(self) -> Optional[PositionState]: """Get current position state.""" with self._lock: return self._position def set_position(self, position: Optional[PositionState]) -> None: """Set current position state.""" with self._lock: self._position = position def update_position_price(self, current_price: float) -> None: """Update current price and recalculate PnL.""" with self._lock: if self._position is None: return self._position.current_price = current_price if self._position.side == "long": pnl = (current_price - self._position.entry_price) self._position.unrealized_pnl = pnl * self._position.size pnl_pct = (current_price / self._position.entry_price - 1) * 100 else: pnl = (self._position.entry_price - current_price) self._position.unrealized_pnl = pnl * self._position.size pnl_pct = (1 - current_price / self._position.entry_price) * 100 self._position.unrealized_pnl_pct = pnl_pct def clear_position(self) -> None: """Clear current position.""" with self._lock: self._position = None # Strategy methods def get_strategy(self) -> StrategyState: """Get current strategy state.""" with self._lock: return StrategyState( z_score=self._strategy.z_score, probability=self._strategy.probability, funding_rate=self._strategy.funding_rate, last_action=self._strategy.last_action, last_reason=self._strategy.last_reason, last_signal_time=self._strategy.last_signal_time, ) def update_strategy( self, z_score: float, probability: float, funding_rate: float, action: str, reason: str, ) -> None: """Update strategy state.""" with self._lock: self._strategy.z_score = z_score self._strategy.probability = probability self._strategy.funding_rate = funding_rate self._strategy.last_action = action self._strategy.last_reason = reason self._strategy.last_signal_time = datetime.now( timezone.utc ).isoformat() # Account methods def get_account(self) -> AccountState: """Get current account state.""" with self._lock: return AccountState( balance=self._account.balance, available=self._account.available, leverage=self._account.leverage, ) def update_account( self, balance: float, available: float, leverage: int, ) -> None: """Update account state.""" with self._lock: self._account.balance = balance self._account.available = available self._account.leverage = leverage # Control methods def is_running(self) -> bool: """Check if trading loop is running.""" with self._lock: return self._is_running def stop(self) -> None: """Signal to stop trading loop.""" with self._lock: self._is_running = False def get_last_cycle_time(self) -> Optional[str]: """Get last trading cycle time.""" with self._lock: return self._last_cycle_time def set_last_cycle_time(self, time_str: str) -> None: """Set last trading cycle time.""" with self._lock: self._last_cycle_time = time_str # Config methods def get_mode(self) -> str: """Get trading mode (DEMO/LIVE).""" with self._lock: return self._mode def set_mode(self, mode: str) -> None: """Set trading mode.""" with self._lock: self._mode = mode def get_symbols(self) -> tuple[str, str]: """Get trading symbols (eth, btc).""" with self._lock: return self._eth_symbol, self._btc_symbol def set_symbols(self, eth_symbol: str, btc_symbol: str) -> None: """Set trading symbols.""" with self._lock: self._eth_symbol = eth_symbol self._btc_symbol = btc_symbol