移除 Bark 推送通知模块及相关依赖
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
10
CLAUDE.md
10
CLAUDE.md
@@ -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;"
|
"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 流程。
|
**主力轮换规则**(`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 代码不重建镜像就跑等于跑旧代码。这是重要陷阱。
|
**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 规则。
|
`.gitignore` 排除范围广(见文件):`data/`、`*.db*`、`.env*`、CTP 流文件(`*.con`/`*.dat`/`ResultInfo.xml` 等)、`.claude/`、所有日志。新增任何账户、token、行情流文件务必先确认匹配 ignore 规则。
|
||||||
|
|||||||
@@ -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` 数据库
|
2. 写入 PostgreSQL `futures` 数据库
|
||||||
3. 运行三层打分模型
|
3. 运行三层打分模型
|
||||||
4. 保存打分结果并输出到 stdout
|
4. 保存打分结果并输出到 stdout
|
||||||
5. 通过 Bark 推送评分摘要
|
|
||||||
|
|
||||||
### 4. 通过 API 触发流水线
|
### 4. 通过 API 触发流水线
|
||||||
|
|
||||||
@@ -167,14 +166,13 @@ trade/
|
|||||||
│ ├── Dockerfile
|
│ ├── Dockerfile
|
||||||
│ ├── requirements.txt
|
│ ├── requirements.txt
|
||||||
│ ├── .env # TUSHARE_TOKEN(本地,不入库)
|
│ ├── .env # TUSHARE_TOKEN(本地,不入库)
|
||||||
│ └── src/ # 数据采集 + 打分 + Bark 推送 + FastAPI
|
│ └── src/ # 数据采集 + 打分 + FastAPI
|
||||||
│ ├── api.py # FastAPI 服务入口
|
│ ├── api.py # FastAPI 服务入口
|
||||||
│ ├── models.py
|
│ ├── models.py
|
||||||
│ ├── fetcher.py
|
│ ├── fetcher.py
|
||||||
│ ├── scorer.py
|
│ ├── scorer.py
|
||||||
│ ├── storage.py # PostgreSQL 读写
|
│ ├── storage.py # PostgreSQL 读写
|
||||||
│ ├── contracts.py
|
│ ├── contracts.py
|
||||||
│ ├── notifier.py
|
|
||||||
│ └── main.py # CLI 入口
|
│ └── main.py # CLI 入口
|
||||||
└── web/ # Web 浏览端
|
└── web/ # Web 浏览端
|
||||||
├── .dockerignore
|
├── .dockerignore
|
||||||
@@ -227,7 +225,7 @@ A: 郑商所用 `.ZCE` 后缀(如 `FG2609.ZCE`),上期所用 `.SHF`,大
|
|||||||
|
|
||||||
**Q: 如何定时自动跑?**
|
**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 报表(浏览端)
|
## Web 报表(浏览端)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
tushare>=1.4.0
|
tushare>=1.4.0
|
||||||
pandas>=2.2.0
|
pandas>=2.2.0
|
||||||
requests>=2.31.0
|
|
||||||
fastapi>=0.115.0
|
fastapi>=0.115.0
|
||||||
uvicorn[standard]>=0.34.0
|
uvicorn[standard]>=0.34.0
|
||||||
psycopg[binary]>=3.2.0
|
psycopg[binary]>=3.2.0
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from datetime import date
|
|||||||
from fastapi import FastAPI, HTTPException, Query
|
from fastapi import FastAPI, HTTPException, Query
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from . import contracts, fetcher, notifier, scorer, storage
|
from . import contracts, fetcher, scorer, storage
|
||||||
|
|
||||||
app = FastAPI(title="期货数据采集与打分服务")
|
app = FastAPI(title="期货数据采集与打分服务")
|
||||||
|
|
||||||
@@ -50,14 +50,6 @@ def run_pipeline(req: RunRequest):
|
|||||||
result = scorer.score_daily(df, req.trade_date)
|
result = scorer.score_daily(df, req.trade_date)
|
||||||
storage.save_score(result)
|
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(
|
return RunResponse(
|
||||||
ts_code=result.ts_code,
|
ts_code=result.ts_code,
|
||||||
trade_date=result.trade_date,
|
trade_date=result.trade_date,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import sys
|
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:
|
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")
|
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
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
Reference in New Issue
Block a user