feat: unify key monitor UI with one-line summary and expandable details across all exchanges
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -5431,6 +5431,12 @@ def render_main_page(page="trade"):
|
||||
orphan_positions = collect_orphan_exchange_positions(order_list, conn)
|
||||
except Exception as exc:
|
||||
print(f"[render_main_page] orphan positions: {exc}")
|
||||
key_gate_rule_text = (
|
||||
f"【箱体/收敛】{KLINE_TIMEFRAME} 两根闭合K|突破越过关键位 0.03%~0.5%|"
|
||||
f"确认K收于箱外|量能>前20均量×1.3|日成交前30|"
|
||||
f"偏离关键位 > {KEY_BREAKOUT_LIMIT_PCT}% 不提醒|"
|
||||
f"【阻力/支撑】收盘突破任一侧即提醒 {KEY_ALERT_MAX_TIMES} 次(间隔 {KEY_ALERT_INTERVAL_MINUTES} 分),不自动开仓"
|
||||
)
|
||||
conn.close()
|
||||
return render_template(
|
||||
"index.html",
|
||||
@@ -5502,6 +5508,7 @@ def render_main_page(page="trade"):
|
||||
journal_chart_default_limit=JOURNAL_CHART_DEFAULT_LIMIT,
|
||||
journal_chart_default_anchor=JOURNAL_CHART_DEFAULT_ANCHOR,
|
||||
exchange_display=EXCHANGE_DISPLAY_NAME,
|
||||
key_gate_rule_text=key_gate_rule_text,
|
||||
**strategy_extra,
|
||||
)
|
||||
|
||||
@@ -5512,6 +5519,12 @@ def index():
|
||||
return redirect("/trade")
|
||||
|
||||
|
||||
@app.route("/key_monitor")
|
||||
@login_required
|
||||
def key_monitor_page():
|
||||
return render_main_page("key_monitor")
|
||||
|
||||
|
||||
@app.route("/trade")
|
||||
@login_required
|
||||
def trade_page():
|
||||
@@ -6284,7 +6297,7 @@ def add_key():
|
||||
conn.commit()
|
||||
conn.close()
|
||||
flash(f"添加成功({symbol} 日成交量排名 {rank}/{total})")
|
||||
return redirect("/")
|
||||
return redirect("/key_monitor")
|
||||
|
||||
@app.route("/add_order", methods=["POST"])
|
||||
@login_required
|
||||
|
||||
@@ -322,6 +322,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="top-nav">
|
||||
<a href="/key_monitor" class="{% if page == 'key_monitor' %}active{% endif %}">关键位监控</a>
|
||||
<a href="/trade" class="{% if page == 'trade' %}active{% endif %}">交易执行</a>
|
||||
<a href="/strategy" class="{% if page in ('strategy', 'strategy_trend', 'strategy_roll') %}active{% endif %}">策略交易</a>
|
||||
<a href="/strategy/records" class="{% if page == 'strategy_records' %}active{% endif %}">策略交易记录</a>
|
||||
@@ -354,6 +355,8 @@
|
||||
<div class="export-bar">
|
||||
<span style="color:#9aa">数据导出(v{{ data_export_version }} CSV,UTF-8;交易记录含开仓类型列及交易所对齐字段):</span>
|
||||
<a href="/export/trade_records">交易记录</a>
|
||||
<a href="/export/key_monitors">关键位(当前)</a>
|
||||
<a href="/export/key_monitor_history">关键位历史</a>
|
||||
</div>
|
||||
<div class="stat-box">
|
||||
<div class="stat-item"><div class="label">交易所</div><div class="value">{{ exchange_display }}</div></div>
|
||||
@@ -368,7 +371,9 @@
|
||||
{% include 'gate_transfer_block.html' %}
|
||||
|
||||
<div class="grid">
|
||||
{% if page == 'trade' %}
|
||||
{% if page == 'key_monitor' %}
|
||||
{% include 'key_monitor_panel.html' %}
|
||||
{% elif page == 'trade' %}
|
||||
<div class="dual-panel-grid" style="grid-column:1/-1">
|
||||
<div class="card">
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:wrap;margin-bottom:8px">
|
||||
@@ -1577,6 +1582,62 @@ if(journalForm){
|
||||
}
|
||||
}
|
||||
|
||||
function syncKeyMonitorFormFields(){
|
||||
const typeEl = document.querySelector('#key-form [name="type"]');
|
||||
const dirEl = document.getElementById("key-direction");
|
||||
const modeEl = document.getElementById("key-sl-tp-mode");
|
||||
const manualTp = document.getElementById("key-manual-tp");
|
||||
const beWrap = document.getElementById("key-breakeven-wrap");
|
||||
if(!typeEl) return;
|
||||
const t = (typeEl.value || "").trim();
|
||||
const autoTypes = new Set(["箱体突破","收敛突破"]);
|
||||
const fibTypes = new Set(["斐波回调0.618","斐波回调0.786"]);
|
||||
const fbTypes = new Set(["假突破"]);
|
||||
const rsTypes = new Set(["关键阻力位","关键支撑位"]);
|
||||
const showAuto = autoTypes.has(t);
|
||||
const showFb = fbTypes.has(t);
|
||||
const showBe = showAuto || fibTypes.has(t) || showFb;
|
||||
const showDir = !rsTypes.has(t);
|
||||
const upperEl = document.getElementById("key-upper");
|
||||
const lowerEl = document.getElementById("key-lower");
|
||||
const fbPriceEl = document.getElementById("key-fb-price");
|
||||
if(dirEl){
|
||||
dirEl.style.display = showDir ? "" : "none";
|
||||
dirEl.required = showDir;
|
||||
if(!showDir) dirEl.value = "";
|
||||
}
|
||||
if(modeEl) modeEl.style.display = showAuto ? "" : "none";
|
||||
if(manualTp){
|
||||
const trend = showAuto && modeEl && modeEl.value === "trend_manual";
|
||||
manualTp.style.display = trend ? "" : "none";
|
||||
manualTp.required = !!trend;
|
||||
}
|
||||
if(beWrap) beWrap.style.display = showBe ? "inline-flex" : "none";
|
||||
if(upperEl){
|
||||
upperEl.style.display = showFb ? "none" : "";
|
||||
upperEl.required = !showFb;
|
||||
if(showFb) upperEl.value = "";
|
||||
}
|
||||
if(lowerEl){
|
||||
lowerEl.style.display = showFb ? "none" : "";
|
||||
lowerEl.required = !showFb;
|
||||
if(showFb) lowerEl.value = "";
|
||||
}
|
||||
if(fbPriceEl){
|
||||
fbPriceEl.style.display = showFb ? "" : "none";
|
||||
fbPriceEl.required = showFb;
|
||||
if(!showFb) fbPriceEl.value = "";
|
||||
fbPriceEl.placeholder = (dirEl && dirEl.value === "short") ? "高点(阻力)" : ((dirEl && dirEl.value === "long") ? "低点(支撑)" : "做空填高点/做多填低点");
|
||||
}
|
||||
}
|
||||
const keyTypeSel = document.querySelector('#key-form [name="type"]');
|
||||
const keyModeSel = document.getElementById("key-sl-tp-mode");
|
||||
const keyDirSel = document.getElementById("key-direction");
|
||||
if(keyTypeSel) keyTypeSel.addEventListener("change", syncKeyMonitorFormFields);
|
||||
if(keyModeSel) keyModeSel.addEventListener("change", syncKeyMonitorFormFields);
|
||||
if(keyDirSel) keyDirSel.addEventListener("change", syncKeyMonitorFormFields);
|
||||
syncKeyMonitorFormFields();
|
||||
|
||||
const keyForm = document.getElementById("key-form");
|
||||
if(keyForm){
|
||||
keyForm.addEventListener("submit", (e)=>{
|
||||
@@ -1874,7 +1935,7 @@ function refreshPriceSnapshotConditional(){
|
||||
(data.key_prices || []).forEach(k=>{
|
||||
const pEl = document.getElementById(`key-price-${k.id}`);
|
||||
if(pEl){
|
||||
pEl.innerText = Number(k.price).toFixed(6);
|
||||
pEl.innerText = k.price_display || (Number.isFinite(Number(k.price)) ? Number(k.price).toFixed(6) : "-");
|
||||
paintPriceTrend(pEl, `k-${k.id}`, Number(k.price));
|
||||
}
|
||||
const upEl = document.getElementById(`key-up-diff-${k.id}`);
|
||||
@@ -1888,6 +1949,7 @@ function refreshPriceSnapshotConditional(){
|
||||
}
|
||||
const gateMetricEl = document.getElementById(`key-gate-metrics-${k.id}`);
|
||||
if(gateMetricEl) gateMetricEl.innerText = k.gate_metrics || "";
|
||||
if(typeof paintKeyMonitorSummary === "function") paintKeyMonitorSummary(k.id, k);
|
||||
});
|
||||
}
|
||||
if(page === "trade"){
|
||||
|
||||
Reference in New Issue
Block a user