<style>
.key-monitor-dual-grid{align-items:stretch}
.key-monitor-dual-grid>.card{
    height:100%;
    min-height:0;
    display:flex;
    flex-direction:column;
}
.key-panel-scroll.panel-scroll.pos-list{
    display:block;
    flex:1 1 auto;
    min-height:0;
    overflow-x:hidden;
    overflow-y:auto;
    padding-bottom:6px;
    -webkit-overflow-scrolling:touch;
    scrollbar-gutter:stable;
}
.key-monitor-panel-scroll{min-height:200px}
.key-history-panel-scroll{
    flex:0 0 auto;
    max-height:calc(8 * 42px + 7 * 8px);
    min-height:calc(3 * 42px + 2 * 8px);
}
.key-panel-scroll.panel-scroll.pos-list .key-row-collapse{flex-shrink:0}
.key-panel-scroll.panel-scroll.pos-list::-webkit-scrollbar{width:8px}
.key-panel-scroll.panel-scroll.pos-list::-webkit-scrollbar-thumb{background:#3a4660;border-radius:4px}
.key-panel-scroll.panel-scroll.pos-list::-webkit-scrollbar-track{background:transparent}
.key-row-collapse{border:1px solid #2a3348;border-radius:10px;background:#141923}
.key-row-collapse:not([open]){overflow:hidden}
.key-row-collapse[open]{overflow:visible}
.key-row-collapse+.key-row-collapse{margin-top:8px}
.key-row-collapse-summary{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:10px 12px;cursor:pointer;list-style:none;font-size:.8rem;color:#c5cde0;line-height:1.45}
.key-row-collapse-summary::-webkit-details-marker{display:none}
.key-row-collapse-summary::before{content:"▸";flex:0 0 auto;color:#6d7a99;transition:transform .15s ease}
.key-row-collapse[open]>.key-row-collapse-summary::before{transform:rotate(90deg)}
.key-row-summary-main{flex:1;min-width:0;display:flex;align-items:center;justify-content:space-between;gap:10px}
.key-row-summary-title{display:flex;align-items:center;gap:6px;flex:0 1 auto;flex-wrap:wrap;min-width:0}
.key-row-summary-title strong{font-size:.88rem;color:#fff}
.key-row-summary-line{color:#9aa8c4;font-size:.76rem;word-break:break-word}
.key-row-summary-live{
    flex:1 1 auto;
    min-width:0;
    color:#8fc8ff;
    font-size:.72rem;
    text-align:right;
    white-space:nowrap;
    overflow:hidden;
    text-overflow:ellipsis;
}
.key-row-summary-live.key-row-summary-pending{color:#4cd97f;font-weight:600}
.key-history-panel-scroll .key-row-summary-main{justify-content:flex-start}
.key-row-summary-actions{flex:0 0 auto;display:flex;gap:6px;align-items:center}
.key-row-collapse-body{padding:0 12px 16px;border-top:1px solid #232b3d}
.key-row-collapse-body .pos-meta{margin-top:10px;margin-bottom:10px}
.key-row-collapse-body .pos-grid{margin-bottom:8px}
.key-history-alert{font-size:.75rem;color:#aab;margin-top:8px;margin-bottom:2px;padding-bottom:4px;white-space:pre-wrap;word-break:break-word;line-height:1.5}
.key-history-outcome-badge{font-size:.7rem;font-weight:600;padding:1px 7px;border-radius:4px;line-height:1.35}
.key-row-collapse.key-history-success{border-color:rgba(76,217,127,.42);background:rgba(18,32,26,.92)}
.key-row-collapse.key-history-success .key-row-collapse-summary{color:#c8f0d6}
.key-row-collapse.key-history-success .key-row-summary-title strong{color:#e8fff0}
.key-row-collapse.key-history-success .key-history-brief,.key-row-collapse.key-history-success .key-history-outcome-badge{color:#4cd97f;background:rgba(76,217,127,.12);border:1px solid rgba(76,217,127,.28)}
.key-row-collapse.key-history-manual{border-color:rgba(136,146,176,.45);background:rgba(22,24,32,.95)}
.key-row-collapse.key-history-manual .key-history-brief,.key-row-collapse.key-history-manual .key-history-outcome-badge{color:#9aa8c4;background:rgba(136,146,176,.12);border:1px solid rgba(136,146,176,.28)}
.key-row-collapse.key-history-failed{border-color:rgba(232,160,144,.4);background:rgba(36,22,24,.95)}
.key-row-collapse.key-history-failed .key-row-collapse-summary{color:#e8cfc8}
.key-row-collapse.key-history-failed .key-history-brief,.key-row-collapse.key-history-failed .key-history-outcome-badge{color:#e8a090;background:rgba(232,160,144,.1);border:1px solid rgba(232,160,144,.28)}
.key-rule-table-wrap{overflow-x:auto;margin:0 -2px}
.key-rule-table{width:100%;min-width:620px;border-collapse:collapse;font-size:.6rem;line-height:1.3}
.key-rule-table th,.key-rule-table td{border:1px solid #2a3348;padding:4px 6px;vertical-align:top;text-align:left}
.key-rule-table th{background:rgba(0,0,0,.28);color:#9ab;font-weight:600;white-space:nowrap;font-size:.58rem}
.key-rule-table td{color:#c5cde0}
.key-rule-table .key-rule-type{color:#fff;font-weight:600;line-height:1.25;white-space:nowrap}
.key-rule-table .key-rule-sub{color:#8fc8ff;font-size:.54rem;font-weight:500}
.key-rule-cell{word-break:break-word}
.key-rule-foot{margin:6px 0 0;font-size:.56rem;color:#8892b0;line-height:1.35}
.key-rule-foot code{font-size:.54rem;color:#8fc8ff}
</style>

{% macro key_monitor_type_label(k) -%}
{%- if k.monitor_type in ['关键阻力位','关键支撑位','关键支撑阻力'] -%}关键支撑阻力{%- else -%}{{ k.monitor_type }}{%- endif -%}
{%- endmacro %}

{% macro key_direction_label(k) -%}
{% if k.direction == 'watch' %}双向{% elif k.direction == 'long' %}做多{% else %}做空{% endif %}
{%- endmacro %}

{% macro key_sl_tp_mode_label(k) -%}
{% if (k.sl_tp_mode or 'standard') == 'standard' %}标准突破{% elif k.sl_tp_mode == 'box_1p5' %}箱体1R·止盈1.5H{% else %}趋势单{% endif %}
{%- endmacro %}

{% macro key_monitor_brief(k) -%}
上{{ k.upper }} / 下{{ k.lower }} · 提醒 {{ k.notification_count or 0 }}/{{ k.max_notify or 3 }}
{%- if k.monitor_type in ['箱体突破','收敛突破'] %} · {{ key_sl_tp_mode_label(k) }}{% endif %}
{%- if k.breakeven_enabled %} · 保本开{% else %} · 保本关{% endif %}
{%- endmacro %}

{% macro key_history_outcome_kind(h) -%}
{%- set r = (h.close_reason or '')|trim -%}
{%- if r in ['fib_filled', 'false_breakout_filled', 'trigger_entry_filled', 'key_level_alert_done', 'alerts_complete', 'auto_opened'] -%}success
{%- elif r == 'manual' -%}manual
{%- elif r -%}failed
{%- else -%}neutral
{%- endif -%}
{%- endmacro %}

{% macro key_history_outcome_label(h) -%}
{%- set r = (h.close_reason or '')|trim -%}
{%- if r == 'fib_filled' -%}斐波成交
{%- elif r == 'false_breakout_filled' -%}假突破成交
{%- elif r == 'trigger_entry_filled' -%}触价成交
{%- elif r == 'key_level_alert_done' -%}提醒完成
{%- elif r == 'alerts_complete' -%}提醒已满
{%- elif r == 'auto_opened' -%}自动开仓
{%- elif r == 'manual' -%}手动删除
{%- elif r == 'fib_invalidate' -%}斐波失效
{%- elif r == 'box_opposite_break' -%}反向突破失效
{%- elif r == 'trigger_tp_invalidate' -%}触价止盈失效
{%- elif r == 'trigger_sl_invalidate' -%}触价止损失效
{%- elif r == 'trigger_entry_expired' -%}触价过期
{%- elif r == 'trigger_exchange_failed' -%}触价下单失败
{%- elif r == 'false_breakout_expired' -%}假突破过期
{%- elif r == 'fib_plan_invalid' -%}计划无效
{%- elif r == 'rr_insufficient' -%}盈亏比不足
{%- elif r == 'exchange_failed' -%}下单失败
{%- else -%}{{ r or '—' }}
{%- endif -%}
{%- endmacro %}

{% macro key_history_brief(h) -%}
{{ key_history_outcome_label(h) }} · {{ (h.closed_at or '-')[:16] }} · 上{{ h.upper }} / 下{{ h.lower }} · 提醒 {{ h.notification_count or 0 }}
{%- endmacro %}

<div class="dual-panel-grid key-monitor-dual-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">
        <h2 style="margin-bottom:0">关键位监控</h2>
        {% if focus_key_id %}
        <a href="/key_focus?key_id={{ focus_key_id }}" class="btn-del" style="text-decoration:none;background:#1f3a5a;color:#8fc8ff">放大查看K线(默认200根)</a>
        {% else %}
        <a href="/key_focus" class="btn-del" style="text-decoration:none;background:#1f3a5a;color:#8fc8ff">输入币种查看K线</a>
        {% endif %}
    </div>
    <form id="key-form" action="/add_key" method="post" class="form-row">
        {% from 'trade_policy_fields.html' import trade_policy_symbol, trade_policy_direction with context %}
        {{ trade_policy_symbol('symbol', 'key-symbol') }}
        <select name="type" id="key-type-select" required>
            {% if key_auto_order_enabled|default(false) and position_sizing_mode != 'full_margin' %}
            <option value="箱体突破">箱体突破</option>
            <option value="收敛突破">收敛突破</option>
            <option value="斐波回调0.618">斐波回调0.618</option>
            <option value="斐波回调0.786">斐波回调0.786</option>
            <option value="假突破">假突破(BTC/ETH)</option>
            {% endif %}
            {% if key_auto_order_enabled|default(false) %}
            <option value="回调触价开仓">回调触价开仓</option>
            <option value="突破触价开仓">突破触价开仓</option>
            {% endif %}
            <option value="关键支撑阻力">关键支撑阻力</option>
        </select>
        {{ trade_policy_direction('direction', 'key-direction') }}
        {% from 'symbol_live_price_snippet.html' import symbol_live_price_hint %}
        {{ symbol_live_price_hint('key-symbol-live-price', 'key-symbol', 'key-direction') }}
        <input name="key_price" id="key-fb-price" step="0.0001" placeholder="做空填高点/做多填低点" style="display:none">
        <input name="trigger_entry" id="key-trigger-entry" step="0.0001" placeholder="计划入场价" style="display:none">
        <input name="trigger_sl" id="key-trigger-sl" step="0.0001" placeholder="止损价" style="display:none">
        <input name="trigger_tp" id="key-trigger-tp" step="0.0001" placeholder="止盈价" style="display:none">
        <input name="upper" id="key-upper" step="0.0001" placeholder="上沿/阻力" required>
        <input name="lower" id="key-lower" step="0.0001" placeholder="下沿/支撑" required>
        <select name="sl_tp_mode" id="key-sl-tp-mode" title="止盈止损方案">
            <option value="standard">标准突破</option>
            <option value="box_1p5">箱体1R·止盈1.5H</option>
            <option value="trend_manual">趋势单·自填止盈</option>
        </select>
        <input name="manual_take_profit" id="key-manual-tp" step="0.0001" placeholder="趋势单止盈价" style="display:none">
        <label id="key-breakeven-wrap" style="display:inline-flex;align-items:center;gap:4px;font-size:.85rem;color:#9aa">
            <input type="checkbox" name="breakeven_enabled" value="1" id="key-breakeven-cb"> 移动保本
        </label>
        <span id="key-time-close-wrap" class="key-time-close-wrap" style="display:inline-flex;align-items:center;gap:4px;font-size:.85rem;color:#9aa">
            <label style="display:inline-flex;align-items:center;gap:4px;margin:0;cursor:pointer">
                <input type="checkbox" name="time_close_enabled" value="1" id="key-time-close-cb"> 时间平仓
            </label>
            <select name="time_close_hours" id="key-time-close-hours" title="持仓满该时长后自动平仓">
                <option value="1">1h</option>
                <option value="2">2h</option>
                <option value="4" selected>4h</option>
            </select>
        </span>
        <button type="submit">添加</button>
    </form>
    <details class="tip-collapse key-rule-collapse">
        <summary class="tip-collapse-summary">关键位监控规则说明</summary>
        <div class="tip-collapse-body rule-tip">
            {% include 'key_monitor_rule_tips.html' %}
        </div>
    </details>
    <div class="panel-scroll pos-list key-panel-scroll key-monitor-panel-scroll">
        {% for k in key %}
        <details class="key-row-collapse" id="key-row-{{ k.id }}">
            <summary class="key-row-collapse-summary">
                <span class="key-row-summary-main">
                    <span class="key-row-summary-title">
                        <strong>{{ k.symbol }}</strong>
                        {% if k.time_close_enabled and k.time_close_hours %}
                        <span class="pos-symbol-time-close pos-meta-on">时间平仓 {{ k.time_close_hours }}h</span>
                        {% endif %}
                        {% if k.direction == 'watch' %}
                        <span class="pos-side-badge" style="background:#2a3152;color:#9ab">双向</span>
                        {% else %}
                        <span class="pos-side-badge {{ 'pos-side-long' if k.direction == 'long' else 'pos-side-short' }}">{{ key_direction_label(k) }}</span>
                        {% endif %}
                        <span class="badge direction">{{ key_monitor_type_label(k) }}</span>
                    </span>
                    <span class="key-row-summary-live" id="key-summary-live-{{ k.id }}">现价 — · 门控 —</span>
                </span>
                <span class="key-row-summary-actions">
                    <button type="button" class="table-del" onclick="event.preventDefault(); event.stopPropagation(); deleteKeyMonitor({{ k.id }})">删</button>
                </span>
            </summary>
            <div class="key-row-collapse-body">
                <div class="key-row-summary-line">{{ key_monitor_brief(k) }}</div>
                <div class="pos-meta">
                    <span class="pos-meta-item">上沿: {{ k.upper }}</span>
                    <span class="pos-meta-item">下沿: {{ k.lower }}</span>
                    {% if k.fib_entry_price and k.monitor_type in ['回调触价开仓','突破触价开仓','触价开仓'] %}<span class="pos-meta-item">E: {{ k.fib_entry_price }} / SL: {{ k.fib_stop_loss }} / TP: {{ k.fib_take_profit }}</span>{% elif k.fib_entry_price %}<span class="pos-meta-item">挂E: {{ k.fib_entry_price }}</span>{% endif %}
                    {% if k.monitor_type == '假突破' and k.fib_stop_loss %}<span class="pos-meta-item">SL: {{ k.fib_stop_loss }} / TP: {{ k.fib_take_profit }}</span>{% endif %}
                    <span class="pos-meta-item">已提醒: {{ k.notification_count or 0 }}/{{ k.max_notify or 3 }}</span>
                    {% if k.monitor_type in ['箱体突破','收敛突破'] %}
                    <span class="pos-meta-item">方案: {{ key_sl_tp_mode_label(k) }}</span>
                    {% endif %}
                    <span class="pos-meta-item">保本: {{ '开' if k.breakeven_enabled else '关' }}</span>
                </div>
                <div class="pos-grid">
                    <div class="pos-cell"><span class="pos-label">现价</span><span class="pos-value" id="key-price-{{ k.id }}">-</span></div>
                    <div class="pos-cell"><span class="pos-label">距上沿</span><span class="pos-value" id="key-up-diff-{{ k.id }}">-</span></div>
                    <div class="pos-cell"><span class="pos-label">距下沿</span><span class="pos-value" id="key-low-diff-{{ k.id }}">-</span></div>
                    <div class="pos-cell"><span class="pos-label">门控</span><span class="pos-value" id="key-gate-{{ k.id }}" style="color:#9aa">-</span></div>
                </div>
                <div class="pos-meta"><span class="pos-meta-item" id="key-gate-metrics-{{ k.id }}" style="color:#8fc8ff"></span></div>
            </div>
        </details>
        {% else %}
        <div class="pos-empty">暂无监控中的关键位</div>
        {% endfor %}
    </div>
</div>
<div class="card">
    <h2 style="margin-bottom:8px">关键位历史</h2>
    <div class="sub" style="font-size:.72rem;color:#8892b0;margin-bottom:8px">失效或已结案的关键位 · 点击展开详情</div>
    <div class="panel-scroll pos-list key-panel-scroll key-history-panel-scroll">
        {% for h in key_history %}
        <details class="key-row-collapse key-history-{{ key_history_outcome_kind(h) }}">
            <summary class="key-row-collapse-summary">
                <span class="key-row-summary-main">
                    <span class="key-row-summary-title">
                        <strong>{{ h.symbol }}</strong>
                        <span class="pos-side-badge {{ 'pos-side-long' if h.direction == 'long' else 'pos-side-short' }}">{{ key_direction_label(h) }}</span>
                        <span class="badge direction">{{ key_monitor_type_label(h) }}</span>
                        <span class="key-history-outcome-badge">{{ key_history_outcome_label(h) }}</span>
                    </span>
                </span>
                <span class="key-row-summary-actions">
                    <button type="button" class="table-del" onclick="event.preventDefault(); event.stopPropagation(); deleteKeyHistory({{ h.id }})">删除</button>
                </span>
            </summary>
            <div class="key-row-collapse-body">
                <div class="key-row-summary-line key-history-brief">{{ key_history_brief(h) }}</div>
                <div class="pos-meta">
                    <span class="pos-meta-item">类型: {{ key_monitor_type_label(h) }}</span>
                    <span class="pos-meta-item">结案: {{ key_history_outcome_label(h) }}{% if h.close_reason %} ({{ h.close_reason }}){% endif %}</span>
                    <span class="pos-meta-item">时间: {{ h.closed_at or '—' }}</span>
                </div>
                <div class="pos-meta">
                    <span class="pos-meta-item">上沿: {{ h.upper }}</span>
                    <span class="pos-meta-item">下沿: {{ h.lower }}</span>
                    <span class="pos-meta-item">提醒次数: {{ h.notification_count or 0 }}</span>
                </div>
                {% if h.last_alert_message %}
                <div class="key-history-alert">{{ h.last_alert_message }}</div>
                {% endif %}
            </div>
        </details>
        {% else %}
        <div class="pos-empty">暂无历史</div>
        {% endfor %}
    </div>
</div>
</div>
<script>
function keySummaryIsPending(snap){
    if(!snap) return false;
    const gs = String(snap.gate_summary || "");
    if(gs.includes("标记价将失效")) return false;
    const gm = String(snap.gate_metrics || "");
    if(gm.includes("限价单") || gm.includes("挂单")) return true;
    if(/等待成交/.test(gs)) return true;
    if(/触价待触发/.test(gs)) return true;
    if(/挂E=/.test(gs) && !gs.includes("将失效")) return true;
    return false;
}
function paintKeyMonitorSummary(id, snap){
    const el = document.getElementById(`key-summary-live-${id}`);
    if(!el || !snap) return;
    const px = snap.price_display || (Number.isFinite(Number(snap.price)) ? Number(snap.price).toFixed(6) : "—");
    const gate = snap.gate_summary || "—";
    el.innerText = `现价 ${px} · 门控 ${gate}`;
    el.classList.toggle("key-row-summary-pending", keySummaryIsPending(snap));
}
document.querySelectorAll(".key-row-collapse").forEach((row)=>{
    row.addEventListener("toggle", ()=>{
        if(!row.open) return;
        requestAnimationFrame(()=>{
            const body = row.querySelector(".key-row-collapse-body");
            const panel = row.closest(".key-panel-scroll");
            if(body && panel){
                const bodyRect = body.getBoundingClientRect();
                const panelRect = panel.getBoundingClientRect();
                if(bodyRect.bottom > panelRect.bottom - 8){
                    panel.scrollTop += bodyRect.bottom - panelRect.bottom + 16;
                } else if(bodyRect.top < panelRect.top + 8){
                    panel.scrollTop -= panelRect.top - bodyRect.top + 16;
                }
            } else {
                row.scrollIntoView({block:"nearest", behavior:"smooth"});
            }
        });
    });
});
</script>
<script src="/static/key_monitor_form.js?v=2"></script>
