169 lines
5.4 KiB
Python
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()) |