""" 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 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 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() restore_running_strategies() return app