移除 Bark 推送通知模块及相关依赖

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
fish
2026-05-03 16:44:59 +08:00
parent 44909f04e2
commit 7d49aff6c7
6 changed files with 9 additions and 58 deletions

View File

@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## 项目概述
基于 Docker + Python(tushare) + PostgreSQL 的中国期货行情分析系统,实现日线数据采集三层加权打分模型与 Bark 推送通知。运行方式支持两种模式:① 宿主机 cron/launchd 定时调用 `docker-compose run` 执行 CLI;② 通过 FastAPI 服务以 HTTP API 触发。详细业务说明见 `README.md`
基于 Docker + Python(tushare) + PostgreSQL 的中国期货行情分析系统,实现日线数据采集三层加权打分模型。运行方式支持两种模式:① 宿主机 cron/launchd 定时调用 `docker-compose run` 执行 CLI;② 通过 FastAPI 服务以 HTTP API 触发。详细业务说明见 `README.md`
## 常用命令
@@ -38,13 +38,13 @@ docker-compose exec postgres psql -U trade -d futures -c \
"SELECT ts_code, trade_date, composite, signal FROM scores ORDER BY trade_date DESC LIMIT 5;"
```
`tushare/.env` 必须存在且含 `TUSHARE_TOKEN=xxx`(已 gitignored)。可选 `BARK_KEY` 覆盖 `notifier.py` 默认 key。
`tushare/.env` 必须存在且含 `TUSHARE_TOKEN=xxx`(已 gitignored)。
## 关键架构
**单进程串行流水线**:`src.main.main()` 先按命令行参数(显式 `ts_code` 优先,否则 `contracts.active_contract(symbol)` 按当月主力自动选)定下合约,再调 `run()` 顺序执行 `fetcher → storage(candles) → scorer → storage(scores) → notifier`。无后台任务、无队列,每次 CLI 调用处理一个合约一日。
**单进程串行流水线**:`src.main.main()` 先按命令行参数(显式 `ts_code` 优先,否则 `contracts.active_contract(symbol)` 按当月主力自动选)定下合约,再调 `run()` 顺序执行 `fetcher → storage(candles) → scorer → storage(scores)`。无后台任务、无队列,每次 CLI 调用处理一个合约一日。
**FastAPI 服务**(`src.api`):容器默认以 `uvicorn src.api:app` 启动,暴露 `/api/v1/run`(触发流水线)、`/api/v1/scores``/api/v1/scores/{id}``/api/v1/contracts``/api/v1/candles` 等端点。启动时自动 `storage.init_db()` 建表。API 与 CLI 共用同一套 `fetcher/storage/scorer/notifier` 逻辑。
**FastAPI 服务**(`src.api`):容器默认以 `uvicorn src.api:app` 启动,暴露 `/api/v1/run`(触发流水线)、`/api/v1/scores``/api/v1/scores/{id}``/api/v1/contracts``/api/v1/candles` 等端点。启动时自动 `storage.init_db()` 建表。API 与 CLI 共用同一套 `fetcher/storage/scorer` 逻辑。
**主力轮换规则**(`contracts.py`):每个品种在 `ROLLOVER_RULES` 中维护 `month -> (主力月, 年份偏移)` 表。FG 当前规则:1-3/12 月→05、4-7 月→09、8-11 月→01,其中 8-11 月与 12 月跨年(`year_offset=1`)。新增品种(如 RB、I)只需在该 dict 里加一条,无需改 main 流程。
@@ -54,8 +54,6 @@ docker-compose exec postgres psql -U trade -d futures -c \
**Docker 边界**:`tushare/src/``web/backend/``web/frontend/` 均在 Dockerfile 的 `COPY` 阶段拷进镜像,**没有源码挂载**——改完 Python/Go/Vue 代码不重建镜像就跑等于跑旧代码。这是重要陷阱。
**Bark 推送**:`notifier.push_bark``requests.get` 走路径形式(`/{key}/{title}/{body}`),所有片段以 `quote(safe='')` URL 编码,失败仅 `print [WARN]` 不抛错。容器内首发请求有时 DNS 慢导致 15s timeout,内置 1 次重试;主机直连通常 <1s。
## 配置/密钥规则
`.gitignore` 排除范围广(见文件):`data/``*.db*``.env*`、CTP 流文件(`*.con`/`*.dat`/`ResultInfo.xml` 等)、`.claude/`、所有日志。新增任何账户、token、行情流文件务必先确认匹配 ignore 规则。

View File

@@ -1,6 +1,6 @@
# 期货行情分析系统 — 使用说明
基于 Docker + Python(tushare) + PostgreSQL 的中国期货行情分析系统。当前阶段已实现数据采集、三层加权打分模型、Bark 推送通知与 Web 报表浏览端。运行方式支持两种模式:① 宿主机定时器触发 `docker-compose run` 执行 CLI;② 通过 FastAPI HTTP API 服务触发。
基于 Docker + Python(tushare) + PostgreSQL 的中国期货行情分析系统。当前阶段已实现数据采集、三层加权打分模型与 Web 报表浏览端。运行方式支持两种模式:① 宿主机定时器触发 `docker-compose run` 执行 CLI;② 通过 FastAPI HTTP API 服务触发。
## 环境准备
@@ -40,7 +40,6 @@ docker-compose run --rm tushare python -m src.main
2. 写入 PostgreSQL `futures` 数据库
3. 运行三层打分模型
4. 保存打分结果并输出到 stdout
5. 通过 Bark 推送评分摘要
### 4. 通过 API 触发流水线
@@ -167,14 +166,13 @@ trade/
│ ├── Dockerfile
│ ├── requirements.txt
│ ├── .env # TUSHARE_TOKEN(本地,不入库)
│ └── src/ # 数据采集 + 打分 + Bark 推送 + FastAPI
│ └── src/ # 数据采集 + 打分 + FastAPI
│ ├── api.py # FastAPI 服务入口
│ ├── models.py
│ ├── fetcher.py
│ ├── scorer.py
│ ├── storage.py # PostgreSQL 读写
│ ├── contracts.py
│ ├── notifier.py
│ └── main.py # CLI 入口
└── web/ # Web 浏览端
├── .dockerignore
@@ -227,7 +225,7 @@ A: 郑商所用 `.ZCE` 后缀(如 `FG2609.ZCE`),上期所用 `.SHF`,大
**Q: 如何定时自动跑?**
A: 通过宿主机 cron / launchd 等定时器调用 `docker-compose run --rm tushare ...`打分结束会通过 Bark 推送结果(见 `tushare/src/notifier.py`)。也可直接调用 API: `curl -X POST http://localhost:8000/api/v1/run ...`
A: 通过宿主机 cron / launchd 等定时器调用 `docker-compose run --rm tushare ...`。也可直接调用 API: `curl -X POST http://localhost:8000/api/v1/run ...`
## Web 报表(浏览端)

View File

@@ -1,6 +1,5 @@
tushare>=1.4.0
pandas>=2.2.0
requests>=2.31.0
fastapi>=0.115.0
uvicorn[standard]>=0.34.0
psycopg[binary]>=3.2.0

View File

@@ -5,7 +5,7 @@ from datetime import date
from fastapi import FastAPI, HTTPException, Query
from pydantic import BaseModel
from . import contracts, fetcher, notifier, scorer, storage
from . import contracts, fetcher, scorer, storage
app = FastAPI(title="期货数据采集与打分服务")
@@ -50,14 +50,6 @@ def run_pipeline(req: RunRequest):
result = scorer.score_daily(df, req.trade_date)
storage.save_score(result)
push_title = f"{result.ts_code.split('.')[0]} {result.trade_date}"
push_body = (
f"综合 {result.composite:.1f}\n"
f"短期 {result.short_term:.1f} | 中期 {result.medium_term:.1f} | 长期 {result.long_term:.1f}\n"
f"{result.signal}"
)
notifier.push_bark(push_title, push_body)
return RunResponse(
ts_code=result.ts_code,
trade_date=result.trade_date,

View File

@@ -1,7 +1,7 @@
import argparse
import sys
from . import contracts, fetcher, notifier, scorer, storage
from . import contracts, fetcher, scorer, storage
def run(ts_code: str, trade_date: Optional[str] = None) -> int:
@@ -63,14 +63,6 @@ def run(ts_code: str, trade_date: Optional[str] = None) -> int:
print(f"\n[OK] 数据已持久化到 PostgreSQL")
push_title = f"{result.ts_code.split('.')[0]} {result.trade_date}"
push_body = (
f"综合 {result.composite:.1f}\n"
f"短期 {result.short_term:.1f} | 中期 {result.medium_term:.1f} | 长期 {result.long_term:.1f}\n"
f"{result.signal}"
)
if notifier.push_bark(push_title, push_body):
print("[Bark] 推送成功")
return 0

View File

@@ -1,28 +0,0 @@
import os
from urllib.parse import quote
import requests
DEFAULT_BARK_KEY = "RvdtHq4py2avatt4AFJn9a"
BARK_BASE_URL = "https://api.day.app"
def push_bark(title: str, body: str, key: str | None = None, timeout: float = 15.0, retries: int = 1) -> bool:
bark_key = key or os.environ.get("BARK_KEY") or DEFAULT_BARK_KEY
url = f"{BARK_BASE_URL}/{bark_key}/{quote(title, safe='')}/{quote(body, safe='')}"
last_err: Exception | None = None
for attempt in range(retries + 1):
try:
resp = requests.get(url, timeout=timeout)
except requests.RequestException as e:
last_err = e
continue
if resp.status_code == 200:
return True
print(f"[WARN] Bark 推送返回非 200: {resp.status_code} {resp.text[:120]}")
return False
print(f"[WARN] Bark 推送失败: {last_err}")
return False