""" amazingData 数据源 API 路由 提供真实期货/股票数据接入接口 """ import logging from datetime import datetime, date, timedelta from typing import List, Optional from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session from app.db.init_db import get_sqlite_db, get_timescale_db from app.services.amazing_data_service import amazing_data_service, get_amazing_data_service from app.services.data_sync_service import DataSyncService from app.schemas import ResponseData logger = logging.getLogger(__name__) router = APIRouter(prefix="/amazing-data", tags=["amazingData 数据源"]) @router.get("/connect", response_model=ResponseData) def connect_data_source(): """ 连接 amazingData 数据源 建立与银河证券星耀数智量化平台的连接 """ try: success = amazing_data_service.connect() if success: return ResponseData( code=0, message="连接成功", data={"connected": True} ) else: return ResponseData( code=-1, message="连接失败", data={"connected": False} ) except Exception as e: logger.error(f"Connect failed: {e}") return ResponseData( code=-1, message=f"连接失败:{str(e)}", data={"connected": False} ) @router.get("/disconnect", response_model=ResponseData) def disconnect_data_source(): """ 断开 amazingData 连接 """ try: amazing_data_service.disconnect() return ResponseData( code=0, message="断开连接成功", data={"connected": False} ) except Exception as e: logger.error(f"Disconnect failed: {e}") return ResponseData( code=-1, message=f"断开连接失败:{str(e)}" ) @router.get("/status", response_model=ResponseData) def get_connection_status(): """ 获取连接状态 """ return ResponseData( code=0, message="success", data={ "connected": amazing_data_service._connected, "host": amazing_data_service._config.host if amazing_data_service._config else None, "port": amazing_data_service._config.port if amazing_data_service._config else None } ) @router.get("/kline", response_model=ResponseData) def get_kline_data( symbol: str = Query(..., description="证券代码,如 IF2406"), period: str = Query(..., description="周期:1m, 5m, 15m, 30m, 1h, 1d"), start_date: str = Query(None, description="开始日期 YYYY-MM-DD"), end_date: str = Query(None, description="结束日期 YYYY-MM-DD"), security_type: str = Query("EXTRA_FUTURE", description="证券类型") ): """ 获取 K 线数据 从 amazingData 获取真实的期货/股票 K 线数据 """ try: # 默认日期范围:最近 7 天 if not end_date: end_date = datetime.now().strftime("%Y-%m-%d") if not start_date: start_date = (datetime.now() - timedelta(days=7)).strftime("%Y-%m-%d") data = amazing_data_service.get_kline_data( symbol=symbol, period=period, start_date=start_date, end_date=end_date, security_type=security_type ) return ResponseData( code=0, message="success", data={ "symbol": symbol, "period": period, "count": len(data), "records": data } ) except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) except Exception as e: logger.error(f"Get kline data failed: {e}") raise HTTPException(status_code=500, detail=f"获取 K 线数据失败:{str(e)}") @router.get("/quotes", response_model=ResponseData) def get_realtime_quotes( symbols: str = Query(..., description="证券代码列表,逗号分隔,如 IF2406,IC2406") ): """ 获取实时行情 从 amazingData 获取真实的期货/股票实时行情 """ try: symbol_list = [s.strip() for s in symbols.split(",")] data = amazing_data_service.get_realtime_quotes(symbol_list) return ResponseData( code=0, message="success", data={ "count": len(data), "quotes": data } ) except Exception as e: logger.error(f"Get realtime quotes failed: {e}") raise HTTPException(status_code=500, detail=f"获取实时行情失败:{str(e)}") @router.get("/codes", response_model=ResponseData) def get_security_codes( security_type: str = Query("EXTRA_FUTURE", description="证券类型"), market: Optional[str] = Query(None, description="市场:SH, SZ, BJ") ): """ 获取证券代码列表 从 amazingData 获取可交易的证券代码列表 """ try: data = amazing_data_service.get_security_codes( security_type=security_type, market=market ) return ResponseData( code=0, message="success", data={ "count": len(data), "codes": data } ) except Exception as e: logger.error(f"Get security codes failed: {e}") raise HTTPException(status_code=500, detail=f"获取证券代码失败:{str(e)}") @router.get("/tick", response_model=ResponseData) def get_tick_data( symbol: str = Query(..., description="证券代码"), date: str = Query(None, description="日期 YYYY-MM-DD,默认今天"), security_type: str = Query("EXTRA_FUTURE", description="证券类型") ): """ 获取 Tick 数据 从 amazingData 获取详细的 Tick 级交易数据 """ try: if not date: date = datetime.now().strftime("%Y-%m-%d") data = amazing_data_service.get_tick_data( symbol=symbol, date=date, security_type=security_type ) return ResponseData( code=0, message="success", data={ "symbol": symbol, "date": date, "count": len(data), "records": data } ) except Exception as e: logger.error(f"Get tick data failed: {e}") raise HTTPException(status_code=500, detail=f"获取 Tick 数据失败:{str(e)}") @router.post("/sync/kline", response_model=ResponseData) def sync_kline_data( symbol: str = Query(..., description="证券代码"), period: str = Query(..., description="周期"), start_date: str = Query(None, description="开始日期"), end_date: str = Query(None, description="结束日期") ): """ 手动同步 K 线数据 将 amazingData 的 K 线数据同步到 TimescaleDB """ try: count = DataSyncService.sync_kline_data( symbol=symbol, period=period, start_date=start_date, end_date=end_date ) return ResponseData( code=0, message="同步成功", data={ "symbol": symbol, "period": period, "records_synced": count } ) except Exception as e: logger.error(f"Sync kline data failed: {e}") raise HTTPException(status_code=500, detail=f"同步数据失败:{str(e)}") @router.post("/sync/all", response_model=ResponseData) async def sync_all_data(): """ 同步所有默认品种数据 批量同步所有默认品种和周期的数据到 TimescaleDB """ try: result = await DataSyncService.sync_all_symbols() return ResponseData( code=0, message="同步完成", data=result ) except Exception as e: logger.error(f"Sync all data failed: {e}") raise HTTPException(status_code=500, detail=f"同步数据失败:{str(e)}")