Restore Gate intraday close/TP-SL controls and document force-close rules.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-05 08:16:02 +08:00
parent 747434a65e
commit 55e94fe059
9 changed files with 44 additions and 82 deletions
-34
View File
@@ -2565,27 +2565,6 @@ def _merge_flask_exchange_tpsl(agent_row: dict, snap: dict | None, hub_mon: dict
)
_INTRADAY_CLOSE_BLOCK_MSG = (
"日内账户禁止中控手动平仓/改委托,请等待计划止损/止盈或整点强制清仓"
)
def _meta_intraday_discipline(meta: dict | None) -> bool:
if not isinstance(meta, dict):
return False
if meta.get("intraday_discipline") is True:
return True
return meta.get("order_entry_profile") == "intraday"
async def _fetch_exchange_intraday_discipline(
client: httpx.AsyncClient, ex: dict
) -> bool:
data = await _fetch_flask_json(client, ex, "/api/hub/meta")
meta = (data or {}).get("meta") if isinstance(data, dict) else None
return _meta_intraday_discipline(meta if isinstance(meta, dict) else None)
async def _fetch_exchange_flask_bundle(
client: httpx.AsyncClient, ex: dict, *, trading_day: str | None = None
) -> tuple[dict | None, dict | None, list | None, dict | None, dict | None, dict | None]:
@@ -2980,8 +2959,6 @@ async def api_close_position(exchange_id: str, body: ClosePositionBody):
raise HTTPException(status_code=400, detail="side 须为 long 或 short")
url = f"{ex['agent_url'].rstrip('/')}/emergency/close-position"
async with httpx.AsyncClient() as client:
if await _fetch_exchange_intraday_discipline(client, ex):
raise HTTPException(status_code=403, detail=_INTRADAY_CLOSE_BLOCK_MSG)
r = await client.post(
url,
headers=_agent_headers(),
@@ -3018,8 +2995,6 @@ async def api_place_tpsl(exchange_id: str, body: PlaceTpslBody):
raise HTTPException(status_code=404, detail="账户未启用")
url = f"{ex['agent_url'].rstrip('/')}/orders/place-tpsl"
async with httpx.AsyncClient() as client:
if await _fetch_exchange_intraday_discipline(client, ex):
raise HTTPException(status_code=403, detail=_INTRADAY_CLOSE_BLOCK_MSG)
r = await client.post(
url,
headers=_agent_headers(),
@@ -3077,8 +3052,6 @@ async def api_close_exchange(exchange_id: str):
raise HTTPException(status_code=404, detail="账户未启用")
url = f"{ex['agent_url'].rstrip('/')}/emergency/close-all"
async with httpx.AsyncClient() as client:
if await _fetch_exchange_intraday_discipline(client, ex):
raise HTTPException(status_code=403, detail=_INTRADAY_CLOSE_BLOCK_MSG)
r = await client.post(url, headers=_agent_headers(), timeout=120.0)
try:
body = r.json()
@@ -3115,13 +3088,6 @@ async def api_close_all(body: CloseAllBody | None = Body(default=None)):
async with httpx.AsyncClient() as client:
async def one(ex: dict):
if await _fetch_exchange_intraday_discipline(client, ex):
return {
"id": ex["id"],
"name": ex["name"],
"skipped": True,
"reason": _INTRADAY_CLOSE_BLOCK_MSG,
}
url = f"{ex['agent_url'].rstrip('/')}/emergency/close-all"
try:
r = await client.post(url, headers=_agent_headers(), timeout=120.0)
+5 -9
View File
@@ -3164,7 +3164,7 @@
const px = orderTriggerOrPrice(o);
const trig = px != null ? fmtSymbolPrice(px, symbol, tickMap) : "—";
const cancelBtn =
!intraday && oid && o.channel !== "plan"
oid && o.channel !== "plan"
? `<button type="button" class="pos-ex-cancel-btn btn-cancel-order" data-ex-id="${esc(exchangeId)}" data-symbol="${symAttr}" data-order-id="${oid}" data-channel="${ch}">撤单</button>`
: "";
const planHint = o.channel === "plan" ? '<span class="pos-ex-plan-hint">(下单监控)</span>' : "";
@@ -3491,9 +3491,7 @@
const tcSymBadge = !isTrend && mo.time_close_enabled ? timeCloseSymbolBadgeHtml(mo) : "";
const fcSymBadge = !isTrend && mo.force_close_enabled ? forceCloseSymbolBadgeHtml(mo) : "";
const mktAttrs = marketOpenBtnAttrs(exchangeId, exchangeKey, symbol, pos, monitorOrder, trendPlan);
const headActions = intraday
? ""
: `<div class="pos-head-actions">
const headActions = `<div class="pos-head-actions">
<button type="button" class="pos-entrust-btn btn-place-tpsl" data-ex-id="${esc(exchangeId)}" data-symbol="${symAttr}" data-side="${sideAttr}" data-contracts="${contractsAttr}" data-sl="${slAttr}" data-tp="${tpAttr}">委托</button>
<button type="button" class="pos-close-btn btn-close-pos" data-ex-id="${esc(exchangeId)}" data-symbol="${symAttr}" data-side="${sideAttr}">平仓</button>
</div>`;
@@ -3657,9 +3655,7 @@
const fcBadge =
!isTrendContext(mo, trendPlan) && mo.force_close_enabled ? forceCloseSymbolBadgeHtml(mo) : "";
const intraday = !!options.intradayDiscipline;
const actionCell = intraday
? ""
: `<div class="pos-action-group">
const actionCell = `<div class="pos-action-group">
<button type="button" class="btn-place-tpsl btn-sm ghost" data-ex-id="${esc(exchangeId)}" data-symbol="${symAttr}" data-side="${sideAttr}" data-contracts="${contractsAttr}" data-sl="${slAttr}" data-tp="${tpAttr}">委托</button>
<button type="button" class="btn-close-pos btn-sm danger" data-ex-id="${esc(exchangeId)}" data-symbol="${symAttr}" data-side="${sideAttr}">平仓</button>
</div>`;
@@ -4128,7 +4124,7 @@
${flaskOpen ? `<a class="btn-link btn-open-instance" href="#" data-ex-id="${esc(row.id)}" data-next="/key_monitor">监控位</a>` : ""}
${flaskOpen ? `<a class="btn-link btn-open-instance" href="#" data-ex-id="${esc(row.id)}" data-next="/records">复盘</a>` : ""}
${flaskOpen && (row.capabilities || []).includes("options") ? `<a class="btn-link btn-open-instance" href="#" data-ex-id="${esc(row.id)}" data-next="/options">期权</a>` : ""}
${intraday ? "" : `<button type="button" class="danger btn-close-ex" data-id="${esc(row.id)}">全平</button>`}
<button type="button" class="danger btn-close-ex" data-id="${esc(row.id)}">全平</button>
</div>
</div>`;
if (!row.http_ok || ag.ok === false) {
@@ -4519,7 +4515,7 @@
: "";
const intraday = isIntradayDisciplineRow(row);
const fcHeadBadge = intraday && !isOkxOptionsCard ? forceCloseHeadBadgeHtml(row.force_close) : "";
const showCloseAll = !intraday && !isOkxOptionsCard;
const showCloseAll = !isOkxOptionsCard;
const layoutCls = opts.okxSplit
? " card-monitor-okx-split"
: opts.splitSide || okxPart
+1 -1
View File
@@ -1767,6 +1767,6 @@
<script src="/assets/options_expiry_countdown.js?v=1"></script>
<script src="/assets/options_position_cards.js?v=3"></script>
<script src="/assets/backup.js?v=1"></script>
<script src="/assets/app.js?v=20260724-display-hide"></script>
<script src="/assets/app.js?v=20260805-intraday-close"></script>
</body>
</html>