feat(hub): trend plan breakeven and stop from monitor fullscreen
Proxy /api/hub/trend/stop and breakeven to instances; enable offset input and actions in hub UI. Add horizontal padding on strategy records page. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1426,6 +1426,22 @@
|
||||
});
|
||||
};
|
||||
});
|
||||
box.querySelectorAll(".btn-hub-trend-stop").forEach((btn) => {
|
||||
btn.onclick = (ev) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
hubTrendPlanStop(btn.dataset.exId, btn.dataset.planId);
|
||||
};
|
||||
});
|
||||
box.querySelectorAll(".btn-hub-trend-be").forEach((btn) => {
|
||||
btn.onclick = (ev) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
const card = btn.closest(".hub-trend-plan-card");
|
||||
const inp = card ? card.querySelector(".hub-plan-be-input") : null;
|
||||
hubTrendPlanBreakeven(btn.dataset.exId, btn.dataset.planId, inp);
|
||||
};
|
||||
});
|
||||
box.querySelectorAll(".btn-close-ex").forEach((btn) => {
|
||||
btn.onclick = () => closeOne(btn.dataset.id);
|
||||
});
|
||||
@@ -1668,18 +1684,27 @@
|
||||
? `≈${fmt(t.plan_margin_capital, 2)}U`
|
||||
: "—";
|
||||
const levTxt = t.leverage != null && t.leverage !== "" ? `${esc(t.leverage)}x` : "—";
|
||||
const bePct =
|
||||
t.breakeven_offset_pct != null && t.breakeven_offset_pct !== ""
|
||||
? esc(t.breakeven_offset_pct)
|
||||
: "0.3";
|
||||
const bePctDefault =
|
||||
t.breakeven_default_offset_pct != null && t.breakeven_default_offset_pct !== ""
|
||||
? t.breakeven_default_offset_pct
|
||||
: t.breakeven_offset_pct != null && t.breakeven_offset_pct !== ""
|
||||
? t.breakeven_offset_pct
|
||||
: "0.3";
|
||||
const exId = exchangeRow && exchangeRow.id != null ? esc(exchangeRow.id) : "";
|
||||
const canOpen = !!(exchangeRow && (exchangeRow.flask_url_browser || exchangeRow.flask_url));
|
||||
const endBtn = canOpen
|
||||
? `<a href="#" class="btn-close-plan btn-open-instance" data-ex-id="${exId}" data-next="/stop_trend_pullback/${esc(t.id)}" data-confirm="结束计划:市价平仓并撤掉该合约全部挂单,确定?">结束计划</a>`
|
||||
const planId = esc(t.id);
|
||||
const caps = (exchangeRow && exchangeRow.capabilities) || [];
|
||||
const flaskOk =
|
||||
exchangeRow && exchangeRow.flask_ok !== false && (exchangeRow.hub_monitor || {}).ok !== false;
|
||||
const canHubTrend = !!(flaskOk && caps.includes("trend") && exId && planId);
|
||||
const beAppliedFlag = !!t.breakeven_applied;
|
||||
const endBtn = canHubTrend
|
||||
? `<button type="button" class="btn-close-plan btn-hub-trend-stop" data-ex-id="${exId}" data-plan-id="${planId}">结束计划</button>`
|
||||
: "";
|
||||
const beBtn = canOpen
|
||||
? `<a href="#" class="hub-plan-be-btn btn-open-instance" data-ex-id="${exId}" data-next="/strategy">保本移交下单监控</a>`
|
||||
: `<span class="hub-plan-be-btn hub-plan-be-btn--static">保本移交下单监控</span>`;
|
||||
const beBtn = canHubTrend && !beAppliedFlag
|
||||
? `<button type="button" class="hub-plan-be-btn btn-hub-trend-be" data-ex-id="${exId}" data-plan-id="${planId}">保本移交下单监控</button>`
|
||||
: beAppliedFlag
|
||||
? ""
|
||||
: `<span class="hub-plan-be-btn hub-plan-be-btn--static">保本移交下单监控</span>`;
|
||||
const beApplied =
|
||||
t.breakeven_applied
|
||||
? `<span class="hub-plan-be-done">已保本 ${esc(String(t.breakeven_applied_at || "").slice(0, 16))}</span>`
|
||||
@@ -1718,7 +1743,7 @@
|
||||
<div class="plan-card-meta hub-plan-breakeven-row">
|
||||
<label class="hub-plan-be-label">
|
||||
保本移交 偏移%
|
||||
<input type="number" disabled value="${bePct}" class="hub-plan-be-input" />
|
||||
<input type="number" min="0" step="0.01" value="${esc(bePctDefault)}" class="hub-plan-be-input" data-ex-id="${exId}" data-plan-id="${planId}" ${canHubTrend && !beAppliedFlag ? "" : "disabled"} />
|
||||
</label>
|
||||
${beBtn}
|
||||
${beApplied}
|
||||
@@ -2446,6 +2471,63 @@
|
||||
</div>`;
|
||||
}
|
||||
|
||||
async function hubTrendPlanStop(exchangeId, planId) {
|
||||
if (!exchangeId || !planId) {
|
||||
showToast("缺少交易所或计划 ID", true);
|
||||
return;
|
||||
}
|
||||
if (!confirm("结束计划:市价平仓并撤掉该合约全部挂单,确定?")) return;
|
||||
try {
|
||||
const r = await apiFetch("/api/trend/" + encodeURIComponent(exchangeId) + "/stop", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ plan_id: Number(planId) }),
|
||||
});
|
||||
const j = await r.json();
|
||||
showToast(j.message || (j.ok ? "已结束趋势回调计划" : "结束失败"), !j.ok);
|
||||
if (j.ok) refreshMonitorBoardNow();
|
||||
} catch (e) {
|
||||
showToast(String(e), true);
|
||||
}
|
||||
}
|
||||
|
||||
async function hubTrendPlanBreakeven(exchangeId, planId, inputEl) {
|
||||
if (!exchangeId || !planId) {
|
||||
showToast("缺少交易所或计划 ID", true);
|
||||
return;
|
||||
}
|
||||
const raw = inputEl ? String(inputEl.value || "").trim() : "";
|
||||
let pct = null;
|
||||
if (raw !== "") {
|
||||
pct = Number(raw);
|
||||
if (!Number.isFinite(pct) || pct < 0) {
|
||||
showToast("保本偏移% 须为非负数", true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (
|
||||
!confirm(
|
||||
"确认保本?将结束本趋势计划,持仓移交「下单监控」,并在交易所挂保本止损与计划止盈;后续平仓写入交易记录。"
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const body = { plan_id: Number(planId) };
|
||||
if (pct != null) body.breakeven_offset_pct = pct;
|
||||
const r = await apiFetch("/api/trend/" + encodeURIComponent(exchangeId) + "/breakeven", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
const j = await r.json();
|
||||
showToast(j.message || (j.ok ? "保本移交成功" : "保本移交失败"), !j.ok);
|
||||
if (j.ok) refreshMonitorBoardNow();
|
||||
} catch (e) {
|
||||
showToast(String(e), true);
|
||||
}
|
||||
}
|
||||
|
||||
async function closeOnePosition(exchangeId, symbol, side) {
|
||||
const label = `${symbol} · ${side}`;
|
||||
if (!confirm(`确认对该账户市价平仓:${label}?`)) return;
|
||||
|
||||
Reference in New Issue
Block a user