""" models.py — Data models for consumer terminal. Provides TickData, BarData, etc. that providers and data_server import. All classes accept and silently ignore unknown keyword arguments so PRO provider code that passes extra fields won't crash. """ from typing import Optional, Dict, Any from datetime import datetime def _make_flex(cls): """Decorator: wraps __init__ to silently drop unknown kwargs.""" orig = cls.__init__ import inspect params = set(inspect.signature(orig).parameters.keys()) - {'self'} def flex_init(self, *args, **kwargs): valid = {k: v for k, v in kwargs.items() if k in params} orig(self, *args, **valid) # Store extras as attributes for k, v in kwargs.items(): if k not in params: setattr(self, k, v) cls.__init__ = flex_init # Add to_dict if not present if not hasattr(cls, 'to_dict'): def to_dict(self): d = {} for k in params: d[k] = getattr(self, k, None) for k, v in self.__dict__.items(): if k not in d: d[k] = v return d cls.to_dict = to_dict return cls from dataclasses import dataclass @_make_flex @dataclass class TickData: ticker: str = "" symbol: str = "" bid: float = 0.0 ask: float = 0.0 last: float = 0.0 volume: float = 0.0 time: Optional[datetime] = None spread: float = 0.0 time_msc: int = 0 flags: int = 0 volume_real: float = 0.0 @_make_flex @dataclass class BarData: ticker: str = "" symbol: str = "" timeframe: str = "" time: Optional[datetime] = None open: float = 0.0 high: float = 0.0 low: float = 0.0 close: float = 0.0 volume: float = 0.0 tick_volume: float = 0.0 spread: int = 0 real_volume: float = 0.0 @_make_flex @dataclass class AccountInfo: balance: float = 0.0 equity: float = 0.0 margin: float = 0.0 free_margin: float = 0.0 currency: str = "USD" leverage: int = 100 server: str = "" login: int = 0 name: str = "" company: str = "" profit: float = 0.0 margin_level: float = 0.0 @_make_flex @dataclass class SymbolInfo: ticker: str = "" symbol: str = "" description: str = "" point: float = 0.0 digits: int = 5 tick_size: float = 0.0 tick_value: float = 0.0 volume_min: float = 0.01 volume_max: float = 100.0 volume_step: float = 0.01 trade_contract_size: float = 100000.0 currency_base: str = "" currency_profit: str = "" currency_margin: str = "" spread: int = 0 trade_mode: int = 0 trade_stops_level: int = 0 swap_long: float = 0.0 swap_short: float = 0.0 @_make_flex @dataclass class OrderRequest: symbol: str = "" ticker: str = "" direction: str = "" volume: float = 0.01 price: float = 0.0 sl: float = 0.0 tp: float = 0.0 comment: str = "" order_type: str = "market" @_make_flex @dataclass class OrderResult: success: bool = False order_id: int = 0 message: str = "" price: float = 0.0 volume: float = 0.0 retcode: int = 0 @_make_flex @dataclass class Position: ticket: int = 0 symbol: str = "" ticker: str = "" direction: str = "" volume: float = 0.0 price_open: float = 0.0 price_current: float = 0.0 sl: float = 0.0 tp: float = 0.0 profit: float = 0.0 comment: str = "" time_open: Optional[datetime] = None swap: float = 0.0 commission: float = 0.0 @_make_flex @dataclass class PendingOrder: """A resting (unfilled) order — LIMIT or STOP — on the broker.""" ticket: int = 0 symbol: str = "" ticker: str = "" direction: str = "" # "BUY" | "SELL" order_type: str = "" # "LIMIT" | "STOP" volume: float = 0.0 price_open: float = 0.0 sl: float = 0.0 tp: float = 0.0 comment: str = "" time_setup: Optional[datetime] = None