""" QuantDinger Python API - Flask application factory. """ from flask import Flask from flask_cors import CORS import logging import traceback from app.utils.logger import setup_logger, get_logger logger = get_logger(__name__) # Global singletons (avoid duplicate strategy threads). _trading_executor = None _pending_order_worker = None _reflection_worker = None def get_trading_executor(): """Get the trading executor singleton.""" global _trading_executor if _trading_executor is None: from app.services.trading_executor import TradingExecutor _trading_executor = TradingExecutor() return _trading_executor def get_pending_order_worker(): """Get the pending order worker singleton.""" global _pending_order_worker if _pending_order_worker is None: from app.services.pending_order_worker import PendingOrderWorker _pending_order_worker = PendingOrderWorker() return _pending_order_worker def get_reflection_worker(): """Get the reflection verification worker singleton.""" global _reflection_worker if _reflection_worker is None: from app.services.agents.reflection_worker import ReflectionWorker _reflection_worker = ReflectionWorker() return _reflection_worker def start_portfolio_monitor(): """Start the portfolio monitor service if enabled. To enable it, set ENABLE_PORTFOLIO_MONITOR=true. """ import os enabled = os.getenv("ENABLE_PORTFOLIO_MONITOR", "true").lower() == "true" if not enabled: logger.info("Portfolio monitor is disabled. Set ENABLE_PORTFOLIO_MONITOR=true to enable.") return # Avoid running twice with Flask reloader debug = os.getenv("PYTHON_API_DEBUG", "false").lower() == "true" if debug: if os.environ.get("WERKZEUG_RUN_MAIN") != "true": return try: from app.services.portfolio_monitor import start_monitor_service start_monitor_service() except Exception as e: logger.error(f"Failed to start portfolio monitor: {e}") def start_reflection_worker(): """ Start the reflection worker if enabled. To enable it, set ENABLE_REFLECTION_WORKER=true. """ import os enabled = os.getenv("ENABLE_REFLECTION_WORKER", "false").lower() == "true" if not enabled: logger.info("Reflection worker is disabled. Set ENABLE_REFLECTION_WORKER=true to enable.") return # Avoid running twice with Flask reloader debug = os.getenv("PYTHON_API_DEBUG", "false").lower() == "true" if debug: if os.environ.get("WERKZEUG_RUN_MAIN") != "true": return try: get_reflection_worker().start() except Exception as e: logger.error(f"Failed to start reflection worker: {e}") def start_pending_order_worker(): """Start the pending order worker (disabled by default in paper mode). To enable it, set ENABLE_PENDING_ORDER_WORKER=true. """ import os # Local deployment: default to enabled so queued orders can be dispatched automatically. # To disable it, set ENABLE_PENDING_ORDER_WORKER=false explicitly. if os.getenv('ENABLE_PENDING_ORDER_WORKER', 'true').lower() != 'true': logger.info("Pending order worker is disabled (paper mode). Set ENABLE_PENDING_ORDER_WORKER=true to enable.") return try: get_pending_order_worker().start() except Exception as e: logger.error(f"Failed to start pending order worker: {e}") def restore_running_strategies(): """ Restore running strategies on startup. Local deployment: only restores IndicatorStrategy. """ import os # You can disable auto-restore to avoid starting many threads on low-resource hosts. if os.getenv('DISABLE_RESTORE_RUNNING_STRATEGIES', 'false').lower() == 'true': logger.info("Startup strategy restore is disabled via DISABLE_RESTORE_RUNNING_STRATEGIES") return try: from app.services.strategy import StrategyService strategy_service = StrategyService() trading_executor = get_trading_executor() running_strategies = strategy_service.get_running_strategies_with_type() if not running_strategies: logger.info("No running strategies to restore.") return logger.info(f"Restoring {len(running_strategies)} running strategies...") restored_count = 0 for strategy_info in running_strategies: strategy_id = strategy_info['id'] strategy_type = strategy_info.get('strategy_type', '') try: if strategy_type and strategy_type != 'IndicatorStrategy': logger.info(f"Skip restore unsupported strategy type: id={strategy_id}, type={strategy_type}") continue success = trading_executor.start_strategy(strategy_id) strategy_type_name = 'IndicatorStrategy' if success: restored_count += 1 logger.info(f"[OK] {strategy_type_name} {strategy_id} restored") else: logger.warning(f"[FAIL] {strategy_type_name} {strategy_id} restore failed (state may be stale)") except Exception as e: logger.error(f"Error restoring strategy {strategy_id}: {str(e)}") logger.error(traceback.format_exc()) logger.info(f"Strategy restore completed: {restored_count}/{len(running_strategies)} restored") except Exception as e: logger.error(f"Failed to restore running strategies: {str(e)}") logger.error(traceback.format_exc()) # Do not raise; avoid breaking app startup. def create_app(config_name='default'): """ Flask application factory. Args: config_name: config name Returns: Flask app """ app = Flask(__name__) app.config['JSON_AS_ASCII'] = False CORS(app) setup_logger() from app.routes import register_routes register_routes(app) # Startup hooks. with app.app_context(): start_pending_order_worker() start_reflection_worker() start_portfolio_monitor() restore_running_strategies() return app