2025-12-27 04:12:50 +00:00

169 lines
5.4 KiB
Python

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())