From dccd490a46723142eda392e43028f92fe7a2ec25 Mon Sep 17 00:00:00 2001 From: dekun Date: Mon, 25 May 2026 11:07:47 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dgate=E7=9B=88=E4=BA=8F?= =?UTF-8?q?=E6=AF=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crypto_monitor_okx/app.py | 33 ++++++++++++++++--- crypto_monitor_okx/templates/index.html | 12 ++++--- crypto_monitor_okx/templates/order_focus.html | 3 +- .../templates/order_focus_v2.html | 3 +- 4 files changed, 40 insertions(+), 11 deletions(-) diff --git a/crypto_monitor_okx/app.py b/crypto_monitor_okx/app.py index 352fc6b..09d5aa8 100644 --- a/crypto_monitor_okx/app.py +++ b/crypto_monitor_okx/app.py @@ -1827,6 +1827,10 @@ def calc_pnl(direction, trigger_price, exit_price, margin_capital, leverage): def calc_rr_ratio(direction, entry_price, stop_loss, take_profit): + """ + 计划盈亏比 = 盈利空间 / 亏损空间(展示为 X:1,即 reward:risk)。 + 做多:止损须低于入场、止盈须高于入场;做空相反。 + """ try: entry = float(entry_price) sl = float(stop_loss) @@ -1846,6 +1850,17 @@ def calc_rr_ratio(direction, entry_price, stop_loss, take_profit): return None +def active_sl_tp_for_rr(stop_loss, initial_stop_loss, take_profit): + """展示/校验用:优先当前 stop_loss(委托改价后),否则回落 initial_stop_loss。""" + sl = stop_loss if stop_loss not in (None, "") else initial_stop_loss + return sl, take_profit + + +def calc_planned_rr_ratio(direction, entry_price, stop_loss, initial_stop_loss, take_profit): + sl, tp = active_sl_tp_for_rr(stop_loss, initial_stop_loss, take_profit) + return calc_rr_ratio(direction, entry_price, sl, tp) + + def calc_risk_fraction(direction, entry_price, stop_loss): try: entry = float(entry_price) @@ -2009,10 +2024,11 @@ def enrich_order_item(raw_item, current_capital): ratio = round(margin / current_capital * 100, 2) if current_capital else 0 item["notional_value"] = notional item["position_ratio"] = ratio - item["rr_ratio"] = calc_rr_ratio( + item["rr_ratio"] = calc_planned_rr_ratio( item.get("direction") or "long", item.get("trigger_price"), - item.get("initial_stop_loss") or item.get("stop_loss"), + item.get("stop_loss"), + item.get("initial_stop_loss"), item.get("take_profit"), ) try: @@ -4980,7 +4996,13 @@ def api_price_snapshot(): entry = float(r["trigger_price"] or 0) pnl = calc_pnl(r["direction"], entry, price, margin, leverage) if entry > 0 else 0 pnl_pct = round((pnl / margin * 100), 4) if margin > 0 else 0 - rr_ratio = calc_rr_ratio(r["direction"], entry, r["initial_stop_loss"] or r["stop_loss"], r["take_profit"]) + rr_ratio = calc_planned_rr_ratio( + r["direction"], + entry, + r["stop_loss"], + r["initial_stop_loss"], + r["take_profit"], + ) ex_sym = resolve_monitor_exchange_symbol(r) prow = _select_live_position_row(all_swap_positions, ex_sym, r["direction"]) lev_row = r["leverage"] if "leverage" in r.keys() else None @@ -5095,13 +5117,16 @@ def api_order_defaults(): exchange_symbol = normalize_okx_symbol(symbol) leverage = get_synced_leverage(exchange_symbol, direction) or infer_leverage(symbol) available = get_available_trading_usdt() + last_price = get_price(symbol) return jsonify({ "ok": True, "symbol": symbol, "exchange_symbol": exchange_symbol, "direction": direction, "leverage": leverage, - "available_trading_usdt": round(available, 4) if available is not None else None + "available_trading_usdt": round(available, 4) if available is not None else None, + "last_price": round(float(last_price), 8) if last_price is not None else None, + "price": round(float(last_price), 8) if last_price is not None else None, }) diff --git a/crypto_monitor_okx/templates/index.html b/crypto_monitor_okx/templates/index.html index 6a33c21..8663265 100644 --- a/crypto_monitor_okx/templates/index.html +++ b/crypto_monitor_okx/templates/index.html @@ -1865,17 +1865,19 @@ if(addOrderForm){ } const sl = Number((document.getElementById("order-sl")||{}).value); const tp = Number((document.getElementById("order-tp")||{}).value); - let entry = sl; if(!symbol){ - if(rejectManualOrderRr(calcClientRr(direction, entry, sl, tp))) return; - allowManualOrderSubmit(addOrderForm); + alert("请先填写币种,以便按市价校验盈亏比"); return; } fetch(`/api/order_defaults?symbol=${encodeURIComponent(symbol)}&direction=${encodeURIComponent(direction)}`) .then(r=>r.json()) .then(data=>{ - const px = data.last_price || data.price; - if(px) entry = Number(px); + const px = data.last_price ?? data.price; + const entry = Number(px); + if(!Number.isFinite(entry) || entry <= 0){ + alert("无法获取市价,请稍后重试"); + return; + } if(rejectManualOrderRr(calcClientRr(direction, entry, sl, tp))) return; allowManualOrderSubmit(addOrderForm); }) diff --git a/crypto_monitor_okx/templates/order_focus.html b/crypto_monitor_okx/templates/order_focus.html index 0811e93..5bc2230 100644 --- a/crypto_monitor_okx/templates/order_focus.html +++ b/crypto_monitor_okx/templates/order_focus.html @@ -143,7 +143,8 @@ function paintOrder(order){ document.getElementById("m-entry").innerText = fmt(order.trigger_price, 8); document.getElementById("m-sl").innerText = fmt(order.stop_loss, 8); document.getElementById("m-tp").innerText = fmt(order.take_profit, 8); - document.getElementById("m-rr").innerText = (order.rr_ratio === null || typeof order.rr_ratio === "undefined") ? "-" : `1:${Number(order.rr_ratio).toFixed(2)}`; + const rr = order.rr_ratio; + document.getElementById("m-rr").innerText = (rr === null || typeof rr === "undefined") ? "-:1" : `${Number(rr).toFixed(2)}:1`; document.getElementById("m-price").innerText = fmt(order.current_price, 8); const pnlEl = document.getElementById("m-pnl"); pnlEl.innerText = `${fmt(order.float_pnl, 4)}U (${fmt(order.float_pct, 2)}%)`; diff --git a/crypto_monitor_okx/templates/order_focus_v2.html b/crypto_monitor_okx/templates/order_focus_v2.html index b2ecdf9..582eaa0 100644 --- a/crypto_monitor_okx/templates/order_focus_v2.html +++ b/crypto_monitor_okx/templates/order_focus_v2.html @@ -143,7 +143,8 @@ function paintOrder(order){ document.getElementById("m-entry").innerText = fmt(order.trigger_price, 8); document.getElementById("m-sl").innerText = fmt(order.stop_loss, 8); document.getElementById("m-tp").innerText = fmt(order.take_profit, 8); - document.getElementById("m-rr").innerText = (order.rr_ratio === null || typeof order.rr_ratio === "undefined") ? "-" : `1:${Number(order.rr_ratio).toFixed(2)}`; + const rr = order.rr_ratio; + document.getElementById("m-rr").innerText = (rr === null || typeof rr === "undefined") ? "-:1" : `${Number(rr).toFixed(2)}:1`; document.getElementById("m-price").innerText = fmt(order.current_price, 8); const pnlEl = document.getElementById("m-pnl"); pnlEl.innerText = `${fmt(order.float_pnl, 4)}U (${fmt(order.float_pct, 2)}%)`;