diff --git a/crypto_monitor_binance/app.py b/crypto_monitor_binance/app.py
index b358187..bdbe065 100644
--- a/crypto_monitor_binance/app.py
+++ b/crypto_monitor_binance/app.py
@@ -5682,6 +5682,13 @@ def render_main_page(page="trade"):
f"箱体/收敛可选 SL/TP 方案(标准 / 箱体1R·止盈1.5H / 趋势单+自填止盈)|移动保本默认关|"
f"斐波:限价 @ E(SL/TP 为 H/L),可选移动保本|趋势止损外侧 {KEY_TREND_STOP_OUTSIDE_PCT}%"
)
+ strategy_extra = {}
+ if page in ("strategy_trend", "strategy_roll"):
+ from strategy_ui import strategy_page_template_vars
+
+ strategy_extra = strategy_page_template_vars(
+ conn, page, default_risk_percent=float(RISK_PERCENT)
+ )
conn.close()
return render_template(
"index.html",
@@ -5737,6 +5744,7 @@ def render_main_page(page="trade"):
key_auto_min_planned_rr=KEY_AUTO_MIN_PLANNED_RR,
key_gate_rule_text=key_gate_rule_text,
kline_timeframe=KLINE_TIMEFRAME,
+ **strategy_extra,
)
@@ -7674,6 +7682,18 @@ except Exception as _hub_err:
print(f"[hub_bridge] binance: {_hub_err}")
+@app.route("/strategy/trend")
+@login_required
+def strategy_trend_page():
+ return render_main_page("strategy_trend")
+
+
+@app.route("/strategy/roll")
+@login_required
+def strategy_roll_page():
+ return render_main_page("strategy_roll")
+
+
from strategy_register import install_strategy_trading
install_strategy_trading(app, _REPO_ROOT, app_module=sys.modules[__name__])
diff --git a/crypto_monitor_binance/templates/index.html b/crypto_monitor_binance/templates/index.html
index 2c0805c..bfbe886 100644
--- a/crypto_monitor_binance/templates/index.html
+++ b/crypto_monitor_binance/templates/index.html
@@ -218,8 +218,7 @@
@@ -540,6 +539,10 @@
+ {% elif page == 'strategy_trend' %}
+ {% include 'strategy_trend_disabled_panel.html' %}
+ {% elif page == 'strategy_roll' %}
+ {% include 'strategy_roll_panel.html' %}
{% endif %}
diff --git a/crypto_monitor_gate/app.py b/crypto_monitor_gate/app.py
index c60224f..aa173e5 100644
--- a/crypto_monitor_gate/app.py
+++ b/crypto_monitor_gate/app.py
@@ -5661,6 +5661,13 @@ def render_main_page(page="trade"):
f"箱体/收敛可选 SL/TP 方案(标准 / 箱体1R·止盈1.5H / 趋势单+自填止盈)|移动保本默认关|"
f"斐波:限价 @ E(SL/TP 为 H/L),可选移动保本|趋势止损外侧 {KEY_TREND_STOP_OUTSIDE_PCT}%"
)
+ strategy_extra = {}
+ if page in ("strategy_trend", "strategy_roll"):
+ from strategy_ui import strategy_page_template_vars
+
+ strategy_extra = strategy_page_template_vars(
+ conn, page, default_risk_percent=float(RISK_PERCENT)
+ )
conn.close()
return render_template(
"index.html",
@@ -5718,6 +5725,7 @@ def render_main_page(page="trade"):
key_gate_rule_text=key_gate_rule_text,
kline_timeframe=KLINE_TIMEFRAME,
exchange_pnl_sync=exchange_pnl_sync,
+ **strategy_extra,
)
@@ -7735,6 +7743,18 @@ except Exception as _hub_err:
print(f"[hub_bridge] gate: {_hub_err}")
+@app.route("/strategy/trend")
+@login_required
+def strategy_trend_page():
+ return render_main_page("strategy_trend")
+
+
+@app.route("/strategy/roll")
+@login_required
+def strategy_roll_page():
+ return render_main_page("strategy_roll")
+
+
from strategy_register import install_strategy_trading
install_strategy_trading(app, _REPO_ROOT, app_module=sys.modules[__name__])
diff --git a/crypto_monitor_gate/templates/index.html b/crypto_monitor_gate/templates/index.html
index 553d7fe..156d5b0 100644
--- a/crypto_monitor_gate/templates/index.html
+++ b/crypto_monitor_gate/templates/index.html
@@ -218,8 +218,7 @@
@@ -540,6 +539,10 @@
+ {% elif page == 'strategy_trend' %}
+ {% include 'strategy_trend_disabled_panel.html' %}
+ {% elif page == 'strategy_roll' %}
+ {% include 'strategy_roll_panel.html' %}
{% endif %}
diff --git a/crypto_monitor_gate_bot/app.py b/crypto_monitor_gate_bot/app.py
index 8fa7ef2..455c392 100644
--- a/crypto_monitor_gate_bot/app.py
+++ b/crypto_monitor_gate_bot/app.py
@@ -5312,6 +5312,15 @@ def render_main_page(page="trade"):
trend_preview_levels.append({"i": i, "price": pair[0], "contracts": pair[1]})
elif pr:
trend_preview_expired = True
+ strategy_extra = {}
+ if page == "strategy_roll":
+ from strategy_ui import fetch_roll_page_data
+
+ strategy_extra = fetch_roll_page_data(
+ conn,
+ default_risk_percent=float(RISK_PERCENT),
+ count_active_trends=lambda c, ta=trend_active: int(ta or 0),
+ )
conn.close()
return render_template(
"index.html",
@@ -5376,6 +5385,7 @@ def render_main_page(page="trade"):
entry_reason_options=list(ENTRY_REASON_OPTIONS),
entry_reason_other_value=ENTRY_REASON_OTHER,
exchange_display=EXCHANGE_DISPLAY_NAME,
+ **strategy_extra,
)
@@ -7371,10 +7381,18 @@ except Exception as _hub_err:
print(f"[hub_bridge] gate_bot: {_hub_err}")
+@app.route("/strategy/trend")
+@login_required
def strategy_trend_page():
return render_main_page("strategy_trend")
+@app.route("/strategy/roll")
+@login_required
+def strategy_roll_page():
+ return render_main_page("strategy_roll")
+
+
from strategy_register import install_strategy_trading
install_strategy_trading(
@@ -7382,7 +7400,6 @@ install_strategy_trading(
_REPO_ROOT,
app_module=sys.modules[__name__],
trend_enabled=True,
- render_trend_page=login_required(strategy_trend_page),
)
diff --git a/crypto_monitor_gate_bot/templates/index.html b/crypto_monitor_gate_bot/templates/index.html
index cbb0a79..f031a06 100644
--- a/crypto_monitor_gate_bot/templates/index.html
+++ b/crypto_monitor_gate_bot/templates/index.html
@@ -205,8 +205,7 @@
@@ -372,6 +371,7 @@
{% elif page == 'strategy_trend' %}
+ {% include 'strategy_subnav.html' %}
趋势回调策略
@@ -553,6 +553,8 @@
+ {% elif page == 'strategy_roll' %}
+ {% include 'strategy_roll_panel.html' %}
{% endif %}
{% if page == 'records' %}
diff --git a/crypto_monitor_okx/app.py b/crypto_monitor_okx/app.py
index 6d6ed20..dbcf3d4 100644
--- a/crypto_monitor_okx/app.py
+++ b/crypto_monitor_okx/app.py
@@ -4192,6 +4192,13 @@ def render_main_page(page="trade"):
f"自动开仓盈亏比 > {KEY_AUTO_MIN_PLANNED_RR}:1|日成交量排名前 {KEY_DAILY_VOLUME_RANK_MAX}|"
f"斐波:添加后立即挂限价 @ E,失效按标记价触达 H/L(未成交撤单)"
)
+ strategy_extra = {}
+ if page in ("strategy_trend", "strategy_roll"):
+ from strategy_ui import strategy_page_template_vars
+
+ strategy_extra = strategy_page_template_vars(
+ conn, page, default_risk_percent=float(RISK_PERCENT)
+ )
conn.close()
return render_template(
"index.html",
@@ -4242,6 +4249,7 @@ def render_main_page(page="trade"):
entry_reason_other_value=ENTRY_REASON_OTHER,
key_gate_rule_text=key_gate_rule_text,
key_auto_min_planned_rr=KEY_AUTO_MIN_PLANNED_RR,
+ **strategy_extra,
)
@@ -5950,6 +5958,18 @@ except Exception as _hub_err:
print(f"[hub_bridge] okx: {_hub_err}")
+@app.route("/strategy/trend")
+@login_required
+def strategy_trend_page():
+ return render_main_page("strategy_trend")
+
+
+@app.route("/strategy/roll")
+@login_required
+def strategy_roll_page():
+ return render_main_page("strategy_roll")
+
+
from strategy_register import install_strategy_trading
install_strategy_trading(app, _REPO_ROOT, app_module=sys.modules[__name__])
diff --git a/crypto_monitor_okx/templates/index.html b/crypto_monitor_okx/templates/index.html
index 23cd7da..343eeaf 100644
--- a/crypto_monitor_okx/templates/index.html
+++ b/crypto_monitor_okx/templates/index.html
@@ -154,8 +154,7 @@
@@ -353,6 +352,10 @@
{% endfor %}
+ {% elif page == 'strategy_trend' %}
+ {% include 'strategy_trend_disabled_panel.html' %}
+ {% elif page == 'strategy_roll' %}
+ {% include 'strategy_roll_panel.html' %}
{% endif %}
{% if page == 'records' %}
diff --git a/strategy_register.py b/strategy_register.py
index 417af4c..957ffef 100644
--- a/strategy_register.py
+++ b/strategy_register.py
@@ -5,7 +5,7 @@ import os
from functools import wraps
from typing import Any, Callable, Optional
-from flask import Flask, flash, jsonify, redirect, render_template, request, url_for
+from flask import Flask, flash, jsonify, redirect, request, url_for
from jinja2 import ChoiceLoader, FileSystemLoader
from strategy_db import init_strategy_tables
@@ -13,14 +13,12 @@ from strategy_roll_lib import preview_roll
def install_strategy_trading(app: Flask, repo_root: str, app_module: Any = None, **build_kw) -> None:
- """在 app.py 末尾调用(login_required 已定义后)。build_kw 传给 build_strategy_config。"""
+ """在 app.py 末尾调用(login_required 已定义后)。仅注册 POST API;页面由各 app 的 render_main_page 渲染。"""
from strategy_config import build_strategy_config
- render_trend_page = build_kw.pop("render_trend_page", None)
+ build_kw.pop("render_trend_page", None)
attach_strategy_templates(app, repo_root)
cfg = build_strategy_config(app_module, **build_kw)
- if render_trend_page is not None:
- cfg["render_trend_page"] = render_trend_page
register_strategy_trading(app, cfg)
@@ -43,66 +41,10 @@ def register_strategy_trading(app: Flask, cfg: dict[str, Any]) -> None:
login_required = cfg["login_required"]
get_db = cfg["get_db"]
- trend_enabled = bool(cfg.get("trend_enabled"))
- render_trend_page = cfg.get("render_trend_page")
def _lr(f):
return login_required(f)
- if trend_enabled and callable(render_trend_page):
- app.add_url_rule(
- "/strategy/trend",
- endpoint="strategy_trend_page",
- view_func=_lr(render_trend_page),
- )
- else:
-
- @_lr
- @app.route("/strategy/trend")
- def strategy_trend_disabled_page():
- return render_template(
- "strategy_trend_disabled.html",
- exchange_display=cfg.get("exchange_display", ""),
- trend_note=cfg.get(
- "trend_disabled_note",
- "趋势回调(自动补仓)当前仅在 Gate 趋势机器人实例中启用。",
- ),
- )
-
- @_lr
- @app.route("/strategy/roll")
- def strategy_roll_page():
- conn = get_db()
- init_strategy_tables(conn)
- monitors = []
- for row in conn.execute(
- "SELECT * FROM order_monitors WHERE status='active' ORDER BY id DESC"
- ).fetchall():
- monitors.append(_row_to_dict(row))
- roll_groups = []
- for row in conn.execute(
- "SELECT * FROM roll_groups WHERE status='active' ORDER BY id DESC"
- ).fetchall():
- roll_groups.append(_row_to_dict(row))
- legs = []
- for row in conn.execute(
- "SELECT * FROM roll_legs ORDER BY id DESC LIMIT 50"
- ).fetchall():
- legs.append(_row_to_dict(row))
- trend_n = _count_active_trends(conn, cfg)
- conn.close()
- return render_template(
- "strategy_roll.html",
- page="strategy_roll",
- exchange_display=cfg.get("exchange_display", ""),
- monitors=monitors,
- roll_groups=roll_groups,
- roll_legs=legs,
- trend_active=trend_n,
- default_risk_percent=cfg.get("default_risk_percent", 2),
- price_fmt=cfg.get("price_fmt"),
- )
-
@_lr
@app.route("/strategy/roll/preview", methods=["POST"])
def strategy_roll_preview():
diff --git a/strategy_templates/strategy_roll_panel.html b/strategy_templates/strategy_roll_panel.html
new file mode 100644
index 0000000..0ee3fee
--- /dev/null
+++ b/strategy_templates/strategy_roll_panel.html
@@ -0,0 +1,75 @@
+{% include 'strategy_subnav.html' %}
+
+
顺势加仓(滚仓)
+
+ 仅人工加仓,程序不会自动触发。须先在「实盘下单」有同向持仓。
+ 做多最多滚仓 3 次;止盈锁定首仓不变;每次填写新统一止损,总风险%按「合并持仓打到新止损≈账户风险」反推张数。
+ 斐波限价:上沿 H、下沿 L 仅用于算 0.618/0.786 加仓价(多:下沿=止损侧;空:上沿=止损侧)。
+ {% if roll_trend_active %}当前有运行中的趋势回调计划,请先结束后再滚仓。{% endif %}
+
+
+
执行前可用开发者工具 POST /strategy/roll/preview 查看 JSON 预览。
+
+
+
+
活跃滚仓组
+
+
+ | ID | 币种 | 方向 | 腿数 | 首仓TP | 当前SL |
+ {% for g in roll_groups %}
+
+ | {{ g.id }} |
+ {{ g.symbol }} |
+ {{ g.direction }} |
+ {{ g.leg_count }} |
+ {% if price_fmt %}{{ price_fmt(g.symbol, g.initial_take_profit) }}{% else %}{{ g.initial_take_profit }}{% endif %} |
+ {% if price_fmt %}{{ price_fmt(g.symbol, g.current_stop_loss) }}{% else %}{{ g.current_stop_loss }}{% endif %} |
+
+ {% else %}
+ | 暂无 |
+ {% endfor %}
+
+
+
+
+
+
最近滚仓腿
+
+
+ | # | 组 | 方式 | 张数 | 新SL | 状态 |
+ {% for leg in roll_legs %}
+
+ | {{ leg.leg_index }} |
+ {{ leg.roll_group_id }} |
+ {{ leg.add_mode }} |
+ {{ leg.amount }} |
+ {{ leg.new_stop_loss }} |
+ {{ leg.status }} |
+
+ {% else %}
+ | 暂无 |
+ {% endfor %}
+
+
+
diff --git a/strategy_templates/strategy_subnav.html b/strategy_templates/strategy_subnav.html
new file mode 100644
index 0000000..f53393d
--- /dev/null
+++ b/strategy_templates/strategy_subnav.html
@@ -0,0 +1,4 @@
+
diff --git a/strategy_templates/strategy_trend_disabled_panel.html b/strategy_templates/strategy_trend_disabled_panel.html
new file mode 100644
index 0000000..5bc5013
--- /dev/null
+++ b/strategy_templates/strategy_trend_disabled_panel.html
@@ -0,0 +1,13 @@
+{% include 'strategy_subnav.html' %}
+
+
趋势回调
+
{{ trend_disabled_note }}
+
+ 趋势回调含自动补仓档位与预览执行,仅在 Gate 趋势机器人(crypto_monitor_gate_bot)实例中运行。
+ 请访问该实例同一菜单「策略交易 → 趋势回调」,或常用地址 :5002/strategy/trend。
+
+
+ 返回实盘下单
+ | 顺势加仓(本实例可用)
+
+
diff --git a/strategy_ui.py b/strategy_ui.py
new file mode 100644
index 0000000..96d817a
--- /dev/null
+++ b/strategy_ui.py
@@ -0,0 +1,88 @@
+"""策略交易页:主站 index.html 所需数据(顺势加仓等)。"""
+from __future__ import annotations
+
+from typing import Any, Callable, Optional
+
+from strategy_db import init_strategy_tables
+
+
+def _row_to_dict(row) -> dict:
+ if row is None:
+ return {}
+ try:
+ return dict(row)
+ except Exception:
+ return {}
+
+
+def count_active_trend_plans(conn, count_fn: Optional[Callable] = None) -> int:
+ if callable(count_fn):
+ return int(count_fn(conn) or 0)
+ try:
+ return int(
+ conn.execute(
+ "SELECT COUNT(*) FROM trend_pullback_plans WHERE status='active'"
+ ).fetchone()[0]
+ )
+ except Exception:
+ return 0
+
+
+def fetch_roll_page_data(
+ conn,
+ *,
+ default_risk_percent: float = 2.0,
+ count_active_trends: Optional[Callable] = None,
+) -> dict[str, Any]:
+ init_strategy_tables(conn)
+ monitors = []
+ for row in conn.execute(
+ "SELECT * FROM order_monitors WHERE status='active' ORDER BY id DESC"
+ ).fetchall():
+ monitors.append(_row_to_dict(row))
+ roll_groups = []
+ for row in conn.execute(
+ "SELECT * FROM roll_groups WHERE status='active' ORDER BY id DESC"
+ ).fetchall():
+ roll_groups.append(_row_to_dict(row))
+ roll_legs = []
+ for row in conn.execute(
+ "SELECT * FROM roll_legs ORDER BY id DESC LIMIT 50"
+ ).fetchall():
+ roll_legs.append(_row_to_dict(row))
+ return {
+ "roll_monitors": monitors,
+ "roll_groups": roll_groups,
+ "roll_legs": roll_legs,
+ "roll_trend_active": count_active_trend_plans(conn, count_active_trends),
+ "default_risk_percent": default_risk_percent,
+ }
+
+
+DEFAULT_TREND_DISABLED_NOTE = (
+ "趋势回调(预览、自动补仓、程序止盈)仅在 Gate 趋势机器人实例 "
+ "(crypto_monitor_gate_bot,常见端口 5002)中启用。"
+ "币安 / Gate 主站 / OKX 可使用本页「顺势加仓」;完整趋势回调请打开该实例。"
+)
+
+
+def strategy_page_template_vars(
+ conn,
+ page: str,
+ *,
+ default_risk_percent: float = 2.0,
+ count_active_trends: Optional[Callable] = None,
+ trend_disabled_note: str = "",
+) -> dict[str, Any]:
+ """render_main_page 在 conn.close() 前合并进 render_template 的变量。"""
+ if page == "strategy_roll":
+ return fetch_roll_page_data(
+ conn,
+ default_risk_percent=default_risk_percent,
+ count_active_trends=count_active_trends,
+ )
+ if page == "strategy_trend":
+ return {
+ "trend_disabled_note": trend_disabled_note or DEFAULT_TREND_DISABLED_NOTE,
+ }
+ return {}
diff --git a/策略交易说明.md b/策略交易说明.md
index 4381e33..57faa7a 100644
--- a/策略交易说明.md
+++ b/策略交易说明.md
@@ -1,6 +1,6 @@
# 策略交易说明
-本文档说明仓库根目录 **共用策略逻辑** 与四个 `crypto_monitor_*` 实例中的 **策略交易** 入口(导航栏「策略·趋势回调」「策略·顺势加仓」)。
+本文档说明仓库根目录 **共用策略逻辑** 与四个 `crypto_monitor_*` 实例中的 **策略交易** 入口(顶栏「策略交易」,页内子 Tab:趋势回调 / 顺势加仓)。
---
@@ -11,9 +11,10 @@ strategy_trend_lib.py # 趋势回调:网格价、补仓拆分、边界校
strategy_roll_lib.py # 顺势加仓:总风险反推、斐波限价、最多 3 腿(纯计算)
strategy_db.py # roll_groups / roll_legs 表结构
strategy_config.py # 各所 app → 统一回调配置(交易所 API)
-strategy_register.py # Flask 路由:/strategy/trend、/strategy/roll
+strategy_register.py # Flask POST:/strategy/roll/preview、/strategy/roll/execute
+strategy_ui.py # 主站 index 页数据(滚仓组、持仓列表等)
strategy_exchange_*.py # 适配器说明(实际下单仍走各所 app 的 ccxt)
-strategy_templates/ # 顺势加仓页、趋势禁用提示页
+strategy_templates/ # 主站内嵌 panel(subnav、roll、trend 禁用说明)
```
| 层级 | 职责 |
@@ -28,22 +29,26 @@ strategy_templates/ # 顺势加仓页、趋势禁用提示页
## 二、导航与页面
-| 路由 | 名称 | 说明 |
-|------|------|------|
-| `/strategy/trend` | 趋势回调 | **完整功能仅在 `crypto_monitor_gate_bot`**;其它所显示说明页 |
-| `/strategy/roll` | 顺势加仓 | **四所均可用**(须已有同向持仓) |
+顶栏一项 **「策略交易」**(高亮含 `/strategy/trend` 与 `/strategy/roll`),页内子导航切换:
+
+| 路由 | 子 Tab | 说明 |
+|------|--------|------|
+| `/strategy/trend` | 趋势回调 | **完整功能仅在 `crypto_monitor_gate_bot`**;其它所在主站 `index.html` 内嵌说明(不再跳转独立 HTML) |
+| `/strategy/roll` | 顺势加仓 | **四所均可用**(须已有同向持仓),与实盘页同一布局 |
| `/trade` | 实盘下单 | 首仓、以损定仓、移动保本(不变) |
+各所 `app.py` 注册 `@app.route("/strategy/trend|roll")` → `render_main_page(...)`;`install_strategy_trading` 仅注册滚仓 POST API。
+
---
## 三、趋势回调(延续 Gate 趋势机器人逻辑)
-- **位置**:`crypto_monitor_gate_bot` → **策略·趋势回调**(原「交易执行」页内区块已迁出)。
+- **位置**:`crypto_monitor_gate_bot` → **策略交易 → 趋势回调**(与 Gate 主站同一顶栏风格,非独立站点)。
- **行为**:与《[crypto_monitor_gate_bot/趋势回调策略说明.md](./crypto_monitor_gate_bot/趋势回调策略说明.md)》一致——预览 → 确认执行 → 首仓 50% + 交易所止损 + 多档 **自动** 市价补仓 + 程序监控止盈。
- **共用代码**:`parse_and_compute_trend_pullback_plan` 中网格/拆档已改为调用 `strategy_trend_lib`。
- **互斥**:与「机器人下单监控」持仓上限、运行中趋势计划互斥(逻辑未改)。
-其它三所打开 `/strategy/trend` 会提示:请使用 Gate 趋势机器人实例。
+其它三所打开 **策略交易 → 趋势回调** 会在主站内嵌说明:完整功能请使用 Gate 趋势机器人实例(常见 `:5002`)。
---