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
})