Fix Gate/Binance memory regression and roll stop offset from avg.

Stop fetch_tickers fallback for volume rank and keep stale cache on failed refresh. Compute roll unified stop as merge-average plus offset percent instead of break-even.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-24 00:21:07 +08:00
parent 7f8ae97a98
commit f63f8810e6
9 changed files with 343 additions and 37 deletions
+45 -8
View File
@@ -9,7 +9,7 @@ from flask import Flask, flash, jsonify, redirect, request, url_for
from jinja2 import ChoiceLoader, FileSystemLoader
from strategy_db import init_strategy_tables
from strategy_roll_lib import preview_roll
from strategy_roll_lib import preview_roll, roll_stop_after_fill
def _dedupe_strategy_snapshots_on_startup(cfg: dict[str, Any]) -> None:
@@ -168,10 +168,29 @@ def _roll_preview_response(cfg: dict, data: dict, json_mode: bool = False) -> di
tp0 = float(mon.get("take_profit") or rg.get("initial_take_profit") or 0)
add_mode = (data.get("add_mode") or "market").strip().lower()
try:
new_sl = float(data.get("new_stop_loss") or data.get("sl"))
risk_pct = float(data.get("risk_percent") or cfg.get("default_risk_percent", 2))
except (TypeError, ValueError):
return {"ok": False, "msg": "止损或风险%格式错误"}
return {"ok": False, "msg": "风险%格式错误"}
stop_offset_raw = data.get("stop_offset_pct")
if stop_offset_raw in (None, ""):
stop_offset_raw = data.get("new_stop_loss") or data.get("sl")
new_sl_abs = None
stop_offset_pct = None
if data.get("stop_offset_pct") not in (None, ""):
try:
stop_offset_pct = float(data.get("stop_offset_pct"))
except (TypeError, ValueError):
return {"ok": False, "msg": "止损偏移%格式错误"}
elif data.get("new_stop_loss") not in (None, "") or data.get("sl") not in (None, ""):
try:
new_sl_abs = float(data.get("new_stop_loss") or data.get("sl"))
except (TypeError, ValueError):
return {"ok": False, "msg": "止损格式错误"}
elif stop_offset_raw not in (None, ""):
try:
new_sl_abs = float(stop_offset_raw)
except (TypeError, ValueError):
return {"ok": False, "msg": "止损格式错误"}
conn_cap = get_db()
try:
capital = float(cfg["get_trading_capital_usdt"](conn_cap))
@@ -193,7 +212,8 @@ def _roll_preview_response(cfg: dict, data: dict, json_mode: bool = False) -> di
entry_existing=entry,
initial_take_profit=tp0,
add_mode=add_mode,
new_stop_loss=new_sl,
new_stop_loss=new_sl_abs,
stop_offset_pct=stop_offset_pct,
risk_percent=risk_pct,
capital_base_usdt=capital,
add_price=float(live) if live else None,
@@ -243,12 +263,27 @@ def _roll_execute(cfg: dict, data: dict) -> tuple[bool, str]:
return False, "监控单已不存在"
rg, legs_done, roll_is_new = _get_or_create_roll_group_meta(conn, mon)
new_sl = float(preview["new_stop_loss"])
stop_offset_pct = preview.get("stop_offset_pct")
tp0 = float(preview["initial_take_profit"])
qty_before = float(preview.get("qty_existing") or 0)
entry_before = float(preview.get("entry_existing") or 0)
if add_mode == "market":
order = cfg["market_add"](ex_sym, direction, amount, leverage)
fill = float(cfg.get("resolve_fill_price", lambda o, s, p: p)(order, ex_sym, preview["add_price"]) or preview["add_price"])
status = "filled"
oid = str(order.get("id") or "") if isinstance(order, dict) else ""
if stop_offset_pct is not None and qty_before > 0 and entry_before > 0:
new_sl = roll_stop_after_fill(
direction,
qty_before,
entry_before,
float(amount),
fill,
stop_offset_pct=float(stop_offset_pct),
)
px_fn = cfg.get("price_to_precision")
if callable(px_fn):
new_sl = float(px_fn(ex_sym, new_sl) or new_sl)
else:
price = cfg["price_to_precision"](ex_sym, float(preview["add_price"]))
order = cfg["limit_add"](ex_sym, direction, amount, price, leverage)
@@ -256,8 +291,8 @@ def _roll_execute(cfg: dict, data: dict) -> tuple[bool, str]:
conn.execute(
"""INSERT INTO roll_legs (
roll_group_id, leg_index, add_mode, fib_upper, fib_lower, limit_price,
amount, new_stop_loss, exchange_order_id, status, created_at
) VALUES (?,?,?,?,?,?,?,?,?,?,?)""",
amount, new_stop_loss, stop_offset_pct, exchange_order_id, status, created_at
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)""",
(
rg["id"],
legs_done + 1,
@@ -267,6 +302,7 @@ def _roll_execute(cfg: dict, data: dict) -> tuple[bool, str]:
price,
amount,
new_sl,
stop_offset_pct,
oid,
"pending",
cfg["app_now_str"](),
@@ -297,8 +333,8 @@ def _roll_execute(cfg: dict, data: dict) -> tuple[bool, str]:
conn.execute(
"""INSERT INTO roll_legs (
roll_group_id, leg_index, add_mode, fib_upper, fib_lower, limit_price,
fill_price, amount, new_stop_loss, exchange_order_id, status, created_at
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)""",
fill_price, amount, new_stop_loss, stop_offset_pct, exchange_order_id, status, created_at
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)""",
(
rg["id"],
legs_done + 1,
@@ -309,6 +345,7 @@ def _roll_execute(cfg: dict, data: dict) -> tuple[bool, str]:
fill,
amount,
new_sl,
stop_offset_pct,
oid,
"filled",
cfg["app_now_str"](),