From b54a0cee589e324b1a7048b9a1332dc6f10b9208 Mon Sep 17 00:00:00 2001 From: Lxy Date: Sat, 11 Apr 2026 22:18:36 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=82=A1=E7=A5=A8=E7=9A=84=E4=B8=80?= =?UTF-8?q?=E9=94=AE=E6=A3=80=E6=B5=8B=E3=80=81=E4=B8=80=E9=94=AE=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E5=AE=8C=E6=88=90=EF=BC=88=E7=BC=93=E5=AD=98=E9=9C=80?= =?UTF-8?q?=E8=A6=81=E4=BC=98=E5=8C=96=E8=BF=9B=E5=BA=A6=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/v1/cache.py | 35 +++++- backend/app/schemas/cache.py | 1 + backend/app/services/amazing_data_adapter.py | 111 +++++++++++++++++- backend/app/services/cache_service.py | 31 +++-- frontend/src/api/cache.ts | 10 ++ .../src/views/CacheManager/DetectMissing.vue | 9 ++ 6 files changed, 187 insertions(+), 10 deletions(-) diff --git a/backend/app/api/v1/cache.py b/backend/app/api/v1/cache.py index 8def87b..75525f7 100644 --- a/backend/app/api/v1/cache.py +++ b/backend/app/api/v1/cache.py @@ -35,7 +35,8 @@ async def detect_all_missing_data( request.security_type, request.period_type, start, - end + end, + request.contract_type ) return ResponseModel(data=result) @@ -56,7 +57,8 @@ async def cache_all_missing_data( request.security_type, request.period_type, start, - end + end, + request.contract_type ) return ResponseModel(data={ @@ -215,4 +217,33 @@ async def get_cache_status( return ResponseModel(data=status) +@router.get("/future-varieties", response_model=ResponseModel) +async def get_future_varieties( + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user) +): + """获取期货品种列表""" + service = CacheService(db) + varieties = service.get_future_varieties() + + return ResponseModel(data={"varieties": varieties}) + + +@router.get("/main-contracts", response_model=ResponseModel) +async def get_main_contracts( + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user) +): + """获取所有品种的主力合约""" + from app.services.sdk_manager import sdk_manager + + adapter = sdk_manager.get_default_connection() + if not adapter: + return ResponseModel(code=500, message="SDK连接失败") + + main_contracts = adapter.get_all_main_contracts() + + return ResponseModel(data={"main_contracts": main_contracts}) + + from app.utils.date_utils import format_date diff --git a/backend/app/schemas/cache.py b/backend/app/schemas/cache.py index b272617..9fb4af5 100644 --- a/backend/app/schemas/cache.py +++ b/backend/app/schemas/cache.py @@ -114,3 +114,4 @@ class AllDataRequest(BaseModel): period_type: str = Field(default="daily", description="周期类型: daily, min1, min5, etc.") start_date: str = Field(..., description="开始日期 YYYYMMDD") end_date: str = Field(..., description="结束日期 YYYYMMDD") + contract_type: str = Field(default="all", description="合约类型: all, main - 仅对期货有效") diff --git a/backend/app/services/amazing_data_adapter.py b/backend/app/services/amazing_data_adapter.py index 7698201..27e7ae8 100644 --- a/backend/app/services/amazing_data_adapter.py +++ b/backend/app/services/amazing_data_adapter.py @@ -113,11 +113,120 @@ class AmazingDataAdapter: raise RuntimeError("SDK未连接") try: - return self._base_data.get_code_list(security_type) + if security_type == "EXTRA_FUTURE": + return self._base_data.get_future_code_list() + elif security_type == "EXTRA_OPTION": + return self._base_data.get_option_code_list() + else: + return self._base_data.get_code_list(security_type) except Exception as e: logger.error(f"获取代码列表失败: {str(e)}") return [] + def get_future_varieties(self) -> List[str]: + """ + 获取期货品种列表 + + Returns: + 品种代码列表 (如: IF, IC, al, zn, etc.) + """ + if not self._is_connected: + raise RuntimeError("SDK未连接") + + try: + codes = self._base_data.get_future_code_list() + varieties = set() + for code in codes: + # 代码格式: 品种代码+月份+交易所 + # 如: IF2401.CFE, al2702.SHF + parts = code.split('.') + if len(parts) >= 1: + contract = parts[0] + # 提取品种代码(去掉月份部分) + # 月份格式: YYMM (如 2401, 2702) + variety = contract[:-4] if len(contract) > 4 else contract + varieties.add(variety) + return sorted(list(varieties)) + except Exception as e: + logger.error(f"获取期货品种列表失败: {str(e)}") + return [] + + def get_main_contract(self, variety: str) -> Optional[str]: + """ + 获取指定品种的当前主力合约 + + Args: + variety: 品种代码 (如: IF, IC, al, zn) + + Returns: + 主力合约代码 (如: IF2401.CFE) + """ + if not self._is_connected: + raise RuntimeError("SDK未连接") + + try: + from datetime import datetime + + codes = self._base_data.get_future_code_list() + current_year = datetime.now().year + current_month = datetime.now().month + + # 筛选该品种的合约 + variety_codes = [] + for code in codes: + parts = code.split('.') + if len(parts) >= 1: + contract = parts[0] + if contract.startswith(variety) and len(contract) > len(variety): + variety_codes.append(code) + + if not variety_codes: + return None + + # 找到最近月份的合约作为主力合约 + # 主力合约通常是当前月份或下个月份的合约 + def get_contract_month(code): + parts = code.split('.') + contract = parts[0] + month_str = contract[-4:] # YYMM + try: + year = int(month_str[:2]) + 2000 + month = int(month_str[2:]) + return (year, month) + except: + return (9999, 99) + + # 按月份排序 + variety_codes.sort(key=get_contract_month) + + # 找到第一个大于当前月份的合约 + for code in variety_codes: + year, month = get_contract_month(code) + if year >= current_year and month >= current_month: + return code + + # 如果没有找到,返回最后一个合约 + return variety_codes[-1] if variety_codes else None + + except Exception as e: + logger.error(f"获取主力合约失败: {str(e)}") + return None + + def get_all_main_contracts(self) -> Dict[str, str]: + """ + 获取所有品种的主力合约 + + Returns: + 字典 {品种: 主力合约} + """ + varieties = self.get_future_varieties() + main_contracts = {} + for variety in varieties: + main_contract = self.get_main_contract(variety) + if main_contract: + main_contracts[variety] = main_contract + return main_contracts + def get_code_info(self, security_type: str) -> pd.DataFrame: """ 获取证券信息 diff --git a/backend/app/services/cache_service.py b/backend/app/services/cache_service.py index 5edb8c8..71716c5 100644 --- a/backend/app/services/cache_service.py +++ b/backend/app/services/cache_service.py @@ -29,12 +29,13 @@ class CacheService: self.stock_service = StockService(db) self.future_service = FutureService(db) - def get_all_codes(self, security_type: str) -> List[str]: + def get_all_codes(self, security_type: str, contract_type: str = "all") -> List[str]: """ 获取所有代码列表 Args: security_type: 证券类型 (stock, future) + contract_type: 合约类型 (all, main) - 仅对期货有效 Returns: 代码列表 @@ -46,16 +47,29 @@ class CacheService: if security_type == "stock": return adapter.get_code_list("EXTRA_STOCK_A") elif security_type == "future": - return adapter.get_code_list("EXTRA_FUTURE") + if contract_type == "main": + # 只获取主力合约 + main_contracts = adapter.get_all_main_contracts() + return list(main_contracts.values()) + else: + return adapter.get_code_list("EXTRA_FUTURE") else: return [] + def get_future_varieties(self) -> List[str]: + """获取期货品种列表""" + adapter = sdk_manager.get_default_connection() + if not adapter: + raise RuntimeError("SDK连接失败") + return adapter.get_future_varieties() + def detect_all_missing_data( self, security_type: str, period_type: str, start_date: date, - end_date: date + end_date: date, + contract_type: str = "all" ) -> Dict: """ 一键检测所有数据的缺失情况 @@ -65,12 +79,13 @@ class CacheService: period_type: 周期类型 (daily, min1, etc.) start_date: 开始日期 end_date: 结束日期 + contract_type: 合约类型 (all, main) - 仅对期货有效 Returns: 检测结果字典 """ # 获取所有代码 - code_list = self.get_all_codes(security_type) + code_list = self.get_all_codes(security_type, contract_type) if not code_list: raise ValueError(f"无法获取{security_type}代码列表") @@ -79,7 +94,7 @@ class CacheService: # 创建检测任务 task = CacheTask( - task_name=f"一键检测所有数据 - {security_type} - {len(code_list)}个代码", + task_name=f"一键检测所有数据 - {security_type} - {contract_type} - {len(code_list)}个代码", task_type="detect_all_missing", security_type=security_type, period_type=period_type, @@ -244,7 +259,8 @@ class CacheService: security_type: str, period_type: str, start_date: date, - end_date: date + end_date: date, + contract_type: str = "all" ) -> CacheTask: """ 一键缓存所有缺失数据 @@ -254,12 +270,13 @@ class CacheService: period_type: 周期类型 start_date: 开始日期 end_date: 结束日期 + contract_type: 合约类型 (all, main) - 仅对期货有效 Returns: 缓存任务对象 """ # 获取所有代码 - code_list = self.get_all_codes(security_type) + code_list = self.get_all_codes(security_type, contract_type) if not code_list: raise ValueError(f"无法获取{security_type}代码列表") diff --git a/frontend/src/api/cache.ts b/frontend/src/api/cache.ts index 28e1642..60230e0 100644 --- a/frontend/src/api/cache.ts +++ b/frontend/src/api/cache.ts @@ -23,6 +23,7 @@ export const batchCacheData = (data: { export const detectAllMissingData = (data: { security_type: string period_type: string + contract_type?: string start_date: string end_date: string }) => { @@ -32,6 +33,7 @@ export const detectAllMissingData = (data: { export const cacheAllMissingData = (data: { security_type: string period_type: string + contract_type?: string start_date: string end_date: string }) => { @@ -56,3 +58,11 @@ export const getCacheStatus = ( ) => { return request.get(`/cache/status/${code}`, { params }) } + +export const getFutureVarieties = () => { + return request.get('/cache/future-varieties') +} + +export const getMainContracts = () => { + return request.get('/cache/main-contracts') +} diff --git a/frontend/src/views/CacheManager/DetectMissing.vue b/frontend/src/views/CacheManager/DetectMissing.vue index bad7b1b..c58e1c8 100644 --- a/frontend/src/views/CacheManager/DetectMissing.vue +++ b/frontend/src/views/CacheManager/DetectMissing.vue @@ -8,6 +8,12 @@ 期货 + + + 所有合约 + 主力合约 + + @@ -222,6 +228,7 @@ const dailyStatsList = computed(() => { const form = reactive({ securityType: 'stock', periodType: 'daily', + contractType: 'all', startDate: getDefaultStartDate(), endDate: getDefaultEndDate() }) @@ -260,6 +267,7 @@ const handleDetectAll = async () => { const res: any = await detectAllMissingData({ security_type: form.securityType, period_type: form.periodType, + contract_type: form.contractType, start_date: form.startDate, end_date: form.endDate }) @@ -286,6 +294,7 @@ const handleCacheAll = async () => { const res: any = await cacheAllMissingData({ security_type: form.securityType, period_type: form.periodType, + contract_type: form.contractType, start_date: form.startDate, end_date: form.endDate })