首次上传

This commit is contained in:
dekun
2026-05-16 22:25:48 +08:00
commit 2b8f902548
88 changed files with 16386 additions and 0 deletions
+218
View File
@@ -0,0 +1,218 @@
# Gate 下单执行器 · 使用说明
本文说明 `gate_order_executor` 的职责、配置项、Web 面板与 HTTP 接口。与 **部署安装** 相关的步骤见 [部署说明.md](./部署说明.md)。
## 1. 系统定位
-`onchain_scout_gate`MATRIX 扫描)**分进程**运行:扫描只用 Gate **公共**行情;本服务接收信号并可在 **`gate.dry_run: false`** 且配置密钥时 **向 Gate 发真实委托**
- **`gate.dry_run: true`(默认)**:只校验占位与日志,不下单。
- 不建议把 Gate API Key 放在扫描服务的配置里;密钥仅配置在本项目的 `config.yaml`(本机、权限收紧)。
## 2. 目录结构(常用)
| 路径 | 说明 |
|------|------|
| `config.yaml` | 本地配置(勿提交 git;由 `config.example.yaml` 复制) |
| `run.py` | 启动入口(uvicorn 读取 `app.host` / `app.port` |
| `app/main.py` | FastAPI:健康检查、登录、面板、`POST /v1/signal``GET /api/state``GET /api/stats/summary``POST /api/test`、**Gate 成交/委托查询与 CSV**`GET /api/gate/*` |
| `app/gate_history.py` | 拉取 Gate **`my_trades_timerange`** / **`orders`**,拼 CSVUTF-8 BOM);分页原始行 `collect_trades_rows` |
| `app/stats.py` | 面板统计:正式起点后历史平仓(`position_close`)的日/周/月聚合 |
| `app/gate_operations.py` | 拉取账户余额、**测试市价接口**(`post_test_market_order`)、列出/撤销计划委托、**非零持仓摘要**(`GET /futures/{settle}/positions` |
| `app/gate_futures_live.py` | 实盘:签名请求、市价 IOC 开仓、计划委托止盈/止损、以损订仓 |
| `app/oco_watcher.py` | 净持仓为 0 后撤掉本次止盈/止损计划单另一腿(见 §3.4.2) |
| `app/gate_auth.py` | Gate APIv4 请求签名 |
| `app/proxy_util.py` | 与扫描相同的代理处理;`httpx_client_kwargs` 供访问 Gate 时使用 |
| `deploy/ecosystem.config.cjs` | PM2 配置 |
| `deploy/bootstrap.sh` | 首次创建 venv 与依赖 |
| `deploy/pm2-start.sh` | PM2 启动/已存在则重启 |
## 3. 配置说明(`config.yaml`
### 3.1 `app`
| 字段 | 说明 |
|------|------|
| `host` | 监听地址。仅本机访问面板/接口用 `127.0.0.1`;局域网或 SSH 隧道外访问用 `0.0.0.0`(请配合防火墙与 `auth`)。 |
| `port` | 默认 `8090`,避免与扫描默认 `8088` 冲突。 |
| `log_file` | 应用日志路径(目录会自动创建)。 |
| `session_secret` | Cookie 会话密钥,请改为长随机串。 |
### 3.2 `auth`
| 字段 | 说明 |
|------|------|
| `enabled` | `false` 时跳过登录(仅建议纯局域网)。公网或不可信网络务必 `true`。 |
| `username` / `password` | 面板登录;密码以 SHA256 摘要校验(与扫描面板思路一致)。 |
### 3.3 `security`
| 字段 | 说明 |
|------|------|
| `webhook_secret` | 调用 `POST /v1/signal``POST /v1/test` 时请求头 `X-Webhook-Secret` 必须与此一致。 |
### 3.4 `gate`
| 字段 | 说明 |
|------|------|
| `api_base` / `settle` | 与扫描端 Gate 公共 API 一致即可。 |
| `api_key` / `api_secret` | 实盘必填(建议子账户、只开合约、IP 白名单)。 |
| `dry_run` | `true` 不下单;**实盘改为 `false`** 且密钥有效时,收到信号将 **市价开仓** 并挂 **计划委托** 止盈/止损。 |
| `test_orders_enabled` | 默认 `false`。为 `true` 时允许通过 `POST /api/test``POST /v1/test``action: micro_market` 发送 **真实** 极小 IOC 市价单(**仅接口联调**,见 §4.1);务必使用子账户、小额、`test_max_contracts` 限制张数。 |
| `test_max_contracts` | 与测试请求里的张数取 **最小值**,上限 30,默认 1。 |
### 3.4.1 实盘逻辑摘要(`app/gate_futures_live.py`
- **开仓**`POST /futures/usdt/orders``price=0``tif=ioc`,做多 `size` 为正、做空为负;`text``t-e` + 清洗后的 `signal_id`
- **止盈 / 止损**:各 `POST /futures/usdt/price_orders``initial``reduce_only` + 市价 IOC + **`close: true`** + **`size: 0`**Gate 要求:单向全平时 `close=true``initial.size` 必须为 0,否则会报 `AUTO_INVALID_PARAM_INITIAL_SIZE`),`trigger` 规则与方向匹配(多:TP 用 rule≥、SL 用 rule≤;空相反)。
- **一腿成交后撤另一腿**:见 **§3.4.2**;默认 **`risk.oco_cleanup_enabled: true`** 时由 **`oco_watcher`**(约每 18s)在净持仓为 0 后 `DELETE` 本次两条 `price_orders` 中仍挂着的一腿。
- **以损订仓**:用 `GET /futures/usdt/accounts``total`USDT)× `risk.risk_per_trade_frac`,再除以 `|参考价−止损|×quanto_multiplier``quanto_multiplier` 来自合约详情)。若低于 `order_size_min` 则拒绝该笔信号。
- **参考价**:优先 `reference_price`;否则用合约 ticker `last`
- **持仓上限**:每次信号前拉取 `GET /futures/usdt/positions`,与本地占位同步;交易所已有 **≥ max_open_positions** 个非零持仓则跳过;**该合约在交易所已有仓**也跳过(避免重复加仓)。
- **假设**:经典 **单向持仓** 模式。若为双向对冲模式,Gate 对平仓字段要求不同,本实现可能需调整(`auto_size` 等);请在子账户上先以单向模式验证。
### 3.4.2 Gate 与「交易所原生 OCO」
- **官方能力**USDT 永续 v4 的 `POST /futures/{settle}/price_orders` 在公开文档中是 **「一条请求 = 一条计划委托」**;**没有**与「网页止盈+止损一体、撮合层保证互撤」等价的、**写进 OpenAPI 且稳定**的单请求 OCO。文档里部分 `order_type`(如 `close-long-order`)存在 **read-only、不可在请求体传入** 的说明,**不能**当作已支持的双绑参数来用。
- **现货**侧 `POST /spot/orders``take_profit`/`stop_loss` 等字段,与 **永续计划委托** 不是同一套接口,不能照搬。
- **本仓库策略**:默认开启 **`oco_cleanup`**(应用侧在持仓清空后撤另一腿),**效果上接近 OCO**,代价是 **秒级延迟**、以及进程/网络异常时的理论竞态;这是在不依赖非官方 `text` 编码技巧的前提下较稳妥的做法。若你关闭 `oco_cleanup_enabled`,则完全依赖交易所对 `reduce_only` 计划单的处理,**常见现象是另一腿仍以 `open` 挂在计划列表**,需自行在网页撤单。
### 3.5 `risk`
| 字段 | 说明 |
|------|------|
| `risk_per_trade_frac` | 以损订仓目标比例(如 `0.005` = 0.5%),用于按止损距离换算张数。 |
| `max_open_positions` | 同时占位/持仓品种上限(默认 5)。 |
| `scheme` | 固定为方案 **A**(与推送文案「入场区间 A」一致)。 |
| `oco_cleanup_enabled` | 默认 `true`:净持仓为 0 后由 **`oco_watcher`** 尝试 `DELETE` 本次挂出的两条计划单中仍有效的一腿(见 §3.4.2)。`false` 则不做应用侧撤单。 |
### 3.6 `stats`(面板「统计」正式口径)
| 字段 | 说明 |
|------|------|
| `timezone` | 统计用 IANA 时区,默认 `Asia/Shanghai`。 |
| `official_start` | **正式统计起点**ISO8601**必须带时区**,如 `2026-05-13T02:00:00+08:00`)。仅统计 Gate `position_close` 返回里 **`time`(平仓时间)不早于此** 的记录。 |
| `max_trade_rows` | 从 `position_close` 分页拉取的最大条数(默认 20000,上限 100000;配置键名沿用 `max_trade_rows`);超过则可能 `truncated: true`,序列型指标(如回撤)在截断下不完整。 |
**时间桶(均按 `timezone` 本地理解)**
- **统计日**`[日历 D 日 08:00, D+1 日 08:00)`。标签 **D**`(本地时刻 8h)` 的日历日(与 08:00 换日对齐)。
- **自然周**:周一至周日(上海日历);聚合 **统计日标签**落在该周内的历史平仓记录。
- **自然月**`[当月 1 日 08:00, 次月 1 日 08:00)`,与统计日对齐。
**统计单元**`GET /futures/{settle}/position_close` 每条历史平仓记录(与 App「历史仓位」同类);**`pnl`(或 `realised_pnl`)可解析**的才计入笔数与盈亏序列,按 **`time`(平仓时间)** 排序。**胜率** = 盈利笔数 / 总笔数(含盈亏为 0);**盈亏比** = 毛利和 / \|毛亏和\|(无亏损时为 `null`);**最大单笔亏损** = 最小 `pnl`**最大回撤** = 按时间累加 `pnl` 的权益曲线相对历史峰值的 `max(peak equity)`**最大连续亏损次数** = 连续 `pnl < 0` 的最大长度。
面板 **「统计」** 分区调用 `GET /api/stats/summary`(需登录),**不会**随 `/api/state` 每 2 秒刷新,需手动点「刷新统计」。
### 3.7 `proxy`
`onchain_scout_gate``proxy` **块写法一致**
- `enabled: true` 且填写 `url`(如 `socks5h://127.0.0.1:1080`)时,使用 `httpx_client_kwargs` 的出站逻辑与扫描端 Gate 客户端相同(`socks5h` 会转为 `socks5` 以兼容 httpx)。
- 使用 **SOCKS** 时需安装 `socksio``pip install socksio``bootstrap.sh` 会尝试安装)。
### 3.8 `wecom`(企业微信群机器人 · 执行结果)
- **定位**:仅推送本执行器侧 **执行结果**(每条 `POST /v1/signal` 处理摘要、面板一键平仓成败、OCO 撤另一腿失败、SQLite 落库失败)。**策略/发现类**仍由 `onchain_scout_gate` 扫描端发企业微信。
- **配置**`enabled: true` 且填写 `webhook_url`(群机器人 Webhook 完整 URL)。走与 Gate 相同的 **proxy** 出站策略(若启用)。
- **关闭**`enabled: false` 或留空 `webhook_url` 即不发送。
## 4. Web 面板
- **必须用运行中的服务访问**,在浏览器地址栏输入 `http://<host>:<port>/dashboard`(例如本机 `http://127.0.0.1:8090/dashboard`)。不要双击或用「打开文件」方式直接打开 `templates/dashboard.html`:那样是 `file://` 协议,**不会**经过 FastAPI、**不会**加载 `/static/style.css`,页面会变成未样式的白底裸 HTML,模板里的 `{{ username }}` 等也不会被渲染。
- 开启 `auth.enabled` 时先访问 `/login`,使用 JSON 方式提交账号密码。
- 面板为顶部分区:**概览**、**持仓与计划**、**成交与委托**、**统计**、**信号流**(`POST /v1/signal` 写入 SQLite,默认 `database.sqlite_path`,配置留空时亦回退为该路径;面板与 `GET /api/signals/export.csv` 读库,**进程重启后记录仍在**`/api/state` 返回 `signals_persisted` / `signals_sqlite_path` 供前端展示持久化状态)。
- 前端约 **每 2 秒** 轮询 `GET /api/state`,已配置密钥时会附带拉取账户、**持仓**、计划委托。
- **联调(拉取余额 / 极小测试市价)不再放在面板**,请在服务器用 **`curl`** 或自写脚本调用 `POST /api/test``POST /v1/test`,见下文 **§4.1** 与 [部署说明.md](./部署说明.md) **§11**。
### 4.1 用 curl 联调 `POST /api/test` 与 `POST /v1/test`
以下端口、账号、密钥请替换为你的 `config.yaml` 实际值;路径以本机 `127.0.0.1:8090` 为例。
**鉴权说明**
- `POST /api/test`:需 **面板登录 Cookie**`auth.enabled: true` 时先登录再带 `-b` cookie 文件)。
- `POST /v1/test`:仅需请求头 **`X-Webhook-Secret`**,与 `security.webhook_secret` 一致,**无需 Cookie**(适合 SSH 在服务器上一条命令联调)。
**1)若开启 `auth.enabled`,先登录保存 Cookie**
```bash
curl -s -c /tmp/gate_exec_cookies.txt -X POST "http://127.0.0.1:8090/login" \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"你的面板密码"}'
```
`auth.enabled: false`,可跳过登录,下面 `/api/test``-b` 可省略。
**2)仅读合约账户(balance**
```bash
curl -s -b /tmp/gate_exec_cookies.txt -X POST "http://127.0.0.1:8090/api/test" \
-H "Content-Type: application/json" \
-d '{"action":"balance"}'
```
**3)极小真实 IOC 市价(micro_market**
须同时满足:`gate.test_orders_enabled: true`,且已配置 `api_key` / `api_secret`。张数与 `test_max_contracts` 取小。
```bash
curl -s -b /tmp/gate_exec_cookies.txt -X POST "http://127.0.0.1:8090/api/test" \
-H "Content-Type: application/json" \
-d '{"action":"micro_market","contract":"BTC_USDT","side":"long","size":1}'
```
**4)无 Cookie:用 Webhook 密钥调同一套逻辑(`/v1/test`)**
```bash
curl -s -X POST "http://127.0.0.1:8090/v1/test" \
-H "Content-Type: application/json" \
-H "X-Webhook-Secret: 与_config.yaml_security.webhook_secret_一致" \
-d '{"action":"balance"}'
```
`micro_market` 同理,把 `body` 换成与上一步相同的 JSON 即可。
**HTTP 状态**`balance` 且 Gate 账户接口失败时一般为 **502**`micro_market``{"ok":false}` 时一般为 **400**
**Pythonhttpx)示例(`/v1/test`**
```python
import httpx
r = httpx.post(
"http://127.0.0.1:8090/v1/test",
headers={"X-Webhook-Secret": "your-secret"},
json={"action": "balance"},
timeout=30.0,
)
print(r.status_code, r.text)
```
## 5. HTTP 接口摘要
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/health` | 健康检查;无需登录。 |
| GET | `/dashboard` | 面板页面。 |
| GET | `/api/state` | JSON 状态;未登录且开启 auth 时返回 401。已配密钥时含 `futures_account` / `futures_account_error``open_price_orders`(最多 50 条 open 计划委托摘要)/ `open_price_orders_error``positions.open_slot_count`(本地占位)、`positions.exchange`(非零持仓摘要列表)、`positions.exchange_error`。 |
| GET | `/api/stats/summary` | 需登录且已配密钥。Query`contract`(可选,大写合约如 `BTC_USDT`)。返回当前 **统计日 / 自然周 / 自然月**(见 §3.6)内正式起点后的历史平仓 `pnl` 聚合;`ok: false` 时见 `error`。拉取可能较慢,勿高频轮询。 |
| GET | `/api/gate/trades` | 需登录。查询成交:Gate `GET /futures/{settle}/my_trades_timerange`。Query`contract`(可选)、`from` / `to`(Unix 秒,均可省略则默认最近 7 天至此刻)、`limit`1500)、`offset`。返回 `rows``error`(接口异常时非空)。 |
| GET | `/api/gate/trades.csv` | 需登录。同上时间范围,分页拉取至最多 `max` 行(默认 2000,上限 5000),返回 CSV 附件 `gate_futures_trades.csv`。 |
| GET | `/api/gate/orders_history` | 需登录。Gate `GET /futures/{settle}/orders`。Query`status=open|finished`(默认 `finished`)、`contract`(可选)、`limit``offset`。 |
| GET | `/api/gate/orders_history.csv` | 需登录。委托导出 CSV`status``contract` 同上,`max` 同成交导出。 |
| POST | `/api/positions/market_close` | 需登录。JSON`{"contract":"BTC_USDT"}`。实盘且非 `dry_run` 时向 Gate 发 **市价 IOC + reduce_only** 平掉该合约净持仓;成功后释放本地占位槽。`dry_run` / 无仓 / 无密钥 时 400Gate 错误 502。 |
| POST | `/api/price_orders/manual` | 需登录。JSON`contract``trigger_price`(字符串数字)、`rule`1 或 2,与 Gate `price_orders` 触发规则一致)。挂一条 **全平** 条件计划单(`close`+`size`0 形态)。`dry_run` 时 400。 |
| POST | `/api/price_orders/cancel` | 需登录 Cookie。JSON`{"order_id":"2054233581303107584"}`(与面板列表一致)。调用 Gate `DELETE .../price_orders/{id}`;失败时 400。 |
| POST | `/api/test` | 需登录 Cookie。JSON`{"action":"balance"}` 仅读余额;`{"action":"micro_market","contract":"BTC_USDT","side":"long"|"short","size":1}` 发极小 IOC 市价(需 `test_orders_enabled`)。HTTP`balance` 且 API 错误时 502`micro_market``ok:false` 时 400。 |
| POST | `/v1/test` | 与 `/api/test` 相同 JSON;鉴权为请求头 `X-Webhook-Secret`(与 `security.webhook_secret` 一致),无需 Cookie。 |
| POST | `/v1/signal` | 扫描端推送信号;请求头 `X-Webhook-Secret`JSON 体见项目根目录 `README.md` 表格。 |
## 6. 与扫描服务协作
`onchain_scout_gate` 判定需要自动执行时,由扫描机 **内网** `httpx` 请求本服务 `POST /v1/signal`(例如 `http://127.0.0.1:8090/v1/signal`)。**不要**解析企业微信文本做下单。
## 7. 日志与排错
- PM2 标准输出/错误:`runtime/pm2-executor-out.log``runtime/pm2-executor-error.log`
-`config.yaml` 后需 **重启进程**`bash deploy/pm2-restart.sh``pm2 restart gate-order-executor`
- 若面板无法访问:检查 `app.host`/`app.port`、本机防火墙、以及是否与扫描端口冲突。
+189
View File
@@ -0,0 +1,189 @@
# Gate 下单执行器 · 部署说明
本文面向 **Ubuntu / Debian** 等 Linux 服务器,说明从零安装、PM2 守护、可选 systemd 开机自启,以及与 `onchain_scout_gate` 同机部署时的注意点。**功能与配置项含义**见 [使用说明.md](./使用说明.md)。
## 1. 环境要求
- Python 3.10+(推荐 3.11
- 可访问公网拉取 PyPI(或自备镜像)
- 使用 PM2`Node.js` + `npm install -g pm2`
- 若使用 **SOCKS** 代理访问 Gate`pip install socksio``bootstrap.sh` 会尝试安装)
## 2. 上传代码
将仓库目录放到服务器,例如:
```text
/root/gate_order_executor/
```
下文以该路径为例;若你的目录不同,请替换所有命令中的路径,并修改 `deploy/gate-order-executor.service` 里的 `WorkingDirectory`
## 3. 首次安装(bootstrap
```bash
cd /root/gate_order_executor
chmod +x deploy/*.sh
bash deploy/bootstrap.sh /root/gate_order_executor
```
脚本会:
- 创建 `.venv` 并安装 `requirements.txt`
- 尝试安装 `socksio`(失败可忽略,按需手动安装)
- 若不存在 `config.yaml`,从 `config.example.yaml` 复制一份
- 创建 `runtime/` 目录
然后 **必须** 编辑 `config.yaml`
- `app.session_secret``security.webhook_secret`
- `auth`(若对外暴露建议 `enabled: true` 并改密码)
- `proxy`:本机走 SOCKS 时 `enabled: true`**云服务器能直连 `api.gateio.ws` 时设为 `false`**(见下文 §6.1
- 需要远端访问面板时,将 `app.host` 设为 `0.0.0.0`,并限制防火墙来源 IP
## 4. PM2 启动与维护
### 4.1 启动(推荐脚本)
```bash
cd /root/gate_order_executor
bash deploy/pm2-start.sh
```
若进程已在 PM2 列表中,脚本会执行 **`pm2 restart`** 而非重复 `start`
### 4.2 常用命令
```bash
pm2 logs gate-order-executor # 实时日志
pm2 status # 状态列表
bash deploy/pm2-restart.sh # 改配置后重启
bash deploy/pm2-stop.sh # 停止
bash deploy/pm2-delete.sh # 从 PM2 删除该应用
pm2 save # 保存进程列表(配合开机自启)
```
### 4.3 等价手动命令
```bash
cd /root/gate_order_executor
mkdir -p runtime
pm2 start deploy/ecosystem.config.cjs
pm2 save
```
`ecosystem.config.cjs` 使用项目内 `.venv/bin/python` 执行 `run.py`,日志写入 `runtime/pm2-executor-*.log`,并设置 `PYTHONUNBUFFERED=1`
## 5. 前台调试(无 PM2
```bash
cd /root/gate_order_executor
bash deploy/start.sh /root/gate_order_executor
```
用于排查问题;生产环境请用 PM2。
## 6. 与扫描服务同机部署
典型端口:
- 扫描:`8088`(以 `onchain_scout_gate/config.yaml` 为准)
- 执行器:`8090`(以本仓库 `config.yaml` 为准);多账户可再起 `8091`
两者使用 **不同 PM2 应用名**`onchain-scout``gate-order-executor`),互不影响。
**多执行器:** 由扫描端 Web 面板维护转发列表(`runtime/order_executors.json`),同一信号广播到多个 URL;本仓库 **不** 提供向扫描端「反向注册」。详见 `onchain_scout_gate/docs/多执行器与信号转发归档.md`
扫描端向本机执行器发信号示例:
```text
POST http://127.0.0.1:8090/v1/signal
Header: X-Webhook-Secret: <与扫描端面板及本机 security.webhook_secret 一致>
```
### 6.1 云服务器关闭代理
境外云主机通常 **无需** SOCKS。在 **本仓库** `config.yaml` 中:
```yaml
proxy:
enabled: false
```
保存后 `pm2 restart gate-order-executor`。自检:`curl -I --max-time 15 https://api.gateio.ws`
扫描端 `onchain_scout_gate``proxy` 也需同样关闭(仅影响其拉行情,不影响 POST 信号)。见 `onchain_scout_gate/交易系统部署说明.md` §8。
## 7. 可选:systemd + pm2-runtime 开机自启
适合希望 **不依赖 `pm2 startup` 脚本**、由 systemd 直接拉起 PM2 托管进程的场景。
1. 编辑 `deploy/gate-order-executor.service`:将 `WorkingDirectory=` 改为你的项目绝对路径;确认 `ExecStart``pm2-runtime` 路径(`which pm2-runtime`)。
2. 安装单元:
```bash
sudo cp /root/gate_order_executor/deploy/gate-order-executor.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable gate-order-executor
sudo systemctl start gate-order-executor
sudo systemctl status gate-order-executor
```
注意:若同时使用 **交互式 `pm2 start`****systemd pm2-runtime**,容易重复启动;二选一即可。
## 8. 防火墙与安全
- 仅本机扫描调用信号时:可将 `app.host` 保持 `127.0.0.1`,则外网无法直连 HTTP。
- 若需浏览器远程看面板:使用 `0.0.0.0` + 防火墙白名单 + **务必开启 `auth`**,并配合 HTTPS 反向代理(Nginx/Caddy)更佳。
- `config.yaml` 含 API Key 与 Webhook 密钥,权限建议:`chmod 600 config.yaml`
## 9. 升级代码后
```bash
cd /root/gate_order_executor
git pull # 或上传新文件
source .venv/bin/activate
pip install -r requirements.txt
bash deploy/pm2-restart.sh
```
## 10. 故障速查
| 现象 | 可能原因 |
|------|----------|
| `TypeError: unhashable type: 'dict'`Jinja 加载模板) | Starlette ≥0.29 起 `TemplateResponse` 须为 `TemplateResponse(request, "x.html", {...})`,勿把 `request` 放在第一个位置。请拉取最新 `app/main.py` 后重启。 |
| PM2 反复重启 | `config.yaml` 校验失败、端口被占用、依赖缺失;看 `pm2 logs``runtime/pm2-executor-error.log` |
| 面板打不开 | `host``127.0.0.1` 却从外网访问;或防火墙未放行 `port` |
| SOCKS 代理失败 | 未安装 `socksio`;或代理地址/协议错误 |
| 401 on `/v1/signal` | `X-Webhook-Secret` 与配置不一致 |
## 11. 联调测试(curl / 无面板)
面板 **不提供**「拉取余额 / 测试市价」按钮;联调请在本机或 SSH 到服务器后用 **`curl`**(或脚本调用 `httpx`)请求 **`POST /api/test`**、**`POST /v1/test`**。详细参数、Cookie 登录、`micro_market` 条件见 [使用说明.md](./使用说明.md) **§4.1**。
**快速示例(已关闭 `auth` 或已另行登录拿到 Cookie 时省略 `-b`**
```bash
# 仅读合约账户(需 Cookie 时加:-b /tmp/gate_exec_cookies.txt
curl -s -X POST "http://127.0.0.1:8090/api/test" \
-H "Content-Type: application/json" \
-d '{"action":"balance"}'
```
```bash
# 无 Cookie,用 Webhook 密钥(与 security.webhook_secret 一致)
curl -s -X POST "http://127.0.0.1:8090/v1/test" \
-H "Content-Type: application/json" \
-H "X-Webhook-Secret: YOUR_WEBHOOK_SECRET" \
-d '{"action":"balance"}'
```
```bash
# 极小真实 IOC(须 gate.test_orders_enabled: true;建议仍用子账户)
curl -s -X POST "http://127.0.0.1:8090/v1/test" \
-H "Content-Type: application/json" \
-H "X-Webhook-Secret: YOUR_WEBHOOK_SECRET" \
-d '{"action":"micro_market","contract":"BTC_USDT","side":"long","size":1}'
```
`config.yaml` 后执行 **`bash deploy/pm2-restart.sh`** 再测。