fix(trend): correct DCA triggers and partial-position PnL across exchanges

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-07 17:09:22 +08:00
parent 9257a8051f
commit 84abf7e7f7
5 changed files with 122 additions and 265 deletions
+36 -27
View File
@@ -24,6 +24,8 @@ from strategy_trend_lib import (
build_grid_prices,
build_leg_amounts_json,
calc_risk_fraction,
trend_dca_level_reached,
trend_effective_margin_capital,
validate_trend_bounds,
)
from strategy_trade_labels import (
@@ -548,14 +550,22 @@ def _finalize_plan(cfg: dict, conn, row, result_label: str, exit_price: float) -
closed_at = m.app_now_str()
opened_at = row["opened_at"] or closed_at
hold_seconds = m.calc_hold_seconds(opened_at, m.parse_dt_for_trading_day(closed_at) or m.app_now())
margin_cap = float(row["plan_margin_capital"] or 0)
plan_margin = float(row["plan_margin_capital"] or 0)
margin_cap = trend_effective_margin_capital(_row(cfg, row))
lev = int(row["leverage"] or 1)
avg_e = float(row["avg_entry_price"] or 0)
pnl_amount = m.calc_pnl(direction, avg_e, float(exit_price), margin_cap, lev)
res = m.normalize_result_with_pnl(result_label, pnl_amount)
risk_amt = m.calc_risk_amount_from_plan(
direction, float(row["add_upper"]), float(row["stop_loss"]), margin_cap, lev
direction, float(row["add_upper"]), float(row["stop_loss"]), plan_margin, lev
)
try:
target = float(row["target_order_amount"] or 0)
open_amt = float(row["order_amount_open"] or 0)
if risk_amt is not None and target > 0 and open_amt > 0:
risk_amt = round(float(risk_amt) * min(1.0, open_amt / target), 6)
except (TypeError, ValueError):
pass
planned_rr = m.calc_rr_ratio(direction, avg_e, float(row["stop_loss"]), float(row["take_profit"]))
try:
cancel_symbol_orders(cfg, ex_sym)
@@ -785,32 +795,31 @@ def check_trend_pullback_plans(cfg: dict) -> None:
_TREND_FLAT_STREAK.pop(plan_id, None)
continue
if int(row["first_order_done"] or 0) and legs_done < len(grid) and legs_done < len(leg_amounts):
level = float(grid[legs_done])
fired = False
if direction == "long":
fired = last_pf > level and pf <= level
else:
fired = last_pf < level and pf >= level
if fired:
while legs_done < len(grid) and legs_done < len(leg_amounts):
level = float(grid[legs_done])
if not trend_dca_level_reached(direction, pf, level):
break
amt = float(m.exchange.amount_to_precision(ex_sym, leg_amounts[legs_done]))
if amt > 0:
add_resp = trend_market_add(cfg, ex_sym, direction, amt, lev)
fill_px = m.extract_trade_price_from_order(add_resp) or pf
old_avg = float(row["avg_entry_price"] or fill_px)
old_open = float(row["order_amount_open"] or 0)
new_avg = _weighted_avg(old_avg, old_open, fill_px, amt)
conn.execute(
"UPDATE trend_pullback_plans SET legs_done=?, avg_entry_price=?, "
"order_amount_open=?, last_mark_price=? WHERE id=?",
(legs_done + 1, new_avg, old_open + amt, pf, row["id"]),
)
row = conn.execute(
"SELECT * FROM trend_pullback_plans WHERE id=?", (row["id"],)
).fetchone()
try:
trend_refresh_stop_only(cfg, ex_sym, direction, sl)
except Exception:
pass
if amt <= 0:
break
add_resp = trend_market_add(cfg, ex_sym, direction, amt, lev)
fill_px = m.extract_trade_price_from_order(add_resp) or pf
old_avg = float(row["avg_entry_price"] or fill_px)
old_open = float(row["order_amount_open"] or 0)
new_avg = _weighted_avg(old_avg, old_open, fill_px, amt)
legs_done += 1
conn.execute(
"UPDATE trend_pullback_plans SET legs_done=?, avg_entry_price=?, "
"order_amount_open=?, last_mark_price=? WHERE id=?",
(legs_done, new_avg, old_open + amt, pf, row["id"]),
)
row = conn.execute(
"SELECT * FROM trend_pullback_plans WHERE id=?", (row["id"],)
).fetchone()
try:
trend_refresh_stop_only(cfg, ex_sym, direction, sl)
except Exception:
pass
conn.execute(
"UPDATE trend_pullback_plans SET last_mark_price=? WHERE id=?",
(pf, row["id"]),