Improve key monitor form with bar period, box direction, and labeled fields.

Match order-monitor layout; persist bar_period and enforce upper-direction filter for box breakouts.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-29 07:24:36 +08:00
parent 169136dd4a
commit bfb1b95471
6 changed files with 187 additions and 68 deletions
+20 -7
View File
@@ -305,6 +305,7 @@ def init_db():
"ALTER TABLE key_monitors ADD COLUMN alert_break_side TEXT", "ALTER TABLE key_monitors ADD COLUMN alert_break_side TEXT",
"ALTER TABLE key_monitors ADD COLUMN breakout_bar_time TEXT", "ALTER TABLE key_monitors ADD COLUMN breakout_bar_time TEXT",
"ALTER TABLE key_monitors ADD COLUMN alert_close_price REAL", "ALTER TABLE key_monitors ADD COLUMN alert_close_price REAL",
"ALTER TABLE key_monitors ADD COLUMN bar_period TEXT DEFAULT '5m'",
"ALTER TABLE review_records ADD COLUMN direction TEXT", "ALTER TABLE review_records ADD COLUMN direction TEXT",
"ALTER TABLE review_records ADD COLUMN entry_price REAL", "ALTER TABLE review_records ADD COLUMN entry_price REAL",
"ALTER TABLE review_records ADD COLUMN stop_loss REAL", "ALTER TABLE review_records ADD COLUMN stop_loss REAL",
@@ -1057,6 +1058,8 @@ def ai_messages_page():
@app.route("/keys") @app.route("/keys")
@login_required @login_required
def keys(): def keys():
from key_monitor_lib import key_monitor_periods
conn = get_db() conn = get_db()
key_list = conn.execute( key_list = conn.execute(
"SELECT * FROM key_monitors WHERE status='active' OR status IS NULL ORDER BY id DESC" "SELECT * FROM key_monitors WHERE status='active' OR status IS NULL ORDER BY id DESC"
@@ -1065,7 +1068,12 @@ def keys():
"SELECT * FROM key_monitors WHERE status='archived' ORDER BY archived_at DESC LIMIT 100" "SELECT * FROM key_monitors WHERE status='archived' ORDER BY archived_at DESC LIMIT 100"
).fetchall() ).fetchall()
conn.close() conn.close()
return render_template("keys.html", keys=key_list, history=history) return render_template(
"keys.html",
keys=key_list,
history=history,
key_periods=key_monitor_periods(),
)
@@ -1103,21 +1111,26 @@ def add_key():
if trailing_be and risk_reward < 3: if trailing_be and risk_reward < 3:
risk_reward = 3.0 risk_reward = 3.0
from key_monitor_lib import normalize_bar_period
bar_period = normalize_bar_period(d.get("bar_period") or "5m")
direction = (d.get("direction") or "").strip().lower() direction = (d.get("direction") or "").strip().lower()
if monitor_type in ("箱体突破", "收敛突破"): if monitor_type == "箱体突破":
direction = direction or "long" if direction not in ("long", "short"):
flash("箱体突破须选择上方向(做多/做空)")
return redirect(url_for("keys"))
else: else:
direction = direction or "long" direction = ""
conn = get_db() conn = get_db()
conn.execute( conn.execute(
"""INSERT INTO key_monitors """INSERT INTO key_monitors
(symbol, symbol_name, market_code, sina_code, monitor_type, direction, (symbol, symbol_name, market_code, sina_code, monitor_type, direction,
upper, lower, trade_mode, risk_reward, trailing_be) upper, lower, trade_mode, risk_reward, trailing_be, bar_period)
VALUES (?,?,?,?,?,?,?,?,?,?,?)""", VALUES (?,?,?,?,?,?,?,?,?,?,?,?)""",
( (
symbol, symbol_name, market_code, sina_code, monitor_type, direction, symbol, symbol_name, market_code, sina_code, monitor_type, direction,
upper, lower, trade_mode, risk_reward, trailing_be, upper, lower, trade_mode, risk_reward, trailing_be, bar_period,
), ),
) )
conn.commit() conn.commit()
+8
View File
@@ -2933,6 +2933,7 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
def _execute_key_breakout(conn, row, bar, break_side): def _execute_key_breakout(conn, row, bar, break_side):
"""关键位箱体/收敛:5m 收盘突破后自动市价开仓。""" """关键位箱体/收敛:5m 收盘突破后自动市价开仓。"""
from key_monitor_lib import ( from key_monitor_lib import (
TYPE_BOX,
calc_breakout_sl_tp, calc_breakout_sl_tp,
format_auto_breakout_msg, format_auto_breakout_msg,
normalize_monitor_type, normalize_monitor_type,
@@ -2966,6 +2967,13 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se
detail=detail, detail=detail,
)) ))
if monitor_type == TYPE_BOX:
cfg_dir = (row.get("direction") or "").strip().lower()
if cfg_dir in ("long", "short") and direction != cfg_dir:
dir_cn = "做多" if cfg_dir == "long" else "做空"
_notify(False, f"突破方向与上方向({dir_cn})不一致", entry=0, sl=0, tp=0, lots=0)
return False, "突破方向与上方向不一致"
try: try:
init_strategy_tables(conn) init_strategy_tables(conn)
mode = get_trading_mode(get_setting) mode = get_trading_mode(get_setting)
+49 -10
View File
@@ -27,8 +27,38 @@ ZONE_TYPES = (TYPE_ZONE, "关键阻力位", "关键支撑位")
ALERT_MAX_PUSH = 3 ALERT_MAX_PUSH = 3
ALERT_INTERVAL_SEC = 300 ALERT_INTERVAL_SEC = 300
SL_TICK_BUFFER = 2 SL_TICK_BUFFER = 2
BAR_PERIOD = "5m" DEFAULT_BAR_PERIOD = "5m"
BAR_MINUTES = 5
PERIOD_MINUTES_MAP = {
"1m": 1, "2m": 2, "3m": 3, "5m": 5, "15m": 15, "30m": 30,
"1h": 60, "2h": 120, "4h": 240, "d": 1440, "1d": 1440,
}
def key_monitor_periods() -> list[dict[str, str]]:
"""关键位监控可选 K 线周期(触发用)。"""
from kline_chart import MARKET_PERIODS
allowed = frozenset({"5m", "15m", "30m", "1h", "2h", "4h", "d"})
return [p for p in MARKET_PERIODS if p["key"] in allowed]
def normalize_bar_period(raw: str) -> str:
valid = {p["key"] for p in key_monitor_periods()}
k = (raw or DEFAULT_BAR_PERIOD).strip()
return k if k in valid else DEFAULT_BAR_PERIOD
def bar_period_label(key: str) -> str:
k = normalize_bar_period(key)
for p in key_monitor_periods():
if p["key"] == k:
return p["label"]
return k
def bar_period_minutes(period: str) -> int:
return PERIOD_MINUTES_MAP.get(normalize_bar_period(period), 5)
def normalize_monitor_type(raw: str) -> str: def normalize_monitor_type(raw: str) -> str:
@@ -95,14 +125,19 @@ def _parse_bar_time(raw: str) -> Optional[datetime]:
return None return None
def last_closed_bar(bars: list[dict], now: Optional[datetime] = None) -> Optional[dict]: def last_closed_bar(
bars: list[dict],
period_minutes: int = 5,
now: Optional[datetime] = None,
) -> Optional[dict]:
"""取最近一根已收盘 K 线。""" """取最近一根已收盘 K 线。"""
dnow = now or datetime.now(TZ) dnow = now or datetime.now(TZ)
mins = max(1, int(period_minutes or 5))
for bar in reversed(bars or []): for bar in reversed(bars or []):
dt = _parse_bar_time(str(bar.get("time") or "")) dt = _parse_bar_time(str(bar.get("time") or ""))
if not dt: if not dt:
continue continue
bar_end = dt + timedelta(minutes=BAR_MINUTES) bar_end = dt + timedelta(minutes=mins)
if dnow >= bar_end: if dnow >= bar_end:
return bar return bar
return None return None
@@ -116,24 +151,26 @@ def detect_break_side(close: float, upper: float, lower: float) -> Optional[str]
return None return None
def fetch_closed_5m_bar( def fetch_closed_bar(
sym: str, sym: str,
period: str,
*, *,
db_path: str, db_path: str,
trading_mode: str, trading_mode: str,
) -> Optional[dict]: ) -> Optional[dict]:
p = normalize_bar_period(period)
try: try:
data = fetch_market_klines( data = fetch_market_klines(
sym, sym,
BAR_PERIOD, p,
db_path=db_path, db_path=db_path,
trading_mode=trading_mode, trading_mode=trading_mode,
prefer_ctp=True, prefer_ctp=True,
) )
bars = data.get("bars") or [] bars = data.get("bars") or []
return last_closed_bar(bars) return last_closed_bar(bars, bar_period_minutes(p))
except Exception as exc: except Exception as exc:
logger.debug("key monitor kline %s: %s", sym, exc) logger.debug("key monitor kline %s %s: %s", sym, p, exc)
return None return None
@@ -201,9 +238,10 @@ def format_auto_breakout_msg(
break_label, _ = break_direction_label(break_side) break_label, _ = break_direction_label(break_side)
dir_cn = "做多" if direction == "long" else "做空" dir_cn = "做多" if direction == "long" else "做空"
rr = float(row.get("risk_reward") or 2) rr = float(row.get("risk_reward") or 2)
period_label = bar_period_label(row.get("bar_period") or DEFAULT_BAR_PERIOD)
lines = [ lines = [
f"{'' if ok else ''} {name} {typ}自动单", f"{'' if ok else ''} {name} {typ}自动单",
f"5m 收盘:{bar_time}", f"{period_label} 收盘:{bar_time}",
f"🎯 {break_label} · {trade_mode} · {dir_cn}", f"🎯 {break_label} · {trade_mode} · {dir_cn}",
f"💹 入场:{entry:g} 止损:{sl:g} 止盈:{tp:g}(盈亏比 {rr:g}", f"💹 入场:{entry:g} 止损:{sl:g} 止盈:{tp:g}(盈亏比 {rr:g}",
f"📦 手数:{lots}", f"📦 手数:{lots}",
@@ -333,7 +371,8 @@ def run_key_monitor_check(
archive_monitor(conn, pid) archive_monitor(conn, pid)
continue continue
bar = fetch_closed_5m_bar(sym, db_path=db_path, trading_mode=mode) bar_period = normalize_bar_period(row.get("bar_period") or DEFAULT_BAR_PERIOD)
bar = fetch_closed_bar(sym, bar_period, db_path=db_path, trading_mode=mode)
if not bar: if not bar:
continue continue
bar_time = str(bar.get("time") or "")[:19] bar_time = str(bar.get("time") or "")[:19]
+17 -8
View File
@@ -3,11 +3,20 @@
.key-rules-body{padding:.35rem 0 .15rem} .key-rules-body{padding:.35rem 0 .15rem}
.key-rules-body ul{margin:.25rem 0 .5rem 1.1rem;padding:0} .key-rules-body ul{margin:.25rem 0 .5rem 1.1rem;padding:0}
.key-rules-body li{margin:.15rem 0} .key-rules-body li{margin:.15rem 0}
.key-check{display:inline-flex;align-items:center;gap:.35rem;font-size:.82rem;flex:1;min-width:0;margin:0} .key-form-rows{display:flex;flex-direction:column;gap:.75rem;margin-bottom:.85rem}
.key-check-text{white-space:nowrap;overflow:hidden;text-overflow:ellipsis} .key-form-line{display:grid;gap:.65rem;align-items:end}
.line-key-actions{display:flex;align-items:center;justify-content:space-between;gap:.75rem;flex-wrap:nowrap} .key-form-line.line-3{grid-template-columns:1.4fr .85fr .85fr}
.line-key-actions.is-hidden{display:none!important} .key-form-line.line-2{grid-template-columns:1fr 1fr}
.line-key-actions .key-submit-btn{flex-shrink:0;min-width:5.5rem;padding:.55rem 1.1rem} .key-field label{display:block;font-size:.72rem;margin-bottom:.28rem;color:var(--text-label)}
.line-key-actions.key-actions-zone{justify-content:flex-end} .key-field select,.key-field input{width:100%;box-sizing:border-box}
.line-key-actions.key-actions-zone .key-check{display:none} .key-action-row{display:flex;flex-direction:column;gap:.35rem;margin-top:.15rem}
#key-trade-mode-wrap.is-hidden,#key-rr-wrap.is-hidden{display:none!important} .key-action-row .trailing-be-toggle{display:flex;align-items:center;gap:.4rem;font-size:.78rem;color:var(--text-label);margin:0;cursor:pointer;user-select:none}
.key-action-row .trailing-be-toggle input{width:auto;margin:0;flex-shrink:0}
.key-trailing-hint{font-size:.72rem;margin:0;color:var(--text-muted);line-height:1.45}
.key-action-row .key-submit-btn{width:100%;padding:.65rem .75rem;font-size:.9rem;margin-top:.25rem}
#key-row-auto.is-hidden,#key-rr-wrap.is-hidden,#key-trade-mode-wrap.is-hidden,#key-direction-wrap.is-hidden,#key-trailing-wrap.is-hidden,#key-trailing-hint.is-hidden{display:none!important}
@media(max-width:720px){
.key-form-line.line-3{grid-template-columns:1fr 1fr}
.key-form-line.line-3 .key-field:first-child{grid-column:1/-1}
.key-form-line.line-2{grid-template-columns:1fr}
}
+13 -4
View File
@@ -5,26 +5,35 @@
(function () { (function () {
var keyTimer = null; var keyTimer = null;
var typeEl = document.getElementById('key-type'); var typeEl = document.getElementById('key-type');
var rowAuto = document.getElementById('key-row-auto');
var tradeModeWrap = document.getElementById('key-trade-mode-wrap'); var tradeModeWrap = document.getElementById('key-trade-mode-wrap');
var directionWrap = document.getElementById('key-direction-wrap');
var rrWrap = document.getElementById('key-rr-wrap'); var rrWrap = document.getElementById('key-rr-wrap');
var rrEl = document.getElementById('key-rr'); var rrEl = document.getElementById('key-rr');
var trailingWrap = document.getElementById('key-trailing-wrap'); var trailingWrap = document.getElementById('key-trailing-wrap');
var trailingHint = document.getElementById('key-trailing-hint');
var trailingEl = document.getElementById('key-trailing'); var trailingEl = document.getElementById('key-trailing');
var rowActions = document.getElementById('key-row-actions'); var directionEl = document.getElementById('key-direction');
var rowPrices = document.getElementById('key-row-prices');
function isAutoType(typ) { function isAutoType(typ) {
return typ === '箱体突破' || typ === '收敛突破'; return typ === '箱体突破' || typ === '收敛突破';
} }
function isBoxType(typ) {
return typ === '箱体突破';
}
function syncKeyForm() { function syncKeyForm() {
var typ = typeEl ? typeEl.value : ''; var typ = typeEl ? typeEl.value : '';
var auto = isAutoType(typ); var auto = isAutoType(typ);
var box = isBoxType(typ);
if (rowAuto) rowAuto.classList.toggle('is-hidden', !auto);
if (tradeModeWrap) tradeModeWrap.classList.toggle('is-hidden', !auto); if (tradeModeWrap) tradeModeWrap.classList.toggle('is-hidden', !auto);
if (rrWrap) rrWrap.classList.toggle('is-hidden', !auto); if (rrWrap) rrWrap.classList.toggle('is-hidden', !auto);
if (directionWrap) directionWrap.classList.toggle('is-hidden', !box);
if (trailingWrap) trailingWrap.classList.toggle('is-hidden', !auto); if (trailingWrap) trailingWrap.classList.toggle('is-hidden', !auto);
if (rowActions) rowActions.classList.toggle('key-actions-zone', !auto); if (trailingHint) trailingHint.classList.toggle('is-hidden', !auto);
if (rowPrices) rowPrices.classList.toggle('key-zone-mode', !auto); if (directionEl) directionEl.disabled = !box;
if (!auto && trailingEl) trailingEl.checked = false; if (!auto && trailingEl) trailingEl.checked = false;
if (auto && trailingEl && trailingEl.checked && rrEl) { if (auto && trailingEl && trailingEl.checked && rrEl) {
if (parseFloat(rrEl.value) < 3) rrEl.value = '3'; if (parseFloat(rrEl.value) < 3) rrEl.value = '3';
+80 -39
View File
@@ -14,56 +14,88 @@
<div class="key-rules-body"> <div class="key-rules-body">
<p><strong>箱体突破 / 收敛突破(自动单)</strong></p> <p><strong>箱体突破 / 收敛突破(自动单)</strong></p>
<ul> <ul>
<li>触发:<strong>5 分钟 K 线收盘</strong>收在上沿或下沿之外</li> <li>触发:<strong>上周期 K 线收盘</strong>收在上沿或下沿之外(默认 5 分)</li>
<li>箱体突破须选<strong>上方向</strong>,仅当突破后下单方向一致时才自动开仓</li>
<li>顺势:上破做多、下破做空;反转:上破做空、下破做多</li> <li>顺势:上破做多、下破做空;反转:上破做空、下破做多</li>
<li>自动<strong>市价开仓</strong>;止损 = 突破 K 线极值 ± 2 跳</li> <li>自动<strong>市价开仓</strong>;止损 = 突破 K 线极值 ± 2 跳</li>
<li>盈亏比默认 2(可改);开启移动保本时默认 3,达目标价自动止盈</li> <li>盈亏比默认 2(可改);开启移动保本时默认 3,达目标价自动止盈</li>
<li>成交后进入「下单监控」持仓,来源备注为监控类型</li>
</ul> </ul>
<p><strong>关键支阻区(仅提醒)</strong></p> <p><strong>关键支阻区(仅提醒)</strong></p>
<ul> <ul>
<li>上沿 = 阻力,下沿 = 支撑,合并为一个区间</li> <li>上沿 = 阻力,下沿 = 支撑,合并为一个区间</li>
<li>5m 收盘突破上沿或跌破下沿 → 微信推送(最多 3 次,间隔约 5 分钟)</li> <li>上周期 K 线收盘突破 → 微信推送(最多 3 次,间隔约 5 分钟)</li>
<li>推送完毕后自动结案,<strong>不参与自动开仓</strong></li> <li>推送完毕后自动结案,<strong>不参与自动开仓</strong></li>
</ul> </ul>
</div> </div>
</details> </details>
<form action="{{ url_for('add_key') }}" method="post" class="form-compact" id="key-add-form"> <form action="{{ url_for('add_key') }}" method="post" id="key-add-form">
<div class="form-line line-3"> <div class="key-form-rows">
<div class="symbol-wrap symbol-mains"> <div class="key-form-line line-3">
<input type="text" class="symbol-input" placeholder="主力合约" autocomplete="off" required> <div class="key-field symbol-wrap symbol-mains">
<input type="hidden" name="symbol" required> <label class="text-label">品种</label>
<input type="hidden" name="symbol_name"> <input type="text" class="symbol-input" placeholder="输入中文或代码,选择主力合约" autocomplete="off" required>
<input type="hidden" name="market_code" required> <input type="hidden" name="symbol" required>
<input type="hidden" name="sina_code"> <input type="hidden" name="symbol_name">
<div class="symbol-dropdown"></div> <input type="hidden" name="market_code" required>
<div class="symbol-selected"></div> <input type="hidden" name="sina_code">
<div class="symbol-dropdown"></div>
<div class="symbol-selected"></div>
</div>
<div class="key-field">
<label class="text-label">类型</label>
<select name="type" id="key-type" required>
<option value="箱体突破">箱体突破</option>
<option value="收敛突破">收敛突破</option>
<option value="关键支阻区">关键支阻区</option>
</select>
</div>
<div class="key-field">
<label class="text-label">上周期</label>
<select name="bar_period" id="key-bar-period">
{% for p in key_periods %}
<option value="{{ p.key }}"{% if p.key == '5m' %} selected{% endif %}>{{ p.label }}</option>
{% endfor %}
</select>
</div>
</div> </div>
<select name="type" id="key-type" required> <div class="key-form-line line-3" id="key-row-auto">
<option value="箱体突破">箱体突破</option> <div class="key-field" id="key-trade-mode-wrap">
<option value="收敛突破">收敛突破</option> <label class="text-label">模式</label>
<option value="关键支阻区">关键支阻区</option> <select name="trade_mode" id="key-trade-mode">
</select> <option value="顺势">顺势</option>
<div id="key-trade-mode-wrap"> <option value="反转">反转</option>
<select name="trade_mode" id="key-trade-mode"> </select>
<option value="顺势">顺势</option> </div>
<option value="反转">反转</option> <div class="key-field" id="key-direction-wrap">
</select> <label class="text-label">上方向</label>
<select name="direction" id="key-direction">
<option value="long">做多</option>
<option value="short">做空</option>
</select>
</div>
<div class="key-field" id="key-rr-wrap">
<label class="text-label">盈亏比</label>
<input name="risk_reward" id="key-rr" type="number" step="0.1" min="0.5" max="10" value="2">
</div>
</div> </div>
</div> <div class="key-form-line line-2" id="key-row-prices">
<div class="form-line line-3" id="key-row-prices"> <div class="key-field">
<input name="upper" type="number" step="0.0001" placeholder="上沿(阻力)" required> <label class="text-label">上沿(阻力)</label>
<input name="lower" type="number" step="0.0001" placeholder="下沿(支撑)" required> <input name="upper" type="number" step="0.0001" required>
<div id="key-rr-wrap"> </div>
<input name="risk_reward" id="key-rr" type="number" step="0.1" min="0.5" max="10" value="2" placeholder="盈亏比"> <div class="key-field">
<label class="text-label">下沿(支撑)</label>
<input name="lower" type="number" step="0.0001" required>
</div>
</div>
<div class="key-action-row" id="key-row-actions">
<label class="trailing-be-toggle" id="key-trailing-wrap">
<input type="checkbox" name="trailing_be" id="key-trailing" value="1">
<span>移动保本</span>
</label>
<p class="hint key-trailing-hint" id="key-trailing-hint">开启后盈亏比默认 3,达 3R 自动止盈并启用移动止损</p>
<button type="submit" class="btn-primary key-submit-btn">添加</button>
</div> </div>
</div>
<div class="form-line line-key-actions" id="key-row-actions">
<label class="key-check" id="key-trailing-wrap">
<input type="checkbox" name="trailing_be" id="key-trailing" value="1">
<span class="key-check-text">移动保本(默认盈亏比 3,达 3R 止盈)</span>
</label>
<button type="submit" class="btn-primary key-submit-btn">添加</button>
</div> </div>
</form> </form>
<h3 class="section-label">监控列表</h3> <h3 class="section-label">监控列表</h3>
@@ -72,8 +104,12 @@
<div class="list-item key-item" data-key-id="{{ k.id }}" style="padding:.75rem;font-size:.85rem"> <div class="list-item key-item" data-key-id="{{ k.id }}" style="padding:.75rem;font-size:.85rem">
<div> <div>
<strong>{{ k.symbol_name or k.symbol }}</strong> {{ k.monitor_type }} <strong>{{ k.symbol_name or k.symbol }}</strong> {{ k.monitor_type }}
<span class="badge planned">{{ k.bar_period or '5m' }}</span>
{% if k.monitor_type in ('箱体突破', '收敛突破') %} {% if k.monitor_type in ('箱体突破', '收敛突破') %}
<span class="badge planned">{{ k.trade_mode or '顺势' }}</span> <span class="badge planned">{{ k.trade_mode or '顺势' }}</span>
{% if k.monitor_type == '箱体突破' and k.direction %}
<span class="badge profit">{{ '做多' if k.direction == 'long' else '做空' }}</span>
{% endif %}
{% endif %} {% endif %}
{% if k.trailing_be %} {% if k.trailing_be %}
<span class="badge profit">移动保本</span> <span class="badge profit">移动保本</span>
@@ -101,15 +137,20 @@
<h2>监控历史</h2> <h2>监控历史</h2>
<div class="card-body card-scroll"> <div class="card-body card-scroll">
<table> <table>
<thead><tr><th>品种</th><th>类型</th><th>模式</th><th>上沿</th><th>下沿</th><th>归档</th></tr></thead> <thead><tr><th>品种</th><th>类型</th><th>周期</th><th>模式</th><th>上沿</th><th>下沿</th><th>归档</th></tr></thead>
<tbody> <tbody>
{% for k in history %} {% for k in history %}
<tr> <tr>
<td>{{ k.symbol_name or k.symbol }}</td> <td>{{ k.symbol_name or k.symbol }}</td>
<td>{{ k.monitor_type }}</td> <td>{{ k.monitor_type }}</td>
<td>{{ k.bar_period or '5m' }}</td>
<td> <td>
{% if k.monitor_type in ('箱体突破', '收敛突破') %} {% if k.monitor_type in ('箱体突破', '收敛突破') %}
{{ k.trade_mode or '顺势' }}{% if k.trailing_be %} · 移动保本{% endif %} {{ k.trade_mode or '顺势' }}
{% if k.monitor_type == '箱体突破' and k.direction %}
· {{ '做多' if k.direction == 'long' else '做空' }}
{% endif %}
{% if k.trailing_be %} · 移动保本{% endif %}
{% elif k.monitor_type in ('关键阻力位', '关键支撑位') %} {% elif k.monitor_type in ('关键阻力位', '关键支撑位') %}
支阻区 支阻区
{% else %} {% else %}
@@ -121,7 +162,7 @@
<td>{{ k.archived_at[:16] if k.archived_at else '' }}</td> <td>{{ k.archived_at[:16] if k.archived_at else '' }}</td>
</tr> </tr>
{% else %} {% else %}
<tr><td colspan="6" class="text-muted">暂无历史</td></tr> <tr><td colspan="7" class="text-muted">暂无历史</td></tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>