翻倍出场监控中按钮改为取消;中控目标监控列显示倍数如1倍。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1500,6 +1500,13 @@
|
||||
);
|
||||
}
|
||||
|
||||
function formatProfitExitMultLabel(mult) {
|
||||
const n = Number(mult);
|
||||
if (!Number.isFinite(n) || n <= 0) return "1倍";
|
||||
if (Math.abs(n - Math.round(n)) < 1e-9) return String(Math.round(n)) + "倍";
|
||||
return fmt(n, 2) + "倍";
|
||||
}
|
||||
|
||||
function renderProfitExitRow(p) {
|
||||
const inst = p.inst_id || "";
|
||||
if (p.hedge_plan_target) {
|
||||
@@ -1511,10 +1518,9 @@
|
||||
: 1;
|
||||
const state = String(p.profit_exit_state || (enabled ? "active" : "idle"));
|
||||
const req = p.profit_exit_required_recycle;
|
||||
let statusTxt = enabled
|
||||
? ("监控中 · " + fmt(mult, 2) + "倍")
|
||||
: "未开启";
|
||||
if (enabled && state === "closing") statusTxt = "平仓挂单中 · " + fmt(mult, 2) + "倍";
|
||||
const multLabel = formatProfitExitMultLabel(mult);
|
||||
let statusTxt = enabled ? ("监控中 · " + multLabel) : "未开启";
|
||||
if (enabled && state === "closing") statusTxt = "平仓挂单中 · " + multLabel;
|
||||
return (
|
||||
'<div class="opt-target-row opt-profit-exit-pos-row" data-inst="' + inst + '">' +
|
||||
'<span class="opt-target-row-label">翻倍</span>' +
|
||||
@@ -1523,7 +1529,8 @@
|
||||
'<input type="number" class="opt-pos-profit-exit-mult" data-inst="' + inst +
|
||||
'" min="0.1" step="0.1" value="' + mult + '"' + (enabled ? "" : " disabled") + ">" +
|
||||
'<button type="button" class="btn-secondary opt-profit-exit-save-btn" data-inst="' +
|
||||
inst + '">应用</button>' +
|
||||
inst + '" data-mode="' + (enabled ? "cancel" : "apply") + '">' +
|
||||
(enabled ? "取消" : "应用") + "</button>" +
|
||||
'<span class="opt-target-armed">' + statusTxt + "</span>" +
|
||||
'<span class="muted opt-target-row-hint">' +
|
||||
(enabled
|
||||
@@ -1853,7 +1860,10 @@
|
||||
const row = card ? card.querySelector(".opt-profit-exit-pos-row") : null;
|
||||
const enabledEl = row ? row.querySelector(".opt-pos-profit-exit-enabled") : null;
|
||||
const multEl = row ? row.querySelector(".opt-pos-profit-exit-mult") : null;
|
||||
const enabled = !!(enabledEl && enabledEl.checked);
|
||||
const mode = btn && btn.getAttribute("data-mode");
|
||||
let enabled = !!(enabledEl && enabledEl.checked);
|
||||
if (mode === "cancel") enabled = false;
|
||||
if (mode === "apply") enabled = true;
|
||||
let mult = 1;
|
||||
if (enabled) {
|
||||
mult = parseFloat(multEl ? multEl.value : "1");
|
||||
|
||||
@@ -121,20 +121,40 @@ def _resolve_options_source(conn, inst_id: str) -> tuple[str, str, int | None]:
|
||||
return default
|
||||
|
||||
|
||||
def _format_profit_exit_mult(mult: Any) -> str:
|
||||
try:
|
||||
n = float(mult)
|
||||
except (TypeError, ValueError):
|
||||
return "1倍"
|
||||
if n <= 0:
|
||||
return "1倍"
|
||||
if abs(n - round(n)) < 1e-9:
|
||||
return f"{int(round(n))}倍"
|
||||
return f"{n:g}倍"
|
||||
|
||||
|
||||
def _format_options_target(p: dict[str, Any]) -> str:
|
||||
hedge = p.get("hedge_plan_target") if isinstance(p.get("hedge_plan_target"), dict) else None
|
||||
opt_type = str(p.get("opt_type") or p.get("optType") or "").upper()
|
||||
if hedge:
|
||||
rr = _safe_float(hedge.get("profit_rr"))
|
||||
pid = hedge.get("plan_id")
|
||||
if rr is not None and rr > 0:
|
||||
return f"对冲#{pid} 盈亏比 {rr:g}" if pid is not None else f"盈亏比 {rr:g}"
|
||||
ot = str(hedge.get("opt_type") or opt_type).upper()
|
||||
side = "Put ≤" if ot == "P" else "Call ≥"
|
||||
tgt = _safe_float(hedge.get("target_index"))
|
||||
pid = hedge.get("plan_id")
|
||||
if tgt is not None:
|
||||
return f"对冲#{pid} {side} {tgt:g}" if pid is not None else f"{side} {tgt:g}"
|
||||
parts: list[str] = []
|
||||
tgt = _safe_float(p.get("target_index"))
|
||||
if tgt is not None and tgt > 0:
|
||||
side = "Put ≤" if opt_type == "P" else "Call ≥"
|
||||
return f"{side} {tgt:g}"
|
||||
parts.append(f"{side} {tgt:g}")
|
||||
if p.get("profit_exit_enabled"):
|
||||
parts.append(_format_profit_exit_mult(p.get("profit_exit_mult")))
|
||||
if parts:
|
||||
return " · ".join(parts)
|
||||
return "—"
|
||||
|
||||
|
||||
@@ -350,11 +370,39 @@ def collect_options_items(
|
||||
raw = fetch_options_positions() or []
|
||||
except Exception:
|
||||
return []
|
||||
pe_map: dict[str, dict[str, Any]] = {}
|
||||
tgt_map: dict[str, dict[str, Any]] = {}
|
||||
hedge_map: dict[str, dict[str, Any]] = {}
|
||||
if conn is not None:
|
||||
try:
|
||||
from lib.options.options_profit_exit_lib import profit_exit_by_inst
|
||||
from lib.options.options_target_lib import targets_by_inst
|
||||
from lib.hedge_plan.hedge_plan_db import active_options_targets_by_inst
|
||||
|
||||
pe_map = profit_exit_by_inst(conn)
|
||||
tgt_map = targets_by_inst(conn)
|
||||
hedge_map = active_options_targets_by_inst(conn)
|
||||
except Exception:
|
||||
pe_map, tgt_map, hedge_map = {}, {}, {}
|
||||
out: list[dict[str, Any]] = []
|
||||
for p in raw:
|
||||
if not isinstance(p, dict):
|
||||
continue
|
||||
out.append(_format_options_item(p, conn=conn))
|
||||
row = dict(p)
|
||||
inst = str(row.get("inst_id") or row.get("instId") or "").strip()
|
||||
mon = tgt_map.get(inst)
|
||||
if mon:
|
||||
row["target_index"] = mon.get("target_index")
|
||||
pe = pe_map.get(inst)
|
||||
if pe:
|
||||
row["profit_exit_enabled"] = pe.get("profit_exit_enabled")
|
||||
row["profit_exit_mult"] = pe.get("profit_exit_mult")
|
||||
hedge = hedge_map.get(inst)
|
||||
if hedge:
|
||||
row["hedge_plan_target"] = hedge
|
||||
if not mon:
|
||||
row["target_index"] = hedge.get("target_index")
|
||||
out.append(_format_options_item(row, conn=conn))
|
||||
return out
|
||||
|
||||
|
||||
|
||||
@@ -28,18 +28,36 @@ def build_options_hub_snapshot(cfg: dict[str, Any]) -> dict[str, Any]:
|
||||
conn = cfg["get_db"]()
|
||||
try:
|
||||
from lib.hedge_plan.hedge_plan_db import active_options_targets_by_inst
|
||||
from lib.options.options_profit_exit_lib import profit_exit_by_inst
|
||||
from lib.options.options_target_lib import list_active_targets, list_closing_targets, targets_by_inst
|
||||
|
||||
target_monitors = list_active_targets(conn) + list_closing_targets(conn)
|
||||
tgt_map = targets_by_inst(conn)
|
||||
hedge_target_map = active_options_targets_by_inst(conn)
|
||||
profit_exit_map = profit_exit_by_inst(conn)
|
||||
target_monitors.extend(hedge_target_map.values())
|
||||
for pe in profit_exit_map.values():
|
||||
if pe.get("profit_exit_enabled"):
|
||||
target_monitors.append(
|
||||
{
|
||||
"inst_id": pe.get("inst_id"),
|
||||
"exit_mode": "profit_exit",
|
||||
"profit_exit_mult": pe.get("profit_exit_mult"),
|
||||
"profit_exit_enabled": True,
|
||||
}
|
||||
)
|
||||
for p in positions:
|
||||
mon = tgt_map.get(str(p.get("inst_id") or ""))
|
||||
if mon:
|
||||
p["target_index"] = mon.get("target_index")
|
||||
p["target_monitor_id"] = mon.get("id")
|
||||
p["target_monitor"] = mon
|
||||
pe = profit_exit_map.get(str(p.get("inst_id") or ""))
|
||||
if pe:
|
||||
p["profit_exit_enabled"] = pe.get("profit_exit_enabled")
|
||||
p["profit_exit_mult"] = pe.get("profit_exit_mult")
|
||||
p["profit_exit_state"] = pe.get("profit_exit_state")
|
||||
p["profit_exit_required_recycle"] = pe.get("required_recycle")
|
||||
hedge_target = hedge_target_map.get(str(p.get("inst_id") or ""))
|
||||
if hedge_target:
|
||||
p["hedge_plan_target"] = hedge_target
|
||||
|
||||
@@ -350,4 +350,4 @@
|
||||
</div>
|
||||
</div>
|
||||
<script src="/static/options_expiry_countdown.js?v=1"></script>
|
||||
<script src="/static/options_panel.js?v=60"></script>
|
||||
<script src="/static/options_panel.js?v=61"></script>
|
||||
|
||||
@@ -3928,9 +3928,15 @@
|
||||
);
|
||||
}
|
||||
|
||||
function renderOptionsTargetCell(target) {
|
||||
if (!target) return "<td>—</td>";
|
||||
if (target.managed_by === "hedge_plan") {
|
||||
function formatProfitExitMultLabel(mult) {
|
||||
const n = Number(mult);
|
||||
if (!Number.isFinite(n) || n <= 0) return "1倍";
|
||||
if (Math.abs(n - Math.round(n)) < 1e-9) return String(Math.round(n)) + "倍";
|
||||
return fmt(n, 2) + "倍";
|
||||
}
|
||||
|
||||
function renderOptionsTargetCell(target, pos) {
|
||||
if (target && target.managed_by === "hedge_plan") {
|
||||
const rr = target.profit_rr != null ? Number(target.profit_rr) : null;
|
||||
if (rr != null && rr > 0) {
|
||||
return `<td class="hub-opt-target-cell is-on is-hedge" title="由对冲计划监控">对冲#${esc(target.plan_id)} 盈亏比 ${esc(fmt(rr, 2))}</td>`;
|
||||
@@ -3939,9 +3945,30 @@
|
||||
const px = target.target_index != null ? fmt(target.target_index, 1) : "—";
|
||||
return `<td class="hub-opt-target-cell is-on is-hedge" title="由对冲计划监控">对冲#${esc(target.plan_id)} ${esc(side)} ${esc(px)}</td>`;
|
||||
}
|
||||
const side = String(target.opt_type || "").toUpperCase() === "P" ? "Put≤" : "Call≥";
|
||||
const px = target.target_index != null ? fmt(target.target_index, 1) : "—";
|
||||
return `<td class="hub-opt-target-cell is-on" title="目标监控">${esc(side)} ${esc(px)}</td>`;
|
||||
const parts = [];
|
||||
const hasIndex =
|
||||
target &&
|
||||
target.exit_mode !== "profit_exit" &&
|
||||
target.target_index != null &&
|
||||
Number(target.target_index) > 0;
|
||||
if (hasIndex) {
|
||||
const side = String(target.opt_type || (pos && pos.opt_type) || "").toUpperCase() === "P" ? "Put≤" : "Call≥";
|
||||
parts.push(side + " " + fmt(target.target_index, 1));
|
||||
}
|
||||
const peOn =
|
||||
!!(pos && pos.profit_exit_enabled) ||
|
||||
!!(target && (target.exit_mode === "profit_exit" || target.profit_exit_enabled));
|
||||
if (peOn) {
|
||||
const mult =
|
||||
pos && pos.profit_exit_mult != null
|
||||
? pos.profit_exit_mult
|
||||
: target && target.profit_exit_mult != null
|
||||
? target.profit_exit_mult
|
||||
: 1;
|
||||
parts.push(formatProfitExitMultLabel(mult));
|
||||
}
|
||||
if (!parts.length) return "<td>—</td>";
|
||||
return `<td class="hub-opt-target-cell is-on" title="目标监控">${esc(parts.join(" · "))}</td>`;
|
||||
}
|
||||
|
||||
function renderOptionsPositionsTable(pos, targets) {
|
||||
@@ -3970,7 +3997,7 @@
|
||||
<td>${esc(optType)}</td>
|
||||
<td>${esc(p.pos)}</td>
|
||||
<td>${optionsExpiryCdHtml(p.exp_time_ms != null ? p.exp_time_ms : p.exp_time)}</td>
|
||||
${renderOptionsTargetCell(target)}`;
|
||||
${renderOptionsTargetCell(target, p)}`;
|
||||
if (showPnl) {
|
||||
html += `<td class="${pnlCls(net)}">${net == null ? "—" : fmt(net, 2)}</td>
|
||||
<td class="${pnlCls(net)}">${roi == null ? "—" : esc(Number(roi).toFixed(2)) + "%"}</td>`;
|
||||
|
||||
@@ -1767,6 +1767,6 @@
|
||||
<script src="/assets/options_expiry_countdown.js?v=1"></script>
|
||||
<script src="/assets/options_position_cards.js?v=4"></script>
|
||||
<script src="/assets/backup.js?v=1"></script>
|
||||
<script src="/assets/app.js?v=20260807-opt-float"></script>
|
||||
<script src="/assets/app.js?v=20260812-profit-exit"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user