From 5d17b8c445e094a39356bb09e9dd7f68895574a3 Mon Sep 17 00:00:00 2001 From: Jafar Muhammad <56466220+mhmmdjafarg@users.noreply.github.com> Date: Fri, 26 Dec 2025 01:47:12 +0700 Subject: [PATCH] [Market Analyst] Use Crypto Data (#3) * add tools binance get market data * update * fix miss symbol * fix tools and bulk taapi --- .env.example | 4 +- .gitignore | 1 + .python-version | 2 +- requirements.txt | 2 + .../agents/analysts/market_analyst.py | 39 +-- .../agents/analysts/social_media_analyst.py | 4 +- tradingagents/agents/utils/agent_states.py | 2 +- tradingagents/agents/utils/agent_utils.py | 8 +- .../agents/utils/core_crypto_tools.py | 21 ++ .../utils/technical_indicators_tools.py | 24 +- tradingagents/dataflows/binance.py | 88 ++++++ tradingagents/dataflows/interface.py | 30 +- tradingagents/dataflows/taapi.py | 270 ++++++++++++++++++ tradingagents/default_config.py | 7 +- tradingagents/graph/propagation.py | 2 +- tradingagents/graph/trading_graph.py | 5 + 16 files changed, 466 insertions(+), 43 deletions(-) create mode 100644 tradingagents/agents/utils/core_crypto_tools.py create mode 100644 tradingagents/dataflows/binance.py create mode 100644 tradingagents/dataflows/taapi.py diff --git a/.env.example b/.env.example index ca3762c6..3a6cffdf 100644 --- a/.env.example +++ b/.env.example @@ -2,4 +2,6 @@ ALPHA_VANTAGE_API_KEY=alpha_vantage_api_key_placeholder OPENAI_API_KEY=openai_api_key_placeholder TELEGRAM_API_ID=telegram_api_placeholder TELEGRAM_API_HASH=telegram_api_hash_placeholder -TELEGRAM_SESSION_NAME=telegram_session_name_placeholder \ No newline at end of file +TELEGRAM_SESSION_NAME=telegram_session_name_placeholder +BINANCE_API_KEY=binance_api_key_placeholder +TAAPI_API_KEY=taapi_api_key_placeholder diff --git a/.gitignore b/.gitignore index acf60dc8..d9c9053b 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ eval_data/ *.egg-info/ .env .python-version +playground.ipynb diff --git a/.python-version b/.python-version index c8cfe395..56bb6605 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.10 +3.12.7 diff --git a/requirements.txt b/requirements.txt index a6154cd2..48de6aa7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -24,3 +24,5 @@ rich questionary langchain_anthropic langchain-google-genai +binance-sdk-spot +telethon diff --git a/tradingagents/agents/analysts/market_analyst.py b/tradingagents/agents/analysts/market_analyst.py index c955dd76..8f54b988 100644 --- a/tradingagents/agents/analysts/market_analyst.py +++ b/tradingagents/agents/analysts/market_analyst.py @@ -1,48 +1,35 @@ from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder -import time -import json -from tradingagents.agents.utils.agent_utils import get_stock_data, get_indicators -from tradingagents.dataflows.config import get_config +from tradingagents.agents.utils.agent_utils import get_crypto_data, get_indicators_bulk def create_market_analyst(llm): def market_analyst_node(state): current_date = state["trade_date"] - ticker = state["company_of_interest"] - company_name = state["company_of_interest"] + symbol = state["ticker_of_interest"] # for crypto, e.g BTC/USDT tools = [ - get_stock_data, - get_indicators, + get_crypto_data, + get_indicators_bulk, ] system_message = ( - """You are a trading assistant tasked with analyzing financial markets. Your role is to select the **most relevant indicators** for a given market condition or trading strategy from the following list. The goal is to choose up to **8 indicators** that provide complementary insights without redundancy. Categories and each category's indicators are: + """You are a crypto trading assistant tasked with analyzing cryptocurrency markets. Your role is to select the **most relevant indicators** for a given crypto market condition or trading strategy from the following list. The goal is to choose the **most effective indicators** that provide complementary insights without redundancy. Available indicators are: Moving Averages: -- 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. +- sma: Simple Moving Average: A basic trend-following indicator that smooths out price data. Usage: Identify trend direction and serve as dynamic support/resistance levels. Tips: Use multiple SMAs for crossover signals; combines well with volume analysis for confirmation. MACD Related: -- 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. +- macd: MACD (Moving Average Convergence Divergence): Measures momentum via differences between fast and slow EMAs. Usage: Look for signal line crossovers, centerline crossovers, and divergence patterns for trend changes. Tips: Most effective in trending markets; combine with RSI to avoid false signals in sideways markets. Momentum Indicators: -- 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. +- rsi: RSI (Relative Strength Index): Oscillator measuring momentum to identify overbought (>70) and oversold (<30) conditions. Usage: Look for reversal signals at extreme levels and divergence with price action. Tips: In strong crypto trends, RSI can remain extreme for extended periods; always confirm with trend analysis. Volatility Indicators: -- 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. +- bbands: Bollinger Bands: Volatility indicator consisting of upper, middle (SMA), and lower bands based on standard deviations. Usage: Identify overbought/oversold conditions, volatility expansion/contraction, and potential breakout zones. Tips: Price touching bands doesn't guarantee reversal; use band squeeze for volatility breakout trades. +- atr: ATR (Average True Range): Measures market volatility by calculating the average of true ranges over a period. Usage: Set stop-loss levels, position sizing, and identify high/low volatility periods for strategy adjustment. Tips: Higher ATR indicates more volatile conditions; use for risk management rather than directional signals. -Volume-Based Indicators: -- 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. - -- Select indicators that provide diverse and complementary information. Avoid redundancy (e.g., do not select both rsi and stochrsi). Also briefly explain why they are suitable for the given market context. When you tool call, please use the exact name of the indicators provided above as they are defined parameters, otherwise your call will fail. Please make sure to call get_stock_data first to retrieve the CSV that is needed to generate indicators. Then use get_indicators with the specific indicator names. Write a very detailed and nuanced report of the trends you observe. Do not simply state the trends are mixed, provide detailed and finegrained analysis and insights that may help traders make decisions.""" +- Select indicators that provide diverse and complementary information. Avoid redundancy and focus on the most effective combination for crypto market analysis. Also briefly explain why they are suitable for the given crypto market context. When you tool call, please use the exact name of the indicators provided above as they are defined parameters, otherwise your call will fail. Please make sure to call get_crypto_data first to retrieve the cryptocurrency price data. Then use get_indicators_bulk with a list of the specific indicator names (e.g., ["sma", "rsi", "macd"]). Write a very detailed and nuanced report of the trends you observe. Do not simply state the trends are mixed, provide detailed and finegrained analysis and insights that may help crypto traders make decisions.""" + """ Make sure to append a Markdown table at the end of the report to organize key points in the report, organized and easy to read.""" ) @@ -57,7 +44,7 @@ Volume-Based Indicators: " If you or any other assistant has the FINAL TRANSACTION PROPOSAL: **BUY/HOLD/SELL** or deliverable," " prefix your response with FINAL TRANSACTION PROPOSAL: **BUY/HOLD/SELL** so the team knows to stop." " You have access to the following tools: {tool_names}.\n{system_message}" - "For your reference, the current date is {current_date}. The company we want to look at is {ticker}", + "For your reference, the current date is {current_date}. The cryptocurrency symbol we want to analyze is {symbol}", ), MessagesPlaceholder(variable_name="messages"), ] @@ -66,7 +53,7 @@ Volume-Based Indicators: prompt = prompt.partial(system_message=system_message) prompt = prompt.partial(tool_names=", ".join([tool.name for tool in tools])) prompt = prompt.partial(current_date=current_date) - prompt = prompt.partial(ticker=ticker) + prompt = prompt.partial(symbol=symbol) chain = prompt | llm.bind_tools(tools) diff --git a/tradingagents/agents/analysts/social_media_analyst.py b/tradingagents/agents/analysts/social_media_analyst.py index 444b1615..cf2ccad0 100644 --- a/tradingagents/agents/analysts/social_media_analyst.py +++ b/tradingagents/agents/analysts/social_media_analyst.py @@ -8,8 +8,8 @@ from tradingagents.dataflows.config import get_config def create_social_media_analyst(llm): def social_media_analyst_node(state): current_date = state["trade_date"] - ticker = state["coin_of_interest"] - coin_name = state["coin_of_interest"] + ticker = state["ticker_of_interest"] + coin_name = state["ticker_of_interest"] tools = [ get_news, diff --git a/tradingagents/agents/utils/agent_states.py b/tradingagents/agents/utils/agent_states.py index fab85be1..4f6c7f34 100644 --- a/tradingagents/agents/utils/agent_states.py +++ b/tradingagents/agents/utils/agent_states.py @@ -49,7 +49,7 @@ class RiskDebateState(TypedDict): class AgentState(MessagesState): company_of_interest: Annotated[str, "Company that we are interested in trading"] - coin_of_interest: Annotated[str, "Coin that we are interested in trading"] + ticker_of_interest: Annotated[str, "Ticker that we are interested in trading"] # e.g BTC/USDT trade_date: Annotated[str, "What date we are trading at"] sender: Annotated[str, "Agent that sent this message"] diff --git a/tradingagents/agents/utils/agent_utils.py b/tradingagents/agents/utils/agent_utils.py index 3a73dd8a..814eb1a1 100644 --- a/tradingagents/agents/utils/agent_utils.py +++ b/tradingagents/agents/utils/agent_utils.py @@ -1,11 +1,15 @@ from langchain_core.messages import HumanMessage, RemoveMessage # Import tools from separate utility files +from tradingagents.agents.utils.core_crypto_tools import ( + get_crypto_data +) from tradingagents.agents.utils.core_stock_tools import ( - get_stock_data + get_stock_data, ) from tradingagents.agents.utils.technical_indicators_tools import ( - get_indicators + get_indicators, + get_indicators_bulk ) from tradingagents.agents.utils.fundamental_data_tools import ( get_fundamentals, diff --git a/tradingagents/agents/utils/core_crypto_tools.py b/tradingagents/agents/utils/core_crypto_tools.py new file mode 100644 index 00000000..3ba5f3ce --- /dev/null +++ b/tradingagents/agents/utils/core_crypto_tools.py @@ -0,0 +1,21 @@ +from langchain_core.tools import tool +from typing import Annotated +from tradingagents.dataflows.interface import route_to_vendor + +@tool +def get_crypto_data( + symbol: Annotated[str, "trading symbol, e.g., BTC/USDT"], + start_date: Annotated[str, "Start date in yyyy-mm-dd format"], + end_date: Annotated[str, "End date in yyyy-mm-dd format"], +) -> str: + """ + Retrieve cryptocurrency price data (OHLCV) for a given trading symbol. + Uses the configured core_crypto_apis vendor. + Args: + symbol (str): Trading symbol, e.g., BTCUSDT + start_date (str): Start date in yyyy-mm-dd format + end_date (str): End date in yyyy-mm-dd format + Returns: + str: A formatted dataframe containing the cryptocurrency price data for the specified trading symbol in the specified date range. + """ + return route_to_vendor("get_crypto_data", symbol, start_date, end_date) diff --git a/tradingagents/agents/utils/technical_indicators_tools.py b/tradingagents/agents/utils/technical_indicators_tools.py index c6c08bca..2e57b7ee 100644 --- a/tradingagents/agents/utils/technical_indicators_tools.py +++ b/tradingagents/agents/utils/technical_indicators_tools.py @@ -1,5 +1,5 @@ from langchain_core.tools import tool -from typing import Annotated +from typing import Annotated, List from tradingagents.dataflows.interface import route_to_vendor @tool @@ -20,4 +20,24 @@ def get_indicators( Returns: str: A formatted dataframe containing the technical indicators for the specified ticker symbol and indicator. """ - return route_to_vendor("get_indicators", symbol, indicator, curr_date, look_back_days) \ No newline at end of file + return route_to_vendor("get_indicators", symbol, indicator, curr_date, look_back_days) + +@tool +def get_indicators_bulk( + symbol: Annotated[str, "ticker symbol of the company"], + indicators: Annotated[List[str], "list of technical indicators to get the analysis and report of"], + curr_date: Annotated[str, "The current trading date you are trading on, YYYY-mm-dd"], + look_back_days: Annotated[int, "how many days to look back"] = 30, +) -> str: + """ + Retrieve multiple technical indicators for a given ticker symbol. + Uses the configured technical_indicators vendor. + Args: + symbol (str): Ticker symbol of the company, e.g. AAPL, TSM + indicators (List[str]): List of technical indicators to get the analysis and report of + curr_date (str): The current trading date you are trading on, YYYY-mm-dd + look_back_days (int): How many days to look back, default is 30 + Returns: + str: A formatted report containing the technical indicators for the specified ticker symbol and indicators. + """ + return route_to_vendor("get_indicators_bulk", symbol, indicators, curr_date, look_back_days) diff --git a/tradingagents/dataflows/binance.py b/tradingagents/dataflows/binance.py new file mode 100644 index 00000000..3ac38282 --- /dev/null +++ b/tradingagents/dataflows/binance.py @@ -0,0 +1,88 @@ +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 + +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 + +configuration = ConfigurationRestAPI(api_key=get_api_key(), base_path=SPOT_REST_API_PROD_URL) +client = Spot(config_rest_api=configuration) + +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. + + Args: + symbol: Trading symbol (e.g., 'BTC/USDT') + start_date: Start date in YYYY-MM-DD format + end_date: End date in YYYY-MM-DD format + + Returns: + CSV formatted string with OHLCV data + """ + # remove / from symbol for binance format + symbol = symbol.replace("/", "") + + # Convert dates to epoch time (milliseconds) + start_epoch = int(datetime.strptime(start_date, "%Y-%m-%d").timestamp() * 1000) + end_epoch = int(datetime.strptime(end_date, "%Y-%m-%d").timestamp() * 1000) + + print(f"DEBUG: Fetching data for {symbol} from {start_date} to {end_date}") + try: + response = client.rest_api.klines( + symbol=symbol, + start_time=start_epoch, + end_time=end_epoch, + interval="1d", + ) + + rate_limits = response.rate_limits + print(f"DEBUG: klines() rate limits: {rate_limits}") + + data = response.data() + + # Convert to CSV format + if not data: + return "No data available" + + # Create CSV string + output = io.StringIO() + writer = csv.writer(output) + + # Write headers + headers = [ + "Open Time", + "Open Price", + "High Price", + "Low Price", + "Close Price", + "Volume", + "Close Time", + "Quote Asset Volume", + "Number of Trades", + "Taker Buy Base Asset Volume", + "Taker Buy Quote Asset Volume", + "Unused Field" + ] + writer.writerow(headers) + + # Write data rows + for row in data: + writer.writerow(row) + + csv_string = output.getvalue() + output.close() + + return csv_string + + except Exception as e: + print(f"ERROR: klines() error: {e}") + return f"Error fetching market data from Binance: {e}" + diff --git a/tradingagents/dataflows/interface.py b/tradingagents/dataflows/interface.py index 85411754..f85757a4 100644 --- a/tradingagents/dataflows/interface.py +++ b/tradingagents/dataflows/interface.py @@ -18,12 +18,20 @@ from .alpha_vantage import ( from .alpha_vantage_common import AlphaVantageRateLimitError from .telegram import get_crypto_news_telegram from .coin_gecko_fundamentals import get_market_cap as get_coin_gecko_market_cap +from .binance import get_market_data as get_binance_crypto_data +from .taapi import get_crypto_stats_indicators_window, get_crypto_stats_indicators # Configuration and routing logic from .config import get_config # Tools organized by category TOOLS_CATEGORIES = { + "core_crypto_apis": { + "description": "OHLCV cryptocurrency price data", + "tools": [ + "get_crypto_data" + ] + }, "core_stock_apis": { "description": "OHLCV stock price data", "tools": [ @@ -33,7 +41,8 @@ TOOLS_CATEGORIES = { "technical_indicators": { "description": "Technical analysis indicators", "tools": [ - "get_indicators" + "get_indicators", + "get_indicators_bulk" ] }, "fundamental_data": { @@ -60,6 +69,7 @@ TOOLS_CATEGORIES = { } VENDOR_LIST = [ + "binance" "local", "yfinance", "openai", @@ -67,21 +77,29 @@ VENDOR_LIST = [ "telegram", "coin_gecko", "alpha_vantage", + "taapi" ] # Mapping of methods to their vendor-specific implementations VENDOR_METHODS = { - # core_stock_apis "get_stock_data": { "alpha_vantage": get_alpha_vantage_stock, "yfinance": get_YFin_data_online, "local": get_YFin_data, }, + # core_crypto_apis + "get_crypto_data": { + "binance": get_binance_crypto_data, + }, # technical_indicators "get_indicators": { - "alpha_vantage": get_alpha_vantage_indicator, - "yfinance": get_stock_stats_indicators_window, - "local": get_stock_stats_indicators_window + "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, }, # fundamental_data "get_fundamentals": { @@ -259,4 +277,4 @@ def route_to_vendor(method: str, *args, **kwargs): return results[0] else: # Convert all results to strings and concatenate - return '\n'.join(str(result) for result in results) \ No newline at end of file + return '\n'.join(str(result) for result in results) diff --git a/tradingagents/dataflows/taapi.py b/tradingagents/dataflows/taapi.py new file mode 100644 index 00000000..aad48a89 --- /dev/null +++ b/tradingagents/dataflows/taapi.py @@ -0,0 +1,270 @@ +import requests +from typing import Annotated, List, Dict +import os +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"], + indicator: Annotated[str, "technical indicator to get the analysis and report of"], + curr_date: Annotated[ + str, "The current trading date you are trading on, YYYY-mm-dd" + ], + look_back_days: Annotated[int, "how many days to look back"], +) -> str: + """Fetch technical indicator data from TAAPI.io for a given symbol. + + Args: + symbol: Ticker symbol of the coin/asset (e.g., 'BTC/USDT') + indicator: Technical indicator to get the analysis and report of (e.g., 'rsi', 'macd', 'sma', 'bbands', 'atr') + curr_date: The current trading date you are trading on, in YYYY-MM-DD format + look_back_days: How many days to look back + Returns: + str: A formatted report containing the technical indicators for the specified ticker symbol and indicator. + """ + + # Supported indicators mapping + supported_indicators = { + "sma": "Simple Moving Average", + "macd": "MACD (Moving Average Convergence Divergence)", + "rsi": "Relative Strength Index", + "bbands": "Bollinger Bands", + "atr": "Average True Range" + } + + # Detailed indicator descriptions and usage + indicator_descriptions = { + "sma": "SMA (Simple Moving Average): A basic trend-following indicator that smooths out price data. Usage: Identify trend direction and serve as dynamic support/resistance levels. Tips: Use multiple SMAs for crossover signals; combines well with volume analysis for confirmation.", + + "macd": "MACD: Measures momentum via differences between fast and slow EMAs. Usage: Look for signal line crossovers, centerline crossovers, and divergence patterns for trend changes. Tips: Most effective in trending markets; combine with RSI to avoid false signals in sideways markets.", + + "rsi": "RSI: Oscillator measuring momentum to identify overbought (>70) and oversold (<30) conditions. Usage: Look for reversal signals at extreme levels and divergence with price action. Tips: In strong trends, RSI can remain extreme for extended periods; always confirm with trend analysis.", + + "bbands": "Bollinger Bands: Volatility indicator consisting of upper, middle (SMA), and lower bands based on standard deviations. Usage: Identify overbought/oversold conditions, volatility expansion/contraction, and potential breakout zones. Tips: Price touching bands doesn't guarantee reversal; use band squeeze for volatility breakout trades.", + + "atr": "ATR: Measures market volatility by calculating the average of true ranges over a period. Usage: Set stop-loss levels, position sizing, and identify high/low volatility periods for strategy adjustment. Tips: Higher ATR indicates more volatile conditions; use for risk management rather than directional signals." + } + + # Validate indicator + if indicator.lower() not in supported_indicators: + 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() + + # Set backtrack as requested + backtrack = look_back_days + + # Construct the API URL + url = f"{base_url}/{indicator.lower()}" + + # Set up parameters for the API call + params = { + "secret": api_key, + "exchange": "binance", # Default to binance exchange for crypto + "symbol": symbol, + "interval": "1d", # Daily interval + "backtrack": backtrack + } + + try: + # Make the API request + response = requests.get(url, params=params) + response.raise_for_status() # Raise an exception for bad status codes + + # Get the JSON response + data = response.json() + + # Format the response based on indicator type + if isinstance(data, list): + # Handle historical data (multiple periods) + result_str = f"## {supported_indicators[indicator.lower()]} ({indicator.upper()}) for {symbol}:\n\n" + + for i, period_data in enumerate(data): + if isinstance(period_data, dict): + result_str += f"Period {i + 1}:\n" + for key, value in period_data.items(): + clean_key = key.replace("value", "").replace("Value", "") + if isinstance(value, (int, float)): + result_str += f" {clean_key}: {value:.4f}\n" + else: + result_str += f" {clean_key}: {value}\n" + result_str += "\n" + + elif isinstance(data, dict): + # Handle single period data + result_str = f"## {supported_indicators[indicator.lower()]} ({indicator.upper()}) for {symbol}:\n\n" + result_str += f"Current Date: {curr_date}\n" + result_str += f"Lookback Days: {look_back_days}\n\n" + + # Generic formatting for all indicators + for key, value in data.items(): + clean_key = key.replace("value", "").replace("Value", "") + if isinstance(value, (int, float)): + result_str += f"{clean_key}: {value:.4f}\n" + else: + result_str += f"{clean_key}: {value}\n" + else: + result_str = f"## {supported_indicators[indicator.lower()]} for {symbol}:\n{str(data)}" + + # Add the indicator description + result_str += f"\n\n{indicator_descriptions.get(indicator.lower(), 'No description available.')}" + + return result_str + + except requests.exceptions.RequestException as e: + return f"Error fetching data from TAAPI.io: {str(e)}" + except ValueError as e: + return f"Error parsing response from TAAPI.io: {str(e)}" + except KeyError as e: + return f"Error: Missing expected field in API response: {str(e)}" + except Exception as e: + return f"Unexpected error: {str(e)}" + + +def get_crypto_stats_indicators( + symbol: Annotated[str, "ticker symbol of the coin/asset"], + indicators: Annotated[List[str], "list of technical indicators to get analysis for"], + curr_date: Annotated[ + str, "The current trading date you are trading on, YYYY-mm-dd" + ], + look_back_days: Annotated[int, "how many days to look back"], +) -> str: + """Fetch multiple technical indicator data from TAAPI.io for a given symbol using bulk API. + + Args: + symbol: Ticker symbol of the coin/asset (e.g., 'BTC/USDT') + indicators: List of technical indicators to get analysis for (e.g., ['rsi', 'macd', 'sma', 'bbands', 'atr']) + curr_date: The current trading date you are trading on, in YYYY-MM-DD format + look_back_days: How many days to look back + Returns: + str: A formatted report containing all requested technical indicators for the specified ticker symbol. + """ + + # Supported indicators mapping + supported_indicators = { + "sma": "Simple Moving Average", + "ema": "Exponential Moving Average", + "macd": "MACD (Moving Average Convergence Divergence)", + "rsi": "Relative Strength Index", + "bbands": "Bollinger Bands", + "atr": "Average True Range", + "kdj": "KDJ (Stochastic KDJ)" + } + + # Detailed indicator descriptions and usage + indicator_descriptions = { + "sma": "SMA (Simple Moving Average): A basic trend-following indicator that smooths out price data. Usage: Identify trend direction and serve as dynamic support/resistance levels. Tips: Use multiple SMAs for crossover signals; combines well with volume analysis for confirmation.", + + "ema": "EMA (Exponential Moving Average): A trend-following indicator that gives more weight to recent prices. Usage: More responsive than SMA for trend changes and crossover signals. Tips: Better for short-term trading; reacts faster to price changes than SMA.", + + "macd": "MACD: Measures momentum via differences between fast and slow EMAs. Usage: Look for signal line crossovers, centerline crossovers, and divergence patterns for trend changes. Tips: Most effective in trending markets; combine with RSI to avoid false signals in sideways markets.", + + "rsi": "RSI: Oscillator measuring momentum to identify overbought (>70) and oversold (<30) conditions. Usage: Look for reversal signals at extreme levels and divergence with price action. Tips: In strong trends, RSI can remain extreme for extended periods; always confirm with trend analysis.", + + "bbands": "Bollinger Bands: Volatility indicator consisting of upper, middle (SMA), and lower bands based on standard deviations. Usage: Identify overbought/oversold conditions, volatility expansion/contraction, and potential breakout zones. Tips: Price touching bands doesn't guarantee reversal; use band squeeze for volatility breakout trades.", + + "atr": "ATR: Measures market volatility by calculating the average of true ranges over a period. Usage: Set stop-loss levels, position sizing, and identify high/low volatility periods for strategy adjustment. Tips: Higher ATR indicates more volatile conditions; use for risk management rather than directional signals.", + + "kdj": "KDJ: Advanced stochastic oscillator with three lines (K, D, J). Usage: Identify overbought/oversold conditions and momentum changes. Tips: J line is most sensitive; use crossovers between K, D, and J lines for entry/exit signals." + } + + # Validate indicators + invalid_indicators = [ind for ind in indicators if ind.lower() not in supported_indicators] + if invalid_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() + + # Construct the bulk API URL + url = f"{base_url}/bulk" + + # Prepare indicators list for the bulk request + indicator_list = [] + for indicator in indicators: + indicator_list.append({ + "indicator": indicator.lower(), + "backtrack": look_back_days + }) + + # Set up the JSON payload for the bulk request + payload = { + "secret": api_key, + "construct": { + "exchange": "binance", + "symbol": symbol, + "interval": "1d", + "indicators": indicator_list + } + } + + try: + # Make the POST request to bulk API + response = requests.post(url, json=payload) + response.raise_for_status() + + # Get the JSON response + data = response.json() + + # Format the bulk response + result_str = f"# Technical Indicators Report for {symbol}\n\n" + result_str += f"**Current Date:** {curr_date}\n" + result_str += f"**Lookback Days:** {look_back_days}\n\n" + + if "data" in data and isinstance(data["data"], list): + for item in data["data"]: + if "errors" in item and item["errors"]: + # Handle errors for individual indicators + result_str += f"## ❌ Error for {item.get('indicator', 'Unknown').upper()}:\n" + result_str += f"- {'; '.join(item['errors'])}\n\n" + continue + + indicator_name = item.get("indicator", "unknown") + indicator_display = supported_indicators.get(indicator_name, indicator_name.upper()) + + result_str += f"## {indicator_display} ({indicator_name.upper()})\n\n" + + # Handle the result data + if "result" in item and isinstance(item["result"], dict): + result_data = item["result"] + + # Format the values + for key, value in result_data.items(): + clean_key = key.replace("value", "").replace("Value", "") + if clean_key == "": + clean_key = "Value" + + if isinstance(value, (int, float)): + result_str += f"**{clean_key}:** {value:.4f}\n" + else: + result_str += f"**{clean_key}:** {value}\n" + + # Add description + if indicator_name in indicator_descriptions: + result_str += f"\n*{indicator_descriptions[indicator_name]}*\n" + + result_str += "\n---\n\n" + else: + result_str += f"Unexpected response format: {str(data)}" + + return result_str + + except requests.exceptions.RequestException as e: + return f"Error fetching bulk data from TAAPI.io: {str(e)}" + except ValueError as e: + return f"Error parsing bulk response from TAAPI.io: {str(e)}" + except KeyError as e: + return f"Error: Missing expected field in bulk API response: {str(e)}" + except Exception as e: + return f"Unexpected error in bulk request: {str(e)}" diff --git a/tradingagents/default_config.py b/tradingagents/default_config.py index 1f40a2a2..aa74f5f5 100644 --- a/tradingagents/default_config.py +++ b/tradingagents/default_config.py @@ -20,8 +20,9 @@ DEFAULT_CONFIG = { # Data vendor configuration # 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": "yfinance", # Options: yfinance, alpha_vantage, local + "technical_indicators": "taapi", # Options: taapi "fundamental_data": "alpha_vantage", # Options: openai, alpha_vantage, local "news_data": "alpha_vantage", # Options: openai, alpha_vantage, google, local }, @@ -30,4 +31,8 @@ DEFAULT_CONFIG = { # Example: "get_stock_data": "alpha_vantage", # Override category default # Example: "get_news": "openai", # Override category default }, + # Tool provider settings + "tool_providers": { + "TAAPI_BASE_URL": "https://api.taapi.io", + } } diff --git a/tradingagents/graph/propagation.py b/tradingagents/graph/propagation.py index c67ee215..bee1d8fb 100644 --- a/tradingagents/graph/propagation.py +++ b/tradingagents/graph/propagation.py @@ -22,7 +22,7 @@ class Propagator: return { "messages": [("human", ticker)], "company_of_interest": ticker, - "coin_of_interest": ticker, + "ticker_of_interest": ticker, "trade_date": str(trade_date), "investment_debate_state": InvestDebateState( {"history": "", "current_response": "", "count": 0} diff --git a/tradingagents/graph/trading_graph.py b/tradingagents/graph/trading_graph.py index aefc110d..489387c8 100644 --- a/tradingagents/graph/trading_graph.py +++ b/tradingagents/graph/trading_graph.py @@ -24,8 +24,10 @@ from tradingagents.dataflows.config import set_config # Import the new abstract tool methods from agent_utils from tradingagents.agents.utils.agent_utils import ( + get_crypto_data, get_stock_data, get_indicators, + get_indicators_bulk, get_fundamentals, get_whitepaper, get_market_cap, @@ -127,10 +129,13 @@ class TradingAgentsGraph: return { "market": ToolNode( [ + # Crypto data tools + get_crypto_data, # Core stock data tools get_stock_data, # Technical indicators get_indicators, + get_indicators_bulk, ] ), "social": ToolNode(