Feature: 3h Performance Reports an Telegram + Obsidian (USDT, aktive Trades, Bot-Aktivität, Holdings)

This commit is contained in:
Marc Blatter 2026-07-09 22:43:41 +02:00
parent 503d2a8a0a
commit 3a65500c6b
1 changed files with 213 additions and 0 deletions

213
src/performance_reporter.py Normal file
View File

@ -0,0 +1,213 @@
#!/usr/bin/env python3
"""
3h Performance Report Generator
Sammelt Metriken und sendet an Telegram + Obsidian
"""
import os
import json
from datetime import datetime, timedelta
from binance.client import Client
from dotenv import load_dotenv
import requests
os.chdir('/home/marc/bot-deploy')
load_dotenv()
client = Client(os.getenv('BINANCE_API_KEY_LIVE'), os.getenv('BINANCE_API_SECRET_LIVE'))
def get_performance_metrics():
"""Sammelt alle Metriken für Report"""
try:
# 1. Portfolio-Status
acc = client.get_account()
balances = {b['asset']: float(b['free']) + float(b['locked'])
for b in acc['balances'] if float(b['free']) + float(b['locked']) > 0}
usdt_total = balances.get('USDT', 0)
# 2. Bot-Status
try:
with open('/home/marc/bot-deploy/active_trades.json') as f:
bot_state = json.load(f)
active_trades = bot_state.get('active_trades', {})
trades_count = bot_state.get('count', 0)
except:
active_trades = {}
trades_count = 0
# 3. Berechne Positionen
holdings = len([k for k in balances.keys() if k not in ['USDT'] and balances[k] > 0.0001])
# 4. Berechne durchschnittlichen Entry Price
if active_trades:
total_locked = sum(float(t['qty']) * float(t['entry_price'])
for t in active_trades.values())
avg_entry = total_locked / max(len(active_trades), 1)
else:
total_locked = 0
avg_entry = 0
# 5. Bot Logs lesen (letzte 3h)
import subprocess
try:
logs_result = subprocess.run(
['journalctl', '-u', 'trading-bot.service', '--since', '3 hours ago', '--no-pager'],
capture_output=True, text=True, timeout=5
)
logs = logs_result.stdout
cycle_count = logs.count('CYCLE START')
buy_count = logs.count('BUY')
sell_count = logs.count('SELL')
except:
cycle_count = 0
buy_count = 0
sell_count = 0
# 6. Zusammenfassung
return {
'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S CET'),
'usdt_free': usdt_total,
'active_positions': trades_count,
'holdings_count': holdings,
'cycles_3h': cycle_count,
'buys_3h': buy_count,
'sells_3h': sell_count,
'active_trades': active_trades,
'total_portfolio_locked': total_locked
}
except Exception as e:
return {'error': str(e)}
def format_telegram_report(metrics):
"""Formatiert Report für Telegram"""
if 'error' in metrics:
return f"❌ Report-Fehler: {metrics['error']}"
report = f"""
📊 **3h Performance Report**
Zeitstempel: {metrics['timestamp']}
💰 **Portfolio:**
USDT verfügbar: ${metrics['usdt_free']:.2f}
Portfolio gesperrt: ${metrics['total_portfolio_locked']:.2f}
📈 **Positionen:**
Aktive Trades: {metrics['active_positions']}
Holdings-Coins: {metrics['holdings_count']}
🔄 **Bot-Aktivität (letzte 3h):**
Zyklen durchgeführt: {metrics['cycles_3h']}
Käufe: {metrics['buys_3h']}
Verkäufe: {metrics['sells_3h']}
🎯 **Aktive Trades:**
"""
if metrics['active_trades']:
for symbol, trade in sorted(metrics['active_trades'].items()):
report += f"{symbol}: {trade['qty']:.8f} @ {trade['entry_price']:.2f}\n"
else:
report += " (keine)\n"
return report
def format_obsidian_report(metrics):
"""Formatiert Report für Obsidian"""
if 'error' in metrics:
return f"## Report-Fehler\n{metrics['error']}"
report = f"""## {metrics['timestamp']}
**Portfolio:**
- USDT verfügbar: ${metrics['usdt_free']:.2f}
- Portfolio gesperrt: ${metrics['total_portfolio_locked']:.2f}
**Positionen:**
- Aktive Trades: {metrics['active_positions']}
- Holdings: {metrics['holdings_count']}
**Bot-Aktivität (3h):**
- Zyklen: {metrics['cycles_3h']}
- Käufe: {metrics['buys_3h']}
- Verkäufe: {metrics['sells_3h']}
**Trades:**
"""
if metrics['active_trades']:
for symbol, trade in sorted(metrics['active_trades'].items()):
report += f"- {symbol}: {trade['qty']:.8f} @ {trade['entry_price']:.2f}\n"
else:
report += "- (keine)\n"
return report
def send_telegram_report(message):
"""Sendet Report an Telegram"""
try:
# Marc Telegram: 7646180954
token = os.getenv('TELEGRAM_BOT_TOKEN')
chat_id = 7646180954
url = f'https://api.telegram.org/bot{token}/sendMessage'
payload = {
'chat_id': chat_id,
'text': message,
'parse_mode': 'Markdown'
}
response = requests.post(url, json=payload, timeout=10)
if response.status_code == 200:
print(f"✅ Telegram Report versendet")
return True
except Exception as e:
print(f"❌ Telegram-Fehler: {e}")
return False
def append_obsidian_report(message):
"""Hängt Report an Obsidian Session-Datei an"""
try:
report_file = '/home/marc/bot-deploy/obsidian_3h_reports.md'
# Erstelle Datei wenn nicht vorhanden
if not os.path.exists(report_file):
header = """---
tags: [3h-reports, performance, tracking]
---
# 3h Performance Reports
"""
with open(report_file, 'w') as f:
f.write(header)
# Füge Report an
with open(report_file, 'a') as f:
f.write(message + '\n\n')
print(f"✅ Obsidian Report geschrieben")
return True
except Exception as e:
print(f"❌ Obsidian-Fehler: {e}")
return False
def main():
print("🔄 Sammle Performance-Metriken...")
metrics = get_performance_metrics()
if 'error' not in metrics:
print(f"{metrics['active_positions']} aktive Trades")
print(f" ✅ ${metrics['usdt_free']:.2f} USDT verfügbar")
print("\n📤 Sende Reports...")
# Telegram
tg_msg = format_telegram_report(metrics)
send_telegram_report(tg_msg)
# Obsidian
obs_msg = format_obsidian_report(metrics)
append_obsidian_report(obs_msg)
print("\n✅ Reports versendet!")
if __name__ == '__main__':
main()