diff --git a/.env.example b/.env.example index 3c6f6dd4..6f5a8964 100644 --- a/.env.example +++ b/.env.example @@ -8,3 +8,4 @@ TAAPI_API_KEY=taapi_api_key_placeholder BYBIT_BASE_URL=https://api-demo.bybit.com BYBIT_API_KEY=bybit_api_key_placeholder BYBIT_API_SECRET=bybit_api_secret_placeholder +COIN_GECKO_API_BASE_URL=https://api.coingecko.com/api/v3 diff --git a/tradingagents/dataflows/binance.py b/tradingagents/dataflows/binance.py index f6c6569d..bdf4c160 100644 --- a/tradingagents/dataflows/binance.py +++ b/tradingagents/dataflows/binance.py @@ -1,20 +1,33 @@ from binance_common.configuration import ConfigurationRestAPI from binance_common.constants import SPOT_REST_API_PROD_URL from binance_sdk_spot.spot import Spot -import os from datetime import datetime import csv import io +from tradingagents.dataflows.config import get_config -def get_api_key() -> str: - """Retrieve the API key for Binance from environment variables.""" - api_key = os.getenv("BINANCE_API_KEY") - if not api_key: - raise ValueError("BINANCE_API_KEY environment variable is not set.") - return api_key +_client = None -configuration = ConfigurationRestAPI(api_key=get_api_key(), base_path=SPOT_REST_API_PROD_URL) -client = Spot(config_rest_api=configuration) +def get_binance_client(): + """Get or create Binance client with lazy initialization.""" + global _client + if _client is None: + try: + config = get_config() + api_key = config["external"].get("BINANCE_API_KEY", "") + if not api_key: + raise ValueError("BINANCE_API_KEY not found in configuration") + + configuration = ConfigurationRestAPI( + api_key=api_key, + base_path=SPOT_REST_API_PROD_URL + ) + _client = Spot(config_rest_api=configuration) + except Exception as e: + print(f"ERROR: Failed to initialize Binance client: {e}") + raise + + return _client def get_market_data(symbol: str, start_date: str, end_date: str): """Fetch market data for a given symbol from Binance. Get OHLCV data. interval is 1 day. @@ -36,6 +49,7 @@ def get_market_data(symbol: str, start_date: str, end_date: str): print(f"DEBUG: Fetching data for {formatted_symbol} from {start_date} to {end_date}") try: + client = get_binance_client() response = client.rest_api.klines( symbol=formatted_symbol, start_time=start_epoch, @@ -80,6 +94,8 @@ def get_market_data(symbol: str, start_date: str, end_date: str): csv_string = output.getvalue() output.close() + title = f"# Market Data for {symbol} from {start_date} to {end_date}\n\n" + csv_string = title + csv_string return csv_string except Exception as e: diff --git a/tradingagents/dataflows/coin_gecko_fundamentals.py b/tradingagents/dataflows/coin_gecko_fundamentals.py index 7e8f5759..15367a69 100644 --- a/tradingagents/dataflows/coin_gecko_fundamentals.py +++ b/tradingagents/dataflows/coin_gecko_fundamentals.py @@ -1,5 +1,6 @@ import requests from .alpha_vantage_common import API_BASE_URL +from tradingagents.dataflows.config import get_config def get_market_cap() -> str: """ @@ -8,8 +9,11 @@ def get_market_cap() -> str: Returns: str: Market capitalization percentage data for cryptocurrencies """ - endpoint = f"https://api.coingecko.com/api/v3/global" + config = get_config() + api_base_url = config["external"].get("COIN_GECKO_API_BASE_URL", "https://api.coingecko.com/api/v3") + endpoint = f"{api_base_url}/global" response = requests.get(endpoint) + print(f"DEBUG: CoinGecko API response status code: {response.status_code}") response.raise_for_status() data = response.json() market_cap_pct = data.get("data", {}).get("market_cap_percentage", {}) @@ -18,4 +22,4 @@ def get_market_cap() -> str: for coin, percentage in market_cap_pct.items(): # Format each line as "Coin: XX.XX%" result += f"- {coin.upper()}: {percentage:.2f}%\n" - return result \ No newline at end of file + return result diff --git a/tradingagents/dataflows/interface.py b/tradingagents/dataflows/interface.py index d79096d0..2c330da5 100644 --- a/tradingagents/dataflows/interface.py +++ b/tradingagents/dataflows/interface.py @@ -94,9 +94,6 @@ VENDOR_METHODS = { # technical_indicators "get_indicators": { "taapi": get_crypto_stats_indicators_window, - # "alpha_vantage": get_alpha_vantage_indicator, - # "yfinance": get_stock_stats_indicators_window, - # "local": get_stock_stats_indicators_window }, "get_indicators_bulk": { "taapi": get_crypto_stats_indicators, diff --git a/tradingagents/dataflows/openai.py b/tradingagents/dataflows/openai.py index abc387f0..f643aee6 100644 --- a/tradingagents/dataflows/openai.py +++ b/tradingagents/dataflows/openai.py @@ -1,10 +1,27 @@ from openai import OpenAI from .config import get_config +_client = None + +def get_openai_client(): + """Get or create OpenAI client with lazy initialization.""" + global _client + if _client is None: + try: + config = get_config() + base_url = config.get("backend_url") + if not base_url: + raise ValueError("backend_url not found in configuration") + _client = OpenAI(base_url=base_url) + except Exception as e: + print(f"ERROR: Failed to initialize OpenAI client: {e}") + raise + + return _client def get_stock_news_openai(query, start_date, end_date): config = get_config() - client = OpenAI(base_url=config["backend_url"]) + client = get_openai_client() response = client.responses.create( model=config["quick_think_llm"], @@ -39,7 +56,7 @@ def get_stock_news_openai(query, start_date, end_date): def get_crypto_news_openai(query, start_date, end_date): config = get_config() - client = OpenAI(base_url=config["backend_url"]) + client = get_openai_client() response = client.responses.create( model=config["quick_think_llm"], @@ -74,7 +91,7 @@ def get_crypto_news_openai(query, start_date, end_date): def get_global_news_openai(curr_date, look_back_days=7, limit=5): config = get_config() - client = OpenAI(base_url=config["backend_url"]) + client = get_openai_client() response = client.responses.create( model=config["quick_think_llm"], @@ -110,7 +127,7 @@ def get_global_news_openai(curr_date, look_back_days=7, limit=5): def get_fundamentals_openai(ticker, curr_date): config = get_config() - client = OpenAI(base_url=config["backend_url"]) + client = get_openai_client() response = client.responses.create( model=config["quick_think_llm"], @@ -146,7 +163,7 @@ def get_fundamentals_openai(ticker, curr_date): def get_whitepaper_openai(symbol): config = get_config() - client = OpenAI(base_url=config["backend_url"]) + client = get_openai_client() response = client.responses.create( model=config["quick_think_llm"], @@ -177,4 +194,4 @@ def get_whitepaper_openai(symbol): store=True, ) - return response.output[1].content[0].text \ No newline at end of file + return response.output[1].content[0].text diff --git a/tradingagents/dataflows/taapi.py b/tradingagents/dataflows/taapi.py index c0d13777..456d28ec 100644 --- a/tradingagents/dataflows/taapi.py +++ b/tradingagents/dataflows/taapi.py @@ -1,16 +1,7 @@ import requests -from typing import Annotated, List, Dict -import os +from typing import Annotated, List from tradingagents.dataflows.config import get_config - -def get_api_key() -> str: - """Retrieve the API key for TAAPI from environment variables.""" - api_key = os.getenv("TAAPI_API_KEY") - if not api_key: - raise ValueError("TAAPI_API_KEY environment variable is not set.") - return api_key - # This is for single indicator, unused for now but kept for reference def get_crypto_stats_indicators_window( symbol: Annotated[str, "ticker symbol of the coin/asset"], @@ -58,8 +49,10 @@ def get_crypto_stats_indicators_window( return f"Error: Indicator '{indicator}' is not supported. Please choose from: {list(supported_indicators.keys())}" config = get_config() - base_url = config["tool_providers"].get("TAAPI_BASE_URL", "https://api.taapi.io") - api_key = get_api_key() + base_url = config["external"].get("TAAPI_BASE_URL", "https://api.taapi.io") + api_key = config["external"].get("TAAPI_API_KEY", "") + if not api_key: + return "Error: TAAPI_API_KEY is not set in the configuration." # Set backtrack as requested backtrack = look_back_days @@ -187,8 +180,10 @@ def get_crypto_stats_indicators( return f"Error: Indicators {invalid_indicators} are not supported. Please choose from: {list(supported_indicators.keys())}" config = get_config() - base_url = config["tool_providers"].get("TAAPI_BASE_URL", "https://api.taapi.io") - api_key = get_api_key() + base_url = config["external"].get("TAAPI_BASE_URL", "https://api.taapi.io") + api_key = config["external"].get("TAAPI_API_KEY", "") + if not api_key: + return "Error: TAAPI_API_KEY is not set in the configuration." # Construct the bulk API URL url = f"{base_url}/bulk" diff --git a/tradingagents/dataflows/telegram.py b/tradingagents/dataflows/telegram.py index 494d2c21..0d9b66ba 100644 --- a/tradingagents/dataflows/telegram.py +++ b/tradingagents/dataflows/telegram.py @@ -1,13 +1,19 @@ import asyncio from telethon import TelegramClient from datetime import datetime, timedelta, timezone -import os +from tradingagents.dataflows.config import get_config def get_api_credentials(): - api_id = int(os.getenv("TELEGRAM_API_ID", "")) - api_hash = os.getenv("TELEGRAM_API_HASH", "") - session_name = os.getenv("TELEGRAM_SESSION_NAME", "") - return api_id, api_hash, session_name + """Retrieve Telegram API credentials from environment variables.""" + config = get_config() + api_id = config["external"]["TELEGRAM_API_ID"] + api_hash = config["external"]["TELEGRAM_API_HASH"] + session_name = config["external"]["TELEGRAM_SESSION_NAME"] + + if not api_id or not api_hash or not session_name: + raise ValueError("Missing required Telegram credentials: TELEGRAM_API_ID, TELEGRAM_API_HASH, or TELEGRAM_SESSION_NAME") + + return int(api_id), api_hash, session_name async def _get_channel_history_async(start_date_str, end_date_str): """ @@ -55,4 +61,4 @@ def get_crypto_news_telegram(curr_date, look_back_days=7, limit=100): start_date_str = start_date.strftime('%Y-%m-%d') end_date_str = end_date.strftime('%Y-%m-%d') - return asyncio.run(_get_channel_history_async(start_date_str, end_date_str)) \ No newline at end of file + return asyncio.run(_get_channel_history_async(start_date_str, end_date_str)) diff --git a/tradingagents/default_config.py b/tradingagents/default_config.py index 419abb06..3709a302 100644 --- a/tradingagents/default_config.py +++ b/tradingagents/default_config.py @@ -21,23 +21,27 @@ DEFAULT_CONFIG = { # Category-level configuration (default for all tools in category) "data_vendors": { "core_crypto_apis": "binance", # Options: binance - "core_stock_apis": "yfinance", # Options: yfinance, alpha_vantage, local "technical_indicators": "taapi", # Options: taapi - "fundamental_data": "alpha_vantage", # Options: openai, alpha_vantage, local - "news_data": "openai", # Options: openai, alpha_vantage, google, local + "fundamental_data": "openai", # Options: openai + "news_data": "openai", # Options: local, openai, telegram }, # Tool-level configuration (takes precedence over category-level) "tool_vendors": { - # Example: "get_stock_data": "alpha_vantage", # Override category default "get_global_news": "telegram" # Override category default }, # Tool provider settings "tool_providers": { - "TAAPI_BASE_URL": os.getenv("TAAPI_BASE_URL", "https://api.taapi.io"), }, "external": { + "BINANCE_API_KEY": os.getenv("BINANCE_API_KEY", ""), + "TAAPI_BASE_URL": os.getenv("TAAPI_BASE_URL", "https://api.taapi.io"), + "TAAPI_API_KEY": os.getenv("TAAPI_API_KEY", ""), "BYBIT_BASE_URL": os.getenv("BYBIT_BASE_URL", "https://api-demo.bybit.com"), "BYBIT_API_KEY": os.getenv("BYBIT_API_KEY", ""), "BYBIT_API_SECRET": os.getenv("BYBIT_API_SECRET", ""), + "COIN_GECKO_API_BASE_URL": os.getenv("COIN_GECKO_API_BASE_URL", "https://api.coingecko.com/api/v3"), + "TELEGRAM_API_ID": os.getenv("TELEGRAM_API_ID", ""), + "TELEGRAM_API_HASH": os.getenv("TELEGRAM_API_HASH", ""), + "TELEGRAM_SESSION_NAME": os.getenv("TELEGRAM_SESSION_NAME", ""), } }