chore: baseline — ORM migration + systemd deploy + MCP server + daily check
- ORM migration: models/ops 重构 - deploy: 标准化 systemd timer/service 部署体系 - MCP server: 5 个只读工具(任务/状态/数据集) - daily check: 数据一致性巡检 - watch: task_watch 后台监控 - kline_daily: 麦蕊优先,时间门禁15:00 - 删除: task_industry_sector, task_sector_features
This commit is contained in:
+63
-1
@@ -32,7 +32,7 @@ from app.core.datasource.utils import (
|
||||
class MairuiSource(DataSource):
|
||||
key = "datasource_mairui"
|
||||
name = "麦蕊智数(mairui.club)"
|
||||
provides = ["kline_daily", "kline_5min", "index_daily", "stock_basic", "moneyflow", "tick_trade", "stock_node"]
|
||||
provides = ["kline_daily", "kline_5min", "index_daily", "stock_basic", "moneyflow", "tick_trade", "stock_node", "indicator_daily"]
|
||||
requires_credential = True
|
||||
credential_key = "MAIRUI_LICENCE"
|
||||
|
||||
@@ -285,6 +285,68 @@ class MairuiSource(DataSource):
|
||||
df = df[df["bar_time"] < pd.Timestamp(end) + pd.Timedelta(days=1)]
|
||||
return df
|
||||
|
||||
# ── 技术指标 fetch(MACD / KDJ / BOLL)─────────────────────
|
||||
# mairui 端点:/hsstock/history/{indicator}/{symbol}/d/n/{licence}
|
||||
# indicator ∈ {macd, kdj, boll};日 K 级别 "/d/";不复权 "/n/"
|
||||
# 各指标字段(除公共 "t" 外):
|
||||
# macd → diff, dea, macd, ema12, ema26
|
||||
# kdj → k, d, j
|
||||
# boll → u(上轨), d(下轨), m(中轨) ← 注意 mairui 用 u/d/m
|
||||
_INDICATOR_FIELDS = {
|
||||
"macd": ["diff", "dea", "macd", "ema12", "ema26"],
|
||||
"kdj": ["k", "d", "j"],
|
||||
"boll": ["u", "d", "m"],
|
||||
}
|
||||
|
||||
def fetch_indicator_daily(
|
||||
self, indicator: str, code6: str, start: str = "", end: str = ""
|
||||
) -> pd.DataFrame:
|
||||
"""拉单只股票某个技术指标的日 K 序列。
|
||||
|
||||
返回列:trade_date + 该指标字段(macd/kdj/boll 各自的原始字段名)。
|
||||
start/end 为 "YYYY-MM-DD"(可空 → 全历史)。
|
||||
"""
|
||||
indicator = indicator.lower()
|
||||
fields = self._INDICATOR_FIELDS.get(indicator)
|
||||
if fields is None:
|
||||
raise ValueError(f"未知指标: {indicator!r},可选 {list(self._INDICATOR_FIELDS)}")
|
||||
lic = self._get_licence()
|
||||
if not lic:
|
||||
return pd.DataFrame()
|
||||
symbol = code6_to_mairui(code6)
|
||||
path = f"/hsstock/history/{indicator}/{symbol}/d/n/{lic}"
|
||||
params = {
|
||||
"st": (start or "").replace("-", ""),
|
||||
"et": (end or "").replace("-", ""),
|
||||
}
|
||||
data = self._fetch(path, params)
|
||||
if not isinstance(data, list):
|
||||
return pd.DataFrame()
|
||||
records = []
|
||||
for item in data:
|
||||
try:
|
||||
t = item.get("t", "")
|
||||
d = t.split(" ")[0] if isinstance(t, str) else ""
|
||||
if not d:
|
||||
continue
|
||||
row = {"trade_date": d}
|
||||
for f in fields:
|
||||
v = item.get(f)
|
||||
row[f] = None if v is None else float(v)
|
||||
records.append(row)
|
||||
except (KeyError, ValueError, TypeError):
|
||||
continue
|
||||
if not records:
|
||||
return pd.DataFrame()
|
||||
df = pd.DataFrame(records)
|
||||
df["trade_date"] = pd.to_datetime(df["trade_date"], errors="coerce")
|
||||
df = df.dropna(subset=["trade_date"]).sort_values("trade_date").reset_index(drop=True)
|
||||
if start:
|
||||
df = df[df["trade_date"] >= pd.Timestamp(start)]
|
||||
if end:
|
||||
df = df[df["trade_date"] <= pd.Timestamp(end)]
|
||||
return df
|
||||
|
||||
def fetch_index_daily(self, index_code: str, start: str, end: str) -> pd.DataFrame:
|
||||
"""指数日 K。index_code 用 mairui 格式(如 '000300.SH')。"""
|
||||
lic = self._get_licence()
|
||||
|
||||
Reference in New Issue
Block a user