332 lines
8.4 KiB
Markdown
332 lines
8.4 KiB
Markdown
# Crypto ATR Signal
|
||
|
||
Version: `v1.3.1`
|
||
|
||
扫描 Binance Futures 交易对,在 4 小时周期识别已收盘 K 线的大阳 / 大阴信号,并通过 FastAPI 网页展示。
|
||
|
||
## 核心逻辑
|
||
|
||
- 数据源:Binance Futures
|
||
- 市场:
|
||
- Crypto:USDT Perpetual crypto 合约
|
||
- TradFi:Binance Futures 传统金融类合约
|
||
- 周期:4H
|
||
- ATR:TradingView 默认 `ATR(14, RMA)`
|
||
- 默认过滤:`High - Low >= ATR * 1.5`
|
||
- 方向:
|
||
- `Close > Open`:大阳
|
||
- `Close < Open`:大阴
|
||
- 已收盘判定:只接受 `close_time <= 当前 UTC 时间` 的 K 线
|
||
- 页面时间:按 `Europe/Madrid` 显示
|
||
|
||
## v1.2 变化
|
||
|
||
- 增加 TradFi 市场模块。
|
||
- 首页增加 Crypto / TradFi 市场切换。
|
||
- Crypto 和 TradFi 使用独立参数,不共用 ATR 长度、过滤倍数、实体过滤设置。
|
||
- 数据库增加 `market_type`,旧 Crypto 数据会自动迁移为 `CRYPTO`。
|
||
- 扫描命令增加 `--market crypto` / `--market tradfi`。
|
||
|
||
## v1.2.1 变化
|
||
|
||
- TradFi 使用休市友好逻辑:按 Binance 实际返回的已收盘 K 线处理,不把休市时间段强行当作漏档。
|
||
- Crypto 保持严格 4H 时间轴检查。
|
||
- 新增 `scan_runs` 扫描记录表。
|
||
|
||
## v1.3.0 变化
|
||
|
||
- 增加 `--market all`,一次完成 Crypto 与 TradFi 扫描。
|
||
- 增加 Discord 聚合推送,一个总开关、每轮一条消息。
|
||
- 页面增加“全部”视图,支持按市场分组或合并排序。
|
||
- 页面默认视图、市场入口、分组和版本信息可通过 `.env` 控制。
|
||
|
||
## v1.3.1 变化
|
||
|
||
- Discord 汇总使用当前最新 K 线信号总数,重复扫描不会误报为 0。
|
||
- 增加扫描进程锁,避免 Cron 任务重叠。
|
||
- 增加 `/health` 健康检查接口。
|
||
- TradFi 新部署默认启用实体占比 `0.5` 过滤。
|
||
|
||
## 项目结构
|
||
|
||
```text
|
||
crypto-atr-signal
|
||
├── scanner.py # 定时扫描:拉 K 线、补缺口、算 ATR、写 signals
|
||
├── webapp.py # FastAPI 网页展示
|
||
├── templates/
|
||
│ └── index.html
|
||
├── data/app.db # SQLite 数据库,运行后生成
|
||
├── .env.example
|
||
├── requirements.txt
|
||
├── VERSION
|
||
├── CHANGELOG.md
|
||
└── README.md
|
||
```
|
||
|
||
## 获取与更新项目
|
||
|
||
推荐使用 SSH 克隆。Gitea 的 SSH 服务使用 `2222` 端口:
|
||
|
||
```bash
|
||
git clone ssh://git@git.polarisx.net:2222/mikemoi/crypto-atr-signal.git
|
||
cd crypto-atr-signal
|
||
```
|
||
|
||
也可以使用 HTTPS:
|
||
|
||
```bash
|
||
git clone https://git.polarisx.net/mikemoi/crypto-atr-signal.git
|
||
cd crypto-atr-signal
|
||
```
|
||
|
||
私有仓库通过 HTTPS 克隆时,请使用 Gitea 用户名和访问令牌,不要把账户密码写进命令或脚本。
|
||
|
||
服务器已经部署过项目时,使用以下命令获取最新版本:
|
||
|
||
```bash
|
||
cd /www/wwwroot/crypto-atr-signal
|
||
git pull --ff-only origin main
|
||
.venv/bin/pip install -r requirements.txt
|
||
sudo systemctl restart crypto-atr-signal
|
||
```
|
||
|
||
`.env`、`data/app.db`、日志和虚拟环境已被 `.gitignore` 排除,执行 `git pull` 不会覆盖这些本地运行数据。更新前不要删除 `data` 目录或 `.env` 文件。
|
||
|
||
## 安装
|
||
|
||
```bash
|
||
cd /www/wwwroot/crypto-atr-signal
|
||
python3 -m venv .venv
|
||
.venv/bin/pip install -r requirements.txt
|
||
cp .env.example .env
|
||
chmod 600 .env
|
||
```
|
||
|
||
## 配置
|
||
|
||
```env
|
||
CRYPTO_ATR_LENGTH=14
|
||
CRYPTO_ATR_MULTIPLE=1.5
|
||
CRYPTO_BODY_RATIO_FILTER_ENABLED=false
|
||
CRYPTO_MIN_BODY_RATIO=0.5
|
||
|
||
TRADFI_ATR_LENGTH=14
|
||
TRADFI_ATR_MULTIPLE=1.5
|
||
TRADFI_BODY_RATIO_FILTER_ENABLED=true
|
||
TRADFI_MIN_BODY_RATIO=0.5
|
||
|
||
DISCORD_ENABLED=false
|
||
DISCORD_WEBHOOK_URL=
|
||
|
||
PAGE_DEFAULT_VIEW=all
|
||
PAGE_SHOW_ALL=true
|
||
PAGE_SHOW_CRYPTO=true
|
||
PAGE_SHOW_TRADFI=true
|
||
PAGE_GROUP_BY_MARKET=true
|
||
PAGE_SHOW_VERSION=true
|
||
|
||
SCAN_LOCK_PATH=data/scanner.lock
|
||
HEALTH_MAX_SCAN_AGE_HOURS=8
|
||
|
||
INIT_KLINES_LIMIT=40
|
||
CONCURRENCY=10
|
||
MAX_RETRIES=3
|
||
RATE_LIMIT_BACKOFF=30
|
||
KLINES_RETENTION_PER_SYMBOL=500
|
||
SIGNAL_RETENTION_DAYS=90
|
||
DB_PATH=data/app.db
|
||
```
|
||
|
||
## 扫描
|
||
|
||
首次运行会为对应市场的每个品种下载最近 `INIT_KLINES_LIMIT` 根 4H 已收盘 K 线,用于初始化 ATR。
|
||
|
||
之后再次运行时:
|
||
|
||
- 没有历史数据:初始化最近 40 根 K 线
|
||
- 有历史数据且无缺口:只拉最新 2 根
|
||
- 有缺口:只补中间缺失的 K 线
|
||
- 单个品种失败:记录错误并跳过,不阻塞整轮扫描
|
||
|
||
手动扫描:
|
||
|
||
```bash
|
||
.venv/bin/python scanner.py --market all
|
||
.venv/bin/python scanner.py --market crypto
|
||
.venv/bin/python scanner.py --market tradfi
|
||
```
|
||
|
||
Discord 聚合推送只由 `--market all` 触发。单独扫描某个市场不会发送汇总消息。
|
||
|
||
## Cron
|
||
|
||
建议服务器使用 UTC 时区。Binance 4H K 线按 UTC 收盘:
|
||
|
||
```text
|
||
00:00 / 04:00 / 08:00 / 12:00 / 16:00 / 20:00 UTC
|
||
```
|
||
|
||
推荐使用一条聚合 cron:
|
||
|
||
```cron
|
||
# Binance 4H close + 1 min, UTC;扫描两个市场后发送一条 Discord 汇总
|
||
1 0,4,8,12,16,20 * * * cd /www/wwwroot/crypto-atr-signal && .venv/bin/python scanner.py --market all >> scanner.log 2>&1
|
||
```
|
||
|
||
对应马德里时间:
|
||
|
||
- 夏令时:02:01 / 06:01 / 10:01 / 14:01 / 18:01 / 22:01
|
||
- 冬令时:01:01 / 05:01 / 09:01 / 13:01 / 17:01 / 21:01
|
||
|
||
## 启动网页
|
||
|
||
```bash
|
||
.venv/bin/python -m uvicorn webapp:app --host 127.0.0.1 --port 8000
|
||
```
|
||
|
||
访问:
|
||
|
||
- 全部:`http://127.0.0.1:8000/?market=all&sort=desc`
|
||
- Crypto:`http://127.0.0.1:8000/?market=crypto&sort=desc`
|
||
- TradFi:`http://127.0.0.1:8000/?market=tradfi&sort=desc`
|
||
|
||
## systemd 常驻网页
|
||
|
||
```ini
|
||
[Unit]
|
||
Description=Crypto ATR Signal Web
|
||
After=network.target
|
||
|
||
[Service]
|
||
WorkingDirectory=/www/wwwroot/crypto-atr-signal
|
||
ExecStart=/www/wwwroot/crypto-atr-signal/.venv/bin/python -m uvicorn webapp:app --host 127.0.0.1 --port 8000
|
||
Restart=always
|
||
RestartSec=3
|
||
User=www
|
||
Group=www
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
```
|
||
|
||
启用:
|
||
|
||
```bash
|
||
sudo systemctl daemon-reload
|
||
sudo systemctl enable crypto-atr-signal
|
||
sudo systemctl start crypto-atr-signal
|
||
sudo systemctl status crypto-atr-signal
|
||
```
|
||
|
||
## Nginx 反向代理
|
||
|
||
FastAPI 只监听 `127.0.0.1:8000`,公网访问交给 Nginx:
|
||
|
||
```nginx
|
||
server {
|
||
listen 80;
|
||
server_name signal.example.com;
|
||
|
||
location / {
|
||
proxy_pass http://127.0.0.1:8000;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
}
|
||
}
|
||
```
|
||
|
||
修改域名后执行:
|
||
|
||
```bash
|
||
sudo nginx -t
|
||
sudo systemctl reload nginx
|
||
```
|
||
|
||
生产环境建议为域名配置 HTTPS;如果页面只供客户使用,可在 Nginx 增加访问认证。
|
||
|
||
## 健康检查
|
||
|
||
```bash
|
||
curl http://127.0.0.1:8000/health
|
||
```
|
||
|
||
数据库正常、Crypto 与 TradFi 最近一次扫描成功且未超过 `HEALTH_MAX_SCAN_AGE_HOURS` 时返回 HTTP `200`。数据库异常、扫描失败或超时未运行时返回 HTTP `503`,可直接接入宝塔或 Uptime Kuma。
|
||
|
||
## 数据库备份与恢复
|
||
|
||
使用 SQLite 自带的在线备份命令,不需要停止网页服务:
|
||
|
||
```bash
|
||
cd /www/wwwroot/crypto-atr-signal
|
||
mkdir -p backups
|
||
sqlite3 data/app.db ".backup 'backups/app-$(date +%F-%H%M).db'"
|
||
```
|
||
|
||
恢复前先停止网页服务,并确保扫描任务没有运行:
|
||
|
||
```bash
|
||
sudo systemctl stop crypto-atr-signal
|
||
cp backups/app-YYYY-MM-DD-HHMM.db data/app.db
|
||
sudo systemctl start crypto-atr-signal
|
||
```
|
||
|
||
## 常见故障排查
|
||
|
||
```bash
|
||
# 查看网页服务状态和最近日志
|
||
sudo systemctl status crypto-atr-signal
|
||
sudo journalctl -u crypto-atr-signal -n 100 --no-pager
|
||
|
||
# 手动执行一轮聚合扫描
|
||
cd /www/wwwroot/crypto-atr-signal
|
||
.venv/bin/python scanner.py --market all
|
||
|
||
# 检查网页和数据库健康状态
|
||
curl -i http://127.0.0.1:8000/health
|
||
```
|
||
|
||
如果日志出现 `another scanner process is already running`,说明上一轮尚未结束,本轮已被进程锁安全跳过,无需手动删除锁文件。
|
||
|
||
## 页面功能
|
||
|
||
- Crypto / TradFi 市场切换
|
||
- 交易对列表校对时间
|
||
- 最新已收盘 K 线时间
|
||
- 当前信号统计
|
||
- 大阳 / 大阴数量
|
||
- 扫描品种数量
|
||
- ATR 倍数点击切换正序 / 倒序
|
||
- 点击品种跳转 TradingView
|
||
|
||
## 数据库
|
||
|
||
使用 SQLite,并开启 WAL:
|
||
|
||
```sql
|
||
PRAGMA journal_mode = WAL;
|
||
```
|
||
|
||
主要表:
|
||
|
||
- `symbols`:交易对列表,按 `market_type + symbol` 区分市场
|
||
- `klines`:4H K 线和 ATR,按 `market_type + symbol + open_time` 存储
|
||
- `signals`:大阳 / 大阴信号,按 `market_type + symbol + open_time` 存储
|
||
|
||
## 资源建议
|
||
|
||
独立 `2C2G` VPS 足够运行:
|
||
|
||
- FastAPI 常驻
|
||
- SQLite
|
||
- cron 每 4 小时聚合扫描 Crypto 和 TradFi
|
||
- Nginx 反向代理
|
||
|
||
建议:
|
||
|
||
- `CONCURRENCY=8~10`
|
||
- 服务器时区用 UTC
|
||
- 页面显示马德里时间
|
||
- 不需要 MySQL / PostgreSQL
|