diff --git a/docs/strategy/README.md b/docs/strategy/README.md
index 236c059..9ec66a2 100644
--- a/docs/strategy/README.md
+++ b/docs/strategy/README.md
@@ -12,8 +12,7 @@
- **不写盈亏比(趋势户)**:币安/OKX 止盈/止损随行情人工设定,趋势 MD 不量化 RR。
- **日内例外**:Gate 日内 **最低 1:1** 才开仓,持仓目标可动态调整(见 [gate-intraday.md](./gate-intraday.md))。
-- **界面短标签(趋势户 · 现状 v0.2)**:`大分歧A` / `大分歧B` / `小分歧`。
-- **界面规划(趋势户 · v0.4 讨论稿)**:两级 — **反转**(启动 A/B)| **顺势**(大分歧 A/B)| **波段**(小分歧)。做多反转细则见 [binance-alt-trend-long.md §3](./binance-alt-trend-long.md);OKX 做空见 [okx-trend-both.md §3.2](./okx-trend-both.md)。
+- **界面(趋势户 · v0.4)**:两级 — **反转**(启动 A/B)| **顺势**(大分歧 A/B)| **波段**(小分歧);`lib/trade/entry_model_lib.py` 三所共用。
- **界面短标签(日内户)**:`假破` / `结构突破`。
- **多空共用三字**:方向由「做多/做空」表达,不复用为「大分歧A多」等。
- **自动联动**:大分歧 A/B → 趋势单;**小分歧 → 波段单**;平仓写入交易记录 `entry_reason`;复盘「填入」自动带入。
diff --git a/docs/strategy/binance-alt-trend-long.md b/docs/strategy/binance-alt-trend-long.md
index 52168b2..bd3072d 100644
--- a/docs/strategy/binance-alt-trend-long.md
+++ b/docs/strategy/binance-alt-trend-long.md
@@ -1,6 +1,6 @@
# 币安山寨·多头趋势账户
-> **状态**:v0.4(讨论稿:反转·启动 A/B 策略正文;**UI 仍为 v0.2 三档**,两级下拉待实现)
+> **状态**:v0.4(反转·启动 A/B 两级 UI 已实现;策略正文 + `entry_model_lib`)
---
@@ -16,33 +16,19 @@
---
-## 2. 开仓类型(规划与现状)
+## 2. 开仓类型(两级 UI · 三所共用)
-### 2.1 规划:两级选择(讨论定稿,待实现)
+### 2.1 趋势户:反转 / 顺势 / 波段
-| 第一级 | 第二级 | 性质 | 计划 code |
-|--------|--------|------|-----------|
-| **反转** | 启动 A / 启动 B | 跌后筑底 → 赌新主升 | `launch_a` / `launch_b` |
-| **顺势** | 大分歧 A / 大分歧 B | 主升已确立 → 回踩/中继 | `big_div_a` / `big_div_b` |
-| **波段** | 小分歧 | 主升途中短打 | `small_div` |
+| 第一级 | 第二级 | code | 联动 |
+|--------|--------|------|------|
+| **反转** | 启动 A / 启动 B | `launch_a` / `launch_b` | 趋势单 |
+| **顺势** | 大分歧 A / 大分歧 B | `big_div_a` / `big_div_b` | 趋势单 |
+| **波段** | 小分歧 | `small_div` | 波段单 |
-- 第一级表达 **交易性质**;第二级表达 **突破前 / 实体突破**(小分歧无 A/B)。
-- **启动 A** 含两条实操路径(界面仍一个选项,复盘可备注 A1/A2):
- - **A1**:第二次摸高前的小收敛企稳;
- - **A2**:启动 B 实体突破失败回箱后,箱子上半区 + 5m 不创新低 + 5m N 字试仓(**不单列类型**)。
-- **小分歧** 前提:主升**已确立**;反转链进行中**不做**小分歧。
+实现:`lib/trade/entry_model_lib.py` + `order_entry_model_fields.html` + `order_entry_model.js`(币安 / OKX / Gate 趋势户共用)。
-### 2.2 现状:下单监控 UI(v0.2 已实现)
-
-| 界面标签 | 存储 code | 联动 |
-|----------|-----------|------|
-| 大分歧A | `big_div_a` | 趋势单 |
-| 大分歧B | `big_div_b` | 趋势单 |
-| 小分歧 | `small_div` | 波段单 |
-
-实现反转·启动前,顺势单仍选大分歧 A/B;反转单复盘可先在「其他」或备注中写 **启动 A/B**,待 UI 上线后写入 `entry_model`。
-
-### 2.3 杠杆(与开仓类型无关)
+### 2.2 杠杆(与开仓类型无关)
| 币种 | 默认杠杆 |
|------|----------|
@@ -193,8 +179,7 @@ MACD 背离(严格筛选)→ 标「参考高点」
| 操作 | 字段 |
|------|------|
-| 下单(现状) | `order_monitors.entry_model`:`big_div_a` / `big_div_b` / `small_div` |
-| 下单(规划) | 增加 `launch_a` / `launch_b`;两级 UI |
+| 下单 | `order_monitors.entry_model`:`launch_a` / `launch_b` / `big_div_a` / `big_div_b` / `small_div` |
| 平仓 | `trade_records.entry_reason` = 界面标签 |
| 复盘 | 与开仓类型一致 + 策略项 +「其他」 |
@@ -204,6 +189,6 @@ MACD 背离(严格筛选)→ 标「参考高点」
| 版本 | 日期 | 说明 |
|------|------|------|
-| v0.4 | 2026-07-06 | 讨论稿:反转·启动 A/B(含 A1 收敛、A2 突破失败后 5m N);两级 UI 规划 |
+| v0.4 | 2026-07-06 | 反转·启动 A/B 两级 UI 上线(共用 entry_model_lib) |
| v0.2 | 2026-07-06 | 定稿 UI 短标签(大分歧/小分歧);实现代码联动 |
| v0.1 | 2026-07-06 | 讨论稿 |
diff --git a/docs/strategy/checklists/binance.json b/docs/strategy/checklists/binance.json
index 79770f1..f3c6683 100644
--- a/docs/strategy/checklists/binance.json
+++ b/docs/strategy/checklists/binance.json
@@ -8,7 +8,7 @@
"items": [
"本账户仅做多,不做空",
"计仓模式为以损定仓(risk),关键位自动单已关闭",
- "已明确第一级:反转 / 顺势 / 波段(UI 待上线时复盘可先备注)"
+ "已明确第一级:反转 / 顺势 / 波段(两级下拉已上线)"
]
},
{
@@ -63,6 +63,6 @@
}
],
"footnotes": [
- "v0.4:反转·启动为讨论定稿;下单 UI 仍为 v0.2 三档时,反转单请在复盘备注启动 A/B。"
+ "v0.4:两级 UI 已实现;启动 A 含 A1 收敛与 A2(B 失败后 5m N 字)。"
]
}
diff --git a/docs/strategy/checklists/okx.json b/docs/strategy/checklists/okx.json
index b6d58ba..9058001 100644
--- a/docs/strategy/checklists/okx.json
+++ b/docs/strategy/checklists/okx.json
@@ -10,7 +10,7 @@
"趋势户 profile(非 Gate 日内 BTC/ETH 白名单)",
"计仓模式为以损定仓(risk),关键位自动单已关闭",
"同一币种无未计划的对冲叠仓",
- "已明确第一级:反转 / 顺势 / 波段(UI 待上线时复盘可先备注)"
+ "已明确第一级:反转 / 顺势 / 波段(两级下拉已上线)"
]
},
{
@@ -67,6 +67,6 @@
}
],
"footnotes": [
- "v0.4:反转·启动为讨论定稿;做多细则见 binance-alt-trend-long.md §3。UI 仍为 v0.2 三档时,反转单请在复盘备注启动 A/B。"
+ "v0.4:两级 UI 反转/顺势/波段;做多细则见 binance-alt-trend-long.md §3。"
]
}
diff --git a/docs/strategy/okx-trend-both.md b/docs/strategy/okx-trend-both.md
index a5476eb..c1579c1 100644
--- a/docs/strategy/okx-trend-both.md
+++ b/docs/strategy/okx-trend-both.md
@@ -22,26 +22,19 @@
## 2. 开仓类型(规划与现状)
-### 2.1 规划:两级选择(讨论定稿,待实现)
+### 2.1 规划:两级选择(与币安共用实现)
| 第一级 | 第二级 | 做多 | 做空 |
|--------|--------|------|------|
-| **反转** | 启动 A / 启动 B | 跌后筑底 → 新主升 | 涨后筑顶 → 新主跌 |
-| **顺势** | 大分歧 A / B | 主升已确立 | 主跌已确立 |
-| **波段** | 小分歧 | 主升途中短打 | 主跌途中短打 |
+| **反转** | 启动 A / 启动 B | `launch_a` / `launch_b` | 同 code,方向在表单 |
+| **顺势** | 大分歧 A / B | `big_div_a` / `big_div_b` | 同左 |
+| **波段** | 小分歧 | `small_div` | 同左 |
-计划 code:`launch_a` / `launch_b` / `big_div_a` / `big_div_b` / `small_div`(与币安一致)。
+`lib/trade/entry_model_lib.py` + 共用模板/JS,三所趋势户一致。
-- **启动 A**:含 **A1**(第二次摸参考极值前小收敛)与 **A2**(启动 B 实体突破失败回箱后的 5m N 字试仓);**5m 试仓不单列**,统一记启动 A。
-- **小分歧**:仅在大级别趋势**已确立**后;反转链进行中**不做**小分歧。
+### 2.2 现状:下单监控 UI
-### 2.2 现状:下单监控 UI(v0.2 已实现)
-
-| 界面标签 | code | 联动 |
-|----------|------|------|
-| 大分歧A / B、小分歧 | `big_div_a` 等 | 趋势单 / 波段单 |
-
-**方向**由表单「做多/做空」决定;**单型**由下拉决定。反转单在 UI 上线前,复盘可备注 **启动 A/B**。
+两级下拉:**性质** → **类型**;提交 `entry_model` code。
### 2.3 杠杆
diff --git a/lib/common/static/instance_page.css b/lib/common/static/instance_page.css
index 28a7859..384540d 100644
--- a/lib/common/static/instance_page.css
+++ b/lib/common/static/instance_page.css
@@ -1,4 +1,7 @@
.order-trade-style-hint{font-size:.78rem;color:#8fc8ff;margin-left:4px;white-space:nowrap}
+.order-entry-model-row{display:flex;flex-wrap:wrap;align-items:center;gap:6px}
+.order-entry-model-row select.order-entry-category{min-width:4.8em;max-width:6.5em}
+.order-entry-model-row select.order-entry-model-sub{min-width:7em;max-width:10rem}
.order-leverage-hint{font-size:.78rem;color:#cfd3ef;white-space:nowrap;align-self:center}
body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;background:#0b0d14;color:#eaeaea;padding:14px 20px}
.container{width:100%;max-width:min(1440px,94vw);margin:0 auto;padding:0 clamp(8px,1.5vw,20px)}
diff --git a/lib/common/static/order_entry_model.js b/lib/common/static/order_entry_model.js
index 4e66ec1..6bfe2d3 100644
--- a/lib/common/static/order_entry_model.js
+++ b/lib/common/static/order_entry_model.js
@@ -1,38 +1,125 @@
(function (global) {
- function tradeStyleForSelect(sel) {
- if (!sel || !sel.value) return "trend";
- var opt = sel.options[sel.selectedIndex];
- if (opt && opt.getAttribute) {
- var ds = opt.getAttribute("data-trade-style");
- if (ds === "swing" || ds === "trend") return ds;
- }
+ function categoriesData() {
+ return global.ORDER_ENTRY_MODEL_CATEGORIES || [];
+ }
+
+ function codeToCategoryMap() {
+ return global.ORDER_ENTRY_MODEL_CODE_TO_CATEGORY || {};
+ }
+
+ function tradeStyleForCode(code) {
var map = global.ORDER_ENTRY_MODEL_TRADE_STYLE || {};
- return map[sel.value] || "trend";
+ return map[code] || "trend";
+ }
+
+ function findOption(catKey, code) {
+ var cats = categoriesData();
+ for (var i = 0; i < cats.length; i++) {
+ if (cats[i].key !== catKey) continue;
+ var opts = cats[i].options || [];
+ for (var j = 0; j < opts.length; j++) {
+ if (opts[j].code === code) return opts[j];
+ }
+ }
+ return null;
+ }
+
+ function rebuildEntryModelSubSelect(preserveCode) {
+ var catSel = document.getElementById("order-entry-category");
+ var modelSel = document.getElementById("order-entry-model");
+ if (!catSel || !modelSel) return;
+
+ var catKey = catSel.value;
+ var cats = categoriesData();
+ var cat = null;
+ for (var i = 0; i < cats.length; i++) {
+ if (cats[i].key === catKey) {
+ cat = cats[i];
+ break;
+ }
+ }
+
+ modelSel.innerHTML = "";
+ var placeholder = document.createElement("option");
+ placeholder.value = "";
+ placeholder.textContent = "类型";
+ modelSel.appendChild(placeholder);
+
+ if (!cat || !cat.options || !cat.options.length) {
+ modelSel.disabled = true;
+ modelSel.value = "";
+ syncOrderEntryModelTradeStyle();
+ return;
+ }
+
+ modelSel.disabled = false;
+ var pick = preserveCode || "";
+ for (var k = 0; k < cat.options.length; k++) {
+ var o = cat.options[k];
+ var opt = document.createElement("option");
+ opt.value = o.code;
+ opt.textContent = o.label;
+ if (o.trade_style) opt.setAttribute("data-trade-style", o.trade_style);
+ if (o.help) opt.title = o.help;
+ modelSel.appendChild(opt);
+ }
+
+ if (pick && findOption(catKey, pick)) {
+ modelSel.value = pick;
+ } else if (cat.options.length === 1) {
+ modelSel.value = cat.options[0].code;
+ } else {
+ modelSel.value = "";
+ }
+ syncOrderEntryModelTradeStyle();
}
function syncOrderEntryModelTradeStyle() {
- var sel = document.getElementById("order-entry-model");
+ var modelSel = document.getElementById("order-entry-model");
var hidden = document.getElementById("order-trade-style-hidden");
var hint = document.getElementById("order-trade-style-hint");
- if (!sel || !hidden) return;
+ if (!modelSel || !hidden) return;
var labels = { trend: "趋势单", swing: "波段单" };
- var ts = tradeStyleForSelect(sel);
+ var code = modelSel.value || "";
+ var ts = tradeStyleForCode(code);
hidden.value = ts;
if (hint) hint.textContent = labels[ts] || ts;
}
function initOrderEntryModelSelect(root) {
var scope = root && root.querySelector ? root : document;
- var sel =
+ var catSel =
+ scope === document
+ ? document.getElementById("order-entry-category")
+ : scope.querySelector("#order-entry-category");
+ var modelSel =
scope === document
? document.getElementById("order-entry-model")
: scope.querySelector("#order-entry-model");
- if (!sel) return;
- if (sel.dataset.entryModelWired !== "1") {
- sel.dataset.entryModelWired = "1";
- sel.addEventListener("change", syncOrderEntryModelTradeStyle);
+ if (!catSel || !modelSel) return;
+
+ if (catSel.dataset.entryModelWired !== "1") {
+ catSel.dataset.entryModelWired = "1";
+ catSel.addEventListener("change", function () {
+ rebuildEntryModelSubSelect("");
+ });
}
- syncOrderEntryModelTradeStyle();
+ if (modelSel.dataset.entryModelWired !== "1") {
+ modelSel.dataset.entryModelWired = "1";
+ modelSel.addEventListener("change", syncOrderEntryModelTradeStyle);
+ }
+
+ var presetCode = modelSel.getAttribute("data-preset-code") || modelSel.value || "";
+ if (presetCode) {
+ var catMap = codeToCategoryMap();
+ var catKey = catMap[presetCode];
+ if (catKey) {
+ catSel.value = catKey;
+ rebuildEntryModelSubSelect(presetCode);
+ return;
+ }
+ }
+ rebuildEntryModelSubSelect("");
}
global.paintOrderLeverageHint = function (leverage) {
@@ -51,12 +138,9 @@
global.initOrderEntryModelSelect = initOrderEntryModelSelect;
global.syncOrderEntryModelTradeStyle = syncOrderEntryModelTradeStyle;
+ global.rebuildEntryModelSubSelect = rebuildEntryModelSubSelect;
- if (document.readyState === "loading") {
- document.addEventListener("DOMContentLoaded", function () {
- initOrderEntryModelSelect();
- });
- } else {
+ document.addEventListener("DOMContentLoaded", function () {
initOrderEntryModelSelect();
- }
+ });
})(typeof window !== "undefined" ? window : globalThis);
diff --git a/lib/instance/templates/embed_boot_scripts.html b/lib/instance/templates/embed_boot_scripts.html
index 94e9576..bf6e31f 100644
--- a/lib/instance/templates/embed_boot_scripts.html
+++ b/lib/instance/templates/embed_boot_scripts.html
@@ -1,52 +1,5 @@
-
+
+
diff --git a/lib/instance/templates/index.html b/lib/instance/templates/index.html
index d98dc4a..265f997 100644
--- a/lib/instance/templates/index.html
+++ b/lib/instance/templates/index.html
@@ -554,13 +554,17 @@
-
+
+