71 lines
2.4 KiB
Python
71 lines
2.4 KiB
Python
from __future__ import annotations
|
|
|
|
import importlib
|
|
import os
|
|
from datetime import datetime
|
|
|
|
import pandas as pd
|
|
|
|
from market.provider_base import MarketDataProvider
|
|
|
|
|
|
class CmesCnProvider(MarketDataProvider):
|
|
def __init__(self, token: str | None = None) -> None:
|
|
self._token = token or os.getenv("CMES_TOKEN", "").strip()
|
|
self._module = importlib.import_module("cmesdata")
|
|
self._login_once()
|
|
|
|
@property
|
|
def provider_name(self) -> str:
|
|
return "cmesdata"
|
|
|
|
@property
|
|
def channel(self) -> str:
|
|
return "cn"
|
|
|
|
def _login_once(self) -> None:
|
|
if not self._token:
|
|
raise RuntimeError("CMES_TOKEN 未配置,无法使用 cmesdata 通道")
|
|
self._module.login(self._token)
|
|
|
|
@staticmethod
|
|
def _to_prefixed_code(code: str) -> str:
|
|
raw = code.strip().upper().replace(".", "")
|
|
if raw.startswith("SH") or raw.startswith("SZ"):
|
|
return f"{raw[:2]}.{raw[2:]}"
|
|
if raw.isdigit() and len(raw) == 6:
|
|
if raw.startswith(("6", "9")):
|
|
return f"SH.{raw}"
|
|
return f"SZ.{raw}"
|
|
raise RuntimeError("cmesdata 通道仅支持 6 位 A 股代码或 SH./SZ. 前缀代码")
|
|
|
|
def fetch_spot(self) -> pd.DataFrame:
|
|
raise RuntimeError("cmesdata 不支持全市场快照拉取,请通过精确代码查询")
|
|
|
|
def search_spot(self, query: str, limit: int) -> pd.DataFrame:
|
|
_ = limit
|
|
code = self._to_prefixed_code(query)
|
|
df = self._module.get_real_hq([code])
|
|
if df is None or df.empty:
|
|
raise RuntimeError(f"未获取到实时行情: {code}")
|
|
return df
|
|
|
|
def fetch_daily_kline(self, code: str, start: datetime, end: datetime) -> pd.DataFrame:
|
|
prefixed = self._to_prefixed_code(code)
|
|
df = self._module.get_history_data(
|
|
prefixed,
|
|
start.strftime("%Y-%m-%d"),
|
|
end.strftime("%Y-%m-%d"),
|
|
"D",
|
|
)
|
|
if df is None or df.empty:
|
|
raise RuntimeError(f"未获取到历史 K 线: {prefixed}")
|
|
frame = pd.DataFrame(
|
|
{
|
|
"date": pd.to_datetime(df["时间"]),
|
|
"close": pd.to_numeric(df["收盘价"], errors="coerce"),
|
|
"volume": pd.to_numeric(df["成交量"], errors="coerce"),
|
|
}
|
|
).dropna()
|
|
return frame.sort_values("date").reset_index(drop=True)
|