refactor: 移除 gate_bot,统一为三所架构并更新文档
删除 crypto_monitor_gate_bot 目录,中控与子代理改为 binance/okx/gate 三账户; 文档与 UI 文案「四所」改为「三所」;新增清库前一次性配置备份脚本。 Co-authored-by: Cursor <cursoragent@cursor.com>
@@ -23,6 +23,7 @@ manual_trading_hub/hub_ai_summaries.json
|
|||||||
manual_trading_hub/hub_ai_chat.json
|
manual_trading_hub/hub_ai_chat.json
|
||||||
manual_trading_hub/hub_ai_fund_history.json
|
manual_trading_hub/hub_ai_fund_history.json
|
||||||
manual_trading_hub/data/
|
manual_trading_hub/data/
|
||||||
|
backups/
|
||||||
|
|
||||||
# 数据库与上传(运行时生成)
|
# 数据库与上传(运行时生成)
|
||||||
**/*.sqlite
|
**/*.sqlite
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# AI 复盘与模型配置说明
|
# AI 复盘与模型配置说明
|
||||||
|
|
||||||
四个 `crypto_monitor_*` 实例共用仓库根目录 **`ai_client.py`**(通过 `PYTHONPATH=..` 导入)。用于 **交易记录与复盘** 页的 AI 点评、短评建议,以及从复盘截图提取结构化 JSON。
|
三个 `crypto_monitor_*` 实例共用仓库根目录 **`ai_client.py`**(通过 `PYTHONPATH=..` 导入)。用于 **交易记录与复盘** 页的 AI 点评、短评建议,以及从复盘截图提取结构化 JSON。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ AI_MODEL=huihui_ai/deepseek-r1-abliterated:latest
|
|||||||
### Ollama
|
### Ollama
|
||||||
|
|
||||||
- 需本机已安装并拉取对应模型;`AI_PROVIDER=ollama` 时使用 `OLLAMA_API` 与 `AI_MODEL`。
|
- 需本机已安装并拉取对应模型;`AI_PROVIDER=ollama` 时使用 `OLLAMA_API` 与 `AI_MODEL`。
|
||||||
- 四所 `app.py` **不再** 直连 Ollama;统一走 `ai_client.ai_generate` / `ai_review` / `ai_short_advice`。
|
- 三所 `app.py` **不再** 直连 Ollama;统一走 `ai_client.ai_generate` / `ai_review` / `ai_short_advice`。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# 复盘交易系统(crypto_monitor)
|
# 复盘交易系统(crypto_monitor)
|
||||||
|
|
||||||
多交易所 **USDT 永续** 的下单监控、**关键位**、**策略交易**、**止盈止损 / 移动保本** 与 **AI 复盘**,四所独立部署 + 可选 **中控** 聚合监控。
|
多交易所 **USDT 永续** 的下单监控、**关键位**、**策略交易**、**止盈止损 / 移动保本** 与 **AI 复盘**,三所独立部署 + 可选 **中控** 聚合监控。
|
||||||
|
|
||||||
**远程仓库**:[https://git.bz121.com/dekun/crypto_monitor.git](https://git.bz121.com/dekun/crypto_monitor.git)
|
**远程仓库**:[https://git.bz121.com/dekun/crypto_monitor.git](https://git.bz121.com/dekun/crypto_monitor.git)
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ bash deploy/setup_env.sh --install-system-deps
|
|||||||
|------|------|------|
|
|------|------|------|
|
||||||
| **关键位监控** | 箱体/收敛自动开仓、阻力支撑提醒、斐波限价;止盈止损方案与 **移动保本** 开关 | 各所 [关键位自动下单说明.md](./crypto_monitor_binance/关键位自动下单说明.md)(Gate/OKX 目录内同名);方案细则 **[关键位止盈止损与移动保本更新说明.md](./关键位止盈止损与移动保本更新说明.md)** |
|
| **关键位监控** | 箱体/收敛自动开仓、阻力支撑提醒、斐波限价;止盈止损方案与 **移动保本** 开关 | 各所 [关键位自动下单说明.md](./crypto_monitor_binance/关键位自动下单说明.md)(Gate/OKX 目录内同名);方案细则 **[关键位止盈止损与移动保本更新说明.md](./关键位止盈止损与移动保本更新说明.md)** |
|
||||||
| **实盘下单 / 下单监控** | 首仓、以损定仓;监控内 **止盈 / 止损**、**移动保本**(步进 R、偏移%) | 各所 [使用说明.md](./crypto_monitor_binance/使用说明.md) · 顶栏「实盘下单」`/trade` |
|
| **实盘下单 / 下单监控** | 首仓、以损定仓;监控内 **止盈 / 止损**、**移动保本**(步进 R、偏移%) | 各所 [使用说明.md](./crypto_monitor_binance/使用说明.md) · 顶栏「实盘下单」`/trade` |
|
||||||
| **策略交易** | **趋势回调** + **顺势加仓**(`/strategy` 双栏) | **[策略交易说明.md](./策略交易说明.md)** · 趋势细则 [crypto_monitor_gate_bot/趋势回调策略说明.md](./crypto_monitor_gate_bot/趋势回调策略说明.md) |
|
| **策略交易** | **趋势回调** + **顺势加仓**(`/strategy` 双栏) | **[策略交易说明.md](./策略交易说明.md)** · 趋势细则 [docs/trend-pullback-strategy.md](./docs/trend-pullback-strategy.md) |
|
||||||
| **策略交易记录** | 已结束计划快照(最近 100 条)、筛选与展开详情 | [策略交易说明.md §五](./策略交易说明.md) · 顶栏 `/strategy/records` |
|
| **策略交易记录** | 已结束计划快照(最近 100 条)、筛选与展开详情 | [策略交易说明.md §五](./策略交易说明.md) · 顶栏 `/strategy/records` |
|
||||||
| **交易复盘** | 平仓记录、错过机会、图表;**AI 点评** | **[AI复盘与模型配置说明.md](./AI复盘与模型配置说明.md)** · 顶栏「交易记录与复盘」`/records` |
|
| **交易复盘** | 平仓记录、错过机会、图表;**AI 点评** | **[AI复盘与模型配置说明.md](./AI复盘与模型配置说明.md)** · 顶栏「交易记录与复盘」`/records` |
|
||||||
| **中控** | 多账户持仓/委托聚合、行情 K 线、紧急全平(**不在中控网页下单**) | [manual_trading_hub/使用说明.md](./manual_trading_hub/使用说明.md) · [部署文档.md](./manual_trading_hub/部署文档.md) |
|
| **中控** | 多账户持仓/委托聚合、行情 K 线、紧急全平(**不在中控网页下单**) | [manual_trading_hub/使用说明.md](./manual_trading_hub/使用说明.md) · [部署文档.md](./manual_trading_hub/部署文档.md) |
|
||||||
@@ -49,8 +49,7 @@ bash deploy/setup_env.sh --install-system-deps
|
|||||||
| 目录 | 交易所 / 角色 | 部署文档 |
|
| 目录 | 交易所 / 角色 | 部署文档 |
|
||||||
|------|----------------|----------|
|
|------|----------------|----------|
|
||||||
| `crypto_monitor_binance/` | Binance U 本位永续 | [部署文档.md](./crypto_monitor_binance/部署文档.md) |
|
| `crypto_monitor_binance/` | Binance U 本位永续 | [部署文档.md](./crypto_monitor_binance/部署文档.md) |
|
||||||
| `crypto_monitor_gate/` | Gate 主号 | [部署文档.md](./crypto_monitor_gate/部署文档.md) |
|
| `crypto_monitor_gate/` | Gate | [部署文档.md](./crypto_monitor_gate/部署文档.md) |
|
||||||
| `crypto_monitor_gate_bot/` | Gate 机器人 / 趋势户 | [部署文档.md](./crypto_monitor_gate_bot/部署文档.md) |
|
|
||||||
| `crypto_monitor_okx/` | OKX 永续 | [部署文档.md](./crypto_monitor_okx/部署文档.md) |
|
| `crypto_monitor_okx/` | OKX 永续 | [部署文档.md](./crypto_monitor_okx/部署文档.md) |
|
||||||
| `manual_trading_hub/` | 中控 + 子代理 | [部署文档.md](./manual_trading_hub/部署文档.md) |
|
| `manual_trading_hub/` | 中控 + 子代理 | [部署文档.md](./manual_trading_hub/部署文档.md) |
|
||||||
| `lib/` | **共用模块**(策略、关键位、交易、中控库、AI、静态与模板) | **[docs/lib-structure.md](./docs/lib-structure.md)** |
|
| `lib/` | **共用模块**(策略、关键位、交易、中控库、AI、静态与模板) | **[docs/lib-structure.md](./docs/lib-structure.md)** |
|
||||||
@@ -64,7 +63,7 @@ bash deploy/setup_env.sh --install-system-deps
|
|||||||
## 技术要点
|
## 技术要点
|
||||||
|
|
||||||
- **Python 3.10+**、Flask、ccxt、SQLite(`crypto.db`)
|
- **Python 3.10+**、Flask、ccxt、SQLite(`crypto.db`)
|
||||||
- 四所 `.env` 前缀不同(`BINANCE_*` / `GATE_*` / `OKX_*`),**不可混用**
|
- 三所 `.env` 前缀不同(`BINANCE_*` / `GATE_*` / `OKX_*`),**不可混用**
|
||||||
- 实盘须 `LIVE_TRADING_ENABLED=true` 且理解 API 权限与 IP 白名单风险
|
- 实盘须 `LIVE_TRADING_ENABLED=true` 且理解 API 权限与 IP 白名单风险
|
||||||
- 经 **SOCKS** 访问交易所时配置各所 `*_SOCKS_PROXY` 并安装 PySocks
|
- 经 **SOCKS** 访问交易所时配置各所 `*_SOCKS_PROXY` 并安装 PySocks
|
||||||
|
|
||||||
@@ -72,7 +71,7 @@ bash deploy/setup_env.sh --install-system-deps
|
|||||||
|
|
||||||
## 推荐阅读顺序
|
## 推荐阅读顺序
|
||||||
|
|
||||||
1. [docs/ubuntu-server.md](./docs/ubuntu-server.md) — 装 Python / Node / PM2,PM2 启动四所 + 中控
|
1. [docs/ubuntu-server.md](./docs/ubuntu-server.md) — 装 Python / Node / PM2,PM2 启动三所 + 中控
|
||||||
2. 各所 **`.env`**(从 `.env.example` 复制)
|
2. 各所 **`.env`**(从 `.env.example` 复制)
|
||||||
3. 所用功能对应上表 **功能导航** 文档
|
3. 所用功能对应上表 **功能导航** 文档
|
||||||
4. [备份与恢复.md](./备份与恢复.md) — 生产机备份习惯
|
4. [备份与恢复.md](./备份与恢复.md) — 生产机备份习惯
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "复盘系统中控",
|
"name": "复盘系统中控",
|
||||||
"short_name": "中控",
|
"short_name": "中控",
|
||||||
"description": "四所交易监控与行情中控",
|
"description": "三所交易监控与行情中控",
|
||||||
"start_url": "/monitor",
|
"start_url": "/monitor",
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
"background_color": "#0b0e18",
|
"background_color": "#0b0e18",
|
||||||
|
|||||||
@@ -140,7 +140,7 @@
|
|||||||
## 升级步骤
|
## 升级步骤
|
||||||
|
|
||||||
1. `git pull` 后对比 `.env.example`,把新增变量合并进本地 `.env`。
|
1. `git pull` 后对比 `.env.example`,把新增变量合并进本地 `.env`。
|
||||||
2. 在 VPS 上为 Binance / Gate / Gate Bot **各执行一次** `bash scripts/install_backup_cron.sh`(若尚未安装)。
|
2. 在 VPS 上为 Binance / Gate / **各执行一次** `bash scripts/install_backup_cron.sh`(若尚未安装)。
|
||||||
3. 重启 Binance 实例(如 `pm2 restart crypto_binance`);SQLite 会自动 `ALTER` 缺列(斐波、交易所盈亏、`entry_reason` 等)。
|
3. 重启 Binance 实例(如 `pm2 restart crypto_binance`);SQLite 会自动 `ALTER` 缺列(斐波、交易所盈亏、`entry_reason` 等)。
|
||||||
4. 浏览器强刷(Ctrl+F5)避免旧版 `index.html` 缓存。
|
4. 浏览器强刷(Ctrl+F5)避免旧版 `index.html` 缓存。
|
||||||
5. 打开任意页确认顶栏出现 **「列表筛选(UTC)」**;`/stats` 可见分品类统计与「北京 8:00 切日」说明。
|
5. 打开任意页确认顶栏出现 **「列表筛选(UTC)」**;`/stats` 可见分品类统计与「北京 8:00 切日」说明。
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ cp .env .env.backup.$(date +%Y%m%d)
|
|||||||
|
|
||||||
### 5.3 AI 复盘与模型(可选)
|
### 5.3 AI 复盘与模型(可选)
|
||||||
|
|
||||||
四所共用仓库根目录 **`ai_client.py`**(PM2 的 **`PYTHONPATH=..`** 须包含仓库根)。在 `.env` 中配置 **`AI_PROVIDER`**:
|
三所共用仓库根目录 **`ai_client.py`**(PM2 的 **`PYTHONPATH=..`** 须包含仓库根)。在 `.env` 中配置 **`AI_PROVIDER`**:
|
||||||
|
|
||||||
| 模式 | 主要变量 |
|
| 模式 | 主要变量 |
|
||||||
|------|----------|
|
|------|----------|
|
||||||
@@ -191,10 +191,10 @@ cd /opt/crypto_monitor/crypto_monitor_gate
|
|||||||
bash scripts/install_backup_cron.sh
|
bash scripts/install_backup_cron.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Gate Bot 实例(趋势回调等):
|
实例(趋势回调等):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /opt/crypto_monitor/crypto_monitor_gate_bot
|
cd /opt/crypto_monitor/crypto_monitor_gate
|
||||||
bash scripts/install_backup_cron.sh
|
bash scripts/install_backup_cron.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -141,7 +141,7 @@
|
|||||||
## 升级步骤
|
## 升级步骤
|
||||||
|
|
||||||
1. `git pull` 后对比 `.env.example`,把新增变量合并进本地 `.env`。
|
1. `git pull` 后对比 `.env.example`,把新增变量合并进本地 `.env`。
|
||||||
2. 在 VPS 上为 Binance / Gate / Gate Bot **各执行一次** `bash scripts/install_backup_cron.sh`(若尚未安装)。
|
2. 在 VPS 上为 Binance / Gate / **各执行一次** `bash scripts/install_backup_cron.sh`(若尚未安装)。
|
||||||
3. 重启 Gate 实例服务(如 `pm2 restart crypto_gate`);首次启动会自动 `ALTER TABLE` 缺列(斐波、交易所盈亏、`entry_reason` 等)。
|
3. 重启 Gate 实例服务(如 `pm2 restart crypto_gate`);首次启动会自动 `ALTER TABLE` 缺列(斐波、交易所盈亏、`entry_reason` 等)。
|
||||||
4. 浏览器强刷(Ctrl+F5)避免旧版 `index.html` 缓存。
|
4. 浏览器强刷(Ctrl+F5)避免旧版 `index.html` 缓存。
|
||||||
5. 打开任意页确认顶栏出现 **「列表筛选(UTC)」**;`/stats` 可见分品类统计与「北京 8:00 切日」说明。
|
5. 打开任意页确认顶栏出现 **「列表筛选(UTC)」**;`/stats` 可见分品类统计与「北京 8:00 切日」说明。
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ bash scripts/backup_data.sh # 试跑
|
|||||||
|
|
||||||
备份目录:`/root/backups/crypto_monitor_gate/YYYY-MM-DD/`。详见 Binance 项目 `部署文档.md` 第 5.4 节(恢复步骤、可选 `.env` 变量相同)。
|
备份目录:`/root/backups/crypto_monitor_gate/YYYY-MM-DD/`。详见 Binance 项目 `部署文档.md` 第 5.4 节(恢复步骤、可选 `.env` 变量相同)。
|
||||||
|
|
||||||
若还部署了 **`crypto_monitor_gate_bot`**,请在该目录同样执行 `bash scripts/install_backup_cron.sh`。
|
若还部署了 **`crypto_monitor_okx`**,请在该目录同样执行 `bash scripts/install_backup_cron.sh`。
|
||||||
|
|
||||||
### 5.5 必填项检查(Gate + 代理)
|
### 5.5 必填项检查(Gate + 代理)
|
||||||
|
|
||||||
|
|||||||
@@ -1,210 +0,0 @@
|
|||||||
# =============================================================================
|
|
||||||
# 环境配置模板(可提交 Git)。程序运行时只读取同目录下的 .env。
|
|
||||||
#
|
|
||||||
# 首次部署 / 新机:
|
|
||||||
# cp .env.example .env
|
|
||||||
# nano .env # 填入真实密钥、端口、代理等
|
|
||||||
#
|
|
||||||
# 升级代码(git pull)前建议备份(.env 不在 Git 中,pull 不会覆盖):
|
|
||||||
# cp .env .env.backup.$(date +%Y%m%d)
|
|
||||||
#
|
|
||||||
# 从备份恢复:
|
|
||||||
# cp .env.backup.YYYYMMDD .env
|
|
||||||
# =============================================================================
|
|
||||||
|
|
||||||
APP_ENV=production
|
|
||||||
# 服务监听地址(云服务器通常用 0.0.0.0)
|
|
||||||
APP_HOST=0.0.0.0
|
|
||||||
# 服务端口
|
|
||||||
APP_PORT=5002
|
|
||||||
# 是否开启调试模式(生产建议 false)
|
|
||||||
APP_DEBUG=false
|
|
||||||
|
|
||||||
# 登录账号
|
|
||||||
APP_USERNAME=admin
|
|
||||||
# 登录密码(请改成你自己的强密码)
|
|
||||||
APP_PASSWORD=admin123
|
|
||||||
# 是否关闭登录校验(局域网可设 true;公网务必 false)
|
|
||||||
APP_AUTH_DISABLED=true
|
|
||||||
# --- 多账户交易中控 manual_trading_hub ---
|
|
||||||
# 中控请求本实例 /api/hub/* 时携带请求头 X-Hub-Token,须与中控启动环境变量 HUB_BRIDGE_TOKEN 一致
|
|
||||||
# 未设置且 APP_AUTH_DISABLED=false 时,仅网页登录后可访问;本机联调可保持 APP_AUTH_DISABLED=true
|
|
||||||
# HUB_BRIDGE_TOKEN=your-long-random-token
|
|
||||||
# Flask 会话密钥(必须替换为长随机字符串)
|
|
||||||
FLASK_SECRET_KEY=CHANGE_TO_LONG_RANDOM_SECRET
|
|
||||||
|
|
||||||
# 企业微信机器人 Webhook(用于行情/风控推送)
|
|
||||||
WECHAT_WEBHOOK=https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=REPLACE_WITH_REAL_KEY
|
|
||||||
|
|
||||||
# 数据库文件路径(相对路径会自动按项目目录解析)
|
|
||||||
DB_PATH=crypto.db
|
|
||||||
# 交易截图上传目录
|
|
||||||
UPLOAD_DIR=static/images
|
|
||||||
|
|
||||||
# 自动备份(scripts/backup_data.sh + cron,可选;默认即可)
|
|
||||||
# BACKUP_ROOT=/root/backups
|
|
||||||
# BACKUP_RETENTION_DAYS=30
|
|
||||||
# BACKUP_INSTANCE=crypto_monitor_gate_bot
|
|
||||||
|
|
||||||
# 已废弃:资金账户仅显示交易所 funding 余额,不再读取此变量
|
|
||||||
# TOTAL_CAPITAL=100
|
|
||||||
# 计仓:risk=以损定仓(默认);full_margin=合约可用×FULL_MARGIN_BUFFER_RATIO 全仓杠杆(须无仓后重启)
|
|
||||||
POSITION_SIZING_MODE=risk
|
|
||||||
# 每天起始基数(U)
|
|
||||||
DAILY_START_CAPITAL=30
|
|
||||||
# 日内回撤后基数(U)
|
|
||||||
DAILY_LOSS_CAPITAL=20
|
|
||||||
# 日内盈利后基数(U)
|
|
||||||
DAILY_PROFIT_CAPITAL=50
|
|
||||||
# BTC 默认杠杆倍数
|
|
||||||
BTC_LEVERAGE=10
|
|
||||||
# 山寨币默认杠杆倍数
|
|
||||||
ALT_LEVERAGE=5
|
|
||||||
# 交易日重置小时(北京时间)
|
|
||||||
TRADING_DAY_RESET_HOUR=8
|
|
||||||
# 整点前禁止新开仓:true=启用(默认),false=关闭(仍可保留 8 点作为交易日划分)
|
|
||||||
TRADING_DAY_RESET_OPEN_GUARD_ENABLED=true
|
|
||||||
|
|
||||||
# 是否开启 Gate 实盘下单(false=只做本地流程,true=真实下单)
|
|
||||||
LIVE_TRADING_ENABLED=true
|
|
||||||
# Gate API Key(实盘)
|
|
||||||
GATE_API_KEY=REPLACE_WITH_GATE_API_KEY
|
|
||||||
# Gate API Secret(实盘)
|
|
||||||
GATE_API_SECRET=REPLACE_WITH_GATE_API_SECRET
|
|
||||||
# 保证金模式:cross=全仓,isolated=逐仓
|
|
||||||
GATE_TD_MODE=cross
|
|
||||||
# 持仓筛选:hedge=双向持仓下按多空腿过滤;其它值(如 single)不按腿过滤
|
|
||||||
GATE_POS_MODE=hedge
|
|
||||||
# 永续止盈止损:是否优先用官方仓位类触发单(POST price_orders,close-*-position);false=仅用旧版两张 ccxt 条件单
|
|
||||||
GATE_TPSL_USE_POSITION_ORDER=true
|
|
||||||
# 触发单超时(秒),默认 604800=7 天;设为 0 或负数则不向 API 传 expiration
|
|
||||||
GATE_TPSL_TRIGGER_EXPIRATION=604800
|
|
||||||
# 触发参考价:0=最新成交 1=标记价 2=指数价(非法值按 0)
|
|
||||||
GATE_TPSL_PRICE_TYPE=0
|
|
||||||
# 仓位类 TP/SL 相对现价的最小间距(%),避免 Gate 1026「触发价须高于/低于现价」
|
|
||||||
GATE_TPSL_LAST_PRICE_GAP_PCT=0.05
|
|
||||||
# 页面与浏览器标签展示的交易所名称(多环境区分时可改成例如 Gate·模拟)
|
|
||||||
# EXCHANGE_DISPLAY_NAME=Gate.io
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 关键位门控(页面「关键位监控」规则条与 _key_hard_checks 共用)
|
|
||||||
# =============================================================================
|
|
||||||
# 【周期】门控 K 线周期,如 5m、15m
|
|
||||||
KLINE_TIMEFRAME=5m
|
|
||||||
# 【确认K】闭合 K 序列中的棒偏移:突破棒默认 -2,确认棒默认 -1
|
|
||||||
KEY_CONFIRM_BREAKOUT_BAR=-2
|
|
||||||
KEY_CONFIRM_BAR=-1
|
|
||||||
# 【量能】突破棒成交量 > 前 N 根均量 × 倍数
|
|
||||||
KEY_VOLUME_MA_BARS=20
|
|
||||||
KEY_VOLUME_RATIO_MIN=1.3
|
|
||||||
# 【突破K实体幅度】占开盘价百分比区间
|
|
||||||
# 【箱体/收敛】突破K收盘越过关键位下限%;无上限(过猛由计划RR过滤)
|
|
||||||
KEY_BREAKOUT_AMP_MIN_PCT=0.03
|
|
||||||
KEY_BREAKOUT_AMP_MAX_PCT=0.5
|
|
||||||
# 【阻力/支撑】突破后微信提醒
|
|
||||||
KEY_ALERT_MAX_TIMES=3
|
|
||||||
KEY_ALERT_INTERVAL_MINUTES=5
|
|
||||||
# 【日成交量排名】品种须在该排名前 N 名
|
|
||||||
KEY_DAILY_VOLUME_RANK_MAX=30
|
|
||||||
# 【关键位自动开仓盈亏比】严格大于该值才市价开仓
|
|
||||||
KEY_AUTO_MIN_PLANNED_RR=1.5
|
|
||||||
# 止损:突破 K 极值向外缓冲的百分比(默认 0.5 即 0.5%)
|
|
||||||
KEY_STOP_OUTSIDE_BREAKOUT_PCT=0.5
|
|
||||||
# 趋势单方案:止损在突破 K 极值外侧的百分比(默认 1 即 1%)
|
|
||||||
KEY_TREND_STOP_OUTSIDE_PCT=1
|
|
||||||
KEY_ALERT_MAX_TIMES=3
|
|
||||||
KEY_ALERT_INTERVAL_MINUTES=5
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 交易执行 / 人工风控(页面「实盘下单」)
|
|
||||||
# =============================================================================
|
|
||||||
# 【最大同时持仓】默认 1=单仓
|
|
||||||
MAX_ACTIVE_POSITIONS=1
|
|
||||||
# 【人工下单最低盈亏比】低于该值前后端均拒绝(默认 1.4,即须 >=1.4:1)
|
|
||||||
MANUAL_MIN_PLANNED_RR=1.4
|
|
||||||
# 【关键位连开计仓】已有持仓时按无仓时资金快照算基数
|
|
||||||
KEY_SIZING_USE_ZERO_POSITION_SNAPSHOT=true
|
|
||||||
# 【单日开仓 AI 提醒】本交易日开仓达到该次数时推送企业微信 AI 克制提醒(不拦单)
|
|
||||||
DAILY_OPEN_ALERT_THRESHOLD=5
|
|
||||||
# 【单日开仓硬上限】本交易日开仓次数>=该值后禁止一切新开仓直至下一交易日(北京时间 TRADING_DAY_RESET_HOUR 切日);0=不启用
|
|
||||||
DAILY_OPEN_HARD_LIMIT=0
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 账户冷静期 / 日冻结风控(手动平仓、外部平仓、复盘情绪标签)
|
|
||||||
# 详见 docs/account-risk-cooldown.md
|
|
||||||
# =============================================================================
|
|
||||||
# RISK_CONTROL_ENABLED=true
|
|
||||||
# RISK_COOLING_HOURS_MANUAL=4
|
|
||||||
# RISK_COOLING_HOURS_MANUAL_JOURNAL=1
|
|
||||||
# RISK_MANUAL_CLOSE_DAILY_LIMIT=2
|
|
||||||
# RISK_MOOD_ISSUES_DAILY_FREEZE=true
|
|
||||||
|
|
||||||
# 资金与仓位刷新周期(秒)
|
|
||||||
BALANCE_REFRESH_SECONDS=60
|
|
||||||
# 前端价格快照轮询(秒)
|
|
||||||
PRICE_REFRESH_SECONDS=5
|
|
||||||
# 后台监控轮询周期(秒)
|
|
||||||
MONITOR_POLL_SECONDS=3
|
|
||||||
# 重启后多少秒内不做「外部平仓」同步(避免 API 未就绪误判)
|
|
||||||
RECONCILE_STARTUP_GRACE_SEC=90
|
|
||||||
# 连续多少次轮询确认交易所空仓后,才记为外部平仓(默认 3 次 ≈ 9 秒)
|
|
||||||
RECONCILE_FLAT_CONFIRM_POLLS=3
|
|
||||||
# 使用可用资金时的缓冲比例(如0.98代表用98%)
|
|
||||||
FULL_MARGIN_BUFFER_RATIO=0.98
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 自动划转(页顶「将 swap 补足到 XU」;与 DAILY_START_CAPITAL 独立,需一致时请设为相同值)
|
|
||||||
# =============================================================================
|
|
||||||
AUTO_TRANSFER_ENABLED=false
|
|
||||||
# 交易账户(swap)目标余额 U:每日 8 点(北京)自动划入或划出至 funding;持仓中不划转
|
|
||||||
AUTO_TRANSFER_AMOUNT=30
|
|
||||||
AUTO_TRANSFER_FROM=funding
|
|
||||||
AUTO_TRANSFER_TO=swap
|
|
||||||
TRANSFER_CCY=USDT
|
|
||||||
# 北京时间该整点小时内尝试;账簿按 UTC 自然日去重
|
|
||||||
AUTO_TRANSFER_BJ_HOUR=8
|
|
||||||
# 强制清仓整点(北京时间,默认 0=凌晨00点)
|
|
||||||
FORCE_CLOSE_BJ_HOUR=0
|
|
||||||
# 是否启用强制清仓(默认关闭,true 才会在整点执行)
|
|
||||||
FORCE_CLOSE_ENABLED=false
|
|
||||||
|
|
||||||
# 推送与AI超时(秒)
|
|
||||||
WECHAT_TIMEOUT_SECONDS=10
|
|
||||||
AI_TIMEOUT_SECONDS=120
|
|
||||||
|
|
||||||
# AI 提供方:openai(默认)| ollama
|
|
||||||
AI_PROVIDER=openai
|
|
||||||
OPENAI_API_BASE=https://op.bz121.com/v1
|
|
||||||
OPENAI_API_KEY=你的密钥
|
|
||||||
OPENAI_MODEL=gemma4:e4b
|
|
||||||
OLLAMA_API=http://127.0.0.1:11434/api/generate
|
|
||||||
AI_MODEL=huihui_ai/deepseek-r1-abliterated:latest
|
|
||||||
|
|
||||||
# Gate 代理(可选):本机网络不稳定时通过 SSH 动态转发 SOCKS5 出口
|
|
||||||
# 1) 先在本机建立隧道(示例):
|
|
||||||
# ssh -N -D 127.0.0.1:1080 root@你的VPS_IP -o ServerAliveInterval=30 -o ExitOnForwardFailure=yes
|
|
||||||
# 2) 再启用下面这一行(推荐 socks5h,让远端解析域名):
|
|
||||||
# GATE_SOCKS_PROXY=socks5h://127.0.0.1:1080
|
|
||||||
#
|
|
||||||
# 如你更偏向 HTTP 代理(VPS 上跑 tinyproxy 之类),可用:
|
|
||||||
# GATE_HTTP_PROXY=http://127.0.0.1:3128
|
|
||||||
# GATE_HTTPS_PROXY=http://127.0.0.1:3128
|
|
||||||
|
|
||||||
# 开仓多周期K线图(可选)
|
|
||||||
# ORDER_CHART_ENABLED=true
|
|
||||||
# ORDER_CHART_TFS=4h,1h,15m,5m
|
|
||||||
# ORDER_CHART_LIMIT=100
|
|
||||||
# ORDER_CHART_DIR=static/images/order_charts
|
|
||||||
# 详见 DAILY_OPEN_ALERT_THRESHOLD / DAILY_OPEN_HARD_LIMIT;说明文档 docs/daily-open-limit.md
|
|
||||||
# 以损定仓(按交易账户资金的百分比)
|
|
||||||
# RISK_PERCENT=2
|
|
||||||
# 移动保本触发(达到多少R触发)与偏移(百分比)
|
|
||||||
# BREAKEVEN_RR_TRIGGER=1.0
|
|
||||||
# 移动保本阶梯(每多少R继续上移一次,默认1R)
|
|
||||||
# BREAKEVEN_STEP_R=1.0
|
|
||||||
# BREAKEVEN_OFFSET_PCT=0.02
|
|
||||||
# 开单风格默认值:trend / swing
|
|
||||||
# DEFAULT_TRADE_STYLE=trend
|
|
||||||
|
|
||||||
APP_TIMEZONE=Asia/Shanghai
|
|
||||||
# TRADING_DAY_RESET_HOUR 现在表示「北京时间」整点,默认 8 点起算新交易日;开仓整点限制见 TRADING_DAY_RESET_OPEN_GUARD_ENABLED
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
# crypto_monitor_gate
|
|
||||||
|
|
||||||
基于 **Flask** 的加密货币 **下单监控 / 关键位监控 / 交易复盘** 小系统,行情与实盘接口统一走 **Gate.io USDT 永续**,通过 **ccxt** 访问。
|
|
||||||
|
|
||||||
## 文档导航
|
|
||||||
|
|
||||||
| 文档 | 说明 |
|
|
||||||
|------|------|
|
|
||||||
| **[使用说明.md](./使用说明.md)** | 日常怎么用:登录、关键位四类、手工开仓、单仓与微信等 |
|
|
||||||
| **[关键位自动下单说明.md](./关键位自动下单说明.md)** | 关键位自动开仓的 RR、止盈止损、结案原因与 `.env` |
|
|
||||||
| **[部署文档.md](./部署文档.md)** | Ubuntu、PM2、**SSH SOCKS** 访问 Gate API 等 |
|
|
||||||
|
|
||||||
另:**Binance U 本位** 对等实现见同级的 **`crypto_monitor_binance`** 仓库。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 功能概要
|
|
||||||
|
|
||||||
- **关键位监控**:5m 收线硬条件、企业微信推送;**箱体 / 收敛** 在 RR 达标时可 **自动市价开仓**(见专门文档);**阻力 / 支撑** 仅单次提醒结案
|
|
||||||
- **下单监控**:本地风控(含移动保本)、止盈/止损触达后轮询尝试平仓并记账
|
|
||||||
- **实盘(可选)**:`LIVE_TRADING_ENABLED=true` 且配置 **`GATE_API_KEY` / `GATE_API_SECRET`** 时,支持开仓、挂单 TP/SL、余额与划转(权限依账户而定)
|
|
||||||
- **止盈止损(Gate)**:市价成交后经 **`_gate_place_tp_sl_orders`** 挂单;优先 **仓位类 `price_orders`**(受 `GATE_TPSL_USE_POSITION_ORDER`、`GATE_TPSL_PRICE_TYPE`、`GATE_POS_MODE` 等影响)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 环境要求
|
|
||||||
|
|
||||||
- Python 3.10+(建议)
|
|
||||||
- 依赖:`flask`、`requests`、`ccxt`、`werkzeug`、`PySocks`(经 SOCKS 代理时);`Pillow`(K 线导出等可选用)
|
|
||||||
|
|
||||||
安装示例:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /opt/crypto_monitor/crypto_monitor_gate
|
|
||||||
source .venv/bin/activate
|
|
||||||
pip install -r ../requirements.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
## 配置(`.env.example` → `.env`)
|
|
||||||
|
|
||||||
- **`.env.example`**:模板(可提交 Git);首次:`cp .env.example .env` 后编辑。
|
|
||||||
- **`.env`**:本机真实配置(勿提交);`git pull` 不覆盖;升级前建议备份(见《部署文档》§5.2)。
|
|
||||||
|
|
||||||
项目启动时加载**仓库根目录**下的 `.env`。常用项:
|
|
||||||
|
|
||||||
| 变量 | 说明 |
|
|
||||||
|------|------|
|
|
||||||
| `GATE_API_KEY` / `GATE_API_SECRET` | Gate API(需合约与对应权限) |
|
|
||||||
| `LIVE_TRADING_ENABLED` | `true` 允许真实下单;`false` 仅本地与推送逻辑 |
|
|
||||||
| `GATE_MARGIN_MODE` / `GATE_POS_MODE` | 保证金与持仓模式 |
|
|
||||||
| `GATE_TPSL_USE_POSITION_ORDER` / `GATE_TPSL_PRICE_TYPE` 等 | 条件止盈止损行为 |
|
|
||||||
| `GATE_SOCKS_PROXY` | 可选;直连不稳时 SSH 动态转发(详见部署文档) |
|
|
||||||
| `APP_PASSWORD` / `FLASK_SECRET_KEY` | Web 登录与 Session |
|
|
||||||
| `WECHAT_WEBHOOK` | 企业微信机器人 |
|
|
||||||
| `EXCHANGE_DISPLAY_NAME` / `GATE_ACCOUNT_LABEL` | 页面与推送展示的账户文案 |
|
|
||||||
|
|
||||||
其余见 **`.env.example` 内注释** 或 **`app.py` 顶部默认值**。
|
|
||||||
|
|
||||||
## 运行
|
|
||||||
|
|
||||||
生产使用 **PM2**(`ecosystem.config.cjs`)。调试:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
source .venv/bin/activate && python app.py
|
|
||||||
```
|
|
||||||
|
|
||||||
见 [docs/ubuntu-server.md](../docs/ubuntu-server.md)。
|
|
||||||
|
|
||||||
端口由 **`APP_PORT`** 控制(未设置默认 **5000**)。浏览器登录 **`/login`**,口令为 **`APP_PASSWORD`**。
|
|
||||||
|
|
||||||
## 部署(Linux / PM2 / SSH SOCKS)
|
|
||||||
|
|
||||||
见 **[部署文档.md](./部署文档.md)**。
|
|
||||||
|
|
||||||
## 自检脚本
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python scripts/verify_gate_funding.py
|
|
||||||
```
|
|
||||||
|
|
||||||
用于核对密钥前缀(不落 Secret)、资金/合约可读性等(需网络与权限)。
|
|
||||||
|
|
||||||
## 数据与脚本
|
|
||||||
|
|
||||||
- 默认 SQLite:由 **`DB_PATH`** 指定(常见为项目下 `crypto.db`)
|
|
||||||
- `scripts/fix_breakeven_labels.py`:修正「止损」但盈亏为正的记录标签(参见部署文档说明)
|
|
||||||
|
|
||||||
## 风险与合规
|
|
||||||
|
|
||||||
实盘有亏损风险。请确认 API 权限、IP 白名单、杠杆与保证金模式与 **Gate.io** 后台一致,并遵守当地法律法规与交易所用户协议。
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
/**
|
|
||||||
* PM2 进程定义(Ubuntu / Linux)。
|
|
||||||
*
|
|
||||||
* 仅托管 Flask 应用。**SSH SOCKS 隧道**用 `ssh -D` 常驻(可用 tmux / autossh),勿交给 PM2。
|
|
||||||
* 与 `.env` 里 `GATE_SOCKS_PROXY` 端口一致即可;不必交给 PM2。
|
|
||||||
*
|
|
||||||
* 使用前:项目根目录存在 `.venv`,且已安装依赖(走 SOCKS 时需 PySocks)。
|
|
||||||
*
|
|
||||||
* 启动:
|
|
||||||
* pm2 start ecosystem.config.cjs
|
|
||||||
* 保存开机列表:
|
|
||||||
* pm2 save && pm2 startup
|
|
||||||
*/
|
|
||||||
const path = require("path");
|
|
||||||
|
|
||||||
const ROOT = __dirname;
|
|
||||||
const REPO_ROOT = path.join(ROOT, "..");
|
|
||||||
const PY = path.join(ROOT, ".venv", "bin", "python");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
apps: [
|
|
||||||
{
|
|
||||||
name: "crypto_gate_bot",
|
|
||||||
cwd: ROOT,
|
|
||||||
script: path.join(ROOT, "app.py"),
|
|
||||||
interpreter: PY,
|
|
||||||
instances: 1,
|
|
||||||
autorestart: true,
|
|
||||||
watch: false,
|
|
||||||
max_memory_restart: "800M",
|
|
||||||
env: { PYTHONPATH: REPO_ROOT },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# Daily backup: SQLite DB + static/images → /root/backups/<instance>/<YYYY-MM-DD>/
|
|
||||||
# Prune backup folders older than RETENTION_DAYS (default 30).
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
||||||
cd "$PROJECT_DIR"
|
|
||||||
|
|
||||||
BACKUP_ROOT="${BACKUP_ROOT:-/root/backups}"
|
|
||||||
RETENTION_DAYS="${RETENTION_DAYS:-30}"
|
|
||||||
INSTANCE_NAME="${BACKUP_INSTANCE:-$(basename "$PROJECT_DIR")}"
|
|
||||||
TZ_NAME="${BACKUP_TZ:-Asia/Shanghai}"
|
|
||||||
|
|
||||||
log() {
|
|
||||||
printf '[%s] %s\n' "$(TZ="$TZ_NAME" date '+%Y-%m-%d %H:%M:%S %Z')" "$*"
|
|
||||||
}
|
|
||||||
|
|
||||||
read_env_var() {
|
|
||||||
local key="$1"
|
|
||||||
local default="$2"
|
|
||||||
local line
|
|
||||||
if [[ ! -f .env ]]; then
|
|
||||||
printf '%s' "$default"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
line="$(grep -E "^${key}=" .env 2>/dev/null | tail -1 || true)"
|
|
||||||
if [[ -z "$line" ]]; then
|
|
||||||
printf '%s' "$default"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
printf '%s' "${line#*=}" | tr -d '\r'
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve_project_path() {
|
|
||||||
local p="$1"
|
|
||||||
if [[ "$p" == /* ]]; then
|
|
||||||
printf '%s' "$p"
|
|
||||||
else
|
|
||||||
printf '%s' "$PROJECT_DIR/$p"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
prune_old_backups() {
|
|
||||||
local base="$BACKUP_ROOT/$INSTANCE_NAME"
|
|
||||||
[[ -d "$base" ]] || return 0
|
|
||||||
local cutoff
|
|
||||||
cutoff="$(TZ="$TZ_NAME" date -d "-${RETENTION_DAYS} days" +%Y-%m-%d 2>/dev/null || true)"
|
|
||||||
if [[ -z "$cutoff" ]]; then
|
|
||||||
find "$base" -mindepth 1 -maxdepth 1 -type d -mtime +"$RETENTION_DAYS" -print0 |
|
|
||||||
xargs -r -0 rm -rf
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
local dir name
|
|
||||||
for dir in "$base"/*/; do
|
|
||||||
[[ -d "$dir" ]] || continue
|
|
||||||
name="$(basename "$dir")"
|
|
||||||
[[ "$name" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] || continue
|
|
||||||
if [[ "$name" < "$cutoff" ]]; then
|
|
||||||
log "prune: remove $dir (older than ${RETENTION_DAYS} days)"
|
|
||||||
rm -rf "$dir"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
DB_REL="$(read_env_var DB_PATH crypto.db)"
|
|
||||||
UPLOAD_REL="$(read_env_var UPLOAD_DIR static/images)"
|
|
||||||
BACKUP_ROOT="$(read_env_var BACKUP_ROOT "$BACKUP_ROOT")"
|
|
||||||
RETENTION_DAYS="$(read_env_var BACKUP_RETENTION_DAYS "$RETENTION_DAYS")"
|
|
||||||
INSTANCE_NAME="$(read_env_var BACKUP_INSTANCE "$INSTANCE_NAME")"
|
|
||||||
|
|
||||||
DB_PATH="$(resolve_project_path "$DB_REL")"
|
|
||||||
UPLOAD_DIR="$(resolve_project_path "$UPLOAD_REL")"
|
|
||||||
DATE_TAG="$(TZ="$TZ_NAME" date +%Y-%m-%d)"
|
|
||||||
DEST="$BACKUP_ROOT/$INSTANCE_NAME/$DATE_TAG"
|
|
||||||
|
|
||||||
if [[ ! -f "$DB_PATH" ]]; then
|
|
||||||
log "error: database not found: $DB_PATH"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p "$DEST"
|
|
||||||
log "start backup instance=$INSTANCE_NAME dest=$DEST"
|
|
||||||
|
|
||||||
if command -v sqlite3 >/dev/null 2>&1; then
|
|
||||||
sqlite3 "$DB_PATH" ".backup '$DEST/crypto.db'"
|
|
||||||
log "db: sqlite3 backup -> $DEST/crypto.db"
|
|
||||||
else
|
|
||||||
cp -a "$DB_PATH" "$DEST/crypto.db"
|
|
||||||
log "db: cp -> $DEST/crypto.db (sqlite3 not installed)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -d "$UPLOAD_DIR" ]]; then
|
|
||||||
tar -czf "$DEST/static_images.tar.gz" -C "$(dirname "$UPLOAD_DIR")" "$(basename "$UPLOAD_DIR")"
|
|
||||||
log "images: $UPLOAD_DIR -> $DEST/static_images.tar.gz"
|
|
||||||
else
|
|
||||||
log "warn: upload dir missing, skip images: $UPLOAD_DIR"
|
|
||||||
fi
|
|
||||||
|
|
||||||
{
|
|
||||||
echo "instance=$INSTANCE_NAME"
|
|
||||||
echo "project_dir=$PROJECT_DIR"
|
|
||||||
echo "backup_date=$DATE_TAG"
|
|
||||||
echo "db_path=$DB_PATH"
|
|
||||||
echo "upload_dir=$UPLOAD_DIR"
|
|
||||||
} >"$DEST/manifest.txt"
|
|
||||||
|
|
||||||
prune_old_backups
|
|
||||||
log "done"
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""One-shot SQLite backup before code deploy. Reads DB_PATH from .env (default crypto.db)."""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import sqlite3
|
|
||||||
from datetime import datetime
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
PROJECT_DIR = Path(__file__).resolve().parent.parent
|
|
||||||
|
|
||||||
|
|
||||||
def _read_env_db_path() -> Path:
|
|
||||||
env_file = PROJECT_DIR / ".env"
|
|
||||||
default = PROJECT_DIR / "crypto.db"
|
|
||||||
if not env_file.is_file():
|
|
||||||
return default
|
|
||||||
for line in env_file.read_text(encoding="utf-8", errors="replace").splitlines():
|
|
||||||
line = line.strip()
|
|
||||||
if not line or line.startswith("#") or "=" not in line:
|
|
||||||
continue
|
|
||||||
key, val = line.split("=", 1)
|
|
||||||
if key.strip() != "DB_PATH":
|
|
||||||
continue
|
|
||||||
val = val.strip().strip('"').strip("'")
|
|
||||||
p = Path(val)
|
|
||||||
return p if p.is_absolute() else PROJECT_DIR / p
|
|
||||||
return default
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
db_path = _read_env_db_path()
|
|
||||||
if not db_path.is_file():
|
|
||||||
print(f"error: database not found: {db_path}")
|
|
||||||
return 1
|
|
||||||
stamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
||||||
dest_dir = PROJECT_DIR / "backups" / stamp
|
|
||||||
dest_dir.mkdir(parents=True, exist_ok=True)
|
|
||||||
dest = dest_dir / db_path.name
|
|
||||||
try:
|
|
||||||
src = sqlite3.connect(str(db_path))
|
|
||||||
dst = sqlite3.connect(str(dest))
|
|
||||||
src.backup(dst)
|
|
||||||
dst.close()
|
|
||||||
src.close()
|
|
||||||
method = "sqlite3 backup"
|
|
||||||
except Exception:
|
|
||||||
shutil.copy2(db_path, dest)
|
|
||||||
method = "file copy"
|
|
||||||
manifest = dest_dir / "manifest.txt"
|
|
||||||
manifest.write_text(
|
|
||||||
"\n".join(
|
|
||||||
[
|
|
||||||
f"project_dir={PROJECT_DIR}",
|
|
||||||
f"source_db={db_path}",
|
|
||||||
f"backup_file={dest}",
|
|
||||||
f"method={method}",
|
|
||||||
f"created_at={stamp}",
|
|
||||||
]
|
|
||||||
),
|
|
||||||
encoding="utf-8",
|
|
||||||
)
|
|
||||||
print(f"ok: {dest} ({method})")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
raise SystemExit(main())
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
一次性修复历史交易记录标签:
|
|
||||||
将 trade_records 里“止损但实际盈利”的记录改为“保本止盈”。
|
|
||||||
|
|
||||||
默认条件(可通过参数修改):
|
|
||||||
- monitor_type = 下单监控
|
|
||||||
- result = 止损
|
|
||||||
- pnl_amount > 0
|
|
||||||
|
|
||||||
用法示例:
|
|
||||||
1) 仅预览(不落库):
|
|
||||||
python scripts/fix_breakeven_labels.py --db ./crypto.db --dry-run
|
|
||||||
|
|
||||||
2) 执行修复:
|
|
||||||
python scripts/fix_breakeven_labels.py --db ./crypto.db --apply
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import sqlite3
|
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
|
|
||||||
def parse_args() -> argparse.Namespace:
|
|
||||||
parser = argparse.ArgumentParser(description="Fix historical stop-loss records with positive pnl.")
|
|
||||||
parser.add_argument("--db", required=True, help="Path to sqlite db file, e.g. ./crypto.db")
|
|
||||||
parser.add_argument("--monitor-type", default="下单监控", help="Filter by monitor_type (default: 下单监控)")
|
|
||||||
parser.add_argument("--from-result", default="止损", help="Source result label (default: 止损)")
|
|
||||||
parser.add_argument("--to-result", default="保本止盈", help="Target result label (default: 保本止盈)")
|
|
||||||
parser.add_argument("--dry-run", action="store_true", help="Preview only, no write")
|
|
||||||
parser.add_argument("--apply", action="store_true", help="Execute update")
|
|
||||||
return parser.parse_args()
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
args = parse_args()
|
|
||||||
db_path = Path(args.db).expanduser().resolve()
|
|
||||||
if not db_path.exists():
|
|
||||||
print(f"[ERR] DB not found: {db_path}")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
if args.dry_run and args.apply:
|
|
||||||
print("[ERR] --dry-run and --apply are mutually exclusive.")
|
|
||||||
return 1
|
|
||||||
if not args.dry_run and not args.apply:
|
|
||||||
print("[INFO] No mode provided, defaulting to --dry-run.")
|
|
||||||
args.dry_run = True
|
|
||||||
|
|
||||||
conn = sqlite3.connect(str(db_path))
|
|
||||||
conn.row_factory = sqlite3.Row
|
|
||||||
cur = conn.cursor()
|
|
||||||
|
|
||||||
where_sql = """
|
|
||||||
monitor_type = ?
|
|
||||||
AND result = ?
|
|
||||||
AND CAST(COALESCE(pnl_amount, 0) AS REAL) > 0
|
|
||||||
"""
|
|
||||||
params = (args.monitor_type, args.from_result)
|
|
||||||
|
|
||||||
cur.execute(f"SELECT COUNT(*) AS c FROM trade_records WHERE {where_sql}", params)
|
|
||||||
will_change = int(cur.fetchone()["c"])
|
|
||||||
print(f"[INFO] Candidate rows: {will_change}")
|
|
||||||
|
|
||||||
if will_change == 0:
|
|
||||||
print("[INFO] Nothing to update.")
|
|
||||||
conn.close()
|
|
||||||
return 0
|
|
||||||
|
|
||||||
cur.execute(
|
|
||||||
f"""
|
|
||||||
SELECT id, symbol, result, pnl_amount, closed_at
|
|
||||||
FROM trade_records
|
|
||||||
WHERE {where_sql}
|
|
||||||
ORDER BY id DESC
|
|
||||||
LIMIT 10
|
|
||||||
""",
|
|
||||||
params,
|
|
||||||
)
|
|
||||||
sample = cur.fetchall()
|
|
||||||
print("[INFO] Sample (latest 10):")
|
|
||||||
for r in sample:
|
|
||||||
print(
|
|
||||||
f" id={r['id']} symbol={r['symbol']} result={r['result']} "
|
|
||||||
f"pnl={r['pnl_amount']} closed_at={r['closed_at']}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if args.dry_run:
|
|
||||||
print("[DRY-RUN] No write executed.")
|
|
||||||
conn.close()
|
|
||||||
return 0
|
|
||||||
|
|
||||||
cur.execute(
|
|
||||||
f"UPDATE trade_records SET result=? WHERE {where_sql}",
|
|
||||||
(args.to_result, *params),
|
|
||||||
)
|
|
||||||
changed = int(cur.rowcount)
|
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
|
||||||
print(f"[DONE] Updated rows: {changed}")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
||||||
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# Install daily backup cron: Beijing 00:00 (CRON_TZ=Asia/Shanghai).
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
||||||
BACKUP_SCRIPT="$SCRIPT_DIR/backup_data.sh"
|
|
||||||
INSTANCE_NAME="${BACKUP_INSTANCE:-$(basename "$PROJECT_DIR")}"
|
|
||||||
LOG_FILE="${BACKUP_CRON_LOG:-/var/log/crypto-monitor-backup-${INSTANCE_NAME}.log}"
|
|
||||||
|
|
||||||
if [[ ! -x "$BACKUP_SCRIPT" ]]; then
|
|
||||||
chmod +x "$BACKUP_SCRIPT"
|
|
||||||
fi
|
|
||||||
|
|
||||||
TMP="$(mktemp)"
|
|
||||||
trap 'rm -f "$TMP"' EXIT
|
|
||||||
|
|
||||||
{
|
|
||||||
crontab -l 2>/dev/null | grep -vF "$BACKUP_SCRIPT" || true
|
|
||||||
echo "CRON_TZ=Asia/Shanghai"
|
|
||||||
echo "0 0 * * * $BACKUP_SCRIPT >> $LOG_FILE 2>&1"
|
|
||||||
} >"$TMP"
|
|
||||||
|
|
||||||
awk '
|
|
||||||
BEGIN { tz = 0 }
|
|
||||||
/^CRON_TZ=Asia\/Shanghai$/ {
|
|
||||||
if (tz++) next
|
|
||||||
}
|
|
||||||
{ print }
|
|
||||||
' "$TMP" >"${TMP}.2"
|
|
||||||
mv "${TMP}.2" "$TMP"
|
|
||||||
|
|
||||||
crontab "$TMP"
|
|
||||||
echo "Installed cron for $INSTANCE_NAME"
|
|
||||||
echo " Schedule : daily 00:00 Asia/Shanghai"
|
|
||||||
echo " Script : $BACKUP_SCRIPT"
|
|
||||||
echo " Log : $LOG_FILE"
|
|
||||||
crontab -l | grep -F "$BACKUP_SCRIPT" || true
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
"""
|
|
||||||
在项目根目录执行(会加载根目录 .env):
|
|
||||||
python scripts/verify_gate_funding.py
|
|
||||||
|
|
||||||
依次探测:[0] swap 余额(与 App「交易账户」同源);[1]–[3] 现货 / 统一账户资金路径。
|
|
||||||
打印 GATE_API_KEY 前 8 位便于与 Gate 控制台核对(不含 Secret)。用于服务器自检。
|
|
||||||
"""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import importlib.util
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
||||||
if ROOT not in sys.path:
|
|
||||||
sys.path.insert(0, ROOT)
|
|
||||||
|
|
||||||
|
|
||||||
def _load_app():
|
|
||||||
path = os.path.join(ROOT, "app.py")
|
|
||||||
spec = importlib.util.spec_from_file_location("crypto_app", path)
|
|
||||||
mod = importlib.util.module_from_spec(spec)
|
|
||||||
spec.loader.exec_module(mod)
|
|
||||||
return mod
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
os.chdir(ROOT)
|
|
||||||
mod = _load_app()
|
|
||||||
print("LIVE_TRADING_ENABLED =", os.getenv("LIVE_TRADING_ENABLED"))
|
|
||||||
ok, reason = mod.ensure_exchange_live_ready()
|
|
||||||
print("ensure_exchange_live_ready =", ok, repr(reason))
|
|
||||||
if not ok:
|
|
||||||
print("跳过私有接口探测")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
mod.ensure_markets_loaded()
|
|
||||||
|
|
||||||
k = (os.getenv("GATE_API_KEY") or "").strip()
|
|
||||||
s = (os.getenv("GATE_API_SECRET") or "").strip()
|
|
||||||
if not k or "REPLACE" in k.upper():
|
|
||||||
print("WARN: GATE_API_KEY 为空或仍像占位符,请核对 .env")
|
|
||||||
if not s or "REPLACE" in s.upper():
|
|
||||||
print("WARN: GATE_API_SECRET 为空或仍像占位符,请核对 .env")
|
|
||||||
print("GATE_API_KEY prefix (8 chars):", (k[:8] + "…") if len(k) > 8 else "(short)")
|
|
||||||
|
|
||||||
# 0) swap — 与 App「交易账户」余额同源(优先看此项是否与网页一致)
|
|
||||||
try:
|
|
||||||
bal = mod.exchange.fetch_balance({"type": "swap"})
|
|
||||||
v0 = mod._extract_usdt_total(bal)
|
|
||||||
print("[0] fetch_balance(swap) USDT total =", v0)
|
|
||||||
except Exception as e:
|
|
||||||
print("[0] fetch_balance(swap) FAILED:", type(e).__name__, e)
|
|
||||||
|
|
||||||
# 1) fetch_balance spot + marginMode spot
|
|
||||||
try:
|
|
||||||
bal = mod.exchange.fetch_balance({"type": "spot", "marginMode": "spot"})
|
|
||||||
v = mod._extract_usdt_total(bal)
|
|
||||||
print("[1] fetch_balance(spot,marginMode=spot) USDT total =", v)
|
|
||||||
except Exception as e:
|
|
||||||
print("[1] fetch_balance(spot) FAILED:", type(e).__name__, e)
|
|
||||||
|
|
||||||
# 2) raw spot accounts
|
|
||||||
try:
|
|
||||||
resp = mod.exchange.privateSpotGetAccounts({})
|
|
||||||
v2 = mod._parse_gate_spot_accounts_response_usdt(resp)
|
|
||||||
print("[2] privateSpotGetAccounts USDT =", v2)
|
|
||||||
except Exception as e:
|
|
||||||
print("[2] privateSpotGetAccounts FAILED:", type(e).__name__, e)
|
|
||||||
|
|
||||||
# 3) unified accounts raw
|
|
||||||
try:
|
|
||||||
raw = mod.exchange.privateUnifiedGetAccounts({})
|
|
||||||
body = raw
|
|
||||||
if isinstance(body, dict) and isinstance(body.get("result"), dict):
|
|
||||||
body = body["result"]
|
|
||||||
if isinstance(body, dict):
|
|
||||||
keys = sorted(body.keys())
|
|
||||||
print("[3] unified top-level keys (sample):", keys[:25], "..." if len(keys) > 25 else "")
|
|
||||||
v3 = mod._parse_usdt_from_gate_unified_accounts_body(body) if isinstance(body, dict) else None
|
|
||||||
print("[3] parsed unified USDT =", v3)
|
|
||||||
except Exception as e:
|
|
||||||
print("[3] privateUnifiedGetAccounts FAILED:", type(e).__name__, e)
|
|
||||||
|
|
||||||
fu = mod._fetch_gate_funding_usdt()
|
|
||||||
print(">>> _fetch_gate_funding_usdt() =", fu)
|
|
||||||
f, t = mod.get_exchange_capitals(force=True)
|
|
||||||
print(">>> get_exchange_capitals(force=True) funding, trading =", f, t)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
raise SystemExit(main())
|
|
||||||
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 181 B |
|
Before Width: | Height: | Size: 162 B |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 497 B |
|
Before Width: | Height: | Size: 5.9 KiB |
@@ -1,17 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
|
|
||||||
<stop offset="0%" stop-color="#22d3ee"/>
|
|
||||||
<stop offset="100%" stop-color="#34d399"/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
<rect width="512" height="512" rx="108" fill="#0c1019"/>
|
|
||||||
<rect x="36" y="36" width="440" height="440" rx="88" fill="#141b2d"/>
|
|
||||||
<rect x="36" y="36" width="440" height="440" rx="88" fill="none" stroke="url(#g)" stroke-width="12"/>
|
|
||||||
<path d="M120 320 L200 248 L280 272 L392 168" fill="none" stroke="url(#g)" stroke-width="20" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<circle cx="392" cy="168" r="18" fill="#34d399"/>
|
|
||||||
<rect x="168" y="268" width="28" height="64" rx="6" fill="#f87171"/>
|
|
||||||
<line x1="182" y1="248" x2="182" y2="340" stroke="#f87171" stroke-width="10" stroke-linecap="round"/>
|
|
||||||
<rect x="268" y="220" width="28" height="96" rx="6" fill="#34d399"/>
|
|
||||||
<line x1="282" y1="200" x2="282" y2="340" stroke="#34d399" stroke-width="10" stroke-linecap="round"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.0 KiB |
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "交易监控复盘",
|
|
||||||
"short_name": "监控",
|
|
||||||
"description": "加密货币永续交易监控与复盘",
|
|
||||||
"start_url": "/",
|
|
||||||
"display": "standalone",
|
|
||||||
"background_color": "#0b0d14",
|
|
||||||
"theme_color": "#0b0d14",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "/static/icons/icon-192.png",
|
|
||||||
"sizes": "192x192",
|
|
||||||
"type": "image/png",
|
|
||||||
"purpose": "any"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "/static/icons/icon-512.png",
|
|
||||||
"sizes": "512x512",
|
|
||||||
"type": "image/png",
|
|
||||||
"purpose": "any maskable"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
ok2
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN" data-theme="dark">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<script src="/static/instance_theme.js?v=4"></script>
|
|
||||||
|
|
||||||
<title>登录 · {{ exchange_display }}</title>
|
|
||||||
<style>
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
background: #0a0a10;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
height: 100vh;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
.login-box {
|
|
||||||
background: #12121a;
|
|
||||||
padding: 2.5rem;
|
|
||||||
border-radius: 16px;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 400px;
|
|
||||||
border: 1px solid #242435;
|
|
||||||
box-shadow: 0 8px 24px rgba(0,0,0,0.3);
|
|
||||||
}
|
|
||||||
.login-box h2 {
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
background: linear-gradient(90deg, #4cc2ff, #7b42ff);
|
|
||||||
-webkit-background-clip: text;
|
|
||||||
-webkit-text-fill-color: transparent;
|
|
||||||
}
|
|
||||||
.form-group {
|
|
||||||
margin-bottom: 1.25rem;
|
|
||||||
}
|
|
||||||
.form-group label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
color: #a9a9ff;
|
|
||||||
}
|
|
||||||
.form-group input {
|
|
||||||
width: 100%;
|
|
||||||
padding: 0.85rem 1rem;
|
|
||||||
border-radius: 10px;
|
|
||||||
border: 1px solid #2e2e45;
|
|
||||||
background: #1a1a29;
|
|
||||||
color: #fff;
|
|
||||||
font-size: 0.95rem;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
.form-group input:focus {
|
|
||||||
border-color: #4cc2ff;
|
|
||||||
}
|
|
||||||
button {
|
|
||||||
width: 100%;
|
|
||||||
padding: 0.9rem;
|
|
||||||
border-radius: 10px;
|
|
||||||
border: none;
|
|
||||||
background: linear-gradient(90deg, #4285f4, #7b42ff);
|
|
||||||
color: #fff;
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: 500;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: 0.2s;
|
|
||||||
}
|
|
||||||
button:hover {
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
.flash {
|
|
||||||
padding: 0.8rem;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
background: #331e24;
|
|
||||||
color: #ff6666;
|
|
||||||
border-radius: 8px;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
}
|
|
||||||
.exchange-line {
|
|
||||||
text-align: center;
|
|
||||||
font-size: 0.82rem;
|
|
||||||
color: #8892b0;
|
|
||||||
margin: -0.5rem 0 1.25rem;
|
|
||||||
}
|
|
||||||
.exchange-line strong {
|
|
||||||
color: #b8f5d0;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<link rel="stylesheet" href="/static/instance_theme.css?v=4">
|
|
||||||
|
|
||||||
</head>
|
|
||||||
<div class="login-theme-bar">
|
|
||||||
<div class="theme-toggle instance-theme-toggle" role="group" aria-label="界面主题">
|
|
||||||
<button type="button" class="theme-toggle-btn is-active" data-theme-value="dark" aria-pressed="true" title="暗色主题">
|
|
||||||
<svg class="theme-icon" viewBox="0 0 24 24" width="18" height="18" aria-hidden="true">
|
|
||||||
<path fill="currentColor" d="M12.1 3a9 9 0 1 0 8.9 11 6.5 6.5 0 1 1-8.9-11z"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<button type="button" class="theme-toggle-btn" data-theme-value="light" aria-pressed="false" title="亮色主题">
|
|
||||||
<svg class="theme-icon" viewBox="0 0 24 24" width="18" height="18" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
|
||||||
<circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<body>
|
|
||||||
<div class="login-box">
|
|
||||||
<h2>交易监控系统登录</h2>
|
|
||||||
<p class="exchange-line">交易所:<strong>{{ exchange_display }}</strong></p>
|
|
||||||
{% with messages = get_flashed_messages() %}
|
|
||||||
{% if messages %}
|
|
||||||
<div class="flash">{{ messages[0] }}</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endwith %}
|
|
||||||
<form method="POST" autocomplete="off">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>账号</label>
|
|
||||||
<input type="text" name="username" required placeholder="请输入账号" autocomplete="off" autocapitalize="off" spellcheck="false">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>密码</label>
|
|
||||||
<input type="password" name="password" required placeholder="请输入密码" autocomplete="new-password">
|
|
||||||
</div>
|
|
||||||
<button type="submit">登录</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,194 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>实盘下单放大 | 100根K线</title>
|
|
||||||
<style>
|
|
||||||
*{margin:0;padding:0;box-sizing:border-box}
|
|
||||||
body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;background:#0b0d14;color:#eaeaea;padding:14px}
|
|
||||||
.container{width:min(98vw,1900px);margin:0 auto}
|
|
||||||
.card{background:#121726;border-radius:10px;padding:12px;border:1px solid #2a3150;margin-bottom:12px}
|
|
||||||
.row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
|
|
||||||
.btn{padding:7px 10px;border-radius:8px;text-decoration:none;border:1px solid #304164;background:#151a2a;color:#8fc8ff;cursor:pointer}
|
|
||||||
.btn:hover{background:#1f2740}
|
|
||||||
select,button{padding:8px 10px;border-radius:8px;border:1px solid #2e2e45;background:#1a1a29;color:#fff}
|
|
||||||
.meta{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:8px;margin-top:10px}
|
|
||||||
.meta-item{background:#141b2f;border:1px solid #27324e;border-radius:8px;padding:8px}
|
|
||||||
.meta-item .k{font-size:.76rem;color:#9fb0d8}
|
|
||||||
.meta-item .v{font-size:1rem;margin-top:4px;word-break:break-all}
|
|
||||||
.status{font-size:.84rem;color:#95a2c2}
|
|
||||||
.status.err{color:#ff8080}
|
|
||||||
#chart-wrap{height:560px;background:#0f1320;border:1px solid #2a3150;border-radius:10px;padding:8px}
|
|
||||||
#chart{width:100%;height:100%}
|
|
||||||
.empty{padding:18px;color:#95a2c2}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<div class="card">
|
|
||||||
<div class="row" style="justify-content:space-between">
|
|
||||||
<div class="row">
|
|
||||||
<a class="btn" href="/">返回首页</a>
|
|
||||||
<strong style="color:#dbe4ff">实盘下单放大(100根K线)</strong>
|
|
||||||
</div>
|
|
||||||
<div class="status">最近刷新:<span id="updated-at">--</span></div>
|
|
||||||
</div>
|
|
||||||
{% if orders %}
|
|
||||||
<div class="row" style="margin-top:10px">
|
|
||||||
<label>订单</label>
|
|
||||||
<select id="order-id">
|
|
||||||
{% for o in orders %}
|
|
||||||
<option value="{{ o.id }}" {% if selected_order and o.id == selected_order.id %}selected{% endif %}>
|
|
||||||
#{{ o.id }} {{ o.symbol }} {{ '做多' if o.direction == 'long' else '做空' }}
|
|
||||||
</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
<label>周期</label>
|
|
||||||
<select id="timeframe">
|
|
||||||
{% for tf in ['1m','3m','5m','15m','30m','1h','4h','1d'] %}
|
|
||||||
<option value="{{ tf }}" {% if tf == default_timeframe %}selected{% endif %}>{{ tf }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
<button id="manual-refresh" type="button">刷新</button>
|
|
||||||
<span id="load-status" class="status"></span>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<div class="empty">当前没有激活订单,无法展示放大K线。</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if orders %}
|
|
||||||
<div class="card">
|
|
||||||
<div class="meta">
|
|
||||||
<div class="meta-item"><div class="k">交易对</div><div class="v" id="m-symbol">-</div></div>
|
|
||||||
<div class="meta-item"><div class="k">方向</div><div class="v" id="m-direction">-</div></div>
|
|
||||||
<div class="meta-item"><div class="k">成交价</div><div class="v" id="m-entry">-</div></div>
|
|
||||||
<div class="meta-item"><div class="k">止损</div><div class="v" id="m-sl">-</div></div>
|
|
||||||
<div class="meta-item"><div class="k">止盈</div><div class="v" id="m-tp">-</div></div>
|
|
||||||
<div class="meta-item"><div class="k">盈亏比</div><div class="v" id="m-rr">-</div></div>
|
|
||||||
<div class="meta-item"><div class="k">现价</div><div class="v" id="m-price">-</div></div>
|
|
||||||
<div class="meta-item"><div class="k">浮盈亏</div><div class="v" id="m-pnl">-</div></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<div id="chart-wrap"><div id="chart"></div></div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if orders %}
|
|
||||||
<script src="https://unpkg.com/lightweight-charts/dist/lightweight-charts.standalone.production.js"></script>
|
|
||||||
<script>
|
|
||||||
const refreshMs = Math.max({{ price_refresh_seconds * 1000 }}, 5000);
|
|
||||||
const orderSelect = document.getElementById("order-id");
|
|
||||||
const tfSelect = document.getElementById("timeframe");
|
|
||||||
const statusEl = document.getElementById("load-status");
|
|
||||||
const updatedAtEl = document.getElementById("updated-at");
|
|
||||||
const chartHost = document.getElementById("chart");
|
|
||||||
const fmt = (v, d=6) => (v === null || typeof v === "undefined" || Number.isNaN(Number(v))) ? "-" : Number(v).toFixed(d);
|
|
||||||
|
|
||||||
let chart = null;
|
|
||||||
let candleSeries = null;
|
|
||||||
let priceLines = [];
|
|
||||||
|
|
||||||
function ensureChart(){
|
|
||||||
if(chart){ return true; }
|
|
||||||
if(!window.LightweightCharts){
|
|
||||||
statusEl.className = "status err";
|
|
||||||
statusEl.innerText = "图表库加载失败";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
chart = LightweightCharts.createChart(chartHost, {
|
|
||||||
layout: { background: { color: "#0f1320" }, textColor: "#d6deff" },
|
|
||||||
grid: { vertLines: { color: "#1e263d" }, horzLines: { color: "#1e263d" } },
|
|
||||||
rightPriceScale: { borderColor: "#2a3150" },
|
|
||||||
timeScale: { borderColor: "#2a3150", timeVisible: true, secondsVisible: false },
|
|
||||||
crosshair: { mode: 0 }
|
|
||||||
});
|
|
||||||
candleSeries = chart.addCandlestickSeries({
|
|
||||||
upColor: "#4cd97f",
|
|
||||||
downColor: "#ff6666",
|
|
||||||
borderVisible: false,
|
|
||||||
wickUpColor: "#4cd97f",
|
|
||||||
wickDownColor: "#ff6666"
|
|
||||||
});
|
|
||||||
window.addEventListener("resize", () => {
|
|
||||||
chart.applyOptions({ width: chartHost.clientWidth, height: chartHost.clientHeight });
|
|
||||||
});
|
|
||||||
chart.applyOptions({ width: chartHost.clientWidth, height: chartHost.clientHeight });
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetPriceLines(){
|
|
||||||
if(!candleSeries){ return; }
|
|
||||||
priceLines.forEach(line => {
|
|
||||||
try { candleSeries.removePriceLine(line); } catch (_) {}
|
|
||||||
});
|
|
||||||
priceLines = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
function addLine(price, title, color){
|
|
||||||
if(!candleSeries || typeof price === "undefined" || price === null){ return; }
|
|
||||||
const p = Number(price);
|
|
||||||
if(Number.isNaN(p) || p <= 0){ return; }
|
|
||||||
priceLines.push(candleSeries.createPriceLine({
|
|
||||||
price: p, color, lineWidth: 1, lineStyle: 0, axisLabelVisible: true, title
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
function paintOrder(order){
|
|
||||||
document.getElementById("m-symbol").innerText = order.symbol || "-";
|
|
||||||
document.getElementById("m-direction").innerText = (order.direction === "short") ? "做空" : "做多";
|
|
||||||
document.getElementById("m-entry").innerText = order.trigger_price_display || fmt(order.trigger_price, 8);
|
|
||||||
document.getElementById("m-sl").innerText = order.stop_loss_display || fmt(order.stop_loss, 8);
|
|
||||||
document.getElementById("m-tp").innerText = order.take_profit_display || fmt(order.take_profit, 8);
|
|
||||||
document.getElementById("m-rr").innerText = (order.rr_ratio === null || typeof order.rr_ratio === "undefined") ? "-" : `1:${Number(order.rr_ratio).toFixed(2)}`;
|
|
||||||
document.getElementById("m-price").innerText = order.current_price_display || fmt(order.current_price, 8);
|
|
||||||
const pnlEl = document.getElementById("m-pnl");
|
|
||||||
pnlEl.innerText = `${fmt(order.float_pnl, 2)}U (${fmt(order.float_pct, 2)}%)`;
|
|
||||||
pnlEl.style.color = Number(order.float_pnl || 0) > 0 ? "#4cd97f" : (Number(order.float_pnl || 0) < 0 ? "#ff6666" : "#d6deff");
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadOrderKline(){
|
|
||||||
if(!ensureChart()){ return; }
|
|
||||||
const orderId = orderSelect.value;
|
|
||||||
const timeframe = tfSelect.value;
|
|
||||||
if(!orderId){ return; }
|
|
||||||
statusEl.className = "status";
|
|
||||||
statusEl.innerText = "加载中...";
|
|
||||||
try{
|
|
||||||
const resp = await fetch(`/api/order_kline?order_id=${encodeURIComponent(orderId)}&timeframe=${encodeURIComponent(timeframe)}`);
|
|
||||||
const data = await resp.json();
|
|
||||||
if(!resp.ok || !data.ok){ throw new Error(data.msg || "请求失败"); }
|
|
||||||
const candles = Array.isArray(data.candles) ? data.candles : [];
|
|
||||||
if(!candles.length){
|
|
||||||
statusEl.className = "status err";
|
|
||||||
statusEl.innerText = "暂无K线数据";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
candleSeries.setData(candles);
|
|
||||||
resetPriceLines();
|
|
||||||
addLine(data.order.trigger_price, "成交价", "#42a5f5");
|
|
||||||
addLine(data.order.stop_loss, "止损", "#ff6666");
|
|
||||||
addLine(data.order.take_profit, "止盈", "#4cd97f");
|
|
||||||
chart.timeScale().fitContent();
|
|
||||||
paintOrder(data.order || {});
|
|
||||||
updatedAtEl.innerText = data.updated_at || "--";
|
|
||||||
statusEl.className = "status";
|
|
||||||
statusEl.innerText = `已加载 ${candles.length} 根K线`;
|
|
||||||
}catch(err){
|
|
||||||
statusEl.className = "status err";
|
|
||||||
statusEl.innerText = err && err.message ? err.message : "加载失败";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById("manual-refresh").addEventListener("click", loadOrderKline);
|
|
||||||
orderSelect.addEventListener("change", loadOrderKline);
|
|
||||||
tfSelect.addEventListener("change", loadOrderKline);
|
|
||||||
loadOrderKline();
|
|
||||||
setInterval(loadOrderKline, refreshMs);
|
|
||||||
</script>
|
|
||||||
{% endif %}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
# 使用说明
|
|
||||||
|
|
||||||
**本文件对应仓库:`crypto_monitor_gate`(Gate.io USDT 永续)。**
|
|
||||||
功能、界面与 **Binance U 本位版**(目录 `crypto_monitor_binance`)基本一致,差异主要在 **`.env` 里交易所密钥与部分参数名**(`GATE_*` / `BINANCE_*`),文末有对照。
|
|
||||||
|
|
||||||
**更细的部署(SSH 代理、PM2、依赖安装)** 见同目录 **`部署文档.md`**。
|
|
||||||
**关键位自动开仓的规则、RR、结案原因** 见 **`关键位自动下单说明.md`**。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. 它能做什么
|
|
||||||
|
|
||||||
面向个人盘面的 **Web 控制台**,主要能力包括:
|
|
||||||
|
|
||||||
| 模块 | 说明 |
|
|
||||||
|------|------|
|
|
||||||
| **关键位监控** | 录入上/下沿与类型,按 **5m 收线** 做硬条件过滤;符合条件后 **企业微信** 提醒,部分类型可 **自动市价开仓**(见第 4 节与专门文档)。 |
|
|
||||||
| **实盘下单监控** | 手工填止损/止盈,**以损定仓** 市价开单,挂上条件止盈止损,并在页面跟踪浮盈亏、保本逻辑等。 |
|
|
||||||
| **交易记录 / 复盘** | 平仓结果、盈亏、错过的单等归档与导出;可选 **AI 复盘**(见 [AI复盘与模型配置说明.md](../AI复盘与模型配置说明.md))。 |
|
|
||||||
| **策略交易** | 顶栏 `/strategy`:趋势回调 + 顺势加仓双栏;见 [策略交易说明.md](../策略交易说明.md)。 |
|
|
||||||
|
|
||||||
后台按 **`MONITOR_POLL_SECONDS`**(默认几秒)轮询行情与监控逻辑。**切勿**在未理解规则时同时运行两套程序共用一个实盘账户。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. 运行前必须配置(`.env`)
|
|
||||||
|
|
||||||
首次在本目录执行 **`cp .env.example .env`**,再编辑 `.env`(`.env` 勿提交 Git;`git pull` 不会改你的 `.env`,升级前建议 `cp .env .env.backup.$(date +%Y%m%d)`)。
|
|
||||||
|
|
||||||
至少检查以下项(具体键名以 **`.env.example`** 为准):
|
|
||||||
|
|
||||||
| 类别 | 说明 |
|
|
||||||
|------|------|
|
|
||||||
| **登录网页** | `APP_PASSWORD`:打开站点后的登录口令。`FLASK_SECRET_KEY`:Session 密钥,请勿使用默认值。 |
|
|
||||||
| **企业微信** | `WECHAT_WEBHOOK`:告警与关键位推送机器人的 Webhook。 |
|
|
||||||
| **是否真下单** | `LIVE_TRADING_ENABLED=false`:**不会**向交易所发送开仓指令(适合测试流程)。改为 `true` 且密钥正确才会实盘。 |
|
|
||||||
| **交易所 API** | **本仓库:** `GATE_API_KEY`、`GATE_API_SECRET`;合约相关见 `GATE_MARGIN_MODE`、`GATE_POS_MODE`、`GATE_TPSL_*` 等。**勿**把 `.env` 提交到 Git。 |
|
|
||||||
| **关键位 RR / 止损外扩** | `KEY_AUTO_MIN_PLANNED_RR`、`KEY_STOP_OUTSIDE_BREAKOUT_PCT`(详见 `关键位自动下单说明.md`)。 |
|
|
||||||
| **AI 复盘** | `AI_PROVIDER=openai`(默认)或 `ollama`;变量见 `.env.example` 与 [AI复盘与模型配置说明.md](../AI复盘与模型配置说明.md)。 |
|
|
||||||
|
|
||||||
网络不稳定时可为 Gate 配置 **`GATE_SOCKS_PROXY`** 等(见 **`部署文档.md`**)。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. 如何启动与登录
|
|
||||||
|
|
||||||
1. 按 **`部署文档.md`** 建好虚拟环境、安装依赖(如 `flask`、`requests`、`ccxt`、按需 `Pillow`、`PySocks` 等),配置好 `.env`。
|
|
||||||
2. 启动 Flask 应用(本仓库可用 **`ecosystem.config.cjs`** 交给 PM2,或本地 `python app.py` / `flask run`,以你当前脚本为准)。
|
|
||||||
3. 浏览器访问站点,打开 **`/login`**,使用 **`.env` 里的 `APP_PASSWORD`** 登录。
|
|
||||||
|
|
||||||
登录后顶栏:**关键位监控** | **实盘下单** | **策略交易**(`/strategy`)| **策略交易记录**(`/strategy/records`)| **交易记录与复盘** | **统计分析**。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. 关键位监控(顶栏「关键位监控」→ `/key_monitor`)
|
|
||||||
|
|
||||||
### 4.1 添加一条关键位
|
|
||||||
|
|
||||||
1. **币种**:如 `BTC` 或 `BTC/USDT`(会规范成内部符号)。
|
|
||||||
2. **类型**(必选其一):
|
|
||||||
|
|
||||||
| 类型 | 行为摘要 |
|
|
||||||
|------|----------|
|
|
||||||
| **箱体突破** | 通过门控且计划 RR 达标 → **自动市价开仓**(需 `LIVE_TRADING_ENABLED=true` 且无其他持仓占位)。结案后本条从列表消失并记入历史。 |
|
|
||||||
| **收敛突破** | 同上(自动开仓类)。 |
|
|
||||||
| **关键阻力位** | **不自动开仓**;触发后 **发 1 次微信**,然后本条 **结案进历史**。 |
|
|
||||||
| **关键支撑位** | 同上(仅提醒)。 |
|
|
||||||
| **回调触价开仓** | **不挂交易所限价**;标记价回调触达 E 后 **下一轮询市价开仓**(RR 门槛同 `KEY_AUTO_MIN_PLANNED_RR`);有效期 **24h** |
|
|
||||||
| **突破触价开仓** | **不挂交易所限价**;标记价 **穿越 E 立即市价开仓**;先触 SL/TP 侧失效;有效期 **24h** |
|
|
||||||
|
|
||||||
3. **方向**:做多 / 做空(触价开仓 / 箱体 / 收敛 / 斐波必选;阻力/支撑不选)。
|
|
||||||
4. **价位**:箱体/收敛/阻力/支撑填 **上沿 / 下沿**;触价开仓填 **入场 E / 止损 SL / 止盈 TP**。
|
|
||||||
|
|
||||||
**限制:**
|
|
||||||
活跃持仓数达到 **`MAX_ACTIVE_POSITIONS`**(默认 1)时,**不允许**再添加「**箱体突破** / **收敛突破**」;仍可添加「**关键阻力位 / 支撑位**」。
|
|
||||||
若 **4h EMA55** 与你的方向逆势,页面会 **额外 Flash 提示**,**不阻挡**提交。
|
|
||||||
|
|
||||||
### 4.2 触发后会发生什么(简版)
|
|
||||||
|
|
||||||
- **箱体 / 收敛**:门控通过后计算计划 SL/TP 与 RR;不达标则 **微信说明 + `rr_insufficient` 结案**;达标则尝试 **市价开仓**,成功 **`auto_opened`**,失败 **`exchange_failed`**——均 **不重试同一关键位**。
|
|
||||||
- **阻力 / 支撑**:仅 **单次推送** → **`key_level_alert_only`** 结案。
|
|
||||||
|
|
||||||
详细公式、结案字段、与企业微信文案口径见 **`关键位自动下单说明.md`**。
|
|
||||||
|
|
||||||
### 4.3 列表与历史
|
|
||||||
|
|
||||||
- 当前条目可 **删除**(会按规则记入历史的情形见页面说明)。
|
|
||||||
- **关键位历史**:已结案记录;可配合导出链接(若有)做备份。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. 实盘下单(顶栏「实盘下单」→ `/trade`)
|
|
||||||
|
|
||||||
用于 **自己点按钮** 开单:
|
|
||||||
|
|
||||||
- 持仓上限由 **`MAX_ACTIVE_POSITIONS`** 控制(默认 1,与关键位自动单共用)。
|
|
||||||
- **人工开仓**时计划盈亏比不得低于 **`MANUAL_MIN_PLANNED_RR`**(默认 1.4:1),否则页面弹窗且后端拒绝。
|
|
||||||
- 填写币种、方向、杠杆(可选)、止损/止盈(价格或百分比按表单说明)。
|
|
||||||
- 勾选是否启用 **移动保本** 等行为以 `.env`/页面默认值为准。
|
|
||||||
|
|
||||||
平仓通过页面 **平仓**(或等价入口),会从交易所市价处理并更新记录。**删除/误操作可能造成真实盈亏**,请先确认环境与方向。
|
|
||||||
|
|
||||||
开仓成功后持仓卡片上会显示 **「来源」**:手工单一般为 **下单监控**;来自关键位自动单的为 **关键位监控**。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. 企业微信会看到什么
|
|
||||||
|
|
||||||
- 关键位:按类型与结案结果推送(RR 不足、下单失败、自动开仓成功、仅阻力支撑提醒等),**每条关键位结案路径原则上一条主推送**(详见 `关键位自动下单说明.md`)。
|
|
||||||
- 手工开仓、平仓、部分异常也会在规则满足时推送(以代码与配置为准)。
|
|
||||||
|
|
||||||
若未配置 **`WECHAT_WEBHOOK`** 或网络失败,可能只是看不到推送,不代表逻辑未执行;要紧操作请以 **交易所端持仓与挂单** 为准核对。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. 强烈建议的风险与运维习惯
|
|
||||||
|
|
||||||
1. **先用 `LIVE_TRADING_ENABLED=false`** 验证页面、录入、推送,再开小资金开实盘。
|
|
||||||
2. **API 权限**:仅开所需合约权限;勿泄露密钥;定期轮换。
|
|
||||||
3. **单进程控盘**:同一账户避免本程序与其他机器人 **重复开仓**。
|
|
||||||
4. **自动备份**:服务器上执行 `bash scripts/install_backup_cron.sh`(每天北京时间 0:00 → `/root/backups`,保留 30 天);升级前也可 `bash scripts/backup_data.sh` 手动跑一次。
|
|
||||||
5. **升级代码后**:启动时会跑 **数据库迁移**(如新列 `order_monitors.monitor_type`);首次启动关注一下日志或无报错页面。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. 常见问题(简要)
|
|
||||||
|
|
||||||
| 现象 | 可自查 |
|
|
||||||
|------|--------|
|
|
||||||
| 关键位永远不触发 | 5m 门控是否全通过(页面门控摘要)、币种日成交量是否在规则内、`KLINE_TIMEFRAME`。 |
|
|
||||||
| 有信号但不自动开仓 | `LIVE_TRADING_ENABLED`、`KEY_AUTO_MIN_PLANNED_RR`、计划 RR、是否已有持仓、API/余额报错(微信或日志)。 |
|
|
||||||
| 加不了箱体/收敛 | 是否已有活跃持仓;先平仓或改用「阻力/支撑位」仅提醒。 |
|
|
||||||
| 推送收不到 | `WECHAT_WEBHOOK`、企业微信机器人配额与网络。 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 9. Binance 版(`crypto_monitor_binance`)差异速查
|
|
||||||
|
|
||||||
| 项目 | Gate 本仓库 | Binance 版 |
|
|
||||||
|------|-------------|------------|
|
|
||||||
| API 变量 | `GATE_API_KEY`、`GATE_API_SECRET`、`GATE_*` | `BINANCE_API_KEY`、`BINANCE_API_SECRET`、`BINANCE_*` |
|
|
||||||
| 实盘开关 | `LIVE_TRADING_ENABLED`(通用) | 同上 |
|
|
||||||
| 止盈止损挂载路径 | `_gate_place_tp_sl_orders` 与 `GATE_TPSL_*` | `_binance_place_tp_sl_orders`(U 本位条件单) |
|
|
||||||
| 资金显示舍入 | 以本仓库为准 | 与 **`FUNDS_DECIMALS`** 等一致 |
|
|
||||||
| 专门文档 | **`关键位自动下单说明.md`**(各仓库有一份,开头标明交易所) | 同左 |
|
|
||||||
|
|
||||||
操作流程(登录、关键位四类、手工单、单仓)**两份程序一致**:换目录、换 `.env` 即可对照使用。
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
# 关键位监控说明(自动开仓 + 人工盯盘)
|
|
||||||
|
|
||||||
**适用:`crypto_monitor_gate`(Gate U 本位永续)**
|
|
||||||
Binance / OKX 见各自目录下同名文档;共享逻辑在仓库根目录 `key_monitor_lib.py`。
|
|
||||||
|
|
||||||
本文档与 `.env`、`check_key_monitors`、`add_key`、`_key_hard_checks`、`_process_key_rs_level_alert` 一致。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 一、监控类型总览
|
|
||||||
|
|
||||||
| 录入类型 | 录入时选方向 | 自动市价开仓 | 触发与结案 |
|
|
||||||
|----------|--------------|--------------|------------|
|
|
||||||
| **箱体突破** | **必选** 多/空 | **是**(门控 + RR) | 条件满足 → 开仓或 `rr_insufficient` / `exchange_failed` → **一次性删除** |
|
|
||||||
| **收敛突破** | **必选** 多/空 | **是**(同上) | 同上 |
|
|
||||||
| **关键阻力位** | **不选**(`direction=watch`) | **否** | 5m 收盘突破上/下沿 → 微信 **3 次** → `key_level_alert_done` |
|
|
||||||
| **关键支撑位** | **不选** | **否** | 同上(与阻力位**相同规则**:填上沿+下沿,程序双向监控) |
|
|
||||||
| 斐波回调 0.618 / 0.786 | 必选 | 限价挂单逻辑 | 见斐波说明(**不在下文展开**) |
|
|
||||||
|
|
||||||
**添加时(所有类型):** 品种须 **日成交量排名前 `KEY_DAILY_VOLUME_RANK_MAX`(默认 30)**;上沿 **>** 下沿。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 二、关键阻力位 / 关键支撑位(人工盯盘)
|
|
||||||
|
|
||||||
### 2.1 录入
|
|
||||||
|
|
||||||
- 填写 **上沿 `upper`** 与 **下沿 `lower`**(程序同时监控两侧,**无法预先判定**做多还是做空)。
|
|
||||||
- 页面 **不显示、不要求** 方向;库中 `direction` 初始为 `watch`,**首次突破后** 写入 `long`(向上突破上沿)或 `short`(向下突破下沿)。
|
|
||||||
|
|
||||||
### 2.2 触发(极简)
|
|
||||||
|
|
||||||
- 周期:**`KLINE_TIMEFRAME`(默认 5m)最近一根已闭合 K** 的 **收盘价**(非影线)。
|
|
||||||
- **向上突破上沿:** `收盘 > upper` → 推断方向 **多 / 向上**,本次监控任务开始按节奏提醒。
|
|
||||||
- **向下突破下沿:** `收盘 < lower` → 推断方向 **空 / 向下**,本次任务同样开始提醒。
|
|
||||||
- **任一侧突破即结束本条监控周期**(不会在突破后再等待另一侧;上沿、下沿谁先满足用谁,同根 K 仅可能满足一侧)。
|
|
||||||
|
|
||||||
**不参与:** 量能、二确 K、越过幅度下限、日成交排名(运行时)、计划 RR、自动开仓。
|
|
||||||
|
|
||||||
### 2.3 微信提醒次数
|
|
||||||
|
|
||||||
| 配置 | 默认 | 含义 |
|
|
||||||
|------|------|------|
|
|
||||||
| `KEY_ALERT_MAX_TIMES` | `3` | 突破后最多推送 3 次 |
|
|
||||||
| `KEY_ALERT_INTERVAL_MINUTES` | `5` | 相邻两次推送至少间隔 5 分钟 |
|
|
||||||
|
|
||||||
- 第 1 次:首次检测到突破的当次轮询(若已闭合 5m 满足条件)。
|
|
||||||
- 第 2、3 次:仅按间隔推送(**不要求**价格仍在箱外)。
|
|
||||||
- 第 3 次推送后:写入 `key_monitor_history`,`close_reason=**key_level_alert_done**`,从 `key_monitors` **删除**。
|
|
||||||
|
|
||||||
### 2.4 与箱体/收敛的区别
|
|
||||||
|
|
||||||
| 项目 | 阻力/支撑 | 箱体/收敛 |
|
|
||||||
|------|-----------|-----------|
|
|
||||||
| 方向 | 程序推断 | 人工选择 |
|
|
||||||
| K 线根数 | 1 根闭合 5m | 2 根(突破 K + 确认 K) |
|
|
||||||
| 提醒次数 | 3 次后结案 | 自动单:触发后 1 次业务推送并结案 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 三、箱体突破 / 收敛突破(自动开仓)
|
|
||||||
|
|
||||||
### 3.1 K 线结构(默认索引)
|
|
||||||
|
|
||||||
| 角色 | 环境变量 | 默认 | 含义 |
|
|
||||||
|------|----------|------|------|
|
|
||||||
| 突破 K | `KEY_CONFIRM_BREAKOUT_BAR` | `-2` | 倒数第 2 根闭合 K |
|
|
||||||
| 确认 K | `KEY_CONFIRM_BAR` | `-1` | 倒数第 1 根闭合 K |
|
|
||||||
|
|
||||||
### 3.2 硬门控(须全部通过)
|
|
||||||
|
|
||||||
1. **有效突破(收盘越界)**
|
|
||||||
- 多:`突破 K 收盘 > upper`
|
|
||||||
- 空:`突破 K 收盘 < lower`
|
|
||||||
|
|
||||||
2. **突破越过幅度(仅下限)**
|
|
||||||
- 多:`(突破 K 收盘 − upper) / upper × 100 > KEY_BREAKOUT_AMP_MIN_PCT`(默认 **0.03%**)
|
|
||||||
- 空:`(lower − 突破 K 收盘) / lower × 100 >` 同上
|
|
||||||
- **无上限**;突破过猛由 **计划 RR** 过滤。
|
|
||||||
- **不再**使用 K 线实体占开盘价比例;`KEY_BREAKOUT_AMP_MAX_PCT` **已不参与门控**。
|
|
||||||
|
|
||||||
3. **确认 K 不进箱体**
|
|
||||||
- 多:确认 K 收盘 **`> upper`**(不得在 `[lower, upper]` 内)
|
|
||||||
- 空:确认 K 收盘 **`< lower`**
|
|
||||||
|
|
||||||
4. **量能:** 突破 K 成交量 > 前 `KEY_VOLUME_MA_BARS`(默认 20)根均量 × `KEY_VOLUME_RATIO_MIN`(默认 1.3)
|
|
||||||
|
|
||||||
5. **日成交量排名:** 运行时仍须前 `KEY_DAILY_VOLUME_RANK_MAX`(默认 30)
|
|
||||||
|
|
||||||
6. **计划 RR(最后经济门控):** 按确认 K 收盘 **E** 计算 SL/TP 后,`RR` **严格大于** `KEY_AUTO_MIN_PLANNED_RR`(默认 1.5)才市价开仓
|
|
||||||
|
|
||||||
### 3.3 止损 / 止盈(确认 K 收盘为 E)
|
|
||||||
|
|
||||||
箱体高 **H = |upper − lower|**。止损锚在 **突破 K 极值** 外侧:
|
|
||||||
|
|
||||||
| 方向 | 止损(标准/趋势方案) |
|
|
||||||
|------|------------------------|
|
|
||||||
| 多 | 突破 K **最低价** × (1 − `KEY_STOP_OUTSIDE_BREAKOUT_PCT`%) |
|
|
||||||
| 空 | 突破 K **最高价** × (1 + `KEY_STOP_OUTSIDE_BREAKOUT_PCT`%) |
|
|
||||||
|
|
||||||
止盈方案见下表(与改版前一致):
|
|
||||||
|
|
||||||
| 方案 | `sl_tp_mode` | 多:SL / TP | 空:SL / TP |
|
|
||||||
|------|--------------|-------------|-------------|
|
|
||||||
| 标准突破 | `standard` | 突破 K 低外侧% / **E+H** | 突破 K 高外侧% / **E−H** |
|
|
||||||
| 箱体 1R·止盈 1.5H | `box_1p5` | **E−H** / **E+1.5×H** | **E+H** / **E−1.5×H** |
|
|
||||||
| 趋势单·自填止盈 | `trend_manual` | 突破 K 低 × (1−`KEY_TREND_STOP_OUTSIDE_PCT`%) / **录入止盈** | 突破 K 高外侧% / **录入止盈** |
|
|
||||||
|
|
||||||
### 3.4 一次性结案(`close_reason`)
|
|
||||||
|
|
||||||
| `close_reason` | 含义 |
|
|
||||||
|----------------|------|
|
|
||||||
| `box_opposite_break` | 标记价先突破反向边界(多:≤下沿;空:≥上沿) |
|
|
||||||
| `rr_insufficient` | 门控通过但 RR 不达标或 SL/TP 几何无效 |
|
|
||||||
| `exchange_failed` | RR 达标但实盘/交易所等原因未开仓 |
|
|
||||||
| `auto_opened` | RR 达标且市价开仓成功 |
|
|
||||||
| `key_level_alert_done` | 阻力/支撑 **3 次提醒** 完成 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 四、环境与参数(`.env` 摘要)
|
|
||||||
|
|
||||||
| 变量 | 箱体/收敛 | 阻力/支撑 |
|
|
||||||
|------|-----------|-----------|
|
|
||||||
| `KEY_BREAKOUT_AMP_MIN_PCT` | 突破越过下限(默认 0.03) | 不用 |
|
|
||||||
| `KEY_BREAKOUT_AMP_MAX_PCT` | **已废弃门控** | 不用 |
|
|
||||||
| `KEY_VOLUME_*` / `KEY_CONFIRM_*` | 用 | 不用 |
|
|
||||||
| `KEY_AUTO_MIN_PLANNED_RR` | 用 | 不用 |
|
|
||||||
| `KEY_ALERT_MAX_TIMES` / `KEY_ALERT_INTERVAL_MINUTES` | 不用 | 用(默认 3 次 / 5 分钟) |
|
|
||||||
| `KEY_DAILY_VOLUME_RANK_MAX` | 添加时 + 运行时 | **仅添加时** |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 五、相关代码
|
|
||||||
|
|
||||||
| 说明 | 位置 |
|
|
||||||
|------|------|
|
|
||||||
| 共享判定 | `key_monitor_lib.py` |
|
|
||||||
| 主循环 | `check_key_monitors` |
|
|
||||||
| 自动门控 | `_key_hard_checks` |
|
|
||||||
| 阻力/支撑提醒 | `_process_key_rs_level_alert` |
|
|
||||||
| 录入 | `add_key` |
|
|
||||||
| 开仓 | `_market_open_for_key_monitor` |
|
|
||||||
@@ -1,148 +0,0 @@
|
|||||||
# 界面与风控更新说明(Gate 实例)
|
|
||||||
|
|
||||||
## 顶栏导航(4 项)
|
|
||||||
|
|
||||||
| 顺序 | 名称 | 路由 | 说明 |
|
|
||||||
|------|------|------|------|
|
|
||||||
| 1 | 关键位监控 | `/key_monitor` | 关键位添加、实时门控、历史 |
|
|
||||||
| 2 | 实盘下单 | `/trade` | 人工开仓、划转、实时持仓(**默认首页** `/` → `/trade`) |
|
|
||||||
| 3 | 交易记录与复盘 | `/records` | 交易记录、复盘表单、AI 历史(受顶栏 UTC 时间窗筛选) |
|
|
||||||
| 4 | 统计分析 | `/stats` | 按北京时间交易日切日 + 分品类统计块 |
|
|
||||||
|
|
||||||
## 关键位监控页
|
|
||||||
|
|
||||||
- 标题去掉「5m」;规则条从 `.env` 读取(周期、确认K、量能、自动开仓盈亏比、日成交量排名)。
|
|
||||||
- 左列:活跃关键位,**pos-card** 样式展示现价/距上沿/距下沿/门控。
|
|
||||||
- 右列:关键位历史(失效/结案),与左列等高滚动;**受顶栏 UTC 列表时间窗筛选**(默认 UTC 当日)。
|
|
||||||
- 监控类型新增:**斐波回调0.618**、**斐波回调0.786**(与 Binance 主站同一套规则,计算逻辑见仓库根目录 `fib_key_monitor_lib.py`)。
|
|
||||||
|
|
||||||
### 斐波关键位监控(方案 A:交易所限价)
|
|
||||||
|
|
||||||
| 项 | 说明 |
|
|
||||||
|----|------|
|
|
||||||
| 同币互斥 | 每个币种只能有一条斐波监控(0.618 与 0.786 不可并存) |
|
|
||||||
| 上下沿 | 上沿 **H**、下沿 **L**(须 H > L) |
|
|
||||||
| 挂单价 E | **做多** `E = H − ratio × (H − L)`(自 H 向下回撤);**做空** `E = L + ratio × (H − L)`(自 L 向上反弹) |
|
|
||||||
| 做多 | 限价 @ E,止损 L,止盈 H |
|
|
||||||
| 做空 | 限价 @ E,止损 H,止盈 L |
|
|
||||||
| 添加后 | **立即**在 Gate 挂限价单;卡片显示 **挂E**、限价单 ID |
|
|
||||||
| 失效 | 以**标记价**判断:做多且标记价 ≥ H、做空且标记价 ≤ L,且限价**未成交** → 撤销该限价单并结案(不写历史开仓) |
|
|
||||||
| 成交后 | 按仓位挂交易所 TP/SL → 写入 **实盘下单监控**(`monitor_type=关键位监控`,`key_signal_type=斐波回调0.618/0.786`)→ 从关键位列表移除 |
|
|
||||||
| 撤单 | 仅撤本条斐波的 `fib_limit_order_id`,**不会** `cancel_all`,避免误伤其他委托 |
|
|
||||||
| 盈亏比 | 计划 RR 须 > `KEY_AUTO_MIN_PLANNED_RR`(与箱体/收敛一致);0.618 理论约 1.6:1,0.786 约 3.7:1 |
|
|
||||||
| 日成交量 | 与箱体/收敛相同,须在前 `KEY_DAILY_VOLUME_RANK_MAX` 名内方可添加 |
|
|
||||||
|
|
||||||
后台轮询:`check_fib_key_monitors()`(标记价失效 / 成交检测);箱体/收敛仍走 `check_key_monitors()`,互不干扰。
|
|
||||||
|
|
||||||
手动删除关键位时,若斐波限价尚未成交,会先撤交易所限价再删库记录。
|
|
||||||
|
|
||||||
### 箱体 / 收敛自动开仓(来源标注)
|
|
||||||
|
|
||||||
- 自动开仓写入 `order_monitors.key_signal_type`:`箱体突破` 或 `收敛突破`。
|
|
||||||
- 持仓卡片、交易记录列表会显示「来源 · 信号类型」。
|
|
||||||
|
|
||||||
## 列表时间窗(UTC,全站顶栏)
|
|
||||||
|
|
||||||
共用模块:仓库根目录 `history_window_lib.py`(Gate / Binance 主站一致)。
|
|
||||||
|
|
||||||
| 项 | 说明 |
|
|
||||||
|----|------|
|
|
||||||
| 默认 | **UTC 当日**(`win_preset=utc_today`,从 UTC 0:00 至当前时刻) |
|
|
||||||
| 可选 | 近 24 小时、近 7 天、自定义起止(UTC,`datetime-local`) |
|
|
||||||
| 作用范围 | 关键位历史、交易记录列表、复盘记录 API、AI 历史 API、导出「交易记录」「关键位历史」 |
|
|
||||||
| 与统计的关系 | **仅影响列表/导出**;**统计分析页仍按北京时间 `TRADING_DAY_RESET_HOUR`(默认 8:00)切交易日** |
|
|
||||||
| 库内时间 | DB 存北京时间字符串;后端用 `utc_window_to_bj_sql_strings()` 换算后再 SQL 比较 |
|
|
||||||
| 切换方式 | 顶栏「列表筛选(UTC)」→ 选预设 → **应用**(保留当前路由,如 `/records?win_preset=…`) |
|
|
||||||
|
|
||||||
查询参数示例:
|
|
||||||
|
|
||||||
- `?win_preset=utc_today`
|
|
||||||
- `?win_preset=utc_last24h` / `utc_last7d`
|
|
||||||
- `?win_preset=custom&from_utc=2026-05-18 00:00:00&to_utc=2026-05-19 12:00:00`
|
|
||||||
|
|
||||||
## 交易记录与复盘
|
|
||||||
|
|
||||||
- 平仓记录可同步交易所已实现盈亏(Gate 仓位历史等);列表盈亏列优先显示交易所数据,标注 **所** / **估**。
|
|
||||||
- 记录页提供 **立即同步**(`POST /api/sync_exchange_pnl`),用于补全或刷新 `exchange_realized_pnl` 等字段。
|
|
||||||
- 未做人工复盘时,展示以交易所盈亏为准(有同步数据时)。
|
|
||||||
- **列表默认只显示当前 UTC 时间窗内**的记录(见上节);导出 CSV 同步该时间窗。
|
|
||||||
- 表头 **「止损(开仓)」**:展示开仓快照 `initial_stop_loss`(无则回退 `stop_loss`);核对/复盘仍可用有效止损字段。
|
|
||||||
- 平仓写入 `trade_records` 时:`stop_loss` 与 `initial_stop_loss` 均写入**开仓时止损快照**;`key_signal_type` 保留箱体/收敛/斐波来源(`fib_key_monitor_lib.key_signal_type_for_trade_record`)。
|
|
||||||
- **开仓类型**(`entry_reason`):机器单平仓入库时,若未手填,按 `key_signal_type` 自动映射(见下表);列表/导出「开仓类型」列 = 复盘核对值优先,否则入库值,否则按信号映射。
|
|
||||||
|
|
||||||
| `key_signal_type` | 自动写入的 `entry_reason` |
|
|
||||||
|-------------------|---------------------------|
|
|
||||||
| 箱体突破 | 关键位箱体突破 |
|
|
||||||
| 收敛突破 | 关键位收敛突破 |
|
|
||||||
| 斐波回调0.618 | 关键位斐波0.618 |
|
|
||||||
| 斐波回调0.786 | 关键位斐波0.786 |
|
|
||||||
|
|
||||||
- 复盘表单 **开仓类型** 下拉新增上述四条固定文案(与趋势/波段类并列)。
|
|
||||||
- 复盘 **离场触发** 新增 **「止盈」**;从交易记录「填入复盘」时,若结果为「止盈/保本止盈/移动止盈/止损/手动平仓」会自动选中对应触发项,并按 `key_signal_type` 预填开仓类型。
|
|
||||||
- 勾选「保存时自动生成多周期 K 线图」时:以 **平仓时间** 为锚点,各周期向前约 `ORDER_CHART_LIMIT`(默认 100)根 K 线(`_fetch_ohlcv_ending_at`),不再固定拉「最近 100 根」。
|
|
||||||
- `/api/journals`、`/api/reviews` 支持同一时间窗 query,与列表一致。
|
|
||||||
|
|
||||||
### 导出(交易记录 v3)
|
|
||||||
|
|
||||||
- 文件名:`trade_records_v3_YYYYMMDD.csv`
|
|
||||||
- 相对 v2 增加:`key_signal_type`、`initial_stop_loss`(及开仓快照列)、`planned_rr`、`actual_rr`、`risk_amount`、交易所盈亏与时间字段等;末列「开仓类型」为有效展示文案。
|
|
||||||
- 「关键位历史」导出同样受 UTC 时间窗限制。
|
|
||||||
|
|
||||||
## 实盘下单页
|
|
||||||
|
|
||||||
- 左列:实盘下单监控(表单、划转、规则)。
|
|
||||||
- 右列:实时持仓(独立模块)。
|
|
||||||
- **人工开仓门控**:计划盈亏比 < `MANUAL_MIN_PLANNED_RR`(默认 **1.4**)时前端弹窗 + 后端拒绝。
|
|
||||||
- **移动保本**(勾选启用):监控轮询达到触发 RR 后,止损阶梯上移时**同步交易所**——调用与页面「挂止盈止损」相同的 **先撤后挂**(`replace_active_monitor_tpsl_on_exchange`:撤该合约全部 TP/SL 条件单 → 按新止损 + 原止盈重挂)。仅交易所成功后才写库;失败发企业微信告警,本地止损不变。未配置实盘 API 时仍只更新本地(与旧行为一致)。
|
|
||||||
|
|
||||||
## 统计分析页(`/stats`)
|
|
||||||
|
|
||||||
| 项 | 说明 |
|
|
||||||
|----|------|
|
|
||||||
| 切日 | **北京时间**;交易日边界 = 每日 `TRADING_DAY_RESET_HOUR:00`(`.env` 默认 **8**) |
|
|
||||||
| 品类下拉 | 页顶 **「统计品类」** 下拉切换(默认「全部交易」):全部交易、下单监控、关键位箱体突破、关键位收敛结构、关键位斐波0.618、关键位斐波0.786;一次只显示所选品类的日/周/月 |
|
|
||||||
| URL | 切换后写入 `stats_segment=`(如 `all`、`manual`、`key_box`、`key_conv`、`key_fib618`、`key_fib786`),刷新 `/stats` 可保持选项 |
|
|
||||||
| 每块指标 | 日 / 周 / 月:开单次数、平仓笔数、胜率、净盈亏、回撤、连续亏损等(与原口径一致) |
|
|
||||||
| 开单次数 | 人工块:`monitor_type=下单监控` 且无 `key_signal_type`;关键位块:按 `order_monitors.key_signal_type` 计数 |
|
|
||||||
| 不受 UTC 窗影响 | 统计始终基于库内全部已平仓记录,按北京交易日归类,**不**随顶栏 UTC 列表窗切换 |
|
|
||||||
|
|
||||||
## 持仓与计仓
|
|
||||||
|
|
||||||
- `MAX_ACTIVE_POSITIONS` 默认 **1**(可在 `.env` 调大)。
|
|
||||||
- 关键位自动开仓:在已有持仓时,若 `KEY_SIZING_USE_ZERO_POSITION_SNAPSHOT=true`,按**首笔开仓前**交易账户资金快照计仓(`trading_sessions.key_sizing_capital_snapshot`)。
|
|
||||||
|
|
||||||
## 配置
|
|
||||||
|
|
||||||
详见 `.env.example` 中「关键位门控」「交易执行 / 人工风控」注释段。Gate 专用项(`GATE_*`、止盈止损触发等)保持原有段落不变。
|
|
||||||
|
|
||||||
## 自动备份(服务器)
|
|
||||||
|
|
||||||
- 脚本:`scripts/backup_data.sh`(`crypto.db` + `static/images`)
|
|
||||||
- 定时:`scripts/install_backup_cron.sh` → 每天 **北京时间 0:00**,目录 **`/root/backups/<实例名>/YYYY-MM-DD/`**,保留 **30** 天
|
|
||||||
- 详见 `部署文档.md` 第 5.4 节(自动备份)
|
|
||||||
|
|
||||||
## 数据库(启动时自动迁移)
|
|
||||||
|
|
||||||
`key_monitors` 新增斐波字段(示例):`fib_limit_order_id`、`fib_entry_price`、`fib_stop_loss`、`fib_take_profit`、`fib_order_amount`、`fib_margin_capital`、`fib_leverage`。
|
|
||||||
|
|
||||||
`trade_records` / `order_monitors` 新增或沿用:`key_signal_type`、`exchange_realized_pnl`、`exchange_opened_at`、`exchange_closed_at`、`exchange_sync_key`、`entry_reason`、`reviewed_entry_reason`、`initial_stop_loss`。
|
|
||||||
|
|
||||||
**历史数据**:本次**不做**旧记录的批量回填(`entry_reason` / `initial_stop_loss` / `key_signal_type` 等);仅**新产生**的平仓与复盘按新逻辑写入。旧行展示可回退已有字段。
|
|
||||||
|
|
||||||
## 涉及文件(便于排查)
|
|
||||||
|
|
||||||
| 路径 | 说明 |
|
|
||||||
|------|------|
|
|
||||||
| `history_window_lib.py` | UTC 时间窗解析与转北京时间 SQL 字符串 |
|
|
||||||
| `fib_key_monitor_lib.py` | 斐波计算、`KEY_ENTRY_REASON_BY_SIGNAL`、`entry_reason_from_key_signal` |
|
|
||||||
| `crypto_monitor_gate/app.py` | 列表筛选、统计分块、导出 v3、复盘 K 线锚点、入库逻辑 |
|
|
||||||
| `crypto_monitor_gate/templates/index.html` | 顶栏时间窗、统计分块 UI、止损(开仓)列、复盘预填 |
|
|
||||||
|
|
||||||
## 升级步骤
|
|
||||||
|
|
||||||
1. `git pull` 后对比 `.env.example`,把新增变量合并进本地 `.env`。
|
|
||||||
2. 在 VPS 上为 Binance / Gate / Gate Bot **各执行一次** `bash scripts/install_backup_cron.sh`(若尚未安装)。
|
|
||||||
3. 重启 Gate 实例服务(如 `pm2 restart crypto_gate`);首次启动会自动 `ALTER TABLE` 缺列(斐波、交易所盈亏、`entry_reason` 等)。
|
|
||||||
4. 浏览器强刷(Ctrl+F5)避免旧版 `index.html` 缓存。
|
|
||||||
5. 打开任意页确认顶栏出现 **「列表筛选(UTC)」**;`/stats` 可见分品类统计与「北京 8:00 切日」说明。
|
|
||||||
6. 建议在测试币上先添加一条斐波监控,确认:限价已挂出、标记价失效会撤单、成交后出现持仓监控且 TP/SL 已挂上;平仓后交易记录止损(开仓)与开仓类型是否正确。
|
|
||||||
@@ -1,339 +0,0 @@
|
|||||||
# `crypto_monitor_gate_bot` 部署指南:SSH SOCKS + Gate.io + PM2(Ubuntu)
|
|
||||||
|
|
||||||
Ubuntu 环境总览见 **[docs/ubuntu-server.md](../docs/ubuntu-server.md)**。
|
|
||||||
|
|
||||||
本文面向:**在本机运行本项目**,但 **直连 Gate.io API 不稳定或被重置** 的场景。思路是:
|
|
||||||
|
|
||||||
- 本机用 `ssh -D` 做动态转发,把 **SOCKS5 出口**放到能正常访问 Gate 的机器(常见为一台境外 VPS)
|
|
||||||
- 项目在 `.env` 中设置 **`GATE_SOCKS_PROXY=socks5h://127.0.0.1:1080`**(或你实际端口),`ccxt` 经 SOCKS 访问交易所
|
|
||||||
- **SSH 隧道**:用 `ssh -D` 在本机常驻(可用 **tmux** 或 **autossh** 保持连接),**不要** 把 `ssh` 交给 PM2
|
|
||||||
- 使用 **PM2** 仅托管 **Flask 应用**;仓库根目录 **`ecosystem.config.cjs`** 只定义 `crypto-monitor-gate`
|
|
||||||
|
|
||||||
> 安全提醒:不要把 `.env`、私钥 `.pem`、Gate API Key 提交到 Git;下文只用占位符。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 0. 你需要准备的东西
|
|
||||||
|
|
||||||
- 一台 **Ubuntu**(或同类 Linux)运行项目的机器(下文称「本机」)
|
|
||||||
- 一台可 SSH 登录、且 **能正常访问 Gate.io API** 的 VPS(示例:`HostName` 填你的服务器 IP,用户如 `root`)
|
|
||||||
- SSH:**私钥登录**(推荐,便于隧道脚本无人值守)
|
|
||||||
- 本机已安装:`python3`、`python3-venv`、`pip`、`curl`、`ssh`、`git`(可选)、`node` + `npm`(安装 PM2)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. 获取代码与目录
|
|
||||||
|
|
||||||
将包含 `app.py` 的项目放到固定目录,例如:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir -p /opt/crypto_monitor
|
|
||||||
cd /opt/crypto_monitor
|
|
||||||
git clone https://git.bz121.com/dekun/crypto_monitor.git
|
|
||||||
cd crypto_monitor/crypto_monitor_gate_bot
|
|
||||||
```
|
|
||||||
|
|
||||||
下文用 **`/opt/crypto_monitor/crypto_monitor_gate_bot`** 仅为示例,请换成你的实际绝对路径。
|
|
||||||
|
|
||||||
拉取代码后,若目录下尚无 `.env`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cp -n .env.example .env
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. 配置 SSH 私钥与 `~/.ssh/config`
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir -p ~/.ssh
|
|
||||||
chmod 700 ~/.ssh
|
|
||||||
# 私钥示例:~/.ssh/vps1.pem
|
|
||||||
chmod 600 ~/.ssh/vps1.pem
|
|
||||||
```
|
|
||||||
|
|
||||||
编辑 `~/.ssh/config`(示例别名 **`gate-vps`**,与你手工启动 `ssh -D ... gate-vps` 一致即可):
|
|
||||||
|
|
||||||
```sshconfig
|
|
||||||
Host gate-vps
|
|
||||||
HostName 你的_VPS_IP
|
|
||||||
User root
|
|
||||||
IdentityFile ~/.ssh/vps1.pem
|
|
||||||
IdentitiesOnly yes
|
|
||||||
ServerAliveInterval 30
|
|
||||||
ServerAliveCountMax 3
|
|
||||||
ExitOnForwardFailure yes
|
|
||||||
BatchMode yes
|
|
||||||
```
|
|
||||||
|
|
||||||
测试:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ssh gate-vps true
|
|
||||||
```
|
|
||||||
|
|
||||||
> 若尚未完全改为密钥登录,可暂时注释 `BatchMode yes`,调试完成后再打开。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. 手工验证:SSH SOCKS + Gate API
|
|
||||||
|
|
||||||
### 3.1 本地 SOCKS(示例端口 1080)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ssh -N -D 127.0.0.1:1080 gate-vps
|
|
||||||
```
|
|
||||||
|
|
||||||
保持运行,另开终端继续。
|
|
||||||
|
|
||||||
### 3.2 验证经 SOCKS 可访问 Gate
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -4 -sS --max-time 15 --proxy socks5h://127.0.0.1:1080 https://api.gateio.ws/api/v4/spot/time
|
|
||||||
```
|
|
||||||
|
|
||||||
应返回 JSON(含服务器时间字段)。若此处失败,**不要先启动应用**:先修隧道或 VPS 出站。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Python 虚拟环境
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /opt/crypto_monitor/crypto_monitor_gate_bot
|
|
||||||
|
|
||||||
python3 -m venv .venv
|
|
||||||
source .venv/bin/activate
|
|
||||||
python -m pip install -U pip
|
|
||||||
pip install flask requests ccxt werkzeug PySocks Pillow
|
|
||||||
```
|
|
||||||
|
|
||||||
走 SOCKS 时 **必须** 安装 **`PySocks`**,否则易出现代理相关报错。
|
|
||||||
|
|
||||||
可选:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export PYTHONDONTWRITEBYTECODE=1
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. 配置环境变量(`.env.example` → `.env`)
|
|
||||||
|
|
||||||
| 文件 | 是否进 Git | 说明 |
|
|
||||||
|------|------------|------|
|
|
||||||
| **`.env.example`** | ✅ 是 | 变量模板与注释,可随 `git pull` 更新 |
|
|
||||||
| **`.env`** | ❌ 否 | 本机真实配置;`app.py` **只读此文件** |
|
|
||||||
|
|
||||||
### 5.1 首次配置
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /opt/crypto_monitor/crypto_monitor_gate_bot
|
|
||||||
|
|
||||||
cp -n .env.example .env
|
|
||||||
nano .env
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5.2 备份与 `git pull`
|
|
||||||
|
|
||||||
- **`.env` 不在 Git 中**:`git pull` **不会**覆盖本地 `.env`。
|
|
||||||
- 远端若更新 **`.env.example`**,pull 后请**手动**把新增变量补进你的 `.env`。
|
|
||||||
- **升级前备份**:`cp .env .env.backup.$(date +%Y%m%d)`;恢复:`cp .env.backup.YYYYMMDD .env`。
|
|
||||||
- **换机**:`scp` 复制 `.env`,或新机 `cp .env.example .env` 后重填。
|
|
||||||
|
|
||||||
### 5.3 AI 复盘与模型(可选)
|
|
||||||
|
|
||||||
共用根目录 **`ai_client.py`**(`PYTHONPATH=..`)。`.env` 默认 **`AI_PROVIDER=openai`** + `OPENAI_API_BASE` / `OPENAI_API_KEY` / `OPENAI_MODEL`;或 **`ollama`** + `OLLAMA_API` / `AI_MODEL`。详见 **[AI复盘与模型配置说明.md](../AI复盘与模型配置说明.md)**。
|
|
||||||
|
|
||||||
### 5.4 自动备份(数据库 + 复盘图片)
|
|
||||||
|
|
||||||
每天 **北京时间 0:00** 备份到 **`/root/backups`**,保留 **30 天**(`crypto.db` + `static/images`)。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /opt/crypto_monitor/crypto_monitor_gate_bot
|
|
||||||
chmod +x scripts/backup_data.sh scripts/install_backup_cron.sh
|
|
||||||
bash scripts/install_backup_cron.sh
|
|
||||||
bash scripts/backup_data.sh # 试跑
|
|
||||||
```
|
|
||||||
|
|
||||||
备份目录:`/root/backups/crypto_monitor_gate_bot/YYYY-MM-DD/`。与 Binance / Gate 实例规则相同,详见 `crypto_monitor_binance/部署文档.md` 第 5.4 节(恢复步骤、可选 `.env` 变量)。
|
|
||||||
|
|
||||||
若服务器同时跑 **binance、gate、gate_bot** 三个实例,请在**各自项目目录**各执行一次 `install_backup_cron.sh`。
|
|
||||||
|
|
||||||
### 5.4 必填项检查(Gate + 代理)
|
|
||||||
|
|
||||||
与交易所相关的变量必须是 **Gate** 前缀(**不要**再写 OKX 变量,否则代理不会生效、密钥也不会被识别)。至少确认:
|
|
||||||
|
|
||||||
```env
|
|
||||||
APP_HOST=127.0.0.1
|
|
||||||
APP_PORT=5000
|
|
||||||
|
|
||||||
# 实盘(按需)
|
|
||||||
LIVE_TRADING_ENABLED=false
|
|
||||||
GATE_API_KEY=你的_Key
|
|
||||||
GATE_API_SECRET=你的_Secret
|
|
||||||
|
|
||||||
# 经本机 SSH 动态转发访问 Gate(端口与隧道一致)
|
|
||||||
GATE_SOCKS_PROXY=socks5h://127.0.0.1:1080
|
|
||||||
|
|
||||||
# 若不用 SOCKS,可改用 HTTP 代理(一般二选一)
|
|
||||||
# GATE_HTTP_PROXY=http://127.0.0.1:7890
|
|
||||||
# GATE_HTTPS_PROXY=http://127.0.0.1:7890
|
|
||||||
```
|
|
||||||
|
|
||||||
说明:**推荐 `socks5h://`**,由 SOCKS 端解析域名,与 `curl --proxy socks5h://...` 行为一致。
|
|
||||||
|
|
||||||
### 5.4 趋势回调策略(可选)
|
|
||||||
|
|
||||||
若使用「交易执行」页的 **趋势回调** 计划:
|
|
||||||
|
|
||||||
- 详细规则见项目根目录 **`趋势回调策略说明.md`**。
|
|
||||||
- **两阶段**:先「生成预览」(默认 **120 秒**内有效),再「确认执行」;执行时若可用余额与预览快照偏差超过 **5%** 会拒绝(可调 `.env`)。
|
|
||||||
- 补仓档位数默认 **5**,预览有效期与余额偏差阈值可在 `.env` 覆盖:
|
|
||||||
|
|
||||||
```env
|
|
||||||
TREND_PULLBACK_DCA_LEGS=5
|
|
||||||
TREND_PULLBACK_PREVIEW_TTL_SECONDS=120
|
|
||||||
TREND_PREVIEW_MAX_BALANCE_DRIFT_PCT=5
|
|
||||||
```
|
|
||||||
|
|
||||||
- **生成预览**与**确认执行**时都会读取 **Gate 永续账户 USDT 可用余额**;请尽量使用 **单独子账户** 承载策略资金。
|
|
||||||
|
|
||||||
**界面与对账(与策略说明 3.4–3.5 节一致)**
|
|
||||||
|
|
||||||
- 页顶 **计划历史**:仅 **已结束** 的趋势计划(不含未执行预览);可 **删除** 计划行,并删除 `trend_plan_id` 关联的「趋势回调」`trade_records`(新数据;旧行无 `trend_plan_id` 不级联)。
|
|
||||||
- **运行中计划**展示交易所 **未实现盈亏**(浮盈亏)。
|
|
||||||
- **交易记录**:趋势单在配置 API Key 后,打开「交易执行 / 交易记录」页会按节流(约 **25 秒**内同进程最多一次)拉取 Gate **平仓历史**,回填 **`exchange_realized_pnl`** 等;列表展示优先用交易所口径(见策略说明)。
|
|
||||||
|
|
||||||
**与交易所对齐的可选环境变量**
|
|
||||||
|
|
||||||
```env
|
|
||||||
# 平仓历史同步起点:北京日期 YYYY-MM-DD 的 0 点(与 APP_TIMEZONE 一致);留空则从近 90 天拉取
|
|
||||||
# EXCHANGE_POSITION_SYNC_FROM_BJ=2026-05-14
|
|
||||||
# EXCHANGE_POSITION_HISTORY_LIMIT=200
|
|
||||||
```
|
|
||||||
|
|
||||||
说明:同步 **只读** 交易所接口,**不要求** `LIVE_TRADING_ENABLED=true`;无 Key 时不拉取,界面仍可用(浮盈亏可能为「—」、交易记录仍为本地「估」)。
|
|
||||||
|
|
||||||
**交易记录 CSV**:导出为 **v3**,含 `trend_plan_id` 与交易所对齐列(详见策略说明数据库一节)。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. 手工启动 Flask(验证)
|
|
||||||
|
|
||||||
1. SOCKS 已监听 `127.0.0.1:1080`
|
|
||||||
2. 已 `source .venv/bin/activate`
|
|
||||||
3. `.env` 已含 `GATE_SOCKS_PROXY`
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /opt/crypto_monitor/crypto_monitor_gate_bot
|
|
||||||
source .venv/bin/activate
|
|
||||||
python app.py
|
|
||||||
```
|
|
||||||
|
|
||||||
浏览器访问:`http://127.0.0.1:5000`(或你在 `.env` 中的端口)。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. 安装 PM2
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo npm i -g pm2
|
|
||||||
pm2 -v
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. PM2:使用仓库内 `ecosystem.config.cjs`(推荐)
|
|
||||||
|
|
||||||
在项目根目录:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /opt/crypto_monitor/crypto_monitor_gate_bot
|
|
||||||
pm2 start ecosystem.config.cjs
|
|
||||||
pm2 status
|
|
||||||
pm2 logs --lines 200
|
|
||||||
```
|
|
||||||
|
|
||||||
默认只启动 **`crypto-monitor-gate`**(`.venv/bin/python app.py`)。
|
|
||||||
|
|
||||||
### 本机已可直连 Gate、不需要隧道时
|
|
||||||
|
|
||||||
`.env` 里应 **去掉或留空** `GATE_SOCKS_PROXY`(除非仍要走别的代理),再 `pm2 start ecosystem.config.cjs`。
|
|
||||||
|
|
||||||
### 开机自启
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pm2 save
|
|
||||||
pm2 startup
|
|
||||||
# 按屏幕提示执行一条 sudo 命令
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 9. 等价手工命令(不使用 ecosystem 文件时)
|
|
||||||
|
|
||||||
### 9.1 SSH SOCKS(自行后台常驻,不推荐用 PM2)
|
|
||||||
|
|
||||||
示例(前台调试;生产请用 **PM2**,见本文与 [docs/ubuntu-server.md](../docs/ubuntu-server.md)):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ssh -N -D 127.0.0.1:1080 gate-vps \
|
|
||||||
-o ServerAliveInterval=30 -o ServerAliveCountMax=3 \
|
|
||||||
-o ExitOnForwardFailure=yes
|
|
||||||
```
|
|
||||||
|
|
||||||
### 9.2 Flask
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /opt/crypto_monitor/crypto_monitor_gate_bot
|
|
||||||
pm2 start /opt/crypto_monitor/crypto_monitor_gate_bot/.venv/bin/python --name crypto-monitor-gate -- \
|
|
||||||
/opt/crypto_monitor/crypto_monitor_gate_bot/app.py
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 10. 交易所「连接不上」排查清单
|
|
||||||
|
|
||||||
1. **`.env` 是否为 Gate 变量**:必须是 `GATE_SOCKS_PROXY` / `GATE_API_KEY` / `GATE_API_SECRET`,不是 OKX。
|
|
||||||
2. **隧道是否在本机端口监听**(若配置了 `GATE_SOCKS_PROXY`):
|
|
||||||
```bash
|
|
||||||
ss -lntp | grep 1080 || true
|
|
||||||
```
|
|
||||||
3. **curl 复测 Gate**(与第 3.2 节相同);curl 不通则应用也不会通。
|
|
||||||
4. **PySocks**:`pip show PySocks`,缺失则 `pip install PySocks`。
|
|
||||||
5. **SSH 隧道连不上**:检查私钥权限、`~/.ssh/config`、VPS 出站与端口是否与 `.env` 一致。
|
|
||||||
6. **启动顺序**:先保证 SOCKS 已监听,再 `pm2 start` 应用(或重启应用)。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 11. 推荐启动顺序(习惯)
|
|
||||||
|
|
||||||
1. 若走代理:先启动并确认 SSH SOCKS 已监听,再 `curl --proxy socks5h://127.0.0.1:1080 https://api.gateio.ws/api/v4/spot/time` 成功
|
|
||||||
2. `pm2 start ecosystem.config.cjs`
|
|
||||||
3. 再确认页面与余额等接口正常
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 12. 免责声明
|
|
||||||
|
|
||||||
交易所有合规与地区政策要求。请确保使用方式符合当地法律法规与交易所条款。本文仅描述网络与工程部署路径。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 附录:数据库标签修复脚本 `scripts/fix_breakeven_labels.py`
|
|
||||||
|
|
||||||
在 Ubuntu 上:
|
|
||||||
|
|
||||||
1)预览(不写库):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python scripts/fix_breakeven_labels.py --db ./crypto.db --dry-run
|
|
||||||
```
|
|
||||||
|
|
||||||
2)确认后执行:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python scripts/fix_breakeven_labels.py --db ./crypto.db --apply
|
|
||||||
```
|
|
||||||
|
|
||||||
默认修复条件:`monitor_type='下单监控'` 且 `result='止损'` 且 `pnl_amount > 0` → 改为 `result='保本止盈'`。
|
|
||||||
@@ -157,7 +157,7 @@ nano .env
|
|||||||
- **升级前备份**:`cp .env .env.backup.$(date +%Y%m%d)`;恢复:`cp .env.backup.YYYYMMDD .env`。
|
- **升级前备份**:`cp .env .env.backup.$(date +%Y%m%d)`;恢复:`cp .env.backup.YYYYMMDD .env`。
|
||||||
- **换机**:`scp` 复制 `.env`,或新机 `cp .env.example .env` 后重填。
|
- **换机**:`scp` 复制 `.env`,或新机 `cp .env.example .env` 后重填。
|
||||||
|
|
||||||
**AI 复盘**:四所共用根目录 **`ai_client.py`**。默认 **`AI_PROVIDER=openai`**,网关 `https://op.bz121.com/v1`,模型 `gemma4:e4b`;或改 **`ollama`** 走本机 Ollama。PM2 须 **`PYTHONPATH=..`**。详见 **[AI复盘与模型配置说明.md](../AI复盘与模型配置说明.md)**。
|
**AI 复盘**:三所共用根目录 **`ai_client.py`**。默认 **`AI_PROVIDER=openai`**,网关 `https://op.bz121.com/v1`,模型 `gemma4:e4b`;或改 **`ollama`** 走本机 Ollama。PM2 须 **`PYTHONPATH=..`**。详见 **[AI复盘与模型配置说明.md](../AI复盘与模型配置说明.md)**。
|
||||||
|
|
||||||
### 5.3 必填项检查(OKX + 代理)
|
### 5.3 必填项检查(OKX + 代理)
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ bash deploy/setup_env.sh --install-system-deps
|
|||||||
常用参数:
|
常用参数:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bash deploy/setup_env.sh --only binance,gate_bot # 仅部分子项目
|
bash deploy/setup_env.sh --only binance,gate # 仅部分子项目
|
||||||
bash deploy/setup_env.sh --recreate-venv # 重建虚拟环境
|
bash deploy/setup_env.sh --recreate-venv # 重建虚拟环境
|
||||||
bash deploy/setup_env.sh --skip-pm2 # 不尝试安装 pm2
|
bash deploy/setup_env.sh --skip-pm2 # 不尝试安装 pm2
|
||||||
bash deploy/setup_env.sh --skip-env-copy # 不复制 .env.example
|
bash deploy/setup_env.sh --skip-env-copy # 不复制 .env.example
|
||||||
@@ -68,11 +68,11 @@ sed -i 's/\r$//' deploy/setup_env.sh
|
|||||||
pm2 save
|
pm2 save
|
||||||
```
|
```
|
||||||
|
|
||||||
3. 四所 `.env` 同步脚本见 **[docs/env-sync-scripts.md](../docs/env-sync-scripts.md)**。
|
3. 三所 `.env` 同步脚本见 **[docs/env-sync-scripts.md](../docs/env-sync-scripts.md)**。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 依赖说明
|
## 依赖说明
|
||||||
|
|
||||||
- 四个监控子项目共用根目录 **[requirements.txt](../requirements.txt)**。
|
- 三个监控子项目共用根目录 **[requirements.txt](../requirements.txt)**。
|
||||||
- 走 SOCKS 须 **PySocks**(已包含在 requirements 中)。
|
- 走 SOCKS 须 **PySocks**(已包含在 requirements 中)。
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#
|
#
|
||||||
# 用法:
|
# 用法:
|
||||||
# bash deploy/setup_env.sh
|
# bash deploy/setup_env.sh
|
||||||
# bash deploy/setup_env.sh --only binance,gate_bot
|
# bash deploy/setup_env.sh --only binance,gate
|
||||||
# bash deploy/setup_env.sh --skip-pm2
|
# bash deploy/setup_env.sh --skip-pm2
|
||||||
# bash deploy/setup_env.sh --recreate-venv
|
# bash deploy/setup_env.sh --recreate-venv
|
||||||
# bash deploy/setup_env.sh --install-system-deps # root + apt 时安装 python*-venv
|
# bash deploy/setup_env.sh --install-system-deps # root + apt 时安装 python*-venv
|
||||||
@@ -244,7 +244,6 @@ ensure_venv_prereqs "${PY}"
|
|||||||
|
|
||||||
should_include binance && setup_monitor crypto_monitor_binance
|
should_include binance && setup_monitor crypto_monitor_binance
|
||||||
should_include gate && setup_monitor crypto_monitor_gate
|
should_include gate && setup_monitor crypto_monitor_gate
|
||||||
should_include gate_bot && setup_monitor crypto_monitor_gate_bot
|
|
||||||
should_include okx && setup_monitor crypto_monitor_okx
|
should_include okx && setup_monitor crypto_monitor_okx
|
||||||
should_include hub && setup_hub
|
should_include hub && setup_hub
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# 账户冷静期 / 日冻结风控
|
# 账户冷静期 / 日冻结风控
|
||||||
|
|
||||||
四所实例(币安 / OKX / Gate / Gate 趋势)共用 `account_risk_lib.py`。
|
三所实例(币安 / OKX / Gate / Gate)共用 `account_risk_lib.py`。
|
||||||
**仅用户主动平仓**计入风控;交易所止盈/止损、空仓同步、改保本/改委托等**不触发**冷静期。
|
**仅用户主动平仓**计入风控;交易所止盈/止损、空仓同步、改保本/改委托等**不触发**冷静期。
|
||||||
|
|
||||||
## 状态展示
|
## 状态展示
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# 每日自动划转(四所统一)
|
# 每日自动划转(三所统一)
|
||||||
|
|
||||||
## 行为
|
## 行为
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
| 余额 **低于** `AUTO_TRANSFER_AMOUNT` | 从 `AUTO_TRANSFER_FROM`(默认 funding)划入差额 |
|
| 余额 **低于** `AUTO_TRANSFER_AMOUNT` | 从 `AUTO_TRANSFER_FROM`(默认 funding)划入差额 |
|
||||||
| 余额 **高于** `AUTO_TRANSFER_AMOUNT` | 将多余划回 `AUTO_TRANSFER_FROM` |
|
| 余额 **高于** `AUTO_TRANSFER_AMOUNT` | 将多余划回 `AUTO_TRANSFER_FROM` |
|
||||||
| 与目标相差 < 0.01U | 跳过,不写划转 |
|
| 与目标相差 < 0.01U | 跳过,不写划转 |
|
||||||
| 存在 **active** 持仓(`order_monitors`,或 Gate 趋势回调已开仓计划) | **不划转**,写账簿 `skipped`,并**企业微信**说明「持仓中,本次资金无划转」 |
|
| 存在 **active** 持仓(`order_monitors`,或 Gate回调已开仓计划) | **不划转**,写账簿 `skipped`,并**企业微信**说明「持仓中,本次资金无划转」 |
|
||||||
|
|
||||||
## 配置示例(目标 50U)
|
## 配置示例(目标 50U)
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ AUTO_TRANSFER_BJ_HOUR=8
|
|||||||
|
|
||||||
API Key 须具备万向划转权限(与手动划转相同)。
|
API Key 须具备万向划转权限(与手动划转相同)。
|
||||||
|
|
||||||
## 用脚本更新四所 `.env`
|
## 用脚本更新三所 `.env`
|
||||||
|
|
||||||
详见 **[env-sync-scripts.md](./env-sync-scripts.md)**。常用命令:
|
详见 **[env-sync-scripts.md](./env-sync-scripts.md)**。常用命令:
|
||||||
|
|
||||||
@@ -41,5 +41,5 @@ python scripts/sync_four_exchange_transfer_env.py --set-amount 50 --enable-auto-
|
|||||||
# 计仓 + 划转一并补全
|
# 计仓 + 划转一并补全
|
||||||
python scripts/sync_four_exchange_env.py --set-transfer-amount 50 --enable-auto-transfer
|
python scripts/sync_four_exchange_env.py --set-transfer-amount 50 --enable-auto-transfer
|
||||||
|
|
||||||
pm2 restart crypto-monitor-binance crypto-monitor-okx crypto-monitor-gate crypto-monitor-gate-bot
|
pm2 restart crypto-monitor-binance crypto-monitor-okx crypto-monitor-gate
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# 单日开仓次数限制(四所统一)
|
# 单日开仓次数限制(三所统一)
|
||||||
|
|
||||||
各交易实例(Binance / OKX / Gate / Gate_bot)在 `.env` 中独立配置,互不影响。
|
各交易实例(Binance / OKX / Gate)在 `.env` 中独立配置,互不影响。
|
||||||
|
|
||||||
## 交易日口径
|
## 交易日口径
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ DAILY_OPEN_HARD_LIMIT=3
|
|||||||
|
|
||||||
- `TRADING_DAY_RESET_OPEN_GUARD_ENABLED`:切日前禁止新开
|
- `TRADING_DAY_RESET_OPEN_GUARD_ENABLED`:切日前禁止新开
|
||||||
- `MAX_ACTIVE_POSITIONS`:同时持仓上限
|
- `MAX_ACTIVE_POSITIONS`:同时持仓上限
|
||||||
- Gate_bot:`precheck_trend_pullback_start` 同样校验单日硬上限
|
- Gate:`precheck_trend_pullback_start` 同样校验单日硬上限
|
||||||
|
|
||||||
## 页面与接口
|
## 页面与接口
|
||||||
|
|
||||||
@@ -71,11 +71,11 @@ DAILY_OPEN_HARD_LIMIT=3
|
|||||||
修改各实例 `.env` 后重启对应 pm2 进程,例如:
|
修改各实例 `.env` 后重启对应 pm2 进程,例如:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pm2 restart crypto_binance crypto_okx crypto_gate crypto_gate_bot
|
pm2 restart crypto_binance crypto_okx crypto_gate
|
||||||
```
|
```
|
||||||
|
|
||||||
## 实现位置
|
## 实现位置
|
||||||
|
|
||||||
- 共享逻辑:`daily_open_limit_lib.py`
|
- 共享逻辑:`daily_open_limit_lib.py`
|
||||||
- 四所 `app.py`:`precheck_risk`、`can_trade`、`api/account_snapshot`、开仓成功后的 AI 提醒文案
|
- 三所 `app.py`:`precheck_risk`、`can_trade`、`api/account_snapshot`、开仓成功后的 AI 提醒文案
|
||||||
- 单元测试:`tests/test_daily_open_limit_lib.py`
|
- 单元测试:`tests/test_daily_open_limit_lib.py`
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
# 四所 `.env` 同步脚本说明
|
# 三所 `.env` 同步脚本说明
|
||||||
|
|
||||||
在**仓库根目录**执行。仅处理四所实例目录下的 `.env`,**不覆盖** API 密钥与已存在的自定义值;若某目录无 `.env` 会 `SKIP`(需先 `cp .env.example .env`)。
|
在**仓库根目录**执行。仅处理三所实例目录下的 `.env`,**不覆盖** API 密钥与已存在的自定义值;若某目录无 `.env` 会 `SKIP`(需先 `cp .env.example .env`)。
|
||||||
|
|
||||||
| 目录 |
|
| 目录 |
|
||||||
|------|
|
|------|
|
||||||
| `crypto_monitor_binance` |
|
| `crypto_monitor_binance` |
|
||||||
| `crypto_monitor_okx` |
|
| `crypto_monitor_okx` |
|
||||||
| `crypto_monitor_gate` |
|
| `crypto_monitor_gate` |
|
||||||
| `crypto_monitor_gate_bot` |
|
| `crypto_monitor_gate` |
|
||||||
|
|
||||||
修改 `.env` 后须 **`pm2 restart`** 对应实例后生效。
|
修改 `.env` 后须 **`pm2 restart`** 对应实例后生效。
|
||||||
|
|
||||||
@@ -37,9 +37,9 @@ python scripts/sync_four_exchange_env.py --set-mode full_margin
|
|||||||
| 参数 | 说明 |
|
| 参数 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `--dry-run` | 只打印将做的变更,不写 `.env` |
|
| `--dry-run` | 只打印将做的变更,不写 `.env` |
|
||||||
| `--set-mode risk\|full_margin` | 强制四所 `POSITION_SIZING_MODE` |
|
| `--set-mode risk\|full_margin` | 强制三所 `POSITION_SIZING_MODE` |
|
||||||
| `--set-transfer-amount U` | 强制四所 `AUTO_TRANSFER_AMOUNT` |
|
| `--set-transfer-amount U` | 强制三所 `AUTO_TRANSFER_AMOUNT` |
|
||||||
| `--enable-auto-transfer` | 强制四所 `AUTO_TRANSFER_ENABLED=true` |
|
| `--enable-auto-transfer` | 强制三所 `AUTO_TRANSFER_ENABLED=true` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ python scripts/sync_four_exchange_position_sizing_env.py --set-buffer 0.98
|
|||||||
## 部署后重启
|
## 部署后重启
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pm2 restart crypto-monitor-binance crypto-monitor-okx crypto-monitor-gate crypto-monitor-gate-bot
|
pm2 restart crypto-monitor-binance crypto-monitor-okx crypto-monitor-gate
|
||||||
```
|
```
|
||||||
|
|
||||||
## 相关文档
|
## 相关文档
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
|
|
||||||
| 项 | 约定 |
|
| 项 | 约定 |
|
||||||
|----|------|
|
|----|------|
|
||||||
| 交易来源 | 四所 `trade_records` + 未落库的 `strategy_trade_snapshots`,经 `/api/hub/trades/archive` 拉取 |
|
| 交易来源 | 三所 `trade_records` + 未落库的 `strategy_trade_snapshots`,经 `/api/hub/trades/archive` 拉取 |
|
||||||
| 犯病标签 | 中控 `trade_overlay.behavior_tag = sick` |
|
| 犯病标签 | 中控 `trade_overlay.behavior_tag = sick` |
|
||||||
| K 线真源 | 仅 **5m** 写入 `hub_symbol_archive.db` |
|
| K 线真源 | 仅 **5m** 写入 `hub_symbol_archive.db` |
|
||||||
| 建档种子 | 该币 **最早开仓** 向前 **30 天** 5m |
|
| 建档种子 | 该币 **最早开仓** 向前 **30 天** 5m |
|
||||||
@@ -80,7 +80,7 @@
|
|||||||
| DELETE | `/api/archive/quotes/{id}` | 删除语录 |
|
| DELETE | `/api/archive/quotes/{id}` | 删除语录 |
|
||||||
| GET | `/api/archive/ohlcv` | K 线视窗(`timeframe` / `mode` / `anchor_ms` / `at`) |
|
| GET | `/api/archive/ohlcv` | K 线视窗(`timeframe` / `mode` / `anchor_ms` / `at`) |
|
||||||
| PATCH | `/api/archive/trade/{exchange_key}/{trade_id}` | 更新标签/备注 |
|
| PATCH | `/api/archive/trade/{exchange_key}/{trade_id}` | 更新标签/备注 |
|
||||||
| POST | `/api/archive/sync` | 立即同步四所交易 + K 线 |
|
| POST | `/api/archive/sync` | 立即同步三所交易 + K 线 |
|
||||||
|
|
||||||
`GET /api/archive/daily-trades` 主要 query:
|
`GET /api/archive/daily-trades` 主要 query:
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
# lib/ 共用模块结构
|
# lib/ 共用模块结构
|
||||||
|
|
||||||
四所实例与中控共用的 Python 库、模板与静态资源统一放在仓库根目录的 **`lib/`** 下。部署单元(`crypto_monitor_*`、`manual_trading_hub`)仍保持独立目录与 PM2 配置不变。
|
三所实例与中控共用的 Python 库、模板与静态资源统一放在仓库根目录的 **`lib/`** 下。部署单元(`crypto_monitor_*`、`manual_trading_hub`)仍保持独立目录与 PM2 配置不变。
|
||||||
|
|
||||||
**重构前快照 Git 标签**:`pre-lib-modularization`(可用 `git checkout pre-lib-modularization` 查看旧布局)。
|
**重构前快照 Git 标签**:`pre-lib-modularization`(可用 `git checkout pre-lib-modularization` 查看旧布局)。
|
||||||
|
**移除 gate_bot 前快照 Git 标签**:`pre-remove-gate-bot`。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -10,9 +11,8 @@
|
|||||||
|
|
||||||
```
|
```
|
||||||
crypto_monitor/
|
crypto_monitor/
|
||||||
├── crypto_monitor_binance/ # 四所:各自 app + .env + PM2
|
├── crypto_monitor_binance/ # 三所:各自 app + .env + PM2
|
||||||
├── crypto_monitor_gate/
|
├── crypto_monitor_gate/
|
||||||
├── crypto_monitor_gate_bot/
|
|
||||||
├── crypto_monitor_okx/
|
├── crypto_monitor_okx/
|
||||||
├── manual_trading_hub/ # 中控 + 子代理 agent
|
├── manual_trading_hub/ # 中控 + 子代理 agent
|
||||||
│
|
│
|
||||||
@@ -52,9 +52,9 @@ crypto_monitor/
|
|||||||
| **`lib/instance/templates/`** | 嵌入页片段(原 `embed_templates/`) | `embed_page_fragment.html` |
|
| **`lib/instance/templates/`** | 嵌入页片段(原 `embed_templates/`) | `embed_page_fragment.html` |
|
||||||
| **`lib/exchange/`** | 特定交易所工具 | `gate_transfer_lib.py`、`okx_orders_lib.py` 等 |
|
| **`lib/exchange/`** | 特定交易所工具 | `gate_transfer_lib.py`、`okx_orders_lib.py` 等 |
|
||||||
| **`lib/common/`** | 跨功能小工具 | `form_submit_lib.py`、`wechat_notify_lib.py` 等 |
|
| **`lib/common/`** | 跨功能小工具 | `form_submit_lib.py`、`wechat_notify_lib.py` 等 |
|
||||||
| **`lib/common/static/`** | 四所与中控共用的 JS/CSS(原根目录 `static/`) | `instance_theme.js`、`strategy_roll.js` 等 |
|
| **`lib/common/static/`** | 三所与中控共用的 JS/CSS(原根目录 `static/`) | `instance_theme.js`、`strategy_roll.js` 等 |
|
||||||
|
|
||||||
> **说明**:`hub_*` 命名表示「中控侧能力或行情聚合」,但部分模块(如 `hub_volume_rank_lib`、`hub_market_info_lib`)四所 `app.py` 也会调用,并非中控独占。
|
> **说明**:`hub_*` 命名表示「中控侧能力或行情聚合」,但部分模块(如 `hub_volume_rank_lib`、`hub_market_info_lib`)三所 `app.py` 也会调用,并非中控独占。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -107,9 +107,9 @@ install_strategy_trend(app, _REPO_ROOT, app_module=sys.modules[__name__])
|
|||||||
|
|
||||||
## 静态资源与 URL
|
## 静态资源与 URL
|
||||||
|
|
||||||
- 四所页面仍通过 **`/static/...`** 访问共用脚本;`hub_bridge.install_instance_theme_static` 从 `lib/common/static/` 提供部分根级静态路由。
|
- 三所页面仍通过 **`/static/...`** 访问共用脚本;`hub_bridge.install_instance_theme_static` 从 `lib/common/static/` 提供部分根级静态路由。
|
||||||
- 各所目录下 **`static/`**(图标、上传图片等)仍为实例私有,未迁入 `lib/`。
|
- 各所目录下 **`static/`**(图标、上传图片等)仍为实例私有,未迁入 `lib/`。
|
||||||
- 中控 `manual_trading_hub/hub.py` 通过 `_REPO_ROOT / "lib" / "common" / "static"` 挂载与四所共用的 badge、复盘 JS 等。
|
- 中控 `manual_trading_hub/hub.py` 通过 `_REPO_ROOT / "lib" / "common" / "static"` 挂载与三所共用的 badge、复盘 JS 等。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ python -m unittest discover -s tests -p "test_*.py"
|
|||||||
|
|
||||||
## 后续可选整理
|
## 后续可选整理
|
||||||
|
|
||||||
- 四所 `app.py` 体量接近,可逐步抽取公共 `exchange_app` 基座(改动面大,单独规划)。
|
- 三所 `app.py` 体量接近,可逐步抽取公共 `exchange_app` 基座(改动面大,单独规划)。
|
||||||
- `manual_trading_hub/okx_orders_lib.py` 为 agent 本地副本,可与 `lib/exchange/okx_orders_lib.py` 合并去重。
|
- `manual_trading_hub/okx_orders_lib.py` 为 agent 本地副本,可与 `lib/exchange/okx_orders_lib.py` 合并去重。
|
||||||
- 可引入 `pyproject.toml` + `pip install -e .`,替代 `sys.path.insert`(长期维护更规范)。
|
- 可引入 `pyproject.toml` + `pip install -e .`,替代 `sys.path.insert`(长期维护更规范)。
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## 功能
|
## 功能
|
||||||
|
|
||||||
四所(Binance / OKX / Gate / Gate趋势)**实盘下单监控**表单中,在「开仓」按钮前显示 **预估盈亏比**。
|
三所(Binance / OKX / Gate)**实盘下单监控**表单中,在「开仓」按钮前显示 **预估盈亏比**。
|
||||||
|
|
||||||
- **价格模式**:填完币种、方向、止损价、止盈价后,调用 `GET /api/order_defaults` 取标记价,按几何距离计算 RR。
|
- **价格模式**:填完币种、方向、止损价、止盈价后,调用 `GET /api/order_defaults` 取标记价,按几何距离计算 RR。
|
||||||
- **百分比模式**:填完币种、方向、止损%、止盈% 后拉快照校验币种,再显示 RR(`止盈% / 止损%`)。
|
- **百分比模式**:填完币种、方向、止损%、止盈% 后拉快照校验币种,再显示 RR(`止盈% / 止损%`)。
|
||||||
@@ -28,4 +28,4 @@
|
|||||||
## 校验记录
|
## 校验记录
|
||||||
|
|
||||||
- `node --check static/manual_order_rr_preview.js`
|
- `node --check static/manual_order_rr_preview.js`
|
||||||
- `tests/test_manual_order_rr_preview.py`:RR 公式与四所 `calc_rr_ratio` 口径一致
|
- `tests/test_manual_order_rr_preview.py`:RR 公式与三所 `calc_rr_ratio` 口径一致
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# 计仓模式(四所统一)
|
# 计仓模式(三所统一)
|
||||||
|
|
||||||
## 配置
|
## 配置
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ FULL_MARGIN_BUFFER_RATIO=0.98
|
|||||||
|
|
||||||
**允许:** 关键位 **回调触价开仓** / **突破触价开仓**(程序盯价、触达/穿越计划入场后市价成交,无交易所挂单;全仓下仅允许一条待触发)。
|
**允许:** 关键位 **回调触价开仓** / **突破触价开仓**(程序盯价、触达/穿越计划入场后市价成交,无交易所挂单;全仓下仅允许一条待触发)。
|
||||||
|
|
||||||
## 用脚本更新四所 `.env`
|
## 用脚本更新三所 `.env`
|
||||||
|
|
||||||
详见 **[env-sync-scripts.md](./env-sync-scripts.md)**。常用命令:
|
详见 **[env-sync-scripts.md](./env-sync-scripts.md)**。常用命令:
|
||||||
|
|
||||||
@@ -53,5 +53,5 @@ python scripts/sync_four_exchange_position_sizing_env.py --set-mode risk
|
|||||||
# 计仓 + 划转一并补全
|
# 计仓 + 划转一并补全
|
||||||
python scripts/sync_four_exchange_env.py
|
python scripts/sync_four_exchange_env.py
|
||||||
|
|
||||||
pm2 restart crypto-monitor-binance crypto-monitor-okx crypto-monitor-gate crypto-monitor-gate-bot
|
pm2 restart crypto-monitor-binance crypto-monitor-okx crypto-monitor-gate
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
3. `link rel="icon"` / `favicon.ico`
|
3. `link rel="icon"` / `favicon.ico`
|
||||||
4. 若都没有 → 灰色地球或网页标题首字
|
4. 若都没有 → 灰色地球或网页标题首字
|
||||||
|
|
||||||
本仓库已在 **中控** 与 **四所监控页** 配置统一品牌图标(深色圆角底 + 青绿趋势线 + 简化的 K 线),与页面 UI 一致。PNG/ICO 由 **Pillow** 生成,避免损坏的 favicon 出现花屏。
|
本仓库已在 **中控** 与 **三所监控页** 配置统一品牌图标(深色圆角底 + 青绿趋势线 + 简化的 K 线),与页面 UI 一致。PNG/ICO 由 **Pillow** 生成,避免损坏的 favicon 出现花屏。
|
||||||
|
|
||||||
## 文件位置
|
## 文件位置
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
|------|----------|
|
|------|----------|
|
||||||
| 源稿 | `brand/icon.svg`、`brand/icons/*.png` |
|
| 源稿 | `brand/icon.svg`、`brand/icons/*.png` |
|
||||||
| 中控 | `manual_trading_hub/static/icons/` → `/assets/icons/...` |
|
| 中控 | `manual_trading_hub/static/icons/` → `/assets/icons/...` |
|
||||||
| 四所 | `crypto_monitor_*/static/icons/` → `/static/icons/...` |
|
| 三所 | `crypto_monitor_*/static/icons/` → `/static/icons/...` |
|
||||||
|
|
||||||
## 重新生成 / 同步
|
## 重新生成 / 同步
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# 趋势回调:中控平仓与交易记录(检阅备忘)
|
# 趋势回调:中控平仓与交易记录(检阅备忘)
|
||||||
|
|
||||||
本文档汇总 **中控手动结束趋势计划**、**交易记录 / 策略记录** 写入规则,以及 **四所展示统一**、**补仓表计价** 相关修复,便于自行检阅与排错。
|
本文档汇总 **中控手动结束趋势计划**、**交易记录 / 策略记录** 写入规则,以及 **三所展示统一**、**补仓表计价** 相关修复,便于自行检阅与排错。
|
||||||
|
|
||||||
适用仓库:`crypto_monitor`(Binance / OKX / Gate / Gate Bot + `manual_trading_hub`)。
|
适用仓库:`crypto_monitor`(Binance / OKX / + `manual_trading_hub`)。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. 调用链(四所统一)
|
## 2. 调用链(三所统一)
|
||||||
|
|
||||||
```
|
```
|
||||||
manual_trading_hub
|
manual_trading_hub
|
||||||
@@ -32,7 +32,7 @@ manual_trading_hub
|
|||||||
→ _finalize_plan(cfg, conn, row, "手动平仓", exit_price)
|
→ _finalize_plan(cfg, conn, row, "手动平仓", exit_price)
|
||||||
```
|
```
|
||||||
|
|
||||||
共用实现:`strategy_trend_register.py`(四所同一套,Gate Bot 的 `stop_trend_pullback` 也调用 `_finalize_plan`)。
|
共用实现:`strategy_trend_register.py`(三所同一套,各所的 `stop_trend_pullback` 也调用 `_finalize_plan`)。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ manual_trading_hub
|
|||||||
|
|
||||||
**现象**:策略记录有(止损 -2.71U),**交易记录没有**。
|
**现象**:策略记录有(止损 -2.71U),**交易记录没有**。
|
||||||
|
|
||||||
**原因**:Gate Bot 的 `insert_trade_record` 曾 **缺少 `entry_reason` 参数**,而 `_finalize_plan` 固定传入 `entry_reason="趋势回调"`,触发:
|
**原因**:各所的 `insert_trade_record` 曾 **缺少 `entry_reason` 参数**,而 `_finalize_plan` 固定传入 `entry_reason="趋势回调"`,触发:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
TypeError: insert_trade_record() got an unexpected keyword argument 'entry_reason'
|
TypeError: insert_trade_record() got an unexpected keyword argument 'entry_reason'
|
||||||
@@ -64,7 +64,7 @@ TypeError: insert_trade_record() got an unexpected keyword argument 'entry_reaso
|
|||||||
|
|
||||||
**修复提交**:`80226ee`
|
**修复提交**:`80226ee`
|
||||||
|
|
||||||
- Gate Bot `insert_trade_record` 增加 `entry_reason`
|
- `insert_trade_record` 增加 `entry_reason`
|
||||||
- `_call_insert_trade_record`:按各所函数 **签名过滤** 参数,避免未知字段导致失败
|
- `_call_insert_trade_record`:按各所函数 **签名过滤** 参数,避免未知字段导致失败
|
||||||
- 调整写入顺序:交易记录 → 计划结束 → commit
|
- 调整写入顺序:交易记录 → 计划结束 → commit
|
||||||
|
|
||||||
@@ -79,11 +79,11 @@ cd /opt/crypto_monitor # 或本机仓库根目录
|
|||||||
|
|
||||||
# 先预览
|
# 先预览
|
||||||
python scripts/backfill_trend_trade_records.py \
|
python scripts/backfill_trend_trade_records.py \
|
||||||
--db crypto_monitor_gate_bot/crypto.db --dry-run
|
--db crypto_monitor_gate/crypto.db --dry-run
|
||||||
|
|
||||||
# 确认后写入
|
# 确认后写入
|
||||||
python scripts/backfill_trend_trade_records.py \
|
python scripts/backfill_trend_trade_records.py \
|
||||||
--db crypto_monitor_gate_bot/crypto.db --apply
|
--db crypto_monitor_gate/crypto.db --apply
|
||||||
```
|
```
|
||||||
|
|
||||||
其它所将 `--db` 换成对应 `crypto.db` 路径即可。
|
其它所将 `--db` 换成对应 `crypto.db` 路径即可。
|
||||||
@@ -99,7 +99,7 @@ python scripts/backfill_trend_trade_records.py \
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 7. 四所展示统一(中控 ↔ 实例)
|
## 7. 三所展示统一(中控 ↔ 实例)
|
||||||
|
|
||||||
### 7.1 数据 enrich 入口
|
### 7.1 数据 enrich 入口
|
||||||
|
|
||||||
@@ -109,13 +109,13 @@ python scripts/backfill_trend_trade_records.py \
|
|||||||
| 中控 `/api/hub/monitor` | `enrich_trend_plan_for_hub` → 同上 |
|
| 中控 `/api/hub/monitor` | `enrich_trend_plan_for_hub` → 同上 |
|
||||||
| 补仓明细表 | `attach_trend_dca_levels` → `enrich_trend_dca_levels_with_tp` |
|
| 补仓明细表 | `attach_trend_dca_levels` → `enrich_trend_dca_levels_with_tp` |
|
||||||
|
|
||||||
Gate Bot 在 `hub_bridge` 安装后调用 `patch_trend_hub_enrich`,与另外三所 `install_strategy_trend` 行为一致。
|
在 `hub_bridge` 安装后调用 `patch_trend_hub_enrich`,与另外三所 `install_strategy_trend` 行为一致。
|
||||||
|
|
||||||
### 7.2 补仓表「触发价 / 加仓后均价」
|
### 7.2 补仓表「触发价 / 加仓后均价」
|
||||||
|
|
||||||
**禁止**为凑均价 **反推虚构成交价**(曾错误出现做多补仓触发价 0.3941 等离谱数值)。
|
**禁止**为凑均价 **反推虚构成交价**(曾错误出现做多补仓触发价 0.3941 等离谱数值)。
|
||||||
|
|
||||||
**`trend_leg_display_price`(四所唯一口径)**:
|
**`trend_leg_display_price`(三所唯一口径)**:
|
||||||
|
|
||||||
| 列 | 规则 |
|
| 列 | 规则 |
|
||||||
|----|------|
|
|----|------|
|
||||||
@@ -138,7 +138,7 @@ Gate Bot 在 `hub_bridge` 安装后调用 `patch_trend_hub_enrich`,与另外
|
|||||||
```bash
|
```bash
|
||||||
cd /opt/crypto_monitor
|
cd /opt/crypto_monitor
|
||||||
git pull # 需含 80226ee、08082eb
|
git pull # 需含 80226ee、08082eb
|
||||||
pm2 restart crypto-monitor-binance crypto-monitor-okx crypto-monitor-gate crypto-monitor-gate-bot manual-trading-hub
|
pm2 restart crypto-monitor-binance crypto-monitor-okx crypto-monitor-gate manual-trading-hub
|
||||||
pm2 save
|
pm2 save
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -157,16 +157,16 @@ pm2 save
|
|||||||
| `strategy_trend_lib.py` | `trend_leg_display_price`、`enrich_trend_dca_levels_with_tp` |
|
| `strategy_trend_lib.py` | `trend_leg_display_price`、`enrich_trend_dca_levels_with_tp` |
|
||||||
| `strategy_snapshot_lib.py` | 策略快照写入 |
|
| `strategy_snapshot_lib.py` | 策略快照写入 |
|
||||||
| `hub_bridge.py` | `/api/hub/trend/stop/<pid>` |
|
| `hub_bridge.py` | `/api/hub/trend/stop/<pid>` |
|
||||||
| `crypto_monitor_gate_bot/app.py` | `insert_trade_record`(含 `entry_reason`) |
|
| `crypto_monitor_gate/app.py` | `insert_trade_record`(含 `entry_reason`) |
|
||||||
| `scripts/backfill_trend_trade_records.py` | 漏记交易记录补录 |
|
| `scripts/backfill_trend_trade_records.py` | 漏记交易记录补录 |
|
||||||
|
|
||||||
### 8.4 相关提交
|
### 8.4 相关提交
|
||||||
|
|
||||||
| 提交 | 说明 |
|
| 提交 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `6a4ec69` | 中控与四所趋势展示 enrich 统一 |
|
| `6a4ec69` | 中控与三所趋势展示 enrich 统一 |
|
||||||
| `08082eb` | 移除补仓表反推虚构成交价 |
|
| `08082eb` | 移除补仓表反推虚构成交价 |
|
||||||
| `80226ee` | 修复 Gate Bot 中控平仓漏写 `trade_records` |
|
| `80226ee` | 修复 中控平仓漏写 `trade_records` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -175,7 +175,7 @@ pm2 save
|
|||||||
| 文档 | 内容 |
|
| 文档 | 内容 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| [策略交易说明.md](../策略交易说明.md) | 策略总览、策略交易记录页 |
|
| [策略交易说明.md](../策略交易说明.md) | 策略总览、策略交易记录页 |
|
||||||
| [crypto_monitor_gate_bot/趋势回调策略说明.md](../crypto_monitor_gate_bot/趋势回调策略说明.md) | 趋势回调业务细则 |
|
| [crypto_monitor_gate/趋势回调策略说明.md](../crypto_monitor_gate/趋势回调策略说明.md) | 趋势回调业务细则 |
|
||||||
| [manual_trading_hub/使用说明.md](../manual_trading_hub/使用说明.md) | 中控监控与趋势卡布局 |
|
| [manual_trading_hub/使用说明.md](../manual_trading_hub/使用说明.md) | 中控监控与趋势卡布局 |
|
||||||
| [hub-symbol-archive-kline.md](./hub-symbol-archive-kline.md) | 币种档案、永久 5m K 线、交易 overlay |
|
| [hub-symbol-archive-kline.md](./hub-symbol-archive-kline.md) | 币种档案、永久 5m K 线、交易 overlay |
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
# 趋势回调策略(机器人)说明
|
# 趋势回调策略说明
|
||||||
|
|
||||||
本文描述 **「趋势回调」** 自动交易计划的业务规则与实现口径。
|
本文描述 **「趋势回调」** 自动交易计划的业务规则与实现口径。
|
||||||
|
|
||||||
**四所主站**(Binance / Gate / OKX / 本目录 `crypto_monitor_gate_bot`)均在顶栏 **策略交易 → `/strategy`** 左栏提供同一套逻辑(共用 `strategy_trend_register.py`);本目录侧重 **Gate 子账户 / 机器人** 实例,可与主 Gate 账户隔离部署。
|
**三所主站**(Binance / Gate / OKX)均在顶栏 **策略交易 → `/strategy`** 左栏提供同一套逻辑(共用 `strategy_trend_register.py`);各所使用各自 API 与 `crypto.db`。
|
||||||
|
|
||||||
**检阅备忘**(中控平仓、交易记录、补仓展示、漏记补录):[docs/trend-hub-close-and-trade-records.md](../docs/trend-hub-close-and-trade-records.md)
|
**检阅备忘**(中控平仓、交易记录、补仓展示、漏记补录):[trend-hub-close-and-trade-records.md](./trend-hub-close-and-trade-records.md)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. 适用场景
|
## 1. 适用场景
|
||||||
|
|
||||||
- 单独用于跑策略的 **Gate.io USDT 永续** 子账户(建议与主资金隔离);其它交易所实例同理,使用各自 API 与 `crypto.db`。
|
- 各 **USDT 永续** 实例独立部署,使用各自 API 与 `crypto.db`。
|
||||||
- 你已明确:**方向、止损价、补仓区间边界价、止盈价、杠杆**,并接受程序按风险预算拆分 **首仓 50% + 多档补仓 50%**。
|
- 你已明确:**方向、止损价、补仓区间边界价、止盈价、杠杆**,并接受程序按风险预算拆分 **首仓 50% + 多档补仓 50%**。
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
|----|------|
|
|----|------|
|
||||||
| **版本** | **Python 3.10 或 3.11**(`python3 --version` ≥ 3.10);脚本会拒绝 3.9 及以下 |
|
| **版本** | **Python 3.10 或 3.11**(`python3 --version` ≥ 3.10);脚本会拒绝 3.9 及以下 |
|
||||||
| **虚拟环境** | 每个子项目独立 **`.venv`**(`deploy/setup_env.sh` 自动创建) |
|
| **虚拟环境** | 每个子项目独立 **`.venv`**(`deploy/setup_env.sh` 自动创建) |
|
||||||
| **依赖文件** | 四所监控共用仓库根目录 **`requirements.txt`**;中控用 **`manual_trading_hub/requirements.txt`** |
|
| **依赖文件** | 三所监控共用仓库根目录 **`requirements.txt`**;中控用 **`manual_trading_hub/requirements.txt`** |
|
||||||
| **SOCKS** | 走代理时必须安装 **PySocks**(已写入 requirements) |
|
| **SOCKS** | 走代理时必须安装 **PySocks**(已写入 requirements) |
|
||||||
|
|
||||||
### 2.1 系统包(root)
|
### 2.1 系统包(root)
|
||||||
@@ -75,13 +75,12 @@ pm2 startup # 按提示执行,保证重启后 PM2 自启
|
|||||||
### 3.2 PM2 启动顺序(推荐)
|
### 3.2 PM2 启动顺序(推荐)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1) 四所 Flask(在各子目录执行,或分别 start)
|
# 1) 三所 Flask(在各子目录执行,或分别 start)
|
||||||
cd /opt/crypto_monitor/crypto_monitor_binance && pm2 start ecosystem.config.cjs
|
cd /opt/crypto_monitor/crypto_monitor_binance && pm2 start ecosystem.config.cjs
|
||||||
cd /opt/crypto_monitor/crypto_monitor_gate && pm2 start ecosystem.config.cjs
|
cd /opt/crypto_monitor/crypto_monitor_gate && pm2 start ecosystem.config.cjs
|
||||||
cd /opt/crypto_monitor/crypto_monitor_gate_bot && pm2 start ecosystem.config.cjs
|
|
||||||
cd /opt/crypto_monitor/crypto_monitor_okx && pm2 start ecosystem.config.cjs
|
cd /opt/crypto_monitor/crypto_monitor_okx && pm2 start ecosystem.config.cjs
|
||||||
|
|
||||||
# 2) 中控 + 四子代理(一条配置 5 进程)
|
# 2) 中控 + 三子代理(一条配置 4 进程:hub + 3 agent)
|
||||||
cd /opt/crypto_monitor/manual_trading_hub
|
cd /opt/crypto_monitor/manual_trading_hub
|
||||||
pm2 start ecosystem.config.cjs
|
pm2 start ecosystem.config.cjs
|
||||||
|
|
||||||
@@ -105,7 +104,6 @@ pm2 restart all # 或按进程名 restart
|
|||||||
|------|---------------------|
|
|------|---------------------|
|
||||||
| `crypto_monitor_binance` | `crypto-monitor-binance` |
|
| `crypto_monitor_binance` | `crypto-monitor-binance` |
|
||||||
| `crypto_monitor_gate` | `crypto-monitor-gate` |
|
| `crypto_monitor_gate` | `crypto-monitor-gate` |
|
||||||
| `crypto_monitor_gate_bot` | `crypto-monitor-gate-bot` |
|
|
||||||
| `crypto_monitor_okx` | `crypto-monitor-okx` |
|
| `crypto_monitor_okx` | `crypto-monitor-okx` |
|
||||||
| `manual_trading_hub` | `manual-trading-hub`、`manual-agent-*` |
|
| `manual_trading_hub` | `manual-trading-hub`、`manual-agent-*` |
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""AI 日复盘 / 周复盘:附图收集与 journal 文本格式化(四所共用)。"""
|
"""AI 日复盘 / 周复盘:附图收集与 journal 文本格式化(三所共用)。"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
@@ -46,7 +46,7 @@ def journal_row_lines_for_ai(
|
|||||||
*,
|
*,
|
||||||
include_hold_duration: bool = True,
|
include_hold_duration: bool = True,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""把 journal 字段拼成给 AI 的文本;四所日复盘/周复盘共用。"""
|
"""把 journal 字段拼成给 AI 的文本;三所日复盘/周复盘共用。"""
|
||||||
lines = [
|
lines = [
|
||||||
(
|
(
|
||||||
f"{idx}. {_journal_nz(_row_get(row, 'coin'))} {_journal_nz(_row_get(row, 'tf'))} "
|
f"{idx}. {_journal_nz(_row_get(row, 'coin'))} {_journal_nz(_row_get(row, 'tf'))} "
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* 账户风控状态徽章 — 四所实例 + 中控共用;兼容 data-theme light/dark */
|
/* 账户风控状态徽章 — 三所实例 + 中控共用;兼容 data-theme light/dark */
|
||||||
|
|
||||||
:root,
|
:root,
|
||||||
html[data-theme="dark"] {
|
html[data-theme="dark"] {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* 账户风控徽章倒计时 — 四所实例 + 中控共用。
|
* 账户风控徽章倒计时 — 三所实例 + 中控共用。
|
||||||
*/
|
*/
|
||||||
(function (global) {
|
(function (global) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|||||||
@@ -975,7 +975,7 @@ html[data-theme="light"] .ai-result.is-loading {
|
|||||||
50% { opacity: 0.55; }
|
50% { opacity: 0.55; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* AI 日复盘 / 周复盘 Markdown(弹窗 + 内联结果区,四所共用) */
|
/* AI 日复盘 / 周复盘 Markdown(弹窗 + 内联结果区,三所共用) */
|
||||||
html[data-theme="light"] .ai-result-md,
|
html[data-theme="light"] .ai-result-md,
|
||||||
html[data-theme="light"] .detail-modal .panel-body.md-review {
|
html[data-theme="light"] .detail-modal .panel-body.md-review {
|
||||||
color: #1a2838 !important;
|
color: #1a2838 !important;
|
||||||
@@ -1028,7 +1028,7 @@ html[data-theme="light"] .detail-modal .panel-body.md-review .md-raw-block-title
|
|||||||
border-top-color: #d0dae4 !important;
|
border-top-color: #d0dae4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Gate Bot 统计分栏(机器人 / 趋势回调)── */
|
/* ── 统计分栏(机器人 / 趋势回调)── */
|
||||||
html[data-theme="light"] .stats-split-col {
|
html[data-theme="light"] .stats-split-col {
|
||||||
background: #fff !important;
|
background: #fff !important;
|
||||||
border-color: #b8c8d8 !important;
|
border-color: #b8c8d8 !important;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* 四所实例主题:默认暗色;单独登录用 instance-theme;中控 iframe/SSO 随 hub-theme 联动。
|
* 三所实例主题:默认暗色;单独登录用 instance-theme;中控 iframe/SSO 随 hub-theme 联动。
|
||||||
*/
|
*/
|
||||||
(function (global) {
|
(function (global) {
|
||||||
const STANDALONE_KEY = "instance-theme";
|
const STANDALONE_KEY = "instance-theme";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* 四所实例共用 UI:复盘详情、盈亏着色等。
|
* 三所实例共用 UI:复盘详情、盈亏着色等。
|
||||||
*/
|
*/
|
||||||
(function (global) {
|
(function (global) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* 关键位监控添加表单:类型切换显隐、成交量排名校验(四所实例共用)。
|
* 关键位监控添加表单:类型切换显隐、成交量排名校验(三所实例共用)。
|
||||||
*/
|
*/
|
||||||
(function (global) {
|
(function (global) {
|
||||||
const RS_TYPES = new Set([
|
const RS_TYPES = new Set([
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* 交易日历:内照明心 + 四所统计分析共用,随 data-theme 浅/深切换 */
|
/* 交易日历:内照明心 + 三所统计分析共用,随 data-theme 浅/深切换 */
|
||||||
.trade-cal-wrap {
|
.trade-cal-wrap {
|
||||||
--trade-cal-wrap-bg: var(--inset-surface, rgba(0, 0, 0, 0.22));
|
--trade-cal-wrap-bg: var(--inset-surface, rgba(0, 0, 0, 0.22));
|
||||||
--trade-cal-cell-bg: var(--section-surface, var(--inset-surface, rgba(0, 0, 0, 0.32)));
|
--trade-cal-cell-bg: var(--section-surface, var(--inset-surface, rgba(0, 0, 0, 0.32)));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* 交易日历组件:内照明心档案 + 四所统计分析共用。
|
* 交易日历组件:内照明心档案 + 三所统计分析共用。
|
||||||
*/
|
*/
|
||||||
(function (global) {
|
(function (global) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Gate.io 资金划转(crypto_monitor_gate / crypto_monitor_gate_bot 共用)。"""
|
"""Gate.io 资金划转(crypto_monitor_gate 共用)。"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any, Callable, Optional
|
from typing import Any, Callable, Optional
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""中控备份与恢复:四所 SQLite、K 线库、env、hub JSON。"""
|
"""中控备份与恢复:三所 SQLite、K 线库、env、hub JSON。"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
@@ -22,7 +22,6 @@ EXCHANGE_DIRS: list[tuple[str, str]] = [
|
|||||||
("binance", "crypto_monitor_binance"),
|
("binance", "crypto_monitor_binance"),
|
||||||
("okx", "crypto_monitor_okx"),
|
("okx", "crypto_monitor_okx"),
|
||||||
("gate", "crypto_monitor_gate"),
|
("gate", "crypto_monitor_gate"),
|
||||||
("gate_bot", "crypto_monitor_gate_bot"),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
HUB_JSON_FILES = (
|
HUB_JSON_FILES = (
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ def _merge_query_into_path(path: str, **params: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def install_instance_theme_static(app) -> None:
|
def install_instance_theme_static(app) -> None:
|
||||||
"""仓库 lib/common/static 下 instance_theme.* 等供四所页面共用。"""
|
"""仓库 lib/common/static 下 instance_theme.* 等供三所页面共用。"""
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from flask import Response, send_file
|
from flask import Response, send_file
|
||||||
@@ -96,7 +96,7 @@ def register_trade_stats_calendar_route(
|
|||||||
reset_hour: int,
|
reset_hour: int,
|
||||||
get_db_fn=None,
|
get_db_fn=None,
|
||||||
):
|
):
|
||||||
"""四所统计分析页:按月返回各交易日盈亏/笔数。"""
|
"""三所统计分析页:按月返回各交易日盈亏/笔数。"""
|
||||||
from flask import jsonify, request
|
from flask import jsonify, request
|
||||||
|
|
||||||
from lib.trade.trade_stats_calendar_lib import build_trade_stats_calendar
|
from lib.trade.trade_stats_calendar_lib import build_trade_stats_calendar
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ def normalize_chart_timeframe(raw: str | None, default: str = "5m") -> str:
|
|||||||
|
|
||||||
|
|
||||||
def normalize_perpetual_symbol(symbol: str) -> str:
|
def normalize_perpetual_symbol(symbol: str) -> str:
|
||||||
"""BTC/USDT → BTC/USDT:USDT(与四所 ccxt swap 行情一致)。"""
|
"""BTC/USDT → BTC/USDT:USDT(与三所 ccxt swap 行情一致)。"""
|
||||||
sym = (symbol or "").strip().upper()
|
sym = (symbol or "").strip().upper()
|
||||||
if not sym:
|
if not sym:
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ def position_side_from_ccxt(p: dict[str, Any], contracts: float | None = None) -
|
|||||||
|
|
||||||
|
|
||||||
def parse_position_entry_price(p: dict[str, Any]) -> float | None:
|
def parse_position_entry_price(p: dict[str, Any]) -> float | None:
|
||||||
"""四所 ccxt 持仓开仓均价。"""
|
"""三所 ccxt 持仓开仓均价。"""
|
||||||
if not isinstance(p, dict):
|
if not isinstance(p, dict):
|
||||||
return None
|
return None
|
||||||
info = p.get("info") or {}
|
info = p.get("info") or {}
|
||||||
@@ -134,7 +134,7 @@ def _coerce_signed(*values: Any) -> float | None:
|
|||||||
|
|
||||||
|
|
||||||
def parse_position_unrealized_pnl(p: dict[str, Any]) -> float | None:
|
def parse_position_unrealized_pnl(p: dict[str, Any]) -> float | None:
|
||||||
"""四所 ccxt 持仓统一解析未实现盈亏(Gate/OKX/Binance 字段名不一致)。"""
|
"""三所 ccxt 持仓统一解析未实现盈亏(Gate/OKX/Binance 字段名不一致)。"""
|
||||||
if not isinstance(p, dict):
|
if not isinstance(p, dict):
|
||||||
return None
|
return None
|
||||||
info = p.get("info") or {}
|
info = p.get("info") or {}
|
||||||
@@ -162,7 +162,7 @@ def enrich_ccxt_position_metrics_out(
|
|||||||
funds_decimals: int = 2,
|
funds_decimals: int = 2,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
四所 parse_ccxt_position_metrics 产出后统一:
|
三所 parse_ccxt_position_metrics 产出后统一:
|
||||||
- 标记价用 hub 兜底
|
- 标记价用 hub 兜底
|
||||||
- 未实现盈亏 = resolve(交易所值, entry/mark/张数/contractSize 推算)
|
- 未实现盈亏 = resolve(交易所值, entry/mark/张数/contractSize 推算)
|
||||||
"""
|
"""
|
||||||
@@ -194,7 +194,7 @@ def enrich_ccxt_position_metrics_out(
|
|||||||
|
|
||||||
|
|
||||||
def parse_position_mark_price(p: dict[str, Any]) -> float | None:
|
def parse_position_mark_price(p: dict[str, Any]) -> float | None:
|
||||||
"""四所 ccxt 持仓统一解析标记价(与 crypto_monitor_* parse_ccxt_position_metrics 口径一致)。"""
|
"""三所 ccxt 持仓统一解析标记价(与 crypto_monitor_* parse_ccxt_position_metrics 口径一致)。"""
|
||||||
if not isinstance(p, dict):
|
if not isinstance(p, dict):
|
||||||
return None
|
return None
|
||||||
info = p.get("info") or {}
|
info = p.get("info") or {}
|
||||||
|
|||||||
@@ -365,7 +365,7 @@ def _collect_scores(exchange, exchange_id: str) -> list[tuple[str, str, float]]:
|
|||||||
return _scores_from_okx(exchange)
|
return _scores_from_okx(exchange)
|
||||||
if ex_id == "binance":
|
if ex_id == "binance":
|
||||||
return _scores_from_binance(exchange)
|
return _scores_from_binance(exchange)
|
||||||
if ex_id in ("gateio", "gate", "gate_bot"):
|
if ex_id in ("gateio", "gate"):
|
||||||
return _scores_from_gate(exchange)
|
return _scores_from_gate(exchange)
|
||||||
tickers = exchange.fetch_tickers()
|
tickers = exchange.fetch_tickers()
|
||||||
return _scores_from_markets(exchange, tickers or {}, ex_id)
|
return _scores_from_markets(exchange, tickers or {}, ex_id)
|
||||||
@@ -373,7 +373,7 @@ def _collect_scores(exchange, exchange_id: str) -> list[tuple[str, str, float]]:
|
|||||||
|
|
||||||
def _uses_lightweight_volume_scores(exchange_id: str) -> bool:
|
def _uses_lightweight_volume_scores(exchange_id: str) -> bool:
|
||||||
ex_id = str(exchange_id or "").lower()
|
ex_id = str(exchange_id or "").lower()
|
||||||
return ex_id in ("okx", "binance", "gateio", "gate", "gate_bot")
|
return ex_id in ("okx", "binance", "gateio", "gate")
|
||||||
|
|
||||||
|
|
||||||
def build_usdt_swap_volume_ranks(
|
def build_usdt_swap_volume_ranks(
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ PATH_TO_EMBED_TAB: dict[str, str] = {
|
|||||||
|
|
||||||
ORDER_RULE_TIPS_BY_EXCHANGE: dict[str, str] = {
|
ORDER_RULE_TIPS_BY_EXCHANGE: dict[str, str] = {
|
||||||
"gate": "order_monitor_rule_tips_gate.html",
|
"gate": "order_monitor_rule_tips_gate.html",
|
||||||
"gate_bot": "order_monitor_rule_tips_gate.html",
|
|
||||||
"binance": "order_monitor_rule_tips_binance.html",
|
"binance": "order_monitor_rule_tips_binance.html",
|
||||||
"okx": "order_monitor_rule_tips_okx.html",
|
"okx": "order_monitor_rule_tips_okx.html",
|
||||||
}
|
}
|
||||||
@@ -45,7 +44,7 @@ def order_rule_tips_template(exchange_key: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def include_transfer_block(exchange_key: str) -> bool:
|
def include_transfer_block(exchange_key: str) -> bool:
|
||||||
return (exchange_key or "").strip().lower() in ("gate", "gate_bot")
|
return (exchange_key or "").strip().lower() == "gate"
|
||||||
|
|
||||||
|
|
||||||
def path_to_embed_tab(path: str) -> str | None:
|
def path_to_embed_tab(path: str) -> str | None:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""关键位监控表结构迁移(四所共用)。"""
|
"""关键位监控表结构迁移(三所共用)。"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""回调/突破触价开仓关键位监控:程序盯价、触达计划入场后市价成交(四所共用逻辑)。"""
|
"""回调/突破触价开仓关键位监控:程序盯价、触达计划入场后市价成交(三所共用逻辑)。"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ def build_strategy_config(
|
|||||||
fn(content)
|
fn(content)
|
||||||
|
|
||||||
note = trend_disabled_note or (
|
note = trend_disabled_note or (
|
||||||
"趋势回调(自动补仓)请在 Gate 趋势机器人实例使用:/strategy/trend"
|
"趋势回调(自动补仓)请在 Gate机器人实例使用:/strategy/trend"
|
||||||
)
|
)
|
||||||
return {
|
return {
|
||||||
"app_module": m,
|
"app_module": m,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Gate.io USDT 永续 — 策略交易交易所侧能力。
|
Gate.io USDT 永续 — 策略交易交易所侧能力。
|
||||||
|
|
||||||
实现方式:各 Gate 实例 app 通过 strategy_config.build_strategy_config(app_module) 注入
|
实现方式:各 Gate 实例 app 通过 strategy_config.build_strategy_config(app_module) 注入
|
||||||
ccxt 下单、精度、换 TP/SL;本文件为文档与类型锚点,避免在四个 app 重复实现滚仓公式。
|
ccxt 下单、精度、换 TP/SL;本文件为文档与类型锚点,避免在各 app 重复实现滚仓公式。
|
||||||
"""
|
"""
|
||||||
from lib.strategy.strategy_exchange_base import StrategyExchangeAdapter
|
from lib.strategy.strategy_exchange_base import StrategyExchangeAdapter
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""策略交易记录页:已结束趋势 / 顺势加仓快照(四所统一)。"""
|
"""策略交易记录页:已结束趋势 / 顺势加仓快照(三所统一)。"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""策略结束快照:趋势回调 / 顺势加仓(四所共用)。"""
|
"""策略结束快照:趋势回调 / 顺势加仓(三所共用)。"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ def compute_trend_plan_core(
|
|||||||
def calc_planned_reward_risk_ratio(
|
def calc_planned_reward_risk_ratio(
|
||||||
direction: str, entry_price: float, stop_loss: float, take_profit: float
|
direction: str, entry_price: float, stop_loss: float, take_profit: float
|
||||||
) -> Optional[float]:
|
) -> Optional[float]:
|
||||||
"""盈亏比(reward/risk),与四所 calc_rr_ratio 口径一致。"""
|
"""盈亏比(reward/risk),与三所 calc_rr_ratio 口径一致。"""
|
||||||
try:
|
try:
|
||||||
entry = float(entry_price)
|
entry = float(entry_price)
|
||||||
sl = float(stop_loss)
|
sl = float(stop_loss)
|
||||||
@@ -375,7 +375,7 @@ def trend_leg_grid_price(plan: dict, leg_idx: int) -> Optional[float]:
|
|||||||
|
|
||||||
def trend_leg_display_price(plan: dict, leg_idx: int) -> Optional[float]:
|
def trend_leg_display_price(plan: dict, leg_idx: int) -> Optional[float]:
|
||||||
"""
|
"""
|
||||||
四所统一:单档展示价 = leg_fill_prices_json 实际记录,否则计划网格(首仓用均价/参考价)。
|
三所统一:单档展示价 = leg_fill_prices_json 实际记录,否则计划网格(首仓用均价/参考价)。
|
||||||
禁止为凑均价反推虚构成交价。
|
禁止为凑均价反推虚构成交价。
|
||||||
"""
|
"""
|
||||||
p = plan or {}
|
p = plan or {}
|
||||||
@@ -398,7 +398,7 @@ def trend_leg_display_price(plan: dict, leg_idx: int) -> Optional[float]:
|
|||||||
|
|
||||||
|
|
||||||
def reconcile_trend_leg_fill_prices(plan: dict) -> list[float]:
|
def reconcile_trend_leg_fill_prices(plan: dict) -> list[float]:
|
||||||
"""首仓(0)+已补仓(1..legs_done) 展示价列表(四所共用 trend_leg_display_price)。"""
|
"""首仓(0)+已补仓(1..legs_done) 展示价列表(三所共用 trend_leg_display_price)。"""
|
||||||
p = plan or {}
|
p = plan or {}
|
||||||
if int(p.get("first_order_done") or 0) == 0:
|
if int(p.get("first_order_done") or 0) == 0:
|
||||||
return []
|
return []
|
||||||
@@ -563,7 +563,7 @@ def build_trend_preview_level_rows(preview: dict) -> tuple[dict, list[dict]]:
|
|||||||
|
|
||||||
def enrich_trend_dca_levels_with_tp(plan: dict, levels: list[dict]) -> list[dict]:
|
def enrich_trend_dca_levels_with_tp(plan: dict, levels: list[dict]) -> list[dict]:
|
||||||
"""
|
"""
|
||||||
四所统一补仓表 enrich(实例策略页 + 中控 monitor 共用)。
|
三所统一补仓表 enrich(实例策略页 + 中控 monitor 共用)。
|
||||||
触发价:实际成交价或计划网格;末档加仓后均价用持仓均价;禁止反推虚构成交价。
|
触发价:实际成交价或计划网格;末档加仓后均价用持仓均价;禁止反推虚构成交价。
|
||||||
"""
|
"""
|
||||||
if not levels:
|
if not levels:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""趋势回调:路由、轮询、页面数据(四所共用,依赖各 app 模块交易所能力)。"""
|
"""趋势回调:路由、轮询、页面数据(三所共用,依赖各 app 模块交易所能力)。"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
@@ -138,8 +138,8 @@ def summarize_trend_dca_probe(cfg: dict, row) -> dict:
|
|||||||
out["block_reason"] = "交易所无持仓"
|
out["block_reason"] = "交易所无持仓"
|
||||||
else:
|
else:
|
||||||
out["block_reason"] = (
|
out["block_reason"] = (
|
||||||
"标记价已触达,轮询应自动下单;若仍未补请确认 PM2 进程 crypto_gate_bot "
|
"标记价已触达,轮询应自动下单;若仍未补请确认 PM2 进程 crypto_gate "
|
||||||
"(非 manual-agent-gate-bot)在运行,并查看 pm2 logs crypto_gate_bot"
|
"(或对应所 Flask 进程)在运行,并查看 pm2 logs"
|
||||||
)
|
)
|
||||||
elif not reached:
|
elif not reached:
|
||||||
out["block_reason"] = f"标记价 {pf} 未触达下一档 {level}"
|
out["block_reason"] = f"标记价 {pf} 未触达下一档 {level}"
|
||||||
@@ -520,7 +520,7 @@ def _patch_hub_trend_views(app: Flask) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def patch_trend_hub_enrich(app: Flask, cfg: dict) -> None:
|
def patch_trend_hub_enrich(app: Flask, cfg: dict) -> None:
|
||||||
"""hub_bridge install 之后调用:四所 /api/hub/monitor 趋势字段与策略页一致。"""
|
"""hub_bridge install 之后调用:三所 /api/hub/monitor 趋势字段与策略页一致。"""
|
||||||
_patch_hub_monitor_enrich(app, cfg)
|
_patch_hub_monitor_enrich(app, cfg)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -77,9 +77,8 @@ def fetch_roll_page_data(
|
|||||||
|
|
||||||
|
|
||||||
DEFAULT_TREND_DISABLED_NOTE = (
|
DEFAULT_TREND_DISABLED_NOTE = (
|
||||||
"趋势回调(预览、自动补仓、程序止盈)仅在 Gate 趋势机器人实例 "
|
"趋势回调(预览、自动补仓、程序止盈)须在本实例 .env 设置 "
|
||||||
"(crypto_monitor_gate_bot,常见端口 5002)中启用。"
|
"`LIVE_TRADING_ENABLED=true` 并重启对应 PM2 进程(如 crypto_gate / crypto_okx / crypto_binance)。"
|
||||||
"币安 / Gate 主站 / OKX 可使用本页「顺势加仓」;完整趋势回调请打开该实例。"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""策略计划(趋势回调 / 滚仓)开始与结束 — 企业微信推送(四所共用)。"""
|
"""策略计划(趋势回调 / 滚仓)开始与结束 — 企业微信推送(三所共用)。"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<div class="box">
|
<div class="box">
|
||||||
<h1>趋势回调</h1>
|
<h1>趋势回调</h1>
|
||||||
<p>{{ trend_note }}</p>
|
<p>{{ trend_note }}</p>
|
||||||
<p style="color:#8892b0;font-size:.9rem">趋势回调含自动补仓档位,仅在 Gate 趋势机器人(crypto_monitor_gate_bot)实例中运行。</p>
|
<p style="color:#8892b0;font-size:.9rem">趋势回调含自动补仓档位,在三所实例(Binance / Gate / OKX)中均可启用,须配置 LIVE_TRADING_ENABLED=true。</p>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
<summary class="tip-collapse-summary">趋势回调说明(本实例未启用)</summary>
|
<summary class="tip-collapse-summary">趋势回调说明(本实例未启用)</summary>
|
||||||
<div class="tip-collapse-body rule-tip">
|
<div class="tip-collapse-body rule-tip">
|
||||||
{{ trend_disabled_note }}<br><br>
|
{{ trend_disabled_note }}<br><br>
|
||||||
趋势回调含自动补仓档位与预览执行,仅在 <strong>Gate 趋势机器人</strong>(<code>crypto_monitor_gate_bot</code>)实例中运行。
|
趋势回调含自动补仓档位与预览执行,在 <strong>Binance / Gate / OKX</strong> 各实例的「策略交易 → 趋势回调」中运行。
|
||||||
请访问该实例同一菜单「策略交易 → 趋势回调」,或常用地址 <code>:5002/strategy/trend</code>。
|
请访问对应实例同一菜单,或常用地址如 Gate <code>:5000/strategy/trend</code>。
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
<p style="margin-top:12px;font-size:.85rem">
|
<p style="margin-top:12px;font-size:.85rem">
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<strong>计划 #{{ p.plan_id }}</strong> 标记价 {{ p.mark_price }} 已触达补仓触发价 {{ p.next_trigger }},但未自动补仓:
|
<strong>计划 #{{ p.plan_id }}</strong> 标记价 {{ p.mark_price }} 已触达补仓触发价 {{ p.next_trigger }},但未自动补仓:
|
||||||
{{ p.block_reason }}。
|
{{ p.block_reason }}。
|
||||||
{% if not live_trading_enabled %}
|
{% if not live_trading_enabled %}
|
||||||
请在 <code>crypto_monitor_gate_bot/.env</code> 设置 <code>LIVE_TRADING_ENABLED=true</code> 后重启 PM2 进程 <strong>crypto_gate_bot</strong>(不是 manual-agent-gate-bot)。
|
请在当前实例 <code>.env</code> 设置 <code>LIVE_TRADING_ENABLED=true</code> 后重启对应 PM2 进程(如 <strong>crypto_gate</strong>、<strong>crypto_okx</strong>、<strong>crypto_binance</strong>)。
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""账户冷静期 / 日冻结风控(四所实例共用)。"""
|
"""账户冷静期 / 日冻结风控(三所实例共用)。"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""单日开仓次数:软提醒阈值 + 硬上限(四所实例共用)。"""
|
"""单日开仓次数:软提醒阈值 + 硬上限(三所实例共用)。"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
四所共用:计仓模式 risk(以损定仓)| full_margin(全仓杠杆)。
|
三所共用:计仓模式 risk(以损定仓)| full_margin(全仓杠杆)。
|
||||||
仅 env POSITION_SIZING_MODE 切换;须无持仓(由部署流程保证)。
|
仅 env POSITION_SIZING_MODE 切换;须无持仓(由部署流程保证)。
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""平仓交易:交易所口径双边成交额与手续费(四所共用聚合逻辑)。"""
|
"""平仓交易:交易所口径双边成交额与手续费(三所共用聚合逻辑)。"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any, Callable, Optional
|
from typing import Any, Callable, Optional
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ HUB_HOST=0.0.0.0
|
|||||||
HUB_PORT=5100
|
HUB_PORT=5100
|
||||||
# 仅本机访问可改为 127.0.0.1,并设 HUB_TRUST_LAN=false
|
# 仅本机访问可改为 127.0.0.1,并设 HUB_TRUST_LAN=false
|
||||||
|
|
||||||
# 与四实例 .env 中 HUB_BRIDGE_TOKEN 相同的长随机串
|
# 与三实例 .env 中 HUB_BRIDGE_TOKEN 相同的长随机串
|
||||||
# 中控 → 各 Flask:请求头 X-Hub-Token
|
# 中控 → 各 Flask:请求头 X-Hub-Token
|
||||||
# 中控 → 各子代理:请求头 X-Control-Token(可与子代理 CONTROL_TOKEN 同值,hub 会用 HUB_BRIDGE_TOKEN 转发)
|
# 中控 → 各子代理:请求头 X-Control-Token(可与子代理 CONTROL_TOKEN 同值,hub 会用 HUB_BRIDGE_TOKEN 转发)
|
||||||
# 中控「打开实例」SSO 链接也复用此令牌签名(默认 2 小时内有效、单次使用)
|
# 中控「打开实例」SSO 链接也复用此令牌签名(默认 2 小时内有效、单次使用)
|
||||||
@@ -41,10 +41,10 @@ HUB_PASSWORD=admin123
|
|||||||
# 限制可嵌入的父页来源(逗号分隔);默认 * 不限制
|
# 限制可嵌入的父页来源(逗号分隔);默认 * 不限制
|
||||||
# HUB_EMBED_ORIGINS=http://192.168.8.6:5070,https://hub.example.com
|
# HUB_EMBED_ORIGINS=http://192.168.8.6:5070,https://hub.example.com
|
||||||
|
|
||||||
# 四实例允许被中控 iframe 内嵌(各 crypto_monitor_*/.env,与 hub 同步部署)
|
# 三实例允许被中控 iframe 内嵌(各 crypto_monitor_*/.env,与 hub 同步部署)
|
||||||
# APP_ALLOW_HUB_EMBED=true
|
# APP_ALLOW_HUB_EMBED=true
|
||||||
# HUB_EMBED_PARENT_ORIGINS=https://hub.example.com
|
# HUB_EMBED_PARENT_ORIGINS=https://hub.example.com
|
||||||
# HTTPS 跨子域 iframe 时四实例还须 APP_COOKIE_SECURE=true(见 crypto_monitor_*/.env.example)
|
# HTTPS 跨子域 iframe 时三实例还须 APP_COOKIE_SECURE=true(见 crypto_monitor_*/.env.example)
|
||||||
|
|
||||||
# 浏览器打开的实例/复盘链接(hub_settings 里 flask_url 为 127.0.0.1 时替换为对外地址)
|
# 浏览器打开的实例/复盘链接(hub_settings 里 flask_url 为 127.0.0.1 时替换为对外地址)
|
||||||
# 局域网:填内网 IP,见《局域网与反代部署说明.md》
|
# 局域网:填内网 IP,见《局域网与反代部署说明.md》
|
||||||
@@ -53,7 +53,7 @@ HUB_PASSWORD=admin123
|
|||||||
# HUB_PUBLIC_HOST=192.168.1.100
|
# HUB_PUBLIC_HOST=192.168.1.100
|
||||||
# HUB_PUBLIC_SCHEME=http
|
# HUB_PUBLIC_SCHEME=http
|
||||||
|
|
||||||
# 四实例网页登录(直链反代/IP:端口 访问时输入;中控点「打开实例」免输)
|
# 三实例网页登录(直链反代/IP:端口 访问时输入;中控点「打开实例」免输)
|
||||||
# 各 crypto_monitor_*/.env 统一:APP_USERNAME=... APP_PASSWORD=...
|
# 各 crypto_monitor_*/.env 统一:APP_USERNAME=... APP_PASSWORD=...
|
||||||
|
|
||||||
# 监控区:hub 后台每 N 秒聚合一次,浏览器经 SSE 收版本号再拉快照(默认 5 秒)
|
# 监控区:hub 后台每 N 秒聚合一次,浏览器经 SSE 收版本号再拉快照(默认 5 秒)
|
||||||
@@ -82,7 +82,7 @@ HUB_PASSWORD=admin123
|
|||||||
# HOST=127.0.0.1
|
# HOST=127.0.0.1
|
||||||
|
|
||||||
# ---------- 中控 AI 教练(/ai,模块 hub_ai/,存 hub_ai_*.json)----------
|
# ---------- 中控 AI 教练(/ai,模块 hub_ai/,存 hub_ai_*.json)----------
|
||||||
# 与四实例相同变量名;默认 OpenAI 兼容网关(改 AI_PROVIDER=ollama 可走本机 Ollama)
|
# 与三实例相同变量名;默认 OpenAI 兼容网关(改 AI_PROVIDER=ollama 可走本机 Ollama)
|
||||||
# 详见 manual_trading_hub/AI教练说明.md 与仓库根 AI复盘与模型配置说明.md
|
# 详见 manual_trading_hub/AI教练说明.md 与仓库根 AI复盘与模型配置说明.md
|
||||||
AI_TIMEOUT_SECONDS=120
|
AI_TIMEOUT_SECONDS=120
|
||||||
# AI 教练聊天(默认:输出 8192 token、续写 4 次、快照约 2 万字符、历史单条 1500 字)
|
# AI 教练聊天(默认:输出 8192 token、续写 4 次、快照约 2 万字符、历史单条 1500 字)
|
||||||
@@ -102,7 +102,7 @@ OPENAI_MODEL=gemma4:e4b
|
|||||||
OLLAMA_API=http://127.0.0.1:11434/api/generate
|
OLLAMA_API=http://127.0.0.1:11434/api/generate
|
||||||
AI_MODEL=huihui_ai/deepseek-r1-abliterated:latest
|
AI_MODEL=huihui_ai/deepseek-r1-abliterated:latest
|
||||||
|
|
||||||
# 交易日切分(与四实例 TRADING_DAY_RESET_HOUR 一致,定义「今日总结」的日期)
|
# 交易日切分(与三实例 TRADING_DAY_RESET_HOUR 一致,定义「今日总结」的日期)
|
||||||
TRADING_DAY_RESET_HOUR=8
|
TRADING_DAY_RESET_HOUR=8
|
||||||
# 资金概况 / AI 上下文:分户资金快照保留交易日数(默认 180)
|
# 资金概况 / AI 上下文:分户资金快照保留交易日数(默认 180)
|
||||||
# HUB_FUND_HISTORY_DAYS=180
|
# HUB_FUND_HISTORY_DAYS=180
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
# 中控 AI 教练说明
|
# 中控 AI 教练说明
|
||||||
|
|
||||||
中控 **AI 教练**(`/ai`)与四实例 `/records` 里的 **AI 复盘** 分离:模块在 `manual_trading_hub/hub_ai/`,数据存同目录 JSON。
|
中控 **AI 教练**(`/ai`)与三实例 `/records` 里的 **AI 复盘** 分离:模块在 `manual_trading_hub/hub_ai/`,数据存同目录 JSON。
|
||||||
|
|
||||||
## 能力
|
## 能力
|
||||||
|
|
||||||
| 功能 | 说明 |
|
| 功能 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| **交易教练** | 口语化陪聊;注入四户监控快照与今日总结摘要(后台自动生成,不在页面展示) |
|
| **交易教练** | 口语化陪聊;注入三户监控快照与今日总结摘要(后台自动生成,不在页面展示) |
|
||||||
| **普通聊天** | 不绑交易数据,适合闲聊、答疑 |
|
| **普通聊天** | 不绑交易数据,适合闲聊、答疑 |
|
||||||
| **交易监管** | 今日长会话;手动/中控开平仓与新开仓自动推送 + 企业微信 + 可回聊(见 [交易监管说明.md](./交易监管说明.md)) |
|
| **交易监管** | 今日长会话;手动/中控开平仓与新开仓自动推送 + 企业微信 + 可回聊(见 [交易监管说明.md](./交易监管说明.md)) |
|
||||||
| **会话历史** | 右侧列表:切换、删除;消息一键复制 |
|
| **会话历史** | 右侧列表:切换、删除;消息一键复制 |
|
||||||
@@ -24,9 +24,9 @@
|
|||||||
|
|
||||||
## 模型配置
|
## 模型配置
|
||||||
|
|
||||||
在 **`manual_trading_hub/.env`** 配置,**变量名与四实例完全相同**;中控 `hub_ai/client.py` 共用仓库根 `ai_client.py`,**默认也是 OpenAI 兼容网关**(`AI_PROVIDER=openai`),与你在四所 `.env` 里配的那套一致即可。
|
在 **`manual_trading_hub/.env`** 配置,**变量名与三实例完全相同**;中控 `hub_ai/client.py` 共用仓库根 `ai_client.py`,**默认也是 OpenAI 兼容网关**(`AI_PROVIDER=openai`),与你在三所 `.env` 里配的那套一致即可。
|
||||||
|
|
||||||
**推荐(与四实例默认一致):**
|
**推荐(与三实例默认一致):**
|
||||||
|
|
||||||
```env
|
```env
|
||||||
AI_PROVIDER=openai
|
AI_PROVIDER=openai
|
||||||
@@ -50,7 +50,7 @@ AI_MODEL=huihui_ai/deepseek-r1-abliterated:latest
|
|||||||
中控通过 HTTP 拉取各实例:
|
中控通过 HTTP 拉取各实例:
|
||||||
|
|
||||||
- `GET /api/hub/monitor`(已有)
|
- `GET /api/hub/monitor`(已有)
|
||||||
- `GET /api/hub/trades/today?trading_day=YYYY-MM-DD`(`hub_bridge` 注册,需四实例更新代码并重启)
|
- `GET /api/hub/trades/today?trading_day=YYYY-MM-DD`(`hub_bridge` 注册,需三实例更新代码并重启)
|
||||||
|
|
||||||
子代理 `GET /status` 提供持仓与余额。
|
子代理 `GET /status` 提供持仓与余额。
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ AI_MODEL=huihui_ai/deepseek-r1-abliterated:latest
|
|||||||
| | 中控 AI 教练 | 实例 AI 复盘 |
|
| | 中控 AI 教练 | 实例 AI 复盘 |
|
||||||
|--|-------------|-------------|
|
|--|-------------|-------------|
|
||||||
| 入口 | `/ai` | 各所 `/records` |
|
| 入口 | `/ai` | 各所 `/records` |
|
||||||
| 数据 | 四户聚合 | 单户 `journal_entries` |
|
| 数据 | 三户聚合 | 单户 `journal_entries` |
|
||||||
| 语气 | 聊天搭档 | 结构化教练报告 |
|
| 语气 | 聊天搭档 | 结构化教练报告 |
|
||||||
| 代码 | `hub_ai/*` | `ai_review_lib` + 各 `app.py` |
|
| 代码 | `hub_ai/*` | `ai_review_lib` + 各 `app.py` |
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|------|------|
|
|------|------|
|
||||||
| 监控区 | 持仓、余额、关键位摘要、趋势计划、机器人单(只读) |
|
| 监控区 | 持仓、余额、关键位摘要、趋势计划、机器人单(只读) |
|
||||||
| 资金概况 | 总/分户资金(资金户+交易户)、180 日曲线、最大回撤 |
|
| 资金概况 | 总/分户资金(资金户+交易户)、180 日曲线、最大回撤 |
|
||||||
| **数据看板** | 四户当日总览/分户/平仓明细,SSE 推送(`/dashboard`;见 [数据看板说明.md](./数据看板说明.md)) |
|
| **数据看板** | 三户当日总览/分户/平仓明细,SSE 推送(`/dashboard`;见 [数据看板说明.md](./数据看板说明.md)) |
|
||||||
| 行情区 | K 线(多周期、本地缓存、技术指标、从监控跳转持仓线) |
|
| 行情区 | K 线(多周期、本地缓存、技术指标、从监控跳转持仓线) |
|
||||||
| **AI 教练** | 交易教练 + 普通聊天、会话历史(`/ai`;见 [AI教练说明.md](./AI教练说明.md)) |
|
| **AI 教练** | 交易教练 + 普通聊天、会话历史(`/ai`;见 [AI教练说明.md](./AI教练说明.md)) |
|
||||||
| 紧急全平 | 单户 / 全局市价减仓 |
|
| 紧急全平 | 单户 / 全局市价减仓 |
|
||||||
@@ -26,12 +26,12 @@
|
|||||||
|
|
||||||
```
|
```
|
||||||
浏览器 → hub.py (:5100) 监控 / 资金概况 / **数据看板** / 行情 / **AI 教练** / 设置 / 登录
|
浏览器 → hub.py (:5100) 监控 / 资金概况 / **数据看板** / 行情 / **AI 教练** / 设置 / 登录
|
||||||
├→ agent.py × N (:15200~15203) 持仓、全平
|
├→ agent.py × N (:15200~15202) 持仓、全平
|
||||||
└→ 各 Flask (:5000/5001/5002/5004) /api/hub/monitor 只读聚合
|
└→ 各 Flask (:5000/5001/5004) /api/hub/monitor 只读聚合
|
||||||
```
|
```
|
||||||
|
|
||||||
- 账户列表:**系统设置** 或默认 `settings_store.py`(不再使用环境变量 `HUB_AGENTS`)。
|
- 账户列表:**系统设置** 或默认 `settings_store.py`(不再使用环境变量 `HUB_AGENTS`)。
|
||||||
- 四实例须注册 **hub_bridge**(仓库根 `hub_bridge.py`);PM2 建议 `PYTHONPATH=..`。
|
- 三实例须注册 **hub_bridge**(仓库根 `hub_bridge.py`);PM2 建议 `PYTHONPATH=..`。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ pip install -r requirements.txt
|
|||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
# 编辑 .env:HUB_PASSWORD、HUB_BRIDGE_TOKEN、HUB_PUBLIC_ORIGIN 等
|
# 编辑 .env:HUB_PASSWORD、HUB_BRIDGE_TOKEN、HUB_PUBLIC_ORIGIN 等
|
||||||
|
|
||||||
pm2 start ecosystem.config.cjs # 4 agent + hub
|
pm2 start ecosystem.config.cjs # 3 agent + hub
|
||||||
pm2 save
|
pm2 save
|
||||||
|
|
||||||
bash scripts/verify_hub_deploy.sh
|
bash scripts/verify_hub_deploy.sh
|
||||||
@@ -60,7 +60,7 @@ curl -s http://127.0.0.1:5100/api/ping
|
|||||||
| 变量 | 说明 |
|
| 变量 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `HUB_PASSWORD` / `HUB_USERNAME` | 非空密码即启用登录 |
|
| `HUB_PASSWORD` / `HUB_USERNAME` | 非空密码即启用登录 |
|
||||||
| `HUB_BRIDGE_TOKEN` | 与四实例一致 |
|
| `HUB_BRIDGE_TOKEN` | 与三实例一致 |
|
||||||
| `HUB_DISABLED_IDS` | 默认 `1` 关闭 OKX |
|
| `HUB_DISABLED_IDS` | 默认 `1` 关闭 OKX |
|
||||||
| `HUB_PUBLIC_ORIGIN` | 其它设备打开复盘/实例外链(替换 127.0.0.1) |
|
| `HUB_PUBLIC_ORIGIN` | 其它设备打开复盘/实例外链(替换 127.0.0.1) |
|
||||||
| `HUB_COOKIE_SECURE` | HTTPS 反代建议 `true` |
|
| `HUB_COOKIE_SECURE` | HTTPS 反代建议 `true` |
|
||||||
@@ -71,14 +71,13 @@ curl -s http://127.0.0.1:5100/api/ping
|
|||||||
|
|
||||||
## 子代理(agent)
|
## 子代理(agent)
|
||||||
|
|
||||||
每所策略目录单独进程,`EXCHANGE` + `PORT`(15200~15203),密钥来自**该目录 `.env`**。PM2 经 `scripts/run_agent.sh` 启动(自动 `source .env`、去 CRLF)。
|
每所策略目录单独进程,`EXCHANGE` + `PORT`(15200~15202),密钥来自**该目录 `.env`**。PM2 经 `scripts/run_agent.sh` 启动(自动 `source .env`、去 CRLF)。
|
||||||
|
|
||||||
| PORT | 目录 |
|
| PORT | 目录 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| 15200 | crypto_monitor_binance |
|
| 15200 | crypto_monitor_binance |
|
||||||
| 15201 | crypto_monitor_okx |
|
| 15201 | crypto_monitor_okx |
|
||||||
| 15202 | crypto_monitor_gate |
|
| 15202 | crypto_monitor_gate |
|
||||||
| 15203 | crypto_monitor_gate_bot |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ cd /opt/crypto_monitor # 或你的仓库路径
|
|||||||
git fetch --tags
|
git fetch --tags
|
||||||
git checkout snapshot/pre-hub-market-20260528
|
git checkout snapshot/pre-hub-market-20260528
|
||||||
# 恢复后重启:
|
# 恢复后重启:
|
||||||
pm2 restart manual-trading-hub crypto_okx crypto_binance crypto_gate crypto_gate_bot
|
pm2 restart manual-trading-hub crypto_okx crypto_binance crypto_gate
|
||||||
```
|
```
|
||||||
|
|
||||||
回到最新主线:
|
回到最新主线:
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
"""
|
"""
|
||||||
子账户极轻代理:GET /status、挂单/条件单查询与撤销、POST /emergency/close-all、POST /emergency/close-position,仅监听 127.0.0.1。
|
子账户极轻代理:GET /status、挂单/条件单查询与撤销、POST /emergency/close-all、POST /emergency/close-position,仅监听 127.0.0.1。
|
||||||
|
|
||||||
与仓库内四个策略/监控目录一一对应时,典型用法(各目录自己的 .env 里已有密钥;子代理用环境变量 PORT,勿与 Flask 的 APP_PORT 相同):
|
与仓库内三个策略/监控目录一一对应时,典型用法(各目录自己的 .env 里已有密钥;子代理用环境变量 PORT,勿与 Flask 的 APP_PORT 相同):
|
||||||
EXCHANGE=binance → crypto_monitor_binance(BINANCE_*)
|
EXCHANGE=binance → crypto_monitor_binance(BINANCE_*)
|
||||||
EXCHANGE=okx → crypto_monitor_okx(OKX_*)
|
EXCHANGE=okx → crypto_monitor_okx(OKX_*)
|
||||||
EXCHANGE=gate → crypto_monitor_gate / crypto_monitor_gate_bot(GATE_*)
|
EXCHANGE=gate → crypto_monitor_gate(GATE_*)
|
||||||
|
|
||||||
环境变量:
|
环境变量:
|
||||||
EXCHANGE binance(默认)| okx | gate
|
EXCHANGE binance(默认)| okx | gate
|
||||||
PORT 默认 15200(与 crypto_monitor_* 的 Flask APP_PORT 错开;中控默认聚合 15200–15203)
|
PORT 默认 15200(与 crypto_monitor_* 的 Flask APP_PORT 错开;中控默认聚合 15200–15202)
|
||||||
HOST 默认 127.0.0.1
|
HOST 默认 127.0.0.1
|
||||||
CONTROL_TOKEN 可选;请求头 X-Control-Token
|
CONTROL_TOKEN 可选;请求头 X-Control-Token
|
||||||
|
|
||||||
@@ -392,7 +392,7 @@ def _position_price_fmt(ex: Any, symbol: str, price: float | None) -> tuple[floa
|
|||||||
|
|
||||||
|
|
||||||
def _position_entry_price(p: dict[str, Any]) -> float | None:
|
def _position_entry_price(p: dict[str, Any]) -> float | None:
|
||||||
"""四所 ccxt 持仓统一解析开仓均价(Binance/OKX/Gate 字段名不一致)。"""
|
"""三所 ccxt 持仓统一解析开仓均价(Binance/OKX/Gate 字段名不一致)。"""
|
||||||
return parse_position_entry_price(p)
|
return parse_position_entry_price(p)
|
||||||
|
|
||||||
|
|
||||||
@@ -406,7 +406,7 @@ def _position_contract_size(ex: Any, symbol: str) -> float:
|
|||||||
|
|
||||||
|
|
||||||
def _position_mark_price(p: dict[str, Any]) -> float | None:
|
def _position_mark_price(p: dict[str, Any]) -> float | None:
|
||||||
"""四所 ccxt 持仓统一解析标记价(与实例 parse_ccxt_position_metrics 一致)。"""
|
"""三所 ccxt 持仓统一解析标记价(与实例 parse_ccxt_position_metrics 一致)。"""
|
||||||
return parse_position_mark_price(p)
|
return parse_position_mark_price(p)
|
||||||
|
|
||||||
|
|
||||||
@@ -740,7 +740,7 @@ def place_tpsl_orders(
|
|||||||
body: PlaceTpslBody,
|
body: PlaceTpslBody,
|
||||||
x_control_token: str | None = Header(default=None, alias="X-Control-Token"),
|
x_control_token: str | None = Header(default=None, alias="X-Control-Token"),
|
||||||
):
|
):
|
||||||
"""先撤该合约全部条件单,再挂止盈+止损(与四实例策略逻辑一致)。"""
|
"""先撤该合约全部条件单,再挂止盈+止损(与三实例策略逻辑一致)。"""
|
||||||
_check_token(x_control_token)
|
_check_token(x_control_token)
|
||||||
sym = (body.symbol or "").strip()
|
sym = (body.symbol or "").strip()
|
||||||
side = (body.side or "").strip().lower()
|
side = (body.side or "").strip().lower()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* PM2:中控 hub + 四路子代理 agent(一次启动全部)
|
* PM2:中控 hub + 三路子代理 agent(一次启动全部)
|
||||||
*
|
*
|
||||||
* 前置:
|
* 前置:
|
||||||
* cd manual_trading_hub
|
* cd manual_trading_hub
|
||||||
@@ -49,7 +49,6 @@ module.exports = {
|
|||||||
agentApp("manual-agent-binance", "crypto_monitor_binance", "binance", 15200),
|
agentApp("manual-agent-binance", "crypto_monitor_binance", "binance", 15200),
|
||||||
agentApp("manual-agent-okx", "crypto_monitor_okx", "okx", 15201),
|
agentApp("manual-agent-okx", "crypto_monitor_okx", "okx", 15201),
|
||||||
agentApp("manual-agent-gate", "crypto_monitor_gate", "gate", 15202),
|
agentApp("manual-agent-gate", "crypto_monitor_gate", "gate", 15202),
|
||||||
agentApp("manual-agent-gate-bot", "crypto_monitor_gate_bot", "gate", 15203),
|
|
||||||
{
|
{
|
||||||
name: "manual-trading-hub",
|
name: "manual-trading-hub",
|
||||||
cwd: HUB_DIR,
|
cwd: HUB_DIR,
|
||||||
|
|||||||
@@ -805,7 +805,7 @@ def replace_position_tpsl(
|
|||||||
take_profit: float,
|
take_profit: float,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
先撤销该合约全部条件单,再挂止盈+止损。与四实例策略页逻辑对齐(读各目录 .env 中 GATE_/BINANCE_/OKX_ 参数)。
|
先撤销该合约全部条件单,再挂止盈+止损。与三实例策略页逻辑对齐(读各目录 .env 中 GATE_/BINANCE_/OKX_ 参数)。
|
||||||
"""
|
"""
|
||||||
kind = (exchange_kind or "binance").lower()
|
kind = (exchange_kind or "binance").lower()
|
||||||
direction = (direction or "long").strip().lower()
|
direction = (direction or "long").strip().lower()
|
||||||
|
|||||||
@@ -353,7 +353,7 @@ async def _run_archive_sync_once() -> dict:
|
|||||||
if st == 404:
|
if st == 404:
|
||||||
msg = (
|
msg = (
|
||||||
"HTTP 404:该 Flask 未注册 /api/hub/trades/archive。"
|
"HTTP 404:该 Flask 未注册 /api/hub/trades/archive。"
|
||||||
"请在仓库根目录 git pull 后 pm2 restart crypto_gate crypto_gate_bot"
|
"请在仓库根目录 git pull 后 pm2 restart crypto_gate"
|
||||||
)
|
)
|
||||||
results.append(
|
results.append(
|
||||||
{
|
{
|
||||||
@@ -619,7 +619,7 @@ _ACCOUNT_RISK_BADGE_JS = _REPO_STATIC / "account_risk_badge.js"
|
|||||||
|
|
||||||
@app.get("/assets/account_risk_badge.css")
|
@app.get("/assets/account_risk_badge.css")
|
||||||
def hub_account_risk_badge_css():
|
def hub_account_risk_badge_css():
|
||||||
"""与四所实例共用仓库根 static/account_risk_badge.css。"""
|
"""与三所实例共用仓库根 static/account_risk_badge.css。"""
|
||||||
if not _ACCOUNT_RISK_BADGE_CSS.is_file():
|
if not _ACCOUNT_RISK_BADGE_CSS.is_file():
|
||||||
raise HTTPException(status_code=404, detail="account_risk_badge.css not found")
|
raise HTTPException(status_code=404, detail="account_risk_badge.css not found")
|
||||||
return FileResponse(
|
return FileResponse(
|
||||||
@@ -630,7 +630,7 @@ def hub_account_risk_badge_css():
|
|||||||
|
|
||||||
@app.get("/assets/account_risk_badge.js")
|
@app.get("/assets/account_risk_badge.js")
|
||||||
def hub_account_risk_badge_js():
|
def hub_account_risk_badge_js():
|
||||||
"""与四所实例共用仓库根 static/account_risk_badge.js。"""
|
"""与三所实例共用仓库根 static/account_risk_badge.js。"""
|
||||||
if not _ACCOUNT_RISK_BADGE_JS.is_file():
|
if not _ACCOUNT_RISK_BADGE_JS.is_file():
|
||||||
raise HTTPException(status_code=404, detail="account_risk_badge.js not found")
|
raise HTTPException(status_code=404, detail="account_risk_badge.js not found")
|
||||||
return FileResponse(
|
return FileResponse(
|
||||||
@@ -641,7 +641,7 @@ def hub_account_risk_badge_js():
|
|||||||
|
|
||||||
@app.get("/assets/ai_review_render.js")
|
@app.get("/assets/ai_review_render.js")
|
||||||
def hub_ai_review_render_js():
|
def hub_ai_review_render_js():
|
||||||
"""与四所实例共用仓库根 static/ai_review_render.js(须在 /assets mount 之前注册)。"""
|
"""与三所实例共用仓库根 static/ai_review_render.js(须在 /assets mount 之前注册)。"""
|
||||||
if not _AI_REVIEW_RENDER_JS.is_file():
|
if not _AI_REVIEW_RENDER_JS.is_file():
|
||||||
raise HTTPException(status_code=404, detail="ai_review_render.js not found")
|
raise HTTPException(status_code=404, detail="ai_review_render.js not found")
|
||||||
return FileResponse(
|
return FileResponse(
|
||||||
@@ -1542,7 +1542,7 @@ def _flask_error_from_hub_mon(hub_mon: dict | None) -> str | None:
|
|||||||
if st == 404:
|
if st == 404:
|
||||||
return (
|
return (
|
||||||
"HTTP 404:该 Flask 未注册 /api/hub/*(hub_bridge 未加载)。"
|
"HTTP 404:该 Flask 未注册 /api/hub/*(hub_bridge 未加载)。"
|
||||||
"请在仓库根目录 git pull 后 pm2 restart crypto_binance crypto_gate crypto_gate_bot,"
|
"请在仓库根目录 git pull 后 pm2 restart crypto_binance crypto_gate,"
|
||||||
"并查看启动日志是否含 [hub_bridge] ImportError"
|
"并查看启动日志是否含 [hub_bridge] ImportError"
|
||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
@@ -2348,7 +2348,7 @@ async def api_close_position(exchange_id: str, body: ClosePositionBody):
|
|||||||
if out.get("ok"):
|
if out.get("ok"):
|
||||||
ex_key = (ex.get("key") or "").strip().lower()
|
ex_key = (ex.get("key") or "").strip().lower()
|
||||||
async with httpx.AsyncClient() as flask_client:
|
async with httpx.AsyncClient() as flask_client:
|
||||||
if ex_key in ("gate", "gate_bot"):
|
if ex_key == "gate":
|
||||||
order_sync = await _fetch_flask_json(
|
order_sync = await _fetch_flask_json(
|
||||||
flask_client,
|
flask_client,
|
||||||
ex,
|
ex,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""中控 AI:四户数据聚合为结构化上下文。"""
|
"""中控 AI:三户数据聚合为结构化上下文。"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""中控数据看板:四户当日总览(无 AI,纯数据聚合)。"""
|
"""中控数据看板:三户当日总览(无 AI,纯数据聚合)。"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# 检查四路子代理端口与 /status(在服务器上运行)
|
# 检查三路子代理端口与 /status(在服务器上运行)
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
check_one() {
|
check_one() {
|
||||||
@@ -29,7 +29,6 @@ check_one() {
|
|||||||
check_one "binance" 15200
|
check_one "binance" 15200
|
||||||
check_one "okx" 15201
|
check_one "okx" 15201
|
||||||
check_one "gate" 15202
|
check_one "gate" 15202
|
||||||
check_one "gate_bot" 15203
|
|
||||||
|
|
||||||
echo "PM2 状态:"
|
echo "PM2 状态:"
|
||||||
pm2 status 2>/dev/null | grep -E 'manual-agent|manual-trading' || true
|
pm2 status 2>/dev/null | grep -E 'manual-agent|manual-trading' || true
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ dirs=(
|
|||||||
"${REPO}/crypto_monitor_binance"
|
"${REPO}/crypto_monitor_binance"
|
||||||
"${REPO}/crypto_monitor_okx"
|
"${REPO}/crypto_monitor_okx"
|
||||||
"${REPO}/crypto_monitor_gate"
|
"${REPO}/crypto_monitor_gate"
|
||||||
"${REPO}/crypto_monitor_gate_bot"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fixed=0
|
fixed=0
|
||||||
@@ -30,5 +29,5 @@ for d in "${dirs[@]}"; do
|
|||||||
done
|
done
|
||||||
|
|
||||||
echo "完成,共修复 ${fixed} 个 .env。"
|
echo "完成,共修复 ${fixed} 个 .env。"
|
||||||
echo "请重启子代理: cd ${REPO}/manual_trading_hub && pm2 restart manual-agent-gate manual-agent-gate-bot manual-agent-binance manual-agent-okx"
|
echo "请重启子代理: cd ${REPO}/manual_trading_hub && pm2 restart manual-agent-gate manual-agent-binance manual-agent-okx"
|
||||||
echo "或: bash scripts/pm2_hub.sh restart"
|
echo "或: bash scripts/pm2_hub.sh restart"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ usage() {
|
|||||||
|
|
||||||
一般请用: bash scripts/pm2_hub.sh start (hub + agent 一起)
|
一般请用: bash scripts/pm2_hub.sh start (hub + agent 一起)
|
||||||
|
|
||||||
本脚本仅操作 4 路子代理(不含中控)
|
本脚本仅操作 3 路子代理(不含中控)
|
||||||
|
|
||||||
仅启动币安: pm2 start ecosystem.agents.config.cjs --only manual-agent-binance
|
仅启动币安: pm2 start ecosystem.agents.config.cjs --only manual-agent-binance
|
||||||
EOF
|
EOF
|
||||||
@@ -33,20 +33,20 @@ case "${cmd}" in
|
|||||||
pm2 save 2>/dev/null || true
|
pm2 save 2>/dev/null || true
|
||||||
;;
|
;;
|
||||||
stop)
|
stop)
|
||||||
pm2 stop manual-agent-binance manual-agent-okx manual-agent-gate manual-agent-gate-bot 2>/dev/null || true
|
pm2 stop manual-agent-binance manual-agent-okx manual-agent-gate 2>/dev/null || true
|
||||||
;;
|
;;
|
||||||
restart)
|
restart)
|
||||||
pm2 restart manual-agent-binance manual-agent-okx manual-agent-gate manual-agent-gate-bot 2>/dev/null \
|
pm2 restart manual-agent-binance manual-agent-okx manual-agent-gate 2>/dev/null \
|
||||||
|| pm2 start "${ECO}"
|
|| pm2 start "${ECO}"
|
||||||
;;
|
;;
|
||||||
status)
|
status)
|
||||||
pm2 status
|
pm2 status
|
||||||
;;
|
;;
|
||||||
logs)
|
logs)
|
||||||
pm2 logs manual-agent-binance manual-agent-okx manual-agent-gate manual-agent-gate-bot --lines 100
|
pm2 logs manual-agent-binance manual-agent-okx manual-agent-gate --lines 100
|
||||||
;;
|
;;
|
||||||
delete)
|
delete)
|
||||||
pm2 delete manual-agent-binance manual-agent-okx manual-agent-gate manual-agent-gate-bot 2>/dev/null || true
|
pm2 delete manual-agent-binance manual-agent-okx manual-agent-gate 2>/dev/null || true
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
usage
|
usage
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ PM2_NAMES=(
|
|||||||
manual-agent-binance
|
manual-agent-binance
|
||||||
manual-agent-okx
|
manual-agent-okx
|
||||||
manual-agent-gate
|
manual-agent-gate
|
||||||
manual-agent-gate-bot
|
|
||||||
manual-trading-hub
|
manual-trading-hub
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,7 +18,7 @@ usage() {
|
|||||||
cat <<'EOF'
|
cat <<'EOF'
|
||||||
用法: bash scripts/pm2_hub.sh <start|stop|restart|status|logs|delete>
|
用法: bash scripts/pm2_hub.sh <start|stop|restart|status|logs|delete>
|
||||||
|
|
||||||
start 启动 ecosystem.config.cjs(4 路子代理 + 中控,已存在则 restart 全部)
|
start 启动 ecosystem.config.cjs(3 路子代理 + 中控,已存在则 restart 全部)
|
||||||
stop 停止全部
|
stop 停止全部
|
||||||
restart 重启全部
|
restart 重启全部
|
||||||
status pm2 status
|
status pm2 status
|
||||||
|
|||||||
@@ -9,18 +9,15 @@ ECO="${HUB_DIR}/ecosystem.config.cjs"
|
|||||||
cd "${HUB_DIR}"
|
cd "${HUB_DIR}"
|
||||||
chmod +x scripts/run_agent.sh scripts/run_hub.sh 2>/dev/null || true
|
chmod +x scripts/run_agent.sh scripts/run_hub.sh 2>/dev/null || true
|
||||||
|
|
||||||
AGENTS=(manual-agent-binance manual-agent-okx manual-agent-gate manual-agent-gate-bot)
|
AGENTS=(manual-agent-binance manual-agent-okx manual-agent-gate)
|
||||||
|
|
||||||
for n in "${AGENTS[@]}"; do
|
for n in "${AGENTS[@]}"; do
|
||||||
pm2 delete "${n}" 2>/dev/null || true
|
pm2 delete "${n}" 2>/dev/null || true
|
||||||
done
|
done
|
||||||
|
|
||||||
pm2 start "${ECO}" --only manual-agent-binance
|
pm2 start "${ECO}" --only manual-agent-binance
|
||||||
|
pm2 start "${ECO}" --only manual-agent-okx
|
||||||
pm2 start "${ECO}" --only manual-agent-gate
|
pm2 start "${ECO}" --only manual-agent-gate
|
||||||
pm2 start "${ECO}" --only manual-agent-gate-bot
|
|
||||||
|
|
||||||
# OKX 若已 online 可跳过;若也挂了则:
|
|
||||||
# pm2 start "${ECO}" --only manual-agent-okx
|
|
||||||
|
|
||||||
pm2 save 2>/dev/null || true
|
pm2 save 2>/dev/null || true
|
||||||
echo "已重建 binance / gate / gate-bot 子代理,请执行: bash scripts/check_agents.sh"
|
echo "已重建 binance / okx / gate 子代理,请执行: bash scripts/check_agents.sh"
|
||||||
|
|||||||
@@ -39,4 +39,4 @@ bash scripts/verify_hub_deploy.sh
|
|||||||
| 文档 | 内容 |
|
| 文档 | 内容 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| [../部署文档.md](../部署文档.md) | 端口、反代、故障排查 |
|
| [../部署文档.md](../部署文档.md) | 端口、反代、故障排查 |
|
||||||
| [../../docs/ubuntu-server.md](../../docs/ubuntu-server.md) | Python / Node / PM2 版本与四所启动顺序 |
|
| [../../docs/ubuntu-server.md](../../docs/ubuntu-server.md) | Python / Node / PM2 版本与三所启动顺序 |
|
||||||
|
|||||||
@@ -54,23 +54,13 @@ DEFAULT_EXCHANGES = [
|
|||||||
{
|
{
|
||||||
"id": "2",
|
"id": "2",
|
||||||
"key": "gate",
|
"key": "gate",
|
||||||
"name": "Gate训练 · crypto_monitor_gate",
|
"name": "Gate · crypto_monitor_gate",
|
||||||
"agent_url": "http://127.0.0.1:15202",
|
"agent_url": "http://127.0.0.1:15202",
|
||||||
"flask_url": "http://127.0.0.1:5000",
|
"flask_url": "http://127.0.0.1:5000",
|
||||||
"review_url": "http://127.0.0.1:5000/records",
|
"review_url": "http://127.0.0.1:5000/records",
|
||||||
"enabled": True,
|
"enabled": True,
|
||||||
"capabilities": ["key", "trend"],
|
"capabilities": ["key", "trend"],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"id": "3",
|
|
||||||
"key": "gate_bot",
|
|
||||||
"name": "Gate趋势 · crypto_monitor_gate_bot",
|
|
||||||
"agent_url": "http://127.0.0.1:15203",
|
|
||||||
"flask_url": "http://127.0.0.1:5002",
|
|
||||||
"review_url": "http://127.0.0.1:5002/records",
|
|
||||||
"enabled": True,
|
|
||||||
"capabilities": ["key", "trend"],
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1980,7 +1980,7 @@ body.market-chart-fs-open {
|
|||||||
border-color: rgba(0, 255, 157, 0.38);
|
border-color: rgba(0, 255, 157, 0.38);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 趋势回调:与四所实例 strategy_trend_panel 同款卡片 */
|
/* 趋势回调:与三所实例 strategy_trend_panel 同款卡片 */
|
||||||
.hub-trend-running-title {
|
.hub-trend-running-title {
|
||||||
margin: 0 0 10px;
|
margin: 0 0 10px;
|
||||||
font-size: 0.95rem;
|
font-size: 0.95rem;
|
||||||
|
|||||||
@@ -609,7 +609,7 @@
|
|||||||
return Number(n).toLocaleString(undefined, { maximumFractionDigits: d });
|
return Number(n).toLocaleString(undefined, { maximumFractionDigits: d });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 交易所持仓开仓价(四所子代理 entry_price) */
|
/** 交易所持仓开仓价(三所子代理 entry_price) */
|
||||||
function positionEntryPrice(pos) {
|
function positionEntryPrice(pos) {
|
||||||
if (!pos) return null;
|
if (!pos) return null;
|
||||||
const n = Number(pos.entry_price);
|
const n = Number(pos.entry_price);
|
||||||
@@ -1302,7 +1302,7 @@
|
|||||||
const sub = el.querySelector(".board-loading-sub");
|
const sub = el.querySelector(".board-loading-sub");
|
||||||
if (sub) {
|
if (sub) {
|
||||||
sub.textContent =
|
sub.textContent =
|
||||||
"后台首次聚合较慢(四所子代理 + Flask)。可检查 PM2、或设 HUB_BOARD_KEY_PRICES=false 加速。";
|
"后台首次聚合较慢(三所子代理 + Flask)。可检查 PM2、或设 HUB_BOARD_KEY_PRICES=false 加速。";
|
||||||
}
|
}
|
||||||
}, 12000);
|
}, 12000);
|
||||||
}
|
}
|
||||||
@@ -2021,7 +2021,7 @@
|
|||||||
if (waitingFirst && showLoading) {
|
if (waitingFirst && showLoading) {
|
||||||
if (box) {
|
if (box) {
|
||||||
const sub = box.querySelector(".board-loading-sub");
|
const sub = box.querySelector(".board-loading-sub");
|
||||||
if (sub) sub.textContent = "后台正在首次聚合四所数据(约 5~15 秒)…";
|
if (sub) sub.textContent = "后台正在首次聚合三所数据(约 5~15 秒)…";
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -4530,7 +4530,7 @@
|
|||||||
? "普通聊天不注入交易快照;发消息后可点气泡下方「复制」。可粘贴截图或上传附件。"
|
? "普通聊天不注入交易快照;发消息后可点气泡下方「复制」。可粘贴截图或上传附件。"
|
||||||
: botMode === "supervisor"
|
: botMode === "supervisor"
|
||||||
? "今日监管为长会话:手动/中控开平仓与新开仓会自动推送;程序止盈止损会鼓励性提醒。可直接回复继续聊。"
|
? "今日监管为长会话:手动/中控开平仓与新开仓会自动推送;程序止盈止损会鼓励性提醒。可直接回复继续聊。"
|
||||||
: "交易教练会结合四户监控数据陪聊;发消息后可点气泡下方「复制」。可粘贴截图或点「附件」上传图片/文档。";
|
: "交易教练会结合三户监控数据陪聊;发消息后可点气泡下方「复制」。可粘贴截图或点「附件」上传图片/文档。";
|
||||||
box.innerHTML = `<p class="ai-placeholder">${hint}</p>`;
|
box.innerHTML = `<p class="ai-placeholder">${hint}</p>`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||