feat(live): alert Telegram su esecuzione ed errori
src/live/notifier.py (stdlib, no-op se non configurato): legge TELEGRAM_BOT_TOKEN/CHAT_ID da env o .env(.mainnet) gitignored. live_execute.py invia alert su: ordine eseguito (✅), ordine non verificato (⚠️), disaster-SL piazzato/fallito (🛡️/⚠️), conto offline, e qualsiasi eccezione (🛑). Nessun alert nei giorni flat/HOLD (no rumore). Config gia' presente in .env -> alert attivi. Test config: uv run python -m src.live.notifier "msg". Test 28/28. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,7 @@ sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
from src.live.deribit import INSTRUMENT
|
||||
from src.live.execution import DeribitTrader
|
||||
from src.live.notifier import notify
|
||||
from src.live.shadow import ASSETS, WEIGHT, shadow_report
|
||||
|
||||
CONFIG = PROJECT_ROOT / "config" / "live.json"
|
||||
@@ -47,7 +48,7 @@ def log_event(rec: dict):
|
||||
f.write(json.dumps(rec) + "\n")
|
||||
|
||||
|
||||
def main():
|
||||
def _run():
|
||||
cfg = load_config()
|
||||
want_execute = "--execute" in sys.argv[1:]
|
||||
enabled = bool(cfg["execution_enabled"])
|
||||
@@ -72,6 +73,8 @@ def main():
|
||||
|
||||
if not r["online"]:
|
||||
print(" conto non leggibile (offline) -> stop, non eseguo a cieco.")
|
||||
if do_execute:
|
||||
notify("⚠️ TP01 LIVE — conto offline", {"nota": "salto l'esecuzione, non opero a cieco"})
|
||||
return
|
||||
|
||||
trader = DeribitTrader() if do_execute else None
|
||||
@@ -100,9 +103,20 @@ def main():
|
||||
log_event(dict(ts_utc=str(pd.Timestamp(r['last_data'])), asset=asset, action=act,
|
||||
side=f.side, filled=f.filled, price=f.price, fee=f.fee_usdc,
|
||||
verified=f.verified, notes=f.notes, pos_after=newpos))
|
||||
det = dict(asset=asset, side=f.side, amount=round(f.filled, 4),
|
||||
price=round(f.price or 0, 1), fee=round(f.fee_usdc, 5), pos_after=round(newpos, 0))
|
||||
if f.verified:
|
||||
notify(f"✅ TP01 {act}", det)
|
||||
else:
|
||||
notify("⚠️ TP01 ORDINE NON VERIFICATO", {**det, "notes": f.notes})
|
||||
print(f" reconcile: pos ${newpos:,.0f}")
|
||||
ds = trader.ensure_disaster_sl(INSTRUMENT[asset], sl_pct) # bracket: piazza se long, pulisce se flat
|
||||
print(f" disaster-SL: {ds.get('state')}" + (f" @ ${ds['stop']:,.1f}" if ds.get("stop") else ""))
|
||||
if ds.get("state") == "placed":
|
||||
notify("🛡️ TP01 disaster-SL piazzato", {"asset": asset, "stop": round(ds.get("stop") or 0, 1),
|
||||
"amount": round(ds.get("amount") or 0, 4)})
|
||||
elif ds.get("state") == "place-failed":
|
||||
notify("⚠️ TP01 disaster-SL FALLITO", {"asset": asset, "notes": ds.get("notes")})
|
||||
actions.append(act)
|
||||
|
||||
print()
|
||||
@@ -115,5 +129,13 @@ def main():
|
||||
print(" => Esecuzione completata (vedi data/live/executions.jsonl).")
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
_run()
|
||||
except Exception as e:
|
||||
notify("🛑 TP01 LIVE — ERRORE", {"error": f"{type(e).__name__}: {e}"})
|
||||
raise
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user