Feature: 3h Performance Reports an Telegram + Obsidian (USDT, aktive Trades, Bot-Aktivität, Holdings)
This commit is contained in:
parent
503d2a8a0a
commit
3a65500c6b
|
|
@ -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()
|
||||||
Loading…
Reference in New Issue