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:
+45
-8
@@ -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"](),
|
||||
|
||||
Reference in New Issue
Block a user