""" CSV Data Access Tools for Mirai AI Copilot. Reads directly from backend/app/data directories. """ import os import glob import json import csv import re from typing import Dict, Any, List BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) DATA_DIR = os.path.join(BASE_DIR, "app", "data") HISTORICAL_DIR = os.path.join(DATA_DIR, "simulation_historical_data") PRICE_DIR = os.path.join(DATA_DIR, "simulation_price_data_July_1-Aug_30") NEWS_DIR = os.path.join(DATA_DIR, "simulation_news_data_July_1-Aug_30") def normalize_symbol(symbol: str) -> str: if not symbol: return "AAPL" sym = symbol.upper().strip() if sym in ["GOOGL", "ALPHABET", "GOOGLE"]: return "GOOG" return sym def extract_symbol(query: str, default_symbol: str = "AAPL") -> str: if not query: return normalize_symbol(default_symbol) query_upper = query.upper() known_symbols = ["GOOGL", "GOOG", "AAPL", "TSLA", "MSFT", "IBM", "WMT", "UL", "NVDA", "AMZN", "META"] for sym in known_symbols: if re.search(r'\b' + sym + r'\b', query_upper): return normalize_symbol(sym) return normalize_symbol(default_symbol) def getHistoricalData(symbol: str, period: str = "3M") -> List[Dict[str, Any]]: sym = normalize_symbol(symbol) bars = [] files = glob.glob(os.path.join(HISTORICAL_DIR, f"*{sym}*.csv")) filepath = files[0] if files else None if filepath and os.path.exists(filepath): with open(filepath, "r", encoding="utf-8") as f: reader = csv.DictReader(f) for row in reader: try: bars.append({ "date": row.get("timestamp", "").strip(), "open": float(row.get("open", 0)), "high": float(row.get("high", 0)), "low": float(row.get("low", 0)), "close": float(row.get("close", 0)), "volume": int(row.get("volume", 0)) }) except (ValueError, KeyError): continue return bars[-63:] if len(bars) > 63 else bars def getLivePrice(symbol: str) -> Dict[str, Any]: sym = normalize_symbol(symbol) ticks = [] files = glob.glob(os.path.join(PRICE_DIR, f"*{sym}*.csv")) filepath = files[0] if files else None if filepath and os.path.exists(filepath): with open(filepath, "r", encoding="utf-8") as f: reader = csv.DictReader(f) for row in reader: try: ticks.append({ "timestamp": row.get("timestamp", "").strip(), "open": float(row.get("open", 0)), "high": float(row.get("high", 0)), "low": float(row.get("low", 0)), "close": float(row.get("close", 0)), "volume": int(row.get("volume", 0)) }) except (ValueError, KeyError): continue if ticks: latest = ticks[-1] prev = ticks[-2] if len(ticks) >= 2 else latest chg = round(latest["close"] - prev["close"], 2) pct = round((chg / prev["close"] * 100), 2) if prev["close"] else 0.0 return { "symbol": sym, "price": latest["close"], "change": chg, "changePercent": pct, "volume": latest["volume"], "bid": round(latest["close"] * 0.9995, 2), "ask": round(latest["close"] * 1.0005, 2) } return {"symbol": sym, "price": 194.50, "change": -0.68, "changePercent": -0.35, "volume": 124500, "bid": 194.49, "ask": 194.51} def getMarketNews(symbol: str = None, limit: int = 10) -> List[Dict[str, Any]]: items = [] sym = normalize_symbol(symbol) if symbol else "" if os.path.exists(NEWS_DIR): for filepath in sorted(glob.glob(os.path.join(NEWS_DIR, "*.json")), reverse=True): try: with open(filepath, "r", encoding="utf-8") as f: data = json.load(f) news_lists = [] if isinstance(data, dict): for date_key in sorted(data.keys(), reverse=True): news_lists.extend(data[date_key]) elif isinstance(data, list): news_lists = data for item in news_lists: tickers = [] if "ticker_sentiment" in item and isinstance(item["ticker_sentiment"], list): tickers = [ts.get("ticker", "").upper() for ts in item["ticker_sentiment"]] elif "symbols" in item: tickers = [s.upper() for s in item.get("symbols", [])] # Check if news matches symbol or return recent news if no symbol specified is_match = False if not symbol: is_match = True else: if sym in tickers or (sym == "GOOG" and "GOOGL" in tickers): is_match = True if is_match: sentiment_label = "Neutral" if "ticker_sentiment" in item and isinstance(item["ticker_sentiment"], list): for ts in item["ticker_sentiment"]: if ts.get("ticker", "").upper() in [sym, "GOOG", "GOOGL"]: sentiment_label = ts.get("ticker_sentiment_label", "Neutral") break items.append({ "title": item.get("title", "Market Update"), "summary": item.get("summary", item.get("title", "")), "source": item.get("source", "Financial News"), "time_published": item.get("time_published", ""), "sentiment": sentiment_label, "tickers": tickers }) if len(items) >= limit: return items except Exception: continue return items[:limit]