112 lines
4.4 KiB
Python
112 lines
4.4 KiB
Python
|
|
# -*- coding: utf-8 -*-
|
||
|
|
"""
|
||
|
|
宏观择时 · 取数与落表 (严格单表访问)
|
||
|
|
======================================
|
||
|
|
两类访问, 都走 153 代理 (source="proxy"):
|
||
|
|
1. 三张行情源表只读 —— 表位置与列名由 2026-08-18 标定实跑钉死
|
||
|
|
(报告 MACRO_CALIB_2026-08-18.md 第一节, 改列名先改那份依据再改这里):
|
||
|
|
zs_day_data 上证日线 symbol='000001.SH', 列 timestamp / close
|
||
|
|
gp_fx_daily 美元兑离岸人民币 ts_code='USDCNH.FXCM', 列 trade_date / bid_close
|
||
|
|
gp_shibor 拆借利率宽表 列 trade_date / rate_1m (单位百分数)
|
||
|
|
2. 信号历史表 pms_macro_signal 读写 —— 每信号每交易日一行, 重扫就地更新 (唯一键)。
|
||
|
|
|
||
|
|
计算不在这里 (core/macro_rules.py), 决策与下命令也不在这里 (services/macro_service.py)。
|
||
|
|
"""
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import json
|
||
|
|
import logging
|
||
|
|
from datetime import datetime
|
||
|
|
|
||
|
|
from app.db.session import execute, fetch_all, fetch_one
|
||
|
|
|
||
|
|
logger = logging.getLogger("pms.macro_repo")
|
||
|
|
|
||
|
|
# 源表口径 (标定钉死)
|
||
|
|
ZS_SYMBOL = "000001.SH"
|
||
|
|
FX_TSCODE = "USDCNH.FXCM"
|
||
|
|
|
||
|
|
|
||
|
|
def _dumps(v):
|
||
|
|
return json.dumps(v, ensure_ascii=False) if not isinstance(v, (str, type(None))) else v
|
||
|
|
|
||
|
|
|
||
|
|
def _loads(v):
|
||
|
|
if v in (None, ""):
|
||
|
|
return {}
|
||
|
|
try:
|
||
|
|
return json.loads(v) if isinstance(v, str) else dict(v)
|
||
|
|
except (TypeError, ValueError):
|
||
|
|
return {}
|
||
|
|
|
||
|
|
|
||
|
|
# ================================================================ 行情源表 (只读)
|
||
|
|
def fetch_hedge_inputs(limit_days: int = 140) -> dict:
|
||
|
|
"""股汇对冲指数的三条原始序列, 各取最近 N 条, 返回升序 (ymd, value) 列表。
|
||
|
|
|
||
|
|
N 默认 140 = 收益窗 20 + 标准化窗 40 + 富余; 单表查询, 符合代理的单表纪律。
|
||
|
|
任一查询失败向上抛 (DBUnavailable), 由 macro_service 按「不可用 → 不动作」处理。
|
||
|
|
"""
|
||
|
|
n = max(80, int(limit_days))
|
||
|
|
zs = fetch_all(
|
||
|
|
"SELECT `timestamp` AS d, `close` AS v FROM zs_day_data "
|
||
|
|
"WHERE symbol = :s ORDER BY `timestamp` DESC LIMIT :n",
|
||
|
|
{"s": ZS_SYMBOL, "n": n}, source="proxy")
|
||
|
|
fx = fetch_all(
|
||
|
|
"SELECT trade_date AS d, bid_close AS v FROM gp_fx_daily "
|
||
|
|
"WHERE ts_code = :c ORDER BY trade_date DESC LIMIT :n",
|
||
|
|
{"c": FX_TSCODE, "n": n}, source="proxy")
|
||
|
|
sh = fetch_all(
|
||
|
|
"SELECT trade_date AS d, rate_1m AS v FROM gp_shibor "
|
||
|
|
"ORDER BY trade_date DESC LIMIT :n",
|
||
|
|
{"n": n}, source="proxy")
|
||
|
|
|
||
|
|
def _ser(rows):
|
||
|
|
out = []
|
||
|
|
for r in rows:
|
||
|
|
if r.get("v") is None:
|
||
|
|
continue
|
||
|
|
out.append((r["d"], float(r["v"])))
|
||
|
|
out.reverse() # DESC 取回 → 升序返回
|
||
|
|
return out
|
||
|
|
|
||
|
|
return {"zs": _ser(zs), "fx": _ser(fx), "shibor": _ser(sh)}
|
||
|
|
|
||
|
|
|
||
|
|
# ================================================================ pms_macro_signal
|
||
|
|
def upsert_signal(*, signal_key: str, trade_date: int, value, zone: str,
|
||
|
|
detail=None, action: str = "NONE", ref_id: str = "",
|
||
|
|
note: str = "") -> int:
|
||
|
|
"""写当日信号行; 同 (信号, 交易日) 重扫就地更新 (唯一键 uk_sig_date)。"""
|
||
|
|
return execute(
|
||
|
|
"INSERT INTO pms_macro_signal (signal_key, trade_date, value, zone, detail_json, "
|
||
|
|
"action, ref_id, note, updated_at) "
|
||
|
|
"VALUES (:k, :d, :v, :z, :dj, :a, :r, :nt, :ts) "
|
||
|
|
"ON DUPLICATE KEY UPDATE value = :v, zone = :z, detail_json = :dj, "
|
||
|
|
"action = :a, ref_id = :r, note = :nt, updated_at = :ts",
|
||
|
|
{"k": signal_key, "d": int(trade_date), "v": value, "z": zone,
|
||
|
|
"dj": _dumps(detail or {}), "a": action or "NONE", "r": ref_id or "",
|
||
|
|
"nt": (note or "")[:500], "ts": datetime.now()},
|
||
|
|
source="proxy")
|
||
|
|
|
||
|
|
|
||
|
|
def get_signal(signal_key: str, trade_date: int):
|
||
|
|
r = fetch_one(
|
||
|
|
"SELECT * FROM pms_macro_signal WHERE signal_key = :k AND trade_date = :d "
|
||
|
|
"ORDER BY id DESC LIMIT 1",
|
||
|
|
{"k": signal_key, "d": int(trade_date)}, source="proxy")
|
||
|
|
if r:
|
||
|
|
r["detail"] = _loads(r.get("detail_json"))
|
||
|
|
return r
|
||
|
|
|
||
|
|
|
||
|
|
def recent_signals(signal_key: str, limit: int = 30) -> list:
|
||
|
|
"""最近的信号行, 按交易日从新到旧。detail_json 已解析进 detail 键。"""
|
||
|
|
rows = fetch_all(
|
||
|
|
"SELECT * FROM pms_macro_signal WHERE signal_key = :k "
|
||
|
|
"ORDER BY trade_date DESC LIMIT :n",
|
||
|
|
{"k": signal_key, "n": max(1, int(limit))}, source="proxy")
|
||
|
|
for r in rows:
|
||
|
|
r["detail"] = _loads(r.get("detail_json"))
|
||
|
|
return rows
|