a
This commit is contained in:
parent
ee4b577519
commit
82fd5e3f58
|
|
@ -274,6 +274,92 @@ async def get_recommendations(
|
||||||
rec.setdefault("current_price_source", rec.get("current_price_source") or "snapshot")
|
rec.setdefault("current_price_source", rec.get("current_price_source") or "snapshot")
|
||||||
rec.setdefault("price_updated", False)
|
rec.setdefault("price_updated", False)
|
||||||
|
|
||||||
|
# 4) 过滤“过期/不再适用”的推荐(短期可借鉴)
|
||||||
|
# 规则:
|
||||||
|
# - 时间过期:超过 max_age_sec 直接丢弃
|
||||||
|
# - 价格偏离:当前价偏离 planned_entry_price / suggested_limit_price 过大丢弃
|
||||||
|
# - 合法性校验:BUY 需要 SL < entry < TP;SELL 需要 SL > entry > TP
|
||||||
|
import time as _time
|
||||||
|
now_s = _time.time()
|
||||||
|
try:
|
||||||
|
max_age_sec = int(os.getenv("RECOMMENDATIONS_MAX_AGE_SEC", "1800")) # 默认30分钟
|
||||||
|
except Exception:
|
||||||
|
max_age_sec = 1800
|
||||||
|
try:
|
||||||
|
max_price_drift_pct = float(os.getenv("RECOMMENDATIONS_MAX_PRICE_DRIFT_PCT", "1.5")) # 默认1.5%
|
||||||
|
except Exception:
|
||||||
|
max_price_drift_pct = 1.5
|
||||||
|
|
||||||
|
dropped_age = 0
|
||||||
|
dropped_drift = 0
|
||||||
|
dropped_invalid = 0
|
||||||
|
|
||||||
|
def _f(v):
|
||||||
|
try:
|
||||||
|
return float(v)
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
filtered_recs: List[Dict[str, Any]] = []
|
||||||
|
for rec in recommendations:
|
||||||
|
if not isinstance(rec, dict):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 4.1 时间过期
|
||||||
|
ts = rec.get("timestamp", 0)
|
||||||
|
try:
|
||||||
|
ts = float(ts) if ts is not None else 0.0
|
||||||
|
except Exception:
|
||||||
|
ts = 0.0
|
||||||
|
if ts and max_age_sec > 0 and (now_s - ts) > max_age_sec:
|
||||||
|
dropped_age += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
direction_u = str(rec.get("direction", "") or "").upper()
|
||||||
|
cur = _f(rec.get("current_price"))
|
||||||
|
# 基准入场价:优先 planned_entry_price(新逻辑),再 suggested_limit_price
|
||||||
|
entry_base = _f(rec.get("planned_entry_price"))
|
||||||
|
if entry_base is None:
|
||||||
|
entry_base = _f(rec.get("suggested_limit_price"))
|
||||||
|
if entry_base is None:
|
||||||
|
entry_base = _f(rec.get("analysis_price")) or cur
|
||||||
|
|
||||||
|
# 4.2 价格偏离过大(挂单参考已失效)
|
||||||
|
if max_price_drift_pct > 0 and cur and entry_base and entry_base > 0:
|
||||||
|
drift = abs((cur - entry_base) / entry_base) * 100
|
||||||
|
if drift > max_price_drift_pct:
|
||||||
|
dropped_drift += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 4.3 合法性校验:止损/止盈相对关系
|
||||||
|
sl = _f(rec.get("suggested_stop_loss"))
|
||||||
|
tp1 = _f(rec.get("suggested_take_profit_1"))
|
||||||
|
tp2 = _f(rec.get("suggested_take_profit_2"))
|
||||||
|
if direction_u == "BUY":
|
||||||
|
if entry_base and sl and sl >= entry_base:
|
||||||
|
dropped_invalid += 1
|
||||||
|
continue
|
||||||
|
if entry_base and tp1 and tp1 <= entry_base:
|
||||||
|
dropped_invalid += 1
|
||||||
|
continue
|
||||||
|
if entry_base and tp2 and tp2 <= entry_base:
|
||||||
|
dropped_invalid += 1
|
||||||
|
continue
|
||||||
|
elif direction_u == "SELL":
|
||||||
|
if entry_base and sl and sl <= entry_base:
|
||||||
|
dropped_invalid += 1
|
||||||
|
continue
|
||||||
|
if entry_base and tp1 and tp1 >= entry_base:
|
||||||
|
dropped_invalid += 1
|
||||||
|
continue
|
||||||
|
if entry_base and tp2 and tp2 >= entry_base:
|
||||||
|
dropped_invalid += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
filtered_recs.append(rec)
|
||||||
|
|
||||||
|
recommendations = filtered_recs
|
||||||
|
|
||||||
# 方向过滤
|
# 方向过滤
|
||||||
if direction:
|
if direction:
|
||||||
recommendations = [r for r in recommendations if r.get('direction') == direction]
|
recommendations = [r for r in recommendations if r.get('direction') == direction]
|
||||||
|
|
@ -291,6 +377,13 @@ async def get_recommendations(
|
||||||
"price_source": "mark_price" if mark_items else None,
|
"price_source": "mark_price" if mark_items else None,
|
||||||
"price_updated_at": mark_updated_at,
|
"price_updated_at": mark_updated_at,
|
||||||
"price_updated_at_ms": mark_updated_at_ms,
|
"price_updated_at_ms": mark_updated_at_ms,
|
||||||
|
"recommendations_max_age_sec": max_age_sec,
|
||||||
|
"recommendations_max_price_drift_pct": max_price_drift_pct,
|
||||||
|
"dropped": {
|
||||||
|
"age": dropped_age,
|
||||||
|
"price_drift": dropped_drift,
|
||||||
|
"invalid": dropped_invalid,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"data": recommendations
|
"data": recommendations
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user