| | |
| | import time |
| | import json |
| | import os |
| | import sys |
| | import logging |
| | from datetime import datetime |
| | from agents.tool_calling_agents import MarketDataAgent, WebResearchAgent |
| |
|
| | |
| | WATCHLIST_FILE = "watchlist.json" |
| | ALERTS_FILE = "alerts.json" |
| | CHECK_INTERVAL = 10 |
| | PRICE_ALERT_THRESHOLD = 0.5 |
| |
|
| | |
| | logging.basicConfig( |
| | level=logging.INFO, |
| | format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', |
| | handlers=[ |
| | logging.StreamHandler(sys.stdout) |
| | ] |
| | ) |
| | logger = logging.getLogger("Aegis_Monitor") |
| |
|
| | |
| | market_agent = MarketDataAgent() |
| | web_agent = WebResearchAgent() |
| |
|
| | def load_watchlist(): |
| | if not os.path.exists(WATCHLIST_FILE): |
| | return [] |
| | try: |
| | with open(WATCHLIST_FILE, 'r') as f: |
| | return json.load(f) |
| | except Exception as e: |
| | logger.error(f"Error loading watchlist: {e}") |
| | return [] |
| |
|
| | def save_alert(alert): |
| | alerts = [] |
| | if os.path.exists(ALERTS_FILE): |
| | try: |
| | with open(ALERTS_FILE, 'r') as f: |
| | alerts = json.load(f) |
| | except: |
| | pass |
| | |
| | |
| | alerts.insert(0, alert) |
| | |
| | alerts = alerts[:100] |
| | |
| | with open(ALERTS_FILE, 'w') as f: |
| | json.dump(alerts, f, indent=2) |
| |
|
| | def check_market_data(symbol): |
| | try: |
| | logger.info(f"Checking market data for {symbol}...") |
| | |
| | result = market_agent.get_market_data(symbol=symbol, time_range="INTRADAY") |
| | |
| | if result.get("status") != "success": |
| | logger.warning(f"Failed to get market data for {symbol}") |
| | return None |
| |
|
| | data = result.get("data", {}) |
| | if not data: |
| | return None |
| |
|
| | |
| | timestamps = sorted(list(data.keys()), reverse=True) |
| | if len(timestamps) < 4: |
| | return None |
| | |
| | latest = data[timestamps[0]] |
| | baseline = data[timestamps[min(3, len(timestamps)-1)]] |
| | |
| | close_latest = float(latest.get("4. close", 0)) |
| | close_baseline = float(baseline.get("4. close", 0)) |
| | |
| | if close_baseline == 0: |
| | return None |
| | |
| | pct_change = ((close_latest - close_baseline) / close_baseline) * 100 |
| | |
| | return { |
| | "price": close_latest, |
| | "change": pct_change, |
| | "timestamp": timestamps[0] |
| | } |
| | except Exception as e: |
| | logger.error(f"Error checking market data for {symbol}: {e}") |
| | return None |
| |
|
| | def check_news(symbol): |
| | try: |
| | logger.info(f"Checking news for {symbol}...") |
| | query = f"breaking news {symbol} stock today" |
| | result = web_agent.research(queries=[query], search_depth="basic") |
| | |
| | if result.get("status") != "success": |
| | return None |
| | |
| | |
| | data = result.get("data", []) |
| | if data and data[0].get("results"): |
| | first_hit = data[0]["results"][0] |
| | return { |
| | "title": first_hit.get("title"), |
| | "url": first_hit.get("url"), |
| | "content": first_hit.get("content")[:200] + "..." |
| | } |
| | return None |
| | except Exception as e: |
| | logger.error(f"Error checking news for {symbol}: {e}") |
| | return None |
| |
|
| | def run_monitor_loop(): |
| | logger.info("--- 🛡️ Aegis Proactive Monitor Started ---") |
| | logger.info(f"Monitoring watchlist every {CHECK_INTERVAL} seconds ({CHECK_INTERVAL/60:.0f} minutes).") |
| | logger.info(f"Price alert threshold: {PRICE_ALERT_THRESHOLD}%") |
| | |
| | while True: |
| | watchlist = load_watchlist() |
| | if not watchlist: |
| | logger.info("Watchlist is empty. Waiting...") |
| | |
| | for symbol in watchlist: |
| | try: |
| | |
| | market_info = check_market_data(symbol) |
| | if market_info: |
| | |
| | if abs(market_info['change']) > PRICE_ALERT_THRESHOLD: |
| | direction = "📈 UP" if market_info['change'] > 0 else "📉 DOWN" |
| | alert_msg = f"{direction} ALERT: {symbol} moved {market_info['change']:+.2f}% to ${market_info['price']:.2f}" |
| | logger.info(alert_msg) |
| | |
| | save_alert({ |
| | "timestamp": datetime.now().isoformat(), |
| | "type": "MARKET", |
| | "symbol": symbol, |
| | "message": alert_msg, |
| | "details": market_info |
| | }) |
| |
|
| | |
| | news_info = check_news(symbol) |
| | if news_info: |
| | |
| | keywords = [ |
| | "acquisition", "merger", "earnings", "crash", "surge", "plunge", |
| | "fda", "lawsuit", "sec", "filing", "8-k", "10-k", "insider", |
| | "partnership", "deal", "bankruptcy", "recall", "investigation", |
| | "upgrade", "downgrade", "target", "buyback", "dividend" |
| | ] |
| | if any(k in news_info['title'].lower() for k in keywords): |
| | alert_msg = f"📰 NEWS ALERT: {symbol} - {news_info['title']}" |
| | logger.info(alert_msg) |
| | |
| | save_alert({ |
| | "timestamp": datetime.now().isoformat(), |
| | "type": "NEWS", |
| | "symbol": symbol, |
| | "message": alert_msg, |
| | "details": news_info |
| | }) |
| | |
| | except Exception as e: |
| | logger.error(f"Error processing {symbol}: {e}") |
| | |
| | logger.info(f"Cycle complete. Sleeping for {CHECK_INTERVAL}s...") |
| | time.sleep(CHECK_INTERVAL) |
| |
|
| | if __name__ == "__main__": |
| | |
| | sys.path.append(os.path.abspath(os.path.dirname(__file__))) |
| | run_monitor_loop() |
| |
|