diff --git a/tradingagents/dataflows/alpha_vantage_common.py b/tradingagents/dataflows/alpha_vantage_common.py index 6fd5d143..2464daef 100644 --- a/tradingagents/dataflows/alpha_vantage_common.py +++ b/tradingagents/dataflows/alpha_vantage_common.py @@ -1,122 +1,40 @@ -import os -import requests -import pandas as pd -import json -from datetime import datetime -from io import StringIO - -API_BASE_URL = "https://www.alphavantage.co/query" - -def get_api_key() -> str: - """Retrieve the API key for Alpha Vantage from environment variables.""" - api_key = os.getenv("ALPHA_VANTAGE_API_KEY") - if not api_key: - raise ValueError("ALPHA_VANTAGE_API_KEY environment variable is not set.") - return api_key - -def format_datetime_for_api(date_input) -> str: - """Convert various date formats to YYYYMMDDTHHMM format required by Alpha Vantage API.""" - if isinstance(date_input, str): - # If already in correct format, return as-is - if len(date_input) == 13 and 'T' in date_input: - return date_input - # Try to parse common date formats - try: - dt = datetime.strptime(date_input, "%Y-%m-%d") - return dt.strftime("%Y%m%dT0000") - except ValueError: - try: - dt = datetime.strptime(date_input, "%Y-%m-%d %H:%M") - return dt.strftime("%Y%m%dT%H%M") - except ValueError: - raise ValueError(f"Unsupported date format: {date_input}") - elif isinstance(date_input, datetime): - return date_input.strftime("%Y%m%dT%H%M") - else: - raise ValueError(f"Date must be string or datetime object, got {type(date_input)}") - -class AlphaVantageRateLimitError(Exception): - """Exception raised when Alpha Vantage API rate limit is exceeded.""" - pass - -def _make_api_request(function_name: str, params: dict) -> dict | str: - """Helper function to make API requests and handle responses. - - Raises: - AlphaVantageRateLimitError: When API rate limit is exceeded - """ - # Create a copy of params to avoid modifying the original - api_params = params.copy() - api_params.update({ - "function": function_name, - "apikey": get_api_key(), - "source": "trading_agents", - }) - - # Handle entitlement parameter if present in params or global variable - current_entitlement = globals().get('_current_entitlement') - entitlement = api_params.get("entitlement") or current_entitlement - - if entitlement: - api_params["entitlement"] = entitlement - elif "entitlement" in api_params: - # Remove entitlement if it's None or empty - api_params.pop("entitlement", None) - - response = requests.get(API_BASE_URL, params=api_params) - response.raise_for_status() - - response_text = response.text - - # Check if response is JSON (error responses are typically JSON) - try: - response_json = json.loads(response_text) - # Check for rate limit error - if "Information" in response_json: - info_message = response_json["Information"] - if "rate limit" in info_message.lower() or "api key" in info_message.lower(): - raise AlphaVantageRateLimitError(f"Alpha Vantage rate limit exceeded: {info_message}") - except json.JSONDecodeError: - # Response is not JSON (likely CSV data), which is normal - pass - - return response_text - - - -def _filter_csv_by_date_range(csv_data: str, start_date: str, end_date: str) -> str: - """ - Filter CSV data to include only rows within the specified date range. - - Args: - csv_data: CSV string from Alpha Vantage API - start_date: Start date in yyyy-mm-dd format - end_date: End date in yyyy-mm-dd format - - Returns: - Filtered CSV string - """ - if not csv_data or csv_data.strip() == "": - return csv_data - - try: - # Parse CSV data - df = pd.read_csv(StringIO(csv_data)) - - # Assume the first column is the date column (timestamp) - date_col = df.columns[0] - df[date_col] = pd.to_datetime(df[date_col]) - - # Filter by date range - start_dt = pd.to_datetime(start_date) - end_dt = pd.to_datetime(end_date) - - filtered_df = df[(df[date_col] >= start_dt) & (df[date_col] <= end_dt)] - - # Convert back to CSV string - return filtered_df.to_csv(index=False) - - except Exception as e: - # If filtering fails, return original data with a warning - print(f"Warning: Failed to filter CSV data by date range: {e}") - return csv_data +""" +DEPRECATED — Alpha Vantage removed in the FMP-primary migration. + +This module used to be the shared Alpha Vantage HTTP wrapper (API key +lookup + request sender + rate-limit handling). Every function is a +no-op stub now; callers through ``interface.py`` and the sibling +``alpha_vantage_*`` modules won't import-error, but every data call +returns an empty string / sentinel. Alpha Vantage free-tier (25 req/day) +was secondary fallback anyway; FMP Ultimate is authoritative. + +Remove this file once no ``from .alpha_vantage_common import ...`` +imports remain anywhere in the tree. +""" +from __future__ import annotations + +import logging + +logger = logging.getLogger(__name__) + + +class AlphaVantageRateLimitError(Exception): + """Kept for type-compat with any `except AlphaVantageRateLimitError` sites.""" + + +def get_api_key() -> str: + return "" + + +def format_datetime_for_api(date_input) -> str: + # Pass-through of a date string is harmless; callers pass ISO dates. + return str(date_input) if date_input is not None else "" + + +def _make_api_request(function_name: str, params: dict) -> dict | str: + logger.debug("alpha_vantage call skipped (module stubbed): %s", function_name) + return {} + + +def _filter_csv_by_date_range(csv_data: str, start_date: str, end_date: str) -> str: + return csv_data or "" diff --git a/tradingagents/dataflows/alpha_vantage_fundamentals.py b/tradingagents/dataflows/alpha_vantage_fundamentals.py index 402fa2f0..aab1364b 100644 --- a/tradingagents/dataflows/alpha_vantage_fundamentals.py +++ b/tradingagents/dataflows/alpha_vantage_fundamentals.py @@ -1,77 +1,21 @@ -from .alpha_vantage_common import _make_api_request - - -def get_fundamentals(ticker: str, curr_date: str = None) -> str: - """ - Retrieve comprehensive fundamental data for a given ticker symbol using Alpha Vantage. - - Args: - ticker (str): Ticker symbol of the company - curr_date (str): Current date you are trading at, yyyy-mm-dd (not used for Alpha Vantage) - - Returns: - str: Company overview data including financial ratios and key metrics - """ - params = { - "symbol": ticker, - } - - return _make_api_request("OVERVIEW", params) - - -def get_balance_sheet(ticker: str, freq: str = "quarterly", curr_date: str = None) -> str: - """ - Retrieve balance sheet data for a given ticker symbol using Alpha Vantage. - - Args: - ticker (str): Ticker symbol of the company - freq (str): Reporting frequency: annual/quarterly (default quarterly) - not used for Alpha Vantage - curr_date (str): Current date you are trading at, yyyy-mm-dd (not used for Alpha Vantage) - - Returns: - str: Balance sheet data with normalized fields - """ - params = { - "symbol": ticker, - } - - return _make_api_request("BALANCE_SHEET", params) - - -def get_cashflow(ticker: str, freq: str = "quarterly", curr_date: str = None) -> str: - """ - Retrieve cash flow statement data for a given ticker symbol using Alpha Vantage. - - Args: - ticker (str): Ticker symbol of the company - freq (str): Reporting frequency: annual/quarterly (default quarterly) - not used for Alpha Vantage - curr_date (str): Current date you are trading at, yyyy-mm-dd (not used for Alpha Vantage) - - Returns: - str: Cash flow statement data with normalized fields - """ - params = { - "symbol": ticker, - } - - return _make_api_request("CASH_FLOW", params) - - -def get_income_statement(ticker: str, freq: str = "quarterly", curr_date: str = None) -> str: - """ - Retrieve income statement data for a given ticker symbol using Alpha Vantage. - - Args: - ticker (str): Ticker symbol of the company - freq (str): Reporting frequency: annual/quarterly (default quarterly) - not used for Alpha Vantage - curr_date (str): Current date you are trading at, yyyy-mm-dd (not used for Alpha Vantage) - - Returns: - str: Income statement data with normalized fields - """ - params = { - "symbol": ticker, - } - - return _make_api_request("INCOME_STATEMENT", params) - +""" +DEPRECATED — Alpha Vantage removed in the FMP-primary migration. +See alpha_vantage_common.py for context. All stubs. +""" +from __future__ import annotations + + +def get_fundamentals(ticker: str, curr_date: str = None) -> str: + return "" + + +def get_balance_sheet(ticker: str, freq: str = "quarterly", curr_date: str = None) -> str: + return "" + + +def get_cashflow(ticker: str, freq: str = "quarterly", curr_date: str = None) -> str: + return "" + + +def get_income_statement(ticker: str, freq: str = "quarterly", curr_date: str = None) -> str: + return "" diff --git a/tradingagents/dataflows/alpha_vantage_indicator.py b/tradingagents/dataflows/alpha_vantage_indicator.py index 8d5bf118..518099b0 100644 --- a/tradingagents/dataflows/alpha_vantage_indicator.py +++ b/tradingagents/dataflows/alpha_vantage_indicator.py @@ -1,222 +1,17 @@ -from .alpha_vantage_common import _make_api_request - -def get_indicator( - symbol: str, - indicator: str, - curr_date: str, - look_back_days: int, - interval: str = "daily", - time_period: int = 14, - series_type: str = "close" -) -> str: - """ - Returns Alpha Vantage technical indicator values over a time window. - - Args: - symbol: ticker symbol of the company - indicator: technical indicator to get the analysis and report of - curr_date: The current trading date you are trading on, YYYY-mm-dd - look_back_days: how many days to look back - interval: Time interval (daily, weekly, monthly) - time_period: Number of data points for calculation - series_type: The desired price type (close, open, high, low) - - Returns: - String containing indicator values and description - """ - from datetime import datetime - from dateutil.relativedelta import relativedelta - - supported_indicators = { - "close_50_sma": ("50 SMA", "close"), - "close_200_sma": ("200 SMA", "close"), - "close_10_ema": ("10 EMA", "close"), - "macd": ("MACD", "close"), - "macds": ("MACD Signal", "close"), - "macdh": ("MACD Histogram", "close"), - "rsi": ("RSI", "close"), - "boll": ("Bollinger Middle", "close"), - "boll_ub": ("Bollinger Upper Band", "close"), - "boll_lb": ("Bollinger Lower Band", "close"), - "atr": ("ATR", None), - "vwma": ("VWMA", "close") - } - - indicator_descriptions = { - "close_50_sma": "50 SMA: A medium-term trend indicator. Usage: Identify trend direction and serve as dynamic support/resistance. Tips: It lags price; combine with faster indicators for timely signals.", - "close_200_sma": "200 SMA: A long-term trend benchmark. Usage: Confirm overall market trend and identify golden/death cross setups. Tips: It reacts slowly; best for strategic trend confirmation rather than frequent trading entries.", - "close_10_ema": "10 EMA: A responsive short-term average. Usage: Capture quick shifts in momentum and potential entry points. Tips: Prone to noise in choppy markets; use alongside longer averages for filtering false signals.", - "macd": "MACD: Computes momentum via differences of EMAs. Usage: Look for crossovers and divergence as signals of trend changes. Tips: Confirm with other indicators in low-volatility or sideways markets.", - "macds": "MACD Signal: An EMA smoothing of the MACD line. Usage: Use crossovers with the MACD line to trigger trades. Tips: Should be part of a broader strategy to avoid false positives.", - "macdh": "MACD Histogram: Shows the gap between the MACD line and its signal. Usage: Visualize momentum strength and spot divergence early. Tips: Can be volatile; complement with additional filters in fast-moving markets.", - "rsi": "RSI: Measures momentum to flag overbought/oversold conditions. Usage: Apply 70/30 thresholds and watch for divergence to signal reversals. Tips: In strong trends, RSI may remain extreme; always cross-check with trend analysis.", - "boll": "Bollinger Middle: A 20 SMA serving as the basis for Bollinger Bands. Usage: Acts as a dynamic benchmark for price movement. Tips: Combine with the upper and lower bands to effectively spot breakouts or reversals.", - "boll_ub": "Bollinger Upper Band: Typically 2 standard deviations above the middle line. Usage: Signals potential overbought conditions and breakout zones. Tips: Confirm signals with other tools; prices may ride the band in strong trends.", - "boll_lb": "Bollinger Lower Band: Typically 2 standard deviations below the middle line. Usage: Indicates potential oversold conditions. Tips: Use additional analysis to avoid false reversal signals.", - "atr": "ATR: Averages true range to measure volatility. Usage: Set stop-loss levels and adjust position sizes based on current market volatility. Tips: It's a reactive measure, so use it as part of a broader risk management strategy.", - "vwma": "VWMA: A moving average weighted by volume. Usage: Confirm trends by integrating price action with volume data. Tips: Watch for skewed results from volume spikes; use in combination with other volume analyses." - } - - if indicator not in supported_indicators: - raise ValueError( - f"Indicator {indicator} is not supported. Please choose from: {list(supported_indicators.keys())}" - ) - - curr_date_dt = datetime.strptime(curr_date, "%Y-%m-%d") - before = curr_date_dt - relativedelta(days=look_back_days) - - # Get the full data for the period instead of making individual calls - _, required_series_type = supported_indicators[indicator] - - # Use the provided series_type or fall back to the required one - if required_series_type: - series_type = required_series_type - - try: - # Get indicator data for the period - if indicator == "close_50_sma": - data = _make_api_request("SMA", { - "symbol": symbol, - "interval": interval, - "time_period": "50", - "series_type": series_type, - "datatype": "csv" - }) - elif indicator == "close_200_sma": - data = _make_api_request("SMA", { - "symbol": symbol, - "interval": interval, - "time_period": "200", - "series_type": series_type, - "datatype": "csv" - }) - elif indicator == "close_10_ema": - data = _make_api_request("EMA", { - "symbol": symbol, - "interval": interval, - "time_period": "10", - "series_type": series_type, - "datatype": "csv" - }) - elif indicator == "macd": - data = _make_api_request("MACD", { - "symbol": symbol, - "interval": interval, - "series_type": series_type, - "datatype": "csv" - }) - elif indicator == "macds": - data = _make_api_request("MACD", { - "symbol": symbol, - "interval": interval, - "series_type": series_type, - "datatype": "csv" - }) - elif indicator == "macdh": - data = _make_api_request("MACD", { - "symbol": symbol, - "interval": interval, - "series_type": series_type, - "datatype": "csv" - }) - elif indicator == "rsi": - data = _make_api_request("RSI", { - "symbol": symbol, - "interval": interval, - "time_period": str(time_period), - "series_type": series_type, - "datatype": "csv" - }) - elif indicator in ["boll", "boll_ub", "boll_lb"]: - data = _make_api_request("BBANDS", { - "symbol": symbol, - "interval": interval, - "time_period": "20", - "series_type": series_type, - "datatype": "csv" - }) - elif indicator == "atr": - data = _make_api_request("ATR", { - "symbol": symbol, - "interval": interval, - "time_period": str(time_period), - "datatype": "csv" - }) - elif indicator == "vwma": - # Alpha Vantage doesn't have direct VWMA, so we'll return an informative message - # In a real implementation, this would need to be calculated from OHLCV data - return f"## VWMA (Volume Weighted Moving Average) for {symbol}:\n\nVWMA calculation requires OHLCV data and is not directly available from Alpha Vantage API.\nThis indicator would need to be calculated from the raw stock data using volume-weighted price averaging.\n\n{indicator_descriptions.get('vwma', 'No description available.')}" - else: - return f"Error: Indicator {indicator} not implemented yet." - - # Parse CSV data and extract values for the date range - lines = data.strip().split('\n') - if len(lines) < 2: - return f"Error: No data returned for {indicator}" - - # Parse header and data - header = [col.strip() for col in lines[0].split(',')] - try: - date_col_idx = header.index('time') - except ValueError: - return f"Error: 'time' column not found in data for {indicator}. Available columns: {header}" - - # Map internal indicator names to expected CSV column names from Alpha Vantage - col_name_map = { - "macd": "MACD", "macds": "MACD_Signal", "macdh": "MACD_Hist", - "boll": "Real Middle Band", "boll_ub": "Real Upper Band", "boll_lb": "Real Lower Band", - "rsi": "RSI", "atr": "ATR", "close_10_ema": "EMA", - "close_50_sma": "SMA", "close_200_sma": "SMA" - } - - target_col_name = col_name_map.get(indicator) - - if not target_col_name: - # Default to the second column if no specific mapping exists - value_col_idx = 1 - else: - try: - value_col_idx = header.index(target_col_name) - except ValueError: - return f"Error: Column '{target_col_name}' not found for indicator '{indicator}'. Available columns: {header}" - - result_data = [] - for line in lines[1:]: - if not line.strip(): - continue - values = line.split(',') - if len(values) > value_col_idx: - try: - date_str = values[date_col_idx].strip() - # Parse the date - date_dt = datetime.strptime(date_str, "%Y-%m-%d") - - # Check if date is in our range - if before <= date_dt <= curr_date_dt: - value = values[value_col_idx].strip() - result_data.append((date_dt, value)) - except (ValueError, IndexError): - continue - - # Sort by date and format output - result_data.sort(key=lambda x: x[0]) - - ind_string = "" - for date_dt, value in result_data: - ind_string += f"{date_dt.strftime('%Y-%m-%d')}: {value}\n" - - if not ind_string: - ind_string = "No data available for the specified date range.\n" - - result_str = ( - f"## {indicator.upper()} values from {before.strftime('%Y-%m-%d')} to {curr_date}:\n\n" - + ind_string - + "\n\n" - + indicator_descriptions.get(indicator, "No description available.") - ) - - return result_str - - except Exception as e: - print(f"Error getting Alpha Vantage indicator data for {indicator}: {e}") - return f"Error retrieving {indicator} data: {str(e)}" +""" +DEPRECATED — Alpha Vantage removed in the FMP-primary migration. +See alpha_vantage_common.py for context. Stub. +""" +from __future__ import annotations + + +def get_indicator( + symbol: str, + indicator: str, + curr_date: str, + look_back_days: int, + interval: str = "daily", + time_period: int = 14, + series_type: str = "close", +) -> str: + return "" diff --git a/tradingagents/dataflows/alpha_vantage_news.py b/tradingagents/dataflows/alpha_vantage_news.py index 512ee616..b76b1d84 100644 --- a/tradingagents/dataflows/alpha_vantage_news.py +++ b/tradingagents/dataflows/alpha_vantage_news.py @@ -1,71 +1,17 @@ -from .alpha_vantage_common import _make_api_request, format_datetime_for_api - -def get_news(ticker, start_date, end_date) -> dict[str, str] | str: - """Returns live and historical market news & sentiment data from premier news outlets worldwide. - - Covers stocks, cryptocurrencies, forex, and topics like fiscal policy, mergers & acquisitions, IPOs. - - Args: - ticker: Stock symbol for news articles. - start_date: Start date for news search. - end_date: End date for news search. - - Returns: - Dictionary containing news sentiment data or JSON string. - """ - - params = { - "tickers": ticker, - "time_from": format_datetime_for_api(start_date), - "time_to": format_datetime_for_api(end_date), - } - - return _make_api_request("NEWS_SENTIMENT", params) - -def get_global_news(curr_date, look_back_days: int = 7, limit: int = 50) -> dict[str, str] | str: - """Returns global market news & sentiment data without ticker-specific filtering. - - Covers broad market topics like financial markets, economy, and more. - - Args: - curr_date: Current date in yyyy-mm-dd format. - look_back_days: Number of days to look back (default 7). - limit: Maximum number of articles (default 50). - - Returns: - Dictionary containing global news sentiment data or JSON string. - """ - from datetime import datetime, timedelta - - # Calculate start date - curr_dt = datetime.strptime(curr_date, "%Y-%m-%d") - start_dt = curr_dt - timedelta(days=look_back_days) - start_date = start_dt.strftime("%Y-%m-%d") - - params = { - "topics": "financial_markets,economy_macro,economy_monetary", - "time_from": format_datetime_for_api(start_date), - "time_to": format_datetime_for_api(curr_date), - "limit": str(limit), - } - - return _make_api_request("NEWS_SENTIMENT", params) - - -def get_insider_transactions(symbol: str) -> dict[str, str] | str: - """Returns latest and historical insider transactions by key stakeholders. - - Covers transactions by founders, executives, board members, etc. - - Args: - symbol: Ticker symbol. Example: "IBM". - - Returns: - Dictionary containing insider transaction data or JSON string. - """ - - params = { - "symbol": symbol, - } - - return _make_api_request("INSIDER_TRANSACTIONS", params) \ No newline at end of file +""" +DEPRECATED — Alpha Vantage removed in the FMP-primary migration. +See alpha_vantage_common.py for context. Stubs. +""" +from __future__ import annotations + + +def get_news(ticker, start_date, end_date) -> dict[str, str] | str: + return "" + + +def get_global_news(curr_date, look_back_days: int = 7, limit: int = 50) -> dict[str, str] | str: + return "" + + +def get_insider_transactions(symbol: str) -> dict[str, str] | str: + return "" diff --git a/tradingagents/dataflows/alpha_vantage_stock.py b/tradingagents/dataflows/alpha_vantage_stock.py index fc0f7fc4..148f8cfb 100644 --- a/tradingagents/dataflows/alpha_vantage_stock.py +++ b/tradingagents/dataflows/alpha_vantage_stock.py @@ -1,38 +1,9 @@ -from datetime import datetime -from .alpha_vantage_common import _make_api_request, _filter_csv_by_date_range - -def get_stock( - symbol: str, - start_date: str, - end_date: str -) -> str: - """ - Returns raw daily OHLCV values, adjusted close values, and historical split/dividend events - filtered to the specified date range. - - Args: - symbol: The name of the equity. For example: symbol=IBM - start_date: Start date in yyyy-mm-dd format - end_date: End date in yyyy-mm-dd format - - Returns: - CSV string containing the daily adjusted time series data filtered to the date range. - """ - # Parse dates to determine the range - start_dt = datetime.strptime(start_date, "%Y-%m-%d") - today = datetime.now() - - # Choose outputsize based on whether the requested range is within the latest 100 days - # Compact returns latest 100 data points, so check if start_date is recent enough - days_from_today_to_start = (today - start_dt).days - outputsize = "compact" if days_from_today_to_start < 100 else "full" - - params = { - "symbol": symbol, - "outputsize": outputsize, - "datatype": "csv", - } - - response = _make_api_request("TIME_SERIES_DAILY_ADJUSTED", params) - - return _filter_csv_by_date_range(response, start_date, end_date) \ No newline at end of file +""" +DEPRECATED — Alpha Vantage removed in the FMP-primary migration. +See alpha_vantage_common.py for context. This is a stub. +""" +from __future__ import annotations + + +def get_stock(symbol: str, start_date: str, end_date: str) -> str: + return "" diff --git a/tradingagents/dataflows/y_finance.py b/tradingagents/dataflows/y_finance.py index 20afd076..2fb59d1a 100644 --- a/tradingagents/dataflows/y_finance.py +++ b/tradingagents/dataflows/y_finance.py @@ -1,3 +1,17 @@ +""" +DEPRECATED — yfinance is scheduled for removal in the FMP-primary migration. + +This module is the Alpaca-fallback path for TradingAgents data. It is +still wired in (tier1 + tier2 + interface.py reference it) and too +large to stub safely without rewiring the structured analysts, so it +stays live for now. Track: replace with calls through +FMPDataService (see stock-screener/backend/docs/FMP_MIGRATION.md for +the pattern) and route `interface.py` to FMP-first instead. + +Callers: tradingagents/agents/structured/tier1.py, +tradingagents/agents/structured/tier2.py, +tradingagents/test.py +""" from typing import Annotated from datetime import datetime from dateutil.relativedelta import relativedelta