Features Added: ============== 📧 EMAIL REPORTING SYSTEM: - EmailReporter: Send reports via SMTP (Gmail, SendGrid, custom) - ReportGenerator: Generate daily/weekly summaries with HTML/text formatting - Configurable via .env (SMTP_HOST, SMTP_PORT, etc.) - Scripts: send_daily_report.py, send_weekly_report.py 🤖 AUTOMATED RUNS: - automated_daily_run.sh: Full daily ETL pipeline + reporting - automated_weekly_run.sh: Weekly pattern analysis + reports - setup_cron.sh: Interactive cron job setup (5-minute setup) - Logs saved to ~/logs/ with automatic cleanup 🔍 HEALTH CHECKS: - health_check.py: System health monitoring - Checks: DB connection, data freshness, counts, recent alerts - JSON output for programmatic use - Exit codes for monitoring integration 🚀 CI/CD PIPELINE: - .github/workflows/ci.yml: Full CI/CD pipeline - GitHub Actions / Gitea Actions compatible - Jobs: lint & test, security scan, dependency scan, Docker build - PostgreSQL service for integration tests - 93 tests passing in CI 📚 COMPREHENSIVE DOCUMENTATION: - AUTOMATION_QUICKSTART.md: 5-minute email setup guide - docs/12_automation_and_reporting.md: Full automation guide - Updated README.md with automation links - Deployment → Production workflow guide 🛠️ IMPROVEMENTS: - All shell scripts made executable - Environment variable examples in .env.example - Report logs saved with timestamps - 30-day log retention with auto-cleanup - Health checks can be scheduled via cron WHAT THIS ENABLES: ================== After deployment, users can: 1. Set up automated daily/weekly email reports (5 min) 2. Receive HTML+text emails with: - New trades, market alerts, suspicious timing - Weekly patterns, rankings, repeat offenders 3. Monitor system health automatically 4. Run full CI/CD pipeline on every commit 5. Deploy with confidence (tests + security scans) USAGE: ====== # One-time setup (on deployed server) ./scripts/setup_cron.sh # Or manually send reports python scripts/send_daily_report.py --to user@example.com python scripts/send_weekly_report.py --to user@example.com # Check system health python scripts/health_check.py See AUTOMATION_QUICKSTART.md for full instructions. 93 tests passing | Full CI/CD | Email reports ready
142 lines
4.4 KiB
Python
Executable File
142 lines
4.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Analyze performance of a specific official.
|
|
"""
|
|
|
|
import argparse
|
|
import logging
|
|
import sys
|
|
|
|
from pote.analytics.metrics import PerformanceMetrics
|
|
from pote.db import get_session
|
|
from pote.db.models import Official
|
|
|
|
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def format_pct(value):
|
|
"""Format percentage."""
|
|
return f"{float(value):+.2f}%"
|
|
|
|
|
|
def format_money(value):
|
|
"""Format money."""
|
|
return f"${float(value):,.0f}"
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Analyze official's trading performance")
|
|
parser.add_argument("name", help="Official's name (e.g., 'Nancy Pelosi')")
|
|
parser.add_argument(
|
|
"--window",
|
|
type=int,
|
|
default=90,
|
|
help="Return window in days (default: 90)",
|
|
)
|
|
parser.add_argument(
|
|
"--benchmark",
|
|
default="SPY",
|
|
help="Benchmark ticker (default: SPY)",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
with next(get_session()) as session:
|
|
# Find official
|
|
official = (
|
|
session.query(Official)
|
|
.filter(Official.name.ilike(f"%{args.name}%"))
|
|
.first()
|
|
)
|
|
|
|
if not official:
|
|
logger.error(f"Official not found: {args.name}")
|
|
logger.info("Available officials:")
|
|
for o in session.query(Official).all():
|
|
logger.info(f" - {o.name}")
|
|
sys.exit(1)
|
|
|
|
# Get performance metrics
|
|
metrics = PerformanceMetrics(session)
|
|
perf = metrics.official_performance(
|
|
official.id,
|
|
window_days=args.window,
|
|
benchmark=args.benchmark,
|
|
)
|
|
|
|
# Display results
|
|
print()
|
|
print("=" * 70)
|
|
print(f" {perf['name']} Performance Analysis")
|
|
print("=" * 70)
|
|
print()
|
|
print(f"Party: {perf['party']}")
|
|
print(f"Chamber: {perf['chamber']}")
|
|
print(f"State: {perf['state']}")
|
|
print(f"Window: {perf['window_days']} days")
|
|
print(f"Benchmark: {perf['benchmark']}")
|
|
print()
|
|
|
|
if perf.get("trades_analyzed", 0) == 0:
|
|
print("⚠️ No trades with sufficient price data to analyze")
|
|
sys.exit(0)
|
|
|
|
print("📊 TRADING ACTIVITY")
|
|
print("-" * 70)
|
|
print(f"Total Trades: {perf['total_trades']}")
|
|
print(f"Analyzed: {perf['trades_analyzed']}")
|
|
print(f"Buy Trades: {perf['buy_trades']}")
|
|
print(f"Sell Trades: {perf['sell_trades']}")
|
|
print(f"Total Value: {format_money(perf['total_value_traded'])}")
|
|
print()
|
|
|
|
print("📈 PERFORMANCE METRICS")
|
|
print("-" * 70)
|
|
print(f"Average Return: {format_pct(perf['avg_return'])}")
|
|
print(f"Median Return: {format_pct(perf['median_return'])}")
|
|
print(f"Max Return: {format_pct(perf['max_return'])}")
|
|
print(f"Min Return: {format_pct(perf['min_return'])}")
|
|
print()
|
|
|
|
print("🎯 VS MARKET ({})".format(perf['benchmark']))
|
|
print("-" * 70)
|
|
print(f"Average Alpha: {format_pct(perf['avg_alpha'])}")
|
|
print(f"Median Alpha: {format_pct(perf['median_alpha'])}")
|
|
print(f"Win Rate: {perf['win_rate']:.1%}")
|
|
print(f"Beat Market Rate: {perf['beat_market_rate']:.1%}")
|
|
print()
|
|
|
|
print("🏆 BEST/WORST TRADES")
|
|
print("-" * 70)
|
|
best = perf['best_trade']
|
|
worst = perf['worst_trade']
|
|
print(f"Best: {best['ticker']:6s} {format_pct(best['return']):>10s} ({best['date']})")
|
|
print(f"Worst: {worst['ticker']:6s} {format_pct(worst['return']):>10s} ({worst['date']})")
|
|
print()
|
|
|
|
# Signal
|
|
alpha = float(perf['avg_alpha'])
|
|
beat_rate = perf['beat_market_rate']
|
|
|
|
print("🔔 RESEARCH SIGNAL")
|
|
print("-" * 70)
|
|
if alpha > 5 and beat_rate > 0.65:
|
|
print("✅ FOLLOW_RESEARCH: Strong positive alpha with high win rate")
|
|
elif alpha > 2 and beat_rate > 0.55:
|
|
print("⭐ FOLLOW_RESEARCH: Moderate positive alpha")
|
|
elif alpha < -5 or beat_rate < 0.35:
|
|
print("🚨 AVOID_RISK: Negative alpha or poor performance")
|
|
elif perf['total_trades'] < 5:
|
|
print("👀 WATCH: Limited data, need more trades for confidence")
|
|
else:
|
|
print("📊 NEUTRAL: Performance close to market")
|
|
print()
|
|
|
|
print("=" * 70)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
|
|
|