Enable hedge review with dual-timeframe K-line charts.

Always install the review module in hedge-only mode, expose 对冲复盘 in nav, and allow auto charts for closed hedge plans.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-10-14 18:43:02 +08:00
parent 6607d09526
commit 16bba2eb1a
10 changed files with 60 additions and 20 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ OKX **币本位期权** + **U 本位永续对冲** 独立实例。
## 产品范围
本仓**仅对冲**。单独期权(含中控)请用专用期权仓库;此处默认不装期权页/复盘/独立盯盘。
本仓**仅对冲**。单独期权(含中控)请用专用期权仓库;此处默认不装单独期权交易页/独立盯盘,但装载**对冲复盘**(已结束计划 + 标的永续双周期 K 线)。
| 保留 | 说明 |
|------|------|
+6 -6
View File
@@ -9941,7 +9941,7 @@ from lib.sim.register import install_sim_trading
install_sim_trading(app, _REPO_ROOT, app_module=sys.modules[__name__])
# 本仓默认仅对冲:不装单独期权页/复盘/独立盯盘(执行层由 hedge 共用 okx_options_lib).
# 本仓默认仅对冲:不装单独期权交易页/独立盯盘;复盘始终安装(对冲复盘+双周期K线).
# 紧急需要单独期权 UI 时设 STANDALONE_OPTIONS_UI=true(不推荐;专用期权仓更完整).
from lib.hedge_plan.okx_trade_mode_lib import standalone_options_ui_enabled as _standalone_opt_ui
@@ -9949,16 +9949,16 @@ if _standalone_opt_ui():
from lib.options.options_register import install_options_trading
install_options_trading(app, _REPO_ROOT, app_module=sys.modules[__name__])
from lib.options.options_review_register import install_options_review
install_options_review(app, _REPO_ROOT, app_module=sys.modules[__name__])
else:
print(
"[boot] STANDALONE_OPTIONS_UI=false → skip options/review UI (hedge-only)",
"[boot] STANDALONE_OPTIONS_UI=false → skip standalone options trading UI",
flush=True,
)
from lib.options.options_review_register import install_options_review
install_options_review(app, _REPO_ROOT, app_module=sys.modules[__name__])
from lib.hedge_plan.hedge_plan_register import install_hedge_plan
install_hedge_plan(app, _REPO_ROOT, app_module=sys.modules[__name__])
+1 -1
View File
@@ -11,7 +11,7 @@
strategy: "/strategy",
strategy_records: "/strategy/records",
options: "/options",
options_review: "/options/review",
options_review: "/hedge-plan/review",
hedge_plan: "/hedge-plan",
records: "/records",
stats: "/stats",
+13 -1
View File
@@ -1161,7 +1161,19 @@
($("or-f-note") || {}).value = e.note || "";
setMoodTags(e.mistake_tags);
resetChartOptions();
setChartOptionsVisible(t.source_type === "option_spot");
// 纯期权与对冲均可生成标的永续双周期 K 线;对冲默认勾选
var chartOk =
t.source_type === "option_spot" ||
t.source_type === "perp_options" ||
t.source_type === "options_options";
setChartOptionsVisible(chartOk);
if (
chartOk &&
(t.source_type === "perp_options" || t.source_type === "options_options")
) {
var cb = $("or-exchange-chart");
if (cb) cb.checked = true;
}
var summary = $("or-journal-summary");
if (summary) {
+1 -1
View File
@@ -43,7 +43,7 @@ DISPLAY_LABELS: dict[str, str] = {
"show_nav_system_guide": "系统说明",
"show_nav_env_config": "env配置",
"show_nav_options": "期权",
"show_nav_options_review": "期权复盘",
"show_nav_options_review": "对冲复盘",
"show_nav_hedge_plan": "永期/期期对冲",
"show_settings_transfer": "资金划转",
"show_settings_export": "数据导出",
+1
View File
@@ -40,6 +40,7 @@ PATH_TO_EMBED_TAB: dict[str, str] = {
"/strategy/records": "strategy_records",
"/options": "options",
"/options/review": "options_review",
"/hedge-plan/review": "options_review",
"/hedge-plan": "hedge_plan",
"/records": "records",
"/stats": "stats",
+6
View File
@@ -37,6 +37,9 @@
{% if options_nav_visible and display.show_nav_options_review %}
<a href="/options/review" data-embed-tab="options_review" class="{% if initial_tab == 'options_review' %}active{% endif %}">期权复盘</a>
{% endif %}
{% if hedge_plan_nav_visible and display.show_nav_options_review %}
<a href="/hedge-plan/review" data-embed-tab="options_review" class="{% if initial_tab == 'options_review' %}active{% endif %}">对冲复盘</a>
{% endif %}
{% if hedge_plan_nav_visible and display.show_nav_hedge_plan %}
<a href="/hedge-plan" data-embed-tab="hedge_plan" class="{% if initial_tab == 'hedge_plan' %}active{% endif %}">{% if hedge_plan_show_options_options %}期期对冲{% else %}永期对冲{% endif %}</a>
{% endif %}
@@ -98,6 +101,9 @@
{% if options_nav_visible and display.show_nav_options_review %}
<a href="/options/review" data-embed-tab="options_review">期权复盘</a>
{% endif %}
{% if hedge_plan_nav_visible and display.show_nav_options_review %}
<a href="/hedge-plan/review" data-embed-tab="options_review">对冲复盘</a>
{% endif %}
{% if display.show_nav_risk_policy %}
<a href="/risk_policy" data-embed-tab="risk_policy">风控说明</a>
{% endif %}
+3
View File
@@ -160,6 +160,9 @@
{% if options_nav_visible and display.show_nav_options_review %}
<a href="/options/review" class="{% if page == 'options_review' %}active{% endif %}">期权复盘</a>
{% endif %}
{% if hedge_plan_nav_visible and display.show_nav_options_review %}
<a href="/hedge-plan/review" class="{% if page == 'options_review' %}active{% endif %}">对冲复盘</a>
{% endif %}
{% if hedge_plan_nav_visible and display.show_nav_hedge_plan %}
<a href="/hedge-plan" class="{% if page == 'hedge_plan' %}active{% endif %}">{% if hedge_plan_show_options_options %}期期对冲{% else %}永期对冲{% endif %}</a>
{% endif %}
+24 -6
View File
@@ -139,8 +139,12 @@ def _maybe_schedule_options_review_chart(
trade: dict[str, Any],
data: dict[str, Any],
) -> bool:
"""纯期权 + 无手动截图 + 勾选自动K线 → 后台生成标的永续双周期拼图(只钉进/出场)."""
if str(trade.get("source_type") or "").strip() != "option_spot":
"""无手动截图 + 勾选自动K线 → 后台生成标的永续双周期拼图(只钉进/出场).
纯期权与永期/期期对冲均可;对冲按计划开平时间钉在标的永续 K 线上.
"""
src = str(trade.get("source_type") or "").strip()
if src not in ("option_spot", "perp_options", "options_options"):
return False
images = data.get("images") if isinstance(data.get("images"), list) else []
has_manual = any(str((x or {}).get("file") or "").strip() for x in images)
@@ -205,12 +209,17 @@ def _maybe_schedule_options_review_chart(
except Exception:
now_ms = None
plan_id = trade.get("hedge_plan_id")
if plan_id:
title_prefix = f"{underlying or 'HEDGE'} plan#{plan_id} review"
else:
inst = str(trade.get("inst_id") or underlying or f"#{trade.get('id') or ''}").strip()
title_prefix = f"{underlying or 'OPT'} {inst} review"
upload_folder = options_review_upload_dir(cfg["upload_folder"])
schedule_options_review_exchange_chart(
trade_id=int(trade["id"]),
exchange_symbol=exchange_symbol,
title_prefix=f"{underlying or 'OPT'} {inst} review",
title_prefix=title_prefix,
journal_tfs=journal_tfs,
journal_limit=journal_limit,
marker_payload={
@@ -231,9 +240,7 @@ def _maybe_schedule_options_review_chart(
def register_options_review_routes(app: Flask, cfg: dict[str, Any], repo_root: str) -> None:
lr = cfg["login_required"]
@app.route("/options/review")
@lr
def options_review_page():
def _review_page():
from lib.instance.instance_embed_lib import redirect_to_embed_shell_if_enabled
redir = redirect_to_embed_shell_if_enabled("options_review")
@@ -241,6 +248,17 @@ def register_options_review_routes(app: Flask, cfg: dict[str, Any], repo_root: s
return redir
return cfg["render_main_page"]("options_review")
@app.route("/options/review")
@lr
def options_review_page():
return _review_page()
@app.route("/hedge-plan/review")
@lr
def hedge_plan_review_page():
"""对冲复盘入口(与 /options/review 同页;本仓主入口)."""
return _review_page()
@app.route("/static/options_review.js")
@lr
def static_options_review_js():
@@ -178,7 +178,7 @@
</style>
<div class="or-page-head">
<h2>期权复盘</h2>
<h2>{% if okx_trade_mode|default('') in ('perp_options', 'options_options') %}对冲复盘{% else %}期权复盘{% endif %}</h2>
<span class="muted" id="or-sync-status" style="font-size:.72rem"></span>
<button type="button" class="btn-secondary" id="or-reload-btn" style="font-size:.76rem;padding:4px 10px">刷新</button>
</div>
@@ -356,7 +356,7 @@
<option value="now" {% if journal_chart_default_anchor == 'now' %}selected{% endif %}>当前时间</option>
</select>
</div>
<div class="sub or-chart-hint hidden" id="or-chart-hint" style="font-size:.72rem;margin-top:2px;margin-bottom:0">纯期权:按标的永续画双周期上下图;只按时间标记进场/出场,不标止损;有手动截图时不自动生成</div>
<div class="sub or-chart-hint hidden" id="or-chart-hint" style="font-size:.72rem;margin-top:2px;margin-bottom:0">{% if okx_trade_mode|default('') in ('perp_options', 'options_options') %}对冲:按标的永续画双周期上下图;按计划开仓/平仓时间钉点;有手动截图时不自动生成{% else %}纯期权:按标的永续画双周期上下图;只按时间标记进场/出场,不标止损;有手动截图时不自动生成{% endif %}</div>
<div class="or-mood-grid mood-grid">
<label><input type="checkbox" class="or-mood" value="怕踏空">怕踏空</label>
@@ -447,4 +447,4 @@
</section>
</div>
<script src="/static/options_review.js?v=23"></script>
<script src="/static/options_review.js?v=24"></script>