import sys import asyncio import yaml from PySide6.QtWidgets import QApplication from loguru import logger from sqlalchemy.orm import sessionmaker from ui.main_window import MainWindow from core.broker import FyersBroker from core.data_feed import FyersDataFeed from storage.database import get_engine, init_db from core.order_manager import OrderManager from core.strategy_engine import StrategyEngine from core.risk_manager import RiskManager async def on_connect(): """Callback on WebSocket connect.""" logger.success("Data feed connected.") async def on_close(): """Callback on WebSocket close.""" logger.warning("Data feed connection closed.") async def on_error(error_msg): """Callback for WebSocket errors.""" logger.error(f"Data feed error: {error_msg}") def setup_logging(settings: dict): """Configures the logger based on settings.""" logger.add( settings["logging"]["file"], level=settings["logging"]["level"], rotation="10 MB", ) async def run_backend( broker, data_feed, db_session, order_manager, risk_manager, strategy_engine, settings, ): try: # --- Broker and Core Components Initialization --- access_token = await broker.get_access_token() if access_token and broker.fyers: profile = await broker.get_profile() logger.info(f"Broker Profile: {profile}") # Now set the order_manager in risk_manager risk_manager.order_manager = order_manager # --- Connect and Run --- ws_access_token = f"{broker.client_id}:{access_token}" await data_feed.connect(ws_access_token) # Load strategy and subscribe to symbols required by it strategy_engine.load_strategy("simple_crossover") await data_feed.subscribe(["NSE:SBIN-EQ"]) logger.info("Auto-trading strategy is now running. Waiting for signals...") # Keep the application running to listen for ticks # The UI event loop will keep the application alive # asyncio.sleep(30) is removed as UI will manage lifetime else: logger.error( "Broker or token initialization failed. Check credentials and logs." ) except Exception as e: logger.opt(exception=True).critical( f"An error occurred during backend runtime: {e}" ) finally: # Cleanup should be handled by the UI's close event or a dedicated shutdown logger.info("Backend run function finished.") async def main(): """Main function to run the application.""" try: with open("config/settings.yaml", "r") as f: settings = yaml.safe_load(f) except FileNotFoundError: print("Error: settings.yaml not found. Please ensure the file exists.") sys.exit(1) except yaml.YAMLError as e: print(f"Error parsing settings.yaml: {e}") sys.exit(1) setup_logging(settings) logger.info(f"Starting {settings['app']['name']} v{settings['app']['version']}") # --- Database Initialization --- db_session = None try: db_path = settings["database"]["path"] engine = get_engine(db_path) init_db(engine) Session = sessionmaker(bind=engine) db_session = Session() logger.success("Database initialized successfully.") except Exception as e: logger.opt(exception=True).critical( f"An error occurred during database initialization: {e}" ) sys.exit(1) broker = None data_feed = None order_manager = None risk_manager = None strategy_engine = None try: broker = FyersBroker(settings=settings["fyers"]) risk_manager = RiskManager(order_manager=None, settings=settings, logger=logger) order_manager = OrderManager( broker=broker, db_session=db_session, risk_manager=risk_manager ) strategy_engine = StrategyEngine(order_manager=order_manager) data_feed = FyersDataFeed( fyers=broker.fyers, on_connect=on_connect, on_tick=strategy_engine.on_tick, on_close=on_close, on_error=on_error, ) # Launch UI and pass backend components app = QApplication(sys.argv) main_window = MainWindow( broker=broker, data_feed=data_feed, db_session=db_session, order_manager=order_manager, risk_manager=risk_manager, strategy_engine=strategy_engine, settings=settings, backend_runner=run_backend # Pass the async backend runner ) main_window.show() logger.info("Application UI started.") # Run the Qt event loop in a separate thread or integrate with asyncio # For simplicity, we'll let QApplication manage the main thread # and run the backend in the asyncio event loop managed by a QThread or similar # For now, simply run the Qt app. The backend needs to be started # from within the UI or via a separate asyncio loop. sys.exit(app.exec()) except Exception as e: logger.opt(exception=True).critical(f"An error occurred: {e}") finally: if db_session: db_session.close() logger.info("Application shutdown complete.") if __name__ == "__main__": asyncio.run(main())