更新bot前端显示

This commit is contained in:
dekun
2026-05-18 20:10:44 +08:00
parent 8b89141b9f
commit 3a06b22142
3 changed files with 45 additions and 18 deletions
+15 -4
View File
@@ -67,6 +67,17 @@ def resolve_path(path_value):
app = Flask(__name__)
app.secret_key = os.getenv("FLASK_SECRET_KEY", "crypto_monitor_2026_secret_key")
def trend_add_zone_label(direction):
"""趋势回调:做多=补仓上沿,做空=补仓下沿(库字段仍为 add_upper)。"""
return "补仓上沿" if (direction or "long").strip().lower() == "long" else "补仓下沿"
@app.context_processor
def _inject_trend_ui_helpers():
return {"trend_add_zone_label": trend_add_zone_label}
# ====================== 登录配置 ======================
USERNAME = os.getenv("APP_USERNAME", "dekun")
PASSWORD = os.getenv("APP_PASSWORD", "Woaini88@")
@@ -2648,7 +2659,7 @@ def parse_and_compute_trend_pullback_plan(form_dict):
return None, "做多:止损价须低于补仓上沿"
else:
if not (stop_loss > add_upper):
return None, "做空:止损价须高于补仓沿"
return None, "做空:止损价须高于补仓沿"
snap = get_available_trading_usdt()
if snap is None or snap <= 0:
return None, "无法读取合约账户 USDT 可用余额,请检查 API 与账户类型"
@@ -2658,7 +2669,7 @@ def parse_and_compute_trend_pullback_plan(form_dict):
exchange_symbol = normalize_exchange_symbol(symbol)
rf = calc_risk_fraction(direction, add_upper, stop_loss)
if rf is None or rf <= 0:
return None, "止损与补仓上沿组合无法计算风险比例"
return None, "止损与补仓区间边界组合无法计算风险比例"
risk_budget = float(snap) * (risk_percent / 100.0)
notional = risk_budget / rf
margin_plan = notional / float(leverage)
@@ -2679,7 +2690,7 @@ def parse_and_compute_trend_pullback_plan(form_dict):
remainder_total = 0.0
n_legs, leg_json, per_ref = _trend_build_leg_amounts_json(exchange_symbol, remainder_total, TREND_PULLBACK_DCA_LEGS)
if n_legs <= 0:
return None, "剩余计划张数不足以拆出补仓档(低于交易所最小张数),请提高风险比例、放宽止损与补仓上沿间距,或减少补仓档数"
return None, "剩余计划张数不足以拆出补仓档(低于交易所最小张数),请提高风险比例、放宽止损与补仓区间间距,或减少补仓档数"
grid = _trend_build_grid_prices(direction, stop_loss, add_upper, n_legs)
if len(grid) != n_legs:
return None, "补仓网格生成失败"
@@ -4067,7 +4078,7 @@ def breakout_too_far(p, edge_price, limit_pct):
def _trend_build_grid_prices(direction, sl, upper, n_legs):
"""在 (止损, 补仓上沿) 开区间内生成 n_legs 个补仓触发价(不含端点)。"""
"""在 (止损, 补仓区间远侧边界 add_upper) 开区间内生成 n_legs 个补仓触发价(不含端点)。"""
sl, upper = float(sl), float(upper)
out = []
if n_legs <= 0:
+22 -7
View File
@@ -339,12 +339,12 @@
<h2 style="margin-bottom:8px">趋势回调策略</h2>
<div class="rule-tip">
<strong>生成预览</strong>:读取合约 USDT <strong>可用余额快照</strong>并计算计划(不下单)。预览有效期 <strong>{{ trend_pullback_preview_ttl }} 秒</strong><br>
<strong>确认执行</strong>:市价首仓 50% + 挂交易所止损;剩余 50% 在止损与补仓上沿之间共 {{ trend_pullback_dca_legs }} 档(程序可能因最小张数自动减档)市价补仓;<strong>止盈由程序监控</strong><br>
<strong>确认执行</strong>:市价首仓 50% + 挂交易所止损;剩余 50% 在止损与补仓区间之间共 {{ trend_pullback_dca_legs }} 档(做多为<strong>上沿</strong>、做空为<strong>下沿</strong>程序可能因最小张数自动减档)市价补仓;<strong>止盈由程序监控</strong><br>
确认执行时若当前可用余额与预览快照相对偏差 &gt; <strong>{{ trend_preview_max_drift_pct }}%</strong> 会拒绝并要求重新预览。
</div>
<form action="{{ url_for('preview_trend_pullback') }}" method="post" class="form-row">
<form id="trend-pullback-form" action="{{ url_for('preview_trend_pullback') }}" method="post" class="form-row">
<input name="symbol" placeholder="BTC 或 ETH/USDT" required>
<select name="direction" required>
<select name="direction" id="trend-direction" required>
<option value="">方向</option>
<option value="long">做多</option>
<option value="short">做空</option>
@@ -352,10 +352,25 @@
<input name="leverage" type="number" min="1" step="1" placeholder="杠杆(必填)" required>
<input name="risk_percent" type="number" min="0.1" step="0.1" value="5" placeholder="风险%相对可用快照" title="默认5:最坏亏损约≤可用余额×5%">
<input name="sl" step="any" placeholder="止损价" required>
<input name="add_upper" step="any" placeholder="补仓上沿价" required>
<input name="add_upper" id="trend-add-upper" step="any" placeholder="补仓上沿价" required>
<input name="take_profit" step="any" placeholder="止盈价(固定)" required>
<button type="submit" {% if not can_trade %}disabled style="opacity:.5;cursor:not-allowed"{% endif %}>生成预览</button>
</form>
<script>
(function(){
const dirSel = document.getElementById("trend-direction");
const addInp = document.getElementById("trend-add-upper");
function syncAddUpperPlaceholder(){
if(!addInp || !dirSel) return;
const d = (dirSel.value || "long").toLowerCase();
addInp.placeholder = d === "short" ? "补仓下沿价" : "补仓上沿价";
}
if(dirSel){
dirSel.addEventListener("change", syncAddUpperPlaceholder);
syncAddUpperPlaceholder();
}
})();
</script>
{% if trend_preview %}
<div style="margin-top:14px;padding:12px;background:#141a2e;border:1px solid #2a3150;border-radius:8px">
@@ -367,7 +382,7 @@
{{ trend_preview.symbol }} {{ '做多' if trend_preview.direction == 'long' else '做空' }} {{ trend_preview.leverage }}x
预览可用快照 <strong>{{ money_fmt(trend_preview.snapshot_available_usdt) }}</strong> U 参考价 {{ price_fmt(trend_preview.symbol, trend_preview.live_price_ref) }}
计划保证金≈{{ money_fmt(trend_preview.plan_margin_capital) }} U 总张≈{{ amt_fmt(trend_preview.symbol, trend_preview.target_order_amount) }}(首仓 {{ amt_fmt(trend_preview.symbol, trend_preview.first_order_amount) }} + 补仓 {{ amt_fmt(trend_preview.symbol, trend_preview.remainder_total) }}<br>
止损 {{ price_fmt(trend_preview.symbol, trend_preview.stop_loss) }} 补仓上沿 {{ price_fmt(trend_preview.symbol, trend_preview.add_upper) }} 止盈 {{ price_fmt(trend_preview.symbol, trend_preview.take_profit) }} 风险比例 {{ trend_preview.risk_percent }}%
止损 {{ price_fmt(trend_preview.symbol, trend_preview.stop_loss) }} {{ trend_add_zone_label(trend_preview.direction) }} {{ price_fmt(trend_preview.symbol, trend_preview.add_upper) }} 止盈 {{ price_fmt(trend_preview.symbol, trend_preview.take_profit) }} 风险比例 {{ trend_preview.risk_percent }}%
</div>
<div class="table-wrap" style="margin-bottom:10px">
<table>
@@ -442,7 +457,7 @@
</div>
<div class="plan-card-meta">
来源: 趋势回调计划 风险: {% if t.risk_percent is not none %}{{ t.risk_percent }}%{% else %}—{% endif %}
<span class="accent">补仓上沿 {{ price_fmt(sym, t.add_upper) }}</span>
<span class="accent">{{ trend_add_zone_label(t.direction) }} {{ price_fmt(sym, t.add_upper) }}</span>
| 已补仓 <strong>{{ t.legs_done }}/{{ t.dca_legs }}</strong>
</div>
<div class="plan-card-grid">
@@ -713,7 +728,7 @@ function openPreviewSnapshotDetail(id){
`总张数:${s.target_order_amount != null ? s.target_order_amount : "-"}`,
`首仓/补仓余:${s.first_order_amount != null ? s.first_order_amount : "-"} / ${s.remainder_total != null ? s.remainder_total : "-"}`,
`补仓档数:${s.dca_legs != null ? s.dca_legs : "-"}`,
`止损 / 补仓上沿 / 止盈:${s.stop_loss} / ${s.add_upper} / ${s.take_profit}`,
`止损 / ${s.direction === "long" ? "补仓上沿" : "补仓下沿"} / 止盈:${s.stop_loss} / ${s.add_upper} / ${s.take_profit}`,
`风险%${s.risk_percent != null ? s.risk_percent : "-"}`,
`网格价 JSON${s.grid_prices_json || "[]"}`,
`分档张数 JSON${s.leg_amounts_json || "[]"}`,
@@ -7,7 +7,7 @@
## 1. 适用场景
- 单独用于跑策略的 **Gate.io USDT 永续** 子账户(建议与主资金隔离)。
- 你已明确:**方向、止损价、补仓上沿、止盈价、杠杆**,并接受程序按风险预算拆分 **首仓 50% + 多档补仓 50%**
- 你已明确:**方向、止损价、补仓区间边界价、止盈价、杠杆**,并接受程序按风险预算拆分 **首仓 50% + 多档补仓 50%**
---
@@ -16,9 +16,9 @@
| 名称 | 含义 |
|------|------|
| **合约 USDT 可用余额** | **生成预览**时通过 API 读取的 **swap 账户 USDT `free`** 快照;**确认执行**时再次读取并与快照比对偏差。 |
| **风险比例** | 默认 **5%**:指「若整笔计划在 **补仓上沿** 这一侧的最坏价格结构下触及止损」,目标亏损上限约为 **可用余额快照 × 风险比例**(实现上用 `calc_risk_fraction``prepare_order_amount` 反推总张数,受交易所最小张数与精度约束)。 |
| **风险比例** | 默认 **5%**:指「若整笔计划在 **补仓区间远侧边界**(做多=上沿、做空=下沿)这一侧的最坏价格结构下触及止损」,目标亏损上限约为 **可用余额快照 × 风险比例**(实现上用 `calc_risk_fraction``prepare_order_amount` 反推总张数,受交易所最小张数与精度约束)。 |
| **止损价** | 用户填写;开仓后挂 **交易所仓位类止损触发单**(全平)。 |
| **补仓上沿** | 用户填写;**仅在该价位与止损价构成的区间内** 才允许程序触发剩余 50% 的市价补仓(做多:`止损 < 补仓上沿`;做空`止损 > 补仓上沿`。 |
| **补仓区间边界**(库字段 `add_upper` | 用户填写;**仅在该价位与止损价构成的区间内** 才允许程序触发剩余 50% 的市价补仓。**界面文案**:做多显示「补仓上沿」,做空显示「补仓下沿」。校验:做多 `止损 < 边界价`;做空 `止损 > 边界价`。 |
| **止盈价** | 用户填写的 **固定价格**;**不由交易所条件止盈单触发**,由应用后台 **按标记价/行情价轮询**,达到后 **市价全平**。 |
| **杠杆** | 计划内固定写入;用于 `set_leverage` 与名义换算。 |
| **补仓档位数** | 默认 **5** 档(环境变量 `TREND_PULLBACK_DCA_LEGS` 可调);程序在满足最小张数前提下可能 **自动减少档数**。 |
@@ -32,7 +32,7 @@
1. **风控**:与「机器人下单监控」**互斥**——存在活跃机器人持仓或运行中趋势计划时,不可生成预览。
2. **读取可用余额快照** `get_available_trading_usdt()`,失败则拒绝。
3. **计算**(写入表 `trend_pullback_previews`,并跳转带 `preview_id`):
-**补仓上沿 ↔ 止损** 区间内生成 `N` 个补仓触发价;
-**补仓区间边界 ↔ 止损** 区间内生成 `N` 个补仓触发价(做多从上沿向止损、做空从下沿向止损)
-**剩余 50% 计划张数** 拆成 `N` 份写入 `leg_amounts_json`
4. **预览有效期**:默认 **120 秒**`TREND_PULLBACK_PREVIEW_TTL_SECONDS`),超时须重新点「生成预览」。
@@ -41,7 +41,7 @@
5. 再次校验:预览未过期;**当前可用余额**与预览快照相对偏差 ≤ `TREND_PREVIEW_MAX_BALANCE_DRIFT_PCT`(默认 **5%**),否则拒绝执行并要求重新预览。
6. **首仓****立即市价** 开立 **总计划张数 × 50%**(不附带交易所止盈单)。
7. **止损**:撤销旧条件单后,挂 **仅止损** 的仓位触发单;之后每次补仓成交会 **刷新** 止损挂单。
8. **补仓**:当价格 **穿越** 下一档触发价(做多为自上向下穿越)时,按该档张数 **市价加仓**;直至 `N` 档执行完毕或计划结束。
8. **补仓**:当价格 **穿越** 下一档触发价(做多为自上向下穿越,做空为自下向上穿越)时,按该档张数 **市价加仓**;直至 `N` 档执行完毕或计划结束。
9. **止盈监控**:后台线程若发现价格触及止盈,则 **市价全平**
10. **止损触发**:若仓位被交易所止损打光,本地检测到 **持仓为 0** 后记账为 **止损** 并结束计划。
11. **计划结束**:任一结束路径(止盈 / 止损 / 用户手动结束)均会 **撤单**(条件单 + 普通挂单,尽力而为)。
@@ -57,7 +57,8 @@
- **不包含**仅存在于 `trend_pullback_previews`、从未「确认执行」的预览。
- 每行提供 **删除**:删除该计划行,并删除 `trade_records`**`trend_plan_id` 与之相同** 且类型为「趋势回调」的记录(用于与计划一一对应的新数据;历史旧行若无 `trend_plan_id` 则不会随删)。
- **运行中的计划(交易执行页)**
- 在计划摘要下方展示 **浮盈亏(交易所)**:来自 Gate 当前持仓接口的 **未实现盈亏**(及标记价,若可得);与本地按均价估算可能略有差异,以交易所为准便于对照。
- 在计划摘要下方展示 **浮盈亏(交易所)**:来自 Gate 当前持仓接口的 **未实现盈亏**(及标记价,若可得);与本地按均价估算可能略有差异,以交易所为准便于对照。
- **补仓边界**按方向显示「补仓上沿」或「补仓下沿」(数值仍为 `add_upper` 字段)。
### 3.5 交易记录与交易所「已实现盈亏」对齐
@@ -109,7 +110,7 @@
## 7. 数据库
- **`trend_pullback_previews`**:未执行的预览行(含 `expires_at_ms`),执行成功或取消后删除;过期可被清理。
- **`trend_pullback_plans`**:趋势回调计划。执行后写入一行,`status='active'` 表示运行中;止盈 / 止损 / 手动结束后变为 **`stopped_tp` / `stopped_sl` / `stopped_manual`** 等非 `active` 状态,并出现在页顶 **计划历史**。字段含快照可用余额、计划保证金、总张数、首仓张数、补仓 JSON、网格价 JSON、已补仓档数、均价、`opened_at``message`(结束说明)等。
- **`trend_pullback_plans`**:趋势回调计划。执行后写入一行,`status='active'` 表示运行中;止盈 / 止损 / 手动结束后变为 **`stopped_tp` / `stopped_sl` / `stopped_manual`** 等非 `active` 状态,并出现在页顶 **计划历史**。字段含快照可用余额、计划保证金、总张数、首仓张数、补仓 JSON、网格价 JSON、已补仓档数、均价、`opened_at``message`(结束说明)等**`add_upper`** 存补仓区间远侧边界价(做多=上沿、做空=下沿)
- **`trade_records`**`monitor_type=趋势回调`):每次计划结束插入一行;含本地估算盈亏等。新写入行带 **`trend_plan_id`** 指向 `trend_pullback_plans.id`。另含 **`exchange_realized_pnl``exchange_opened_at``exchange_closed_at``exchange_sync_key`**,由页面触发的交易所平仓历史同步填充(见 3.5)。
**CSV 导出**:交易记录导出为 **v3**,包含上述交易所对齐字段及 `trend_plan_id`