1
This commit is contained in:
parent
b732324d63
commit
1e927521b7
@ -8,6 +8,7 @@ from datetime import datetime, timedelta
|
|||||||
|
|
||||||
from app.config.system_config import paper_trading_config
|
from app.config.system_config import paper_trading_config
|
||||||
from app.db.schema import get_conn
|
from app.db.schema import get_conn
|
||||||
|
from app.integrations.feishu_push import push_card
|
||||||
|
|
||||||
|
|
||||||
def _now() -> str:
|
def _now() -> str:
|
||||||
@ -174,6 +175,43 @@ def _record_event(conn, trade_id: int, rec_id: int, symbol: str, event_type: str
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _push_event_card(event_type: str, trade: dict, result: dict, event_time: str = "") -> None:
|
||||||
|
try:
|
||||||
|
symbol = str(trade.get("symbol") or "")
|
||||||
|
title = {
|
||||||
|
"open": f"📒 模拟交易开仓 — {symbol.replace('/USDT', '')}",
|
||||||
|
"close": f"🏁 模拟交易平仓 — {symbol.replace('/USDT', '')}",
|
||||||
|
"trailing_activate": f"🛡️ 模拟交易移动止盈启动 — {symbol.replace('/USDT', '')}",
|
||||||
|
"trailing_move": f"🛡️ 模拟交易移动止盈上移 — {symbol.replace('/USDT', '')}",
|
||||||
|
}.get(event_type)
|
||||||
|
if not title:
|
||||||
|
return
|
||||||
|
card = {
|
||||||
|
"config": {"wide_screen_mode": True},
|
||||||
|
"header": {
|
||||||
|
"template": "blue" if event_type == "open" else ("yellow" if event_type.startswith("trailing") else "red"),
|
||||||
|
"title": {"tag": "plain_text", "content": title},
|
||||||
|
},
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"tag": "div",
|
||||||
|
"text": {
|
||||||
|
"tag": "lark_md",
|
||||||
|
"content": (
|
||||||
|
f"**币种**: {symbol}\n"
|
||||||
|
f"**事件**: {event_type}\n"
|
||||||
|
f"**成交价**: ${result.get('entry_price') or result.get('exit_price') or result.get('trailing_stop') or trade.get('current_price') or 0}\n"
|
||||||
|
f"**时间**: {event_time or ''}"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
push_card(card)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def _open_trade(conn, rec: dict, current_price: float, event_time: str) -> dict:
|
def _open_trade(conn, rec: dict, current_price: float, event_time: str) -> dict:
|
||||||
rec_id = _safe_int(rec.get("id"))
|
rec_id = _safe_int(rec.get("id"))
|
||||||
symbol = str(rec.get("symbol") or "").strip().upper()
|
symbol = str(rec.get("symbol") or "").strip().upper()
|
||||||
@ -246,6 +284,7 @@ def _open_trade(conn, rec: dict, current_price: float, event_time: str) -> dict:
|
|||||||
},
|
},
|
||||||
now,
|
now,
|
||||||
)
|
)
|
||||||
|
_push_event_card("open", {"symbol": symbol}, {"entry_price": entry_price}, now)
|
||||||
return {
|
return {
|
||||||
"opened": True,
|
"opened": True,
|
||||||
"trade_id": trade_id,
|
"trade_id": trade_id,
|
||||||
@ -307,6 +346,7 @@ def _close_trade(conn, trade: dict, current_price: float, reason: str, event_tim
|
|||||||
{"realized_pnl_usdt": pnl_usdt, "fee_usdt": total_fee},
|
{"realized_pnl_usdt": pnl_usdt, "fee_usdt": total_fee},
|
||||||
now,
|
now,
|
||||||
)
|
)
|
||||||
|
_push_event_card("close", trade, {"exit_price": exit_price}, now)
|
||||||
return {"closed": True, "trade_id": trade["id"], "exit_reason": reason, "pnl_pct": pnl_pct, "pnl_usdt": pnl_usdt}
|
return {"closed": True, "trade_id": trade["id"], "exit_reason": reason, "pnl_pct": pnl_pct, "pnl_usdt": pnl_usdt}
|
||||||
|
|
||||||
|
|
||||||
@ -352,6 +392,7 @@ def _update_trailing_stop(conn, trade: dict, current_price: float, pnl_pct: floa
|
|||||||
},
|
},
|
||||||
event_time,
|
event_time,
|
||||||
)
|
)
|
||||||
|
_push_event_card(event_type, trade, {"trailing_stop": new_trail}, event_time)
|
||||||
return new_trail, {
|
return new_trail, {
|
||||||
"activated": activated,
|
"activated": activated,
|
||||||
"moved": moved,
|
"moved": moved,
|
||||||
|
|||||||
@ -4,27 +4,14 @@ Separates eligibility / cooldown decisions from payload rendering and transport.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from app.db.recommendation_queries import log_push, should_push
|
from app.db.recommendation_queries import log_push, should_push
|
||||||
from app.integrations.feishu_push import push_altcoin_tp_sl_alert, push_recommendation_state_alert
|
from app.integrations.feishu_push import build_trade_action_card, push_card
|
||||||
|
|
||||||
|
|
||||||
def push_mainline_state_update(symbol: str, rec_id: int, mainline_item: dict, title_prefix: str | None = None, entry_push_type: str = "entry", watch_push_type: str = "watch_pool") -> bool:
|
def push_mainline_state_update(symbol: str, rec_id: int, mainline_item: dict, title_prefix: str | None = None, entry_push_type: str = "entry", watch_push_type: str = "watch_pool") -> bool:
|
||||||
if not mainline_item or mainline_item.get("execution_status") not in ("buy_now", "wait_pullback"):
|
"""主链路状态只记录,不再飞书推送。"""
|
||||||
status = mainline_item.get("execution_status") if mainline_item else "missing"
|
status = mainline_item.get("execution_status") if mainline_item else "missing"
|
||||||
print(f"[push] skip {symbol}: mainline_status={status}")
|
action = mainline_item.get("action_status", "") if mainline_item else ""
|
||||||
return False
|
print(f"[push] skip {symbol}: mainline notifications disabled (status={status}, action={action})")
|
||||||
|
|
||||||
push_type = entry_push_type if mainline_item.get("execution_status") == "buy_now" else watch_push_type
|
|
||||||
action = mainline_item.get("action_status", "")
|
|
||||||
if not should_push(symbol, push_type, action):
|
|
||||||
print(f"⏭ 跳过推送({symbol}): {push_type}/{action} 12h冷却中")
|
|
||||||
return False
|
|
||||||
|
|
||||||
ok, resp = push_recommendation_state_alert(mainline_item, title_prefix=title_prefix)
|
|
||||||
if ok:
|
|
||||||
log_push(symbol, push_type, action, rec_id=rec_id)
|
|
||||||
return True
|
|
||||||
|
|
||||||
print(f"[push] failed {symbol}: {resp}")
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@ -34,7 +21,7 @@ def push_trade_action_update(symbol: str, rec_id: int, state_decision: dict, fin
|
|||||||
if not should_push(symbol, push_type, final_action):
|
if not should_push(symbol, push_type, final_action):
|
||||||
print(f"⏭ 跳过推送({symbol}): {push_type}/{final_action} 12h冷却中")
|
print(f"⏭ 跳过推送({symbol}): {push_type}/{final_action} 12h冷却中")
|
||||||
return False
|
return False
|
||||||
ok, resp = push_altcoin_tp_sl_alert(
|
card = build_trade_action_card(
|
||||||
state_decision["push_symbol"],
|
state_decision["push_symbol"],
|
||||||
state_decision["push_current_price"],
|
state_decision["push_current_price"],
|
||||||
state_decision["push_entry_price"],
|
state_decision["push_entry_price"],
|
||||||
@ -45,6 +32,10 @@ def push_trade_action_update(symbol: str, rec_id: int, state_decision: dict, fin
|
|||||||
state_decision.get("tp1", 0),
|
state_decision.get("tp1", 0),
|
||||||
state_decision.get("tp2", 0),
|
state_decision.get("tp2", 0),
|
||||||
)
|
)
|
||||||
|
if isinstance(card, tuple):
|
||||||
|
ok, resp = card
|
||||||
|
else:
|
||||||
|
ok, resp = push_card(card)
|
||||||
if ok:
|
if ok:
|
||||||
log_push(symbol, push_type, final_action, rec_id=rec_id)
|
log_push(symbol, push_type, final_action, rec_id=rec_id)
|
||||||
return True
|
return True
|
||||||
|
|||||||
@ -30,13 +30,14 @@ from app.db.altcoin_db import (
|
|||||||
update_latest_price_cache,
|
update_latest_price_cache,
|
||||||
)
|
)
|
||||||
from app.db.paper_trading import sync_recommendation as sync_paper_trade
|
from app.db.paper_trading import sync_recommendation as sync_paper_trade
|
||||||
|
from app.integrations.push_orchestrator import push_trade_action_update
|
||||||
from app.core.pa_engine import (
|
from app.core.pa_engine import (
|
||||||
calc_atr, full_pa_analysis, detect_trend_exhaustion,
|
calc_atr, full_pa_analysis, detect_trend_exhaustion,
|
||||||
analyze_entry_point,
|
analyze_entry_point,
|
||||||
)
|
)
|
||||||
from app.integrations.push_orchestrator import push_trade_action_update
|
|
||||||
from app.config.config_loader import load_rules
|
from app.config.config_loader import load_rules
|
||||||
from app.core.opportunity_lifecycle import apply_entry_quality_gate
|
from app.core.opportunity_lifecycle import apply_entry_quality_gate
|
||||||
|
from app.db.paper_trading import sync_recommendation as sync_paper_trade
|
||||||
|
|
||||||
exchange = ccxt.binance({"enableRateLimit": True})
|
exchange = ccxt.binance({"enableRateLimit": True})
|
||||||
REPO_ROOT = Path(__file__).resolve().parents[2]
|
REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||||
@ -337,7 +338,8 @@ def track_prices():
|
|||||||
current_price,
|
current_price,
|
||||||
event_time=datetime.now().isoformat(),
|
event_time=datetime.now().isoformat(),
|
||||||
)
|
)
|
||||||
push_trade_action_update(symbol, rec["id"], state_decision, final_action, push_type="entry")
|
if paper_result.get("opened") or paper_result.get("closed") or paper_result.get("activated") or paper_result.get("moved"):
|
||||||
|
print(f" {symbol}: paper trading event -> {paper_result}")
|
||||||
|
|
||||||
results.append({
|
results.append({
|
||||||
"symbol": symbol,
|
"symbol": symbol,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user