Files
simonandClaude Opus 4.7 271a9343a5 Initial commit: cc-cursor 全链路量化研究平台
7 Sprints 全部完成:
  Sprint 0: 基础设施 (DataManager + MariaDB)
  Sprint 1: 因子引擎 (34因子/12分类)
  Sprint 2: VectorBT 回测 (5策略+截面)
  Sprint 3: Optuna 优化 (+Walk-Forward)
  Sprint 4: ML 模型 (LightGBM+CatBoost)
  Sprint 5: Qwen 情绪因子 (三源新闻+日期对齐)
  Sprint 6: Agent 系统 (4Agent+日报.md/.html)

生产加固 (15项): Tushare双源fallback, SSH自动恢复, pool_pre_ping,
  save_daily先删后插, load_dotenv绝对路径, 日报5d/20d修复,
  RiskAgent改上证指数, 昨日对比+数据截止, mac_report utf8mb4,
  CLAUDE-*.md 9条已知Bug, demo全参数化, djapi数据源归一化,
  indexDatas API修正

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 15:59:05 +08:00

122 lines
3.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
报告持久化模块。
将 CLI 脚本输出的 Markdown 报告存入 DBmac_report 表)。
同一日期+研究对象的新报告入库时,旧报告自动标记为失效(is_active=0)。
"""
from datetime import datetime
from sqlalchemy import text
from database.connection import get_engine
from database.models import Report, create_all_tables
def save_report(
content: str,
title: str,
report_date: str | None = None,
subject_type: str = "daily",
subject_code: str = "",
) -> int:
"""
保存报告到 DB。
如果同一天、同一研究对象已有报告,先将其标记为 is_active=0
然后插入新报告。
参数:
content: Markdown 报告内容
title: 报告标题
report_date: 报告日期 YYYYMMDD,默认今天
subject_type: 研究对象类型 (stock/index/sector/portfolio/daily)
subject_code: 研究对象代码 (如 000001.SZ 或 000300.SH)
返回:
新报告的 id
"""
report_date = report_date or datetime.now().strftime("%Y%m%d")
engine = get_engine()
# 确保表存在
create_all_tables()
# 将同日期+同对象的旧报告标记失效
with engine.connect() as conn:
conn.execute(
text(
"UPDATE {} SET is_active = 0 "
"WHERE report_date = :d AND subject_type = :st AND subject_code = :sc AND is_active = 1"
.format(Report.__tablename__)
),
{"d": report_date, "st": subject_type, "sc": subject_code},
)
conn.commit()
# 插入新报告
report = Report(
report_date=report_date,
title=title,
subject_type=subject_type,
subject_code=subject_code,
content=content,
created_at=datetime.now(),
is_active=1.0,
)
from sqlalchemy.orm import Session
with Session(engine) as session:
session.add(report)
session.commit()
report_id = report.id
session.expunge_all()
return report_id
def query_reports(
report_date: str | None = None,
subject_type: str | None = None,
subject_code: str | None = None,
active_only: bool = True,
limit: int = 20,
) -> list[dict]:
"""查询报告列表。"""
engine = get_engine()
table = Report.__tablename__
sql = "SELECT * FROM {} WHERE 1=1".format(table)
params = {}
if report_date:
sql += " AND report_date = :d"
params["d"] = report_date
if subject_type:
sql += " AND subject_type = :st"
params["st"] = subject_type
if subject_code:
sql += " AND subject_code = :sc"
params["sc"] = subject_code
if active_only:
sql += " AND is_active = 1"
sql += " ORDER BY id DESC LIMIT :lim"
params["lim"] = limit
with engine.connect() as conn:
rows = conn.execute(text(sql), params).fetchall()
result = []
for r in rows:
d = dict(r._mapping)
# DATE/DATETIME 列 → 字符串
for key in ("report_date", "created_at"):
val = d.get(key)
if hasattr(val, "strftime"):
fmt = "%Y%m%d" if key == "report_date" else "%Y-%m-%d %H:%M:%S"
d[key] = val.strftime(fmt)
result.append(d)
return result