Merge branch 'main' into profile-analyst

This commit is contained in:
Randy Zakya Suchrady 2025-12-31 12:03:17 +08:00 committed by GitHub
commit 3ba1d5fee9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 324 additions and 116 deletions

View File

@ -8,3 +8,20 @@ TAAPI_API_KEY=taapi_api_key_placeholder
BYBIT_BASE_URL=https://api-demo.bybit.com BYBIT_BASE_URL=https://api-demo.bybit.com
BYBIT_API_KEY=bybit_api_key_placeholder BYBIT_API_KEY=bybit_api_key_placeholder
BYBIT_API_SECRET=bybit_api_secret_placeholder BYBIT_API_SECRET=bybit_api_secret_placeholder
COIN_GECKO_API_BASE_URL=https://api.coingecko.com/api/v3
# Model settings
LLM_PROVIDER=openai
BACKEND_URL=https://api.openai.com/v1
DEEP_THINK_LLM=gpt-4o-mini
QUICK_THINK_LLM=gpt-4o-mini
# App settings
APP_HOST=localhost
APP_PORT=8000
# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=default-password
REDIS_DB=0

View File

@ -1 +0,0 @@
3.12.7

View File

@ -110,23 +110,25 @@ conda activate tradingagents
pyenv pyenv
pyenv local 3.12.7 pyenv local 3.12.7
python -m venv .venv python -m venv .venv
source./venv/bin/activate
python --version
pip install --upgrade pip
pip install -r requirements.txt
pip list
# daily use
source .venv/bin/activate source .venv/bin/activate
python --version
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
python -m pip list
deactivate deactivate
if error mini-racer if error mini-racer
source venv/bin/activate && pip install --no-deps -r requirements.txt #install without miniracer source .venv/bin/activate && pip install --no-deps -r requirements.txt #install without miniracer
Api
python webapp.py
``` ```
Install dependencies: ### Connect to Redis (Local)
```bash ```
pip install -r requirements.txt docker-compose up -d
redis-cli -h localhost -p 6379 -a {REDIS_PASSWORD}
``` ```
### Required APIs ### Required APIs

18
docker-compose.yml Normal file
View File

@ -0,0 +1,18 @@
services:
redis:
image: redis:7.4.7
container_name: ${REDIS_CONTAINER_NAME:-trading_agents_redis}
restart: unless-stopped
ports:
- "6379:6379"
volumes:
- redis-data:/data
environment:
REDIS_PASSWORD: ${REDIS_PASSWORD}
command: >
redis-server
--appendonly yes
--requirepass ${REDIS_PASSWORD}
volumes:
redis-data:

View File

@ -28,6 +28,8 @@ dependencies = [
"rich>=14.0.0", "rich>=14.0.0",
"setuptools>=80.9.0", "setuptools>=80.9.0",
"stockstats>=0.6.5", "stockstats>=0.6.5",
"fastapi>=0.115.0",
"uvicorn[standard]>=0.32.0",
"tqdm>=4.67.1", "tqdm>=4.67.1",
"tushare>=1.4.21", "tushare>=1.4.21",
"typing-extensions>=4.14.0", "typing-extensions>=4.14.0",

View File

@ -26,4 +26,6 @@ langchain_anthropic
langchain-google-genai langchain-google-genai
binance-sdk-spot binance-sdk-spot
telethon telethon
pybit fastapi
uvicorn[standard]
redis[hiredis]

View File

@ -1,15 +1,11 @@
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
import time
import json
from tradingagents.agents.utils.agent_utils import get_fundamentals, get_whitepaper, get_market_cap from tradingagents.agents.utils.agent_utils import get_fundamentals, get_whitepaper, get_market_cap
from tradingagents.dataflows.config import get_config
def create_fundamentals_analyst(llm): def create_fundamentals_analyst(llm):
def fundamentals_analyst_node(state): def fundamentals_analyst_node(state):
current_date = state["trade_date"] current_date = state["trade_date"]
ticker = state["ticker_of_interest"] ticker = state["ticker_of_interest"]
company_name = state["ticker_of_interest"]
tools = [ tools = [
get_fundamentals, get_fundamentals,

View File

@ -1,9 +1,5 @@
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
import time
import json
from tradingagents.agents.utils.agent_utils import get_news, get_global_news from tradingagents.agents.utils.agent_utils import get_news, get_global_news
from tradingagents.dataflows.config import get_config
def create_news_analyst(llm): def create_news_analyst(llm):
def news_analyst_node(state): def news_analyst_node(state):

View File

@ -1,15 +1,11 @@
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
import time
import json
from tradingagents.agents.utils.agent_utils import get_news, get_fear_and_greed from tradingagents.agents.utils.agent_utils import get_news, get_fear_and_greed
from tradingagents.dataflows.config import get_config
def create_social_media_analyst(llm): def create_social_media_analyst(llm):
def social_media_analyst_node(state): def social_media_analyst_node(state):
current_date = state["trade_date"] current_date = state["trade_date"]
ticker = state["ticker_of_interest"] ticker = state["ticker_of_interest"]
coin_name = state["ticker_of_interest"]
tools = [ tools = [
get_news, get_news,

View File

@ -1,8 +1,6 @@
import time from tradingagents.agents.utils.memory import FinancialSituationMemory
import json
def create_research_manager(llm, memory: FinancialSituationMemory):
def create_research_manager(llm, memory):
def research_manager_node(state) -> dict: def research_manager_node(state) -> dict:
history = state["investment_debate_state"].get("history", "") history = state["investment_debate_state"].get("history", "")
market_research_report = state["market_report"] market_research_report = state["market_report"]

View File

@ -1,12 +1,8 @@
import time from tradingagents.agents.utils.memory import FinancialSituationMemory
import json
def create_risk_manager(llm, memory: FinancialSituationMemory):
def create_risk_manager(llm, memory):
def risk_manager_node(state) -> dict: def risk_manager_node(state) -> dict:
company_name = state["company_of_interest"]
history = state["risk_debate_state"]["history"] history = state["risk_debate_state"]["history"]
risk_debate_state = state["risk_debate_state"] risk_debate_state = state["risk_debate_state"]
market_research_report = state["market_report"] market_research_report = state["market_report"]

View File

@ -1,9 +1,6 @@
from langchain_core.messages import AIMessage from tradingagents.agents.utils.memory import FinancialSituationMemory
import time
import json
def create_bear_researcher(llm, memory: FinancialSituationMemory):
def create_bear_researcher(llm, memory):
def bear_node(state) -> dict: def bear_node(state) -> dict:
investment_debate_state = state["investment_debate_state"] investment_debate_state = state["investment_debate_state"]
history = investment_debate_state.get("history", "") history = investment_debate_state.get("history", "")
@ -38,7 +35,7 @@ Resources available:
Market research report: {market_research_report} Market research report: {market_research_report}
Social media sentiment report: {sentiment_report} Social media sentiment report: {sentiment_report}
Latest world affairs news: {news_report} Latest world affairs news: {news_report}
Company fundamentals report: {fundamentals_report} Coin fundamentals report: {fundamentals_report}
Profile analysis report: {profile_report} Profile analysis report: {profile_report}
Conversation history of the debate: {history} Conversation history of the debate: {history}
Last bull argument: {current_response} Last bull argument: {current_response}

View File

@ -1,9 +1,6 @@
from langchain_core.messages import AIMessage from tradingagents.agents.utils.memory import FinancialSituationMemory
import time
import json
def create_bull_researcher(llm, memory: FinancialSituationMemory):
def create_bull_researcher(llm, memory):
def bull_node(state) -> dict: def bull_node(state) -> dict:
investment_debate_state = state["investment_debate_state"] investment_debate_state = state["investment_debate_state"]
history = investment_debate_state.get("history", "") history = investment_debate_state.get("history", "")

View File

@ -1,7 +1,3 @@
import time
import json
def create_risky_debator(llm): def create_risky_debator(llm):
def risky_node(state) -> dict: def risky_node(state) -> dict:
risk_debate_state = state["risk_debate_state"] risk_debate_state = state["risk_debate_state"]

View File

@ -1,8 +1,3 @@
from langchain_core.messages import AIMessage
import time
import json
def create_safe_debator(llm): def create_safe_debator(llm):
def safe_node(state) -> dict: def safe_node(state) -> dict:
risk_debate_state = state["risk_debate_state"] risk_debate_state = state["risk_debate_state"]

View File

@ -1,7 +1,3 @@
import time
import json
def create_neutral_debator(llm): def create_neutral_debator(llm):
def neutral_node(state) -> dict: def neutral_node(state) -> dict:
risk_debate_state = state["risk_debate_state"] risk_debate_state = state["risk_debate_state"]

View File

@ -1,7 +1,4 @@
import functools import functools
import time
import json
def create_trader(llm, memory): def create_trader(llm, memory):
def trader_node(state, name): def trader_node(state, name):
@ -40,7 +37,9 @@ def create_trader(llm, memory):
messages = [ messages = [
{ {
"role": "system", "role": "system",
"content": f"""You are a crypto trading agent analyzing cryptocurrency market data for a specific trading pair (e.g., BTC/USDT). Based on your analysis, provide a specific recommendation to BUY, SELL, or HOLD the base asset relative to the quote asset for the pair {pair_context}. End with a firm decision and always conclude your response with 'FINAL TRANSACTION PROPOSAL: **BUY/HOLD/SELL**' to confirm your recommendation. Do not forget to utilize lessons from past decisions to learn from your mistakes. Here is some reflections from similar situations you traded in and the lessons learned: {past_memory_str}""", "content": f"""You are a crypto trading agent analyzing cryptocurrency market data for a specific trading pair (e.g., BTC/USDT). Based on your analysis, provide a specific recommendation to BUY, SELL, or HOLD the base asset relative to the quote asset for the pair {pair_context}, along with the quantity for BUY and SELL \
End with a firm decision and always conclude your response with 'FINAL TRANSACTION PROPOSAL: **BUY/HOLD/SELL** **QUANTITY**' to confirm your recommendation. \
Do not forget to utilize lessons from past decisions to learn from your mistakes. Here is some reflections from similar situations you traded in and the lessons learned: {past_memory_str}""",
}, },
context, context,
] ]

View File

@ -1,10 +1,7 @@
from typing import Annotated, Sequence from typing import Annotated
from datetime import date, timedelta, datetime from typing_extensions import TypedDict
from typing_extensions import TypedDict, Optional
from langchain_openai import ChatOpenAI
from tradingagents.agents import * from tradingagents.agents import *
from langgraph.prebuilt import ToolNode from langgraph.graph import MessagesState
from langgraph.graph import END, StateGraph, START, MessagesState
# Researcher team state # Researcher team state
@ -48,7 +45,6 @@ class RiskDebateState(TypedDict):
class AgentState(MessagesState): class AgentState(MessagesState):
company_of_interest: Annotated[str, "Company that we are interested in trading"]
ticker_of_interest: Annotated[str, "Ticker that we are interested in trading"] # e.g BTC/USDT 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"] trade_date: Annotated[str, "What date we are trading at"]

View File

@ -11,7 +11,7 @@ class FinancialSituationMemory:
self.embedding = "text-embedding-3-small" self.embedding = "text-embedding-3-small"
self.client = OpenAI(base_url=config["backend_url"]) self.client = OpenAI(base_url=config["backend_url"])
self.chroma_client = chromadb.Client(Settings(allow_reset=True)) self.chroma_client = chromadb.Client(Settings(allow_reset=True))
self.situation_collection = self.chroma_client.create_collection(name=name) self.situation_collection = self.chroma_client.get_or_create_collection(name=name)
def get_embedding(self, text): def get_embedding(self, text):
"""Get OpenAI embedding for a text""" """Get OpenAI embedding for a text"""

View File

@ -1,20 +1,33 @@
from binance_common.configuration import ConfigurationRestAPI from binance_common.configuration import ConfigurationRestAPI
from binance_common.constants import SPOT_REST_API_PROD_URL from binance_common.constants import SPOT_REST_API_PROD_URL
from binance_sdk_spot.spot import Spot from binance_sdk_spot.spot import Spot
import os
from datetime import datetime from datetime import datetime
import csv import csv
import io import io
from tradingagents.dataflows.config import get_config
def get_api_key() -> str: _client = None
"""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) def get_binance_client():
client = Spot(config_rest_api=configuration) """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): 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. """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}") # print(f"DEBUG: Fetching data for {formatted_symbol} from {start_date} to {end_date}")
try: try:
client = get_binance_client()
response = client.rest_api.klines( response = client.rest_api.klines(
symbol=formatted_symbol, symbol=formatted_symbol,
start_time=start_epoch, start_time=start_epoch,
@ -80,6 +94,8 @@ def get_market_data(symbol: str, start_date: str, end_date: str):
csv_string = output.getvalue() csv_string = output.getvalue()
output.close() output.close()
title = f"# Market Data for {symbol} from {start_date} to {end_date}\n\n"
csv_string = title + csv_string
return csv_string return csv_string
except Exception as e: except Exception as e:

View File

@ -1,5 +1,6 @@
import requests import requests
from .alpha_vantage_common import API_BASE_URL from .alpha_vantage_common import API_BASE_URL
from tradingagents.dataflows.config import get_config
def get_market_cap() -> str: def get_market_cap() -> str:
""" """
@ -8,8 +9,11 @@ def get_market_cap() -> str:
Returns: Returns:
str: Market capitalization percentage data for cryptocurrencies 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) response = requests.get(endpoint)
print(f"DEBUG: CoinGecko API response status code: {response.status_code}")
response.raise_for_status() response.raise_for_status()
data = response.json() data = response.json()
market_cap_pct = data.get("data", {}).get("market_cap_percentage", {}) 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(): for coin, percentage in market_cap_pct.items():
# Format each line as "Coin: XX.XX%" # Format each line as "Coin: XX.XX%"
result += f"- {coin.upper()}: {percentage:.2f}%\n" result += f"- {coin.upper()}: {percentage:.2f}%\n"
return result return result

View File

@ -1,10 +1,27 @@
from openai import OpenAI from openai import OpenAI
from .config import get_config 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): def get_stock_news_openai(query, start_date, end_date):
config = get_config() config = get_config()
client = OpenAI(base_url=config["backend_url"]) client = get_openai_client()
response = client.responses.create( response = client.responses.create(
model=config["quick_think_llm"], 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): def get_crypto_news_openai(query, start_date, end_date):
config = get_config() config = get_config()
client = OpenAI(base_url=config["backend_url"]) client = get_openai_client()
response = client.responses.create( response = client.responses.create(
model=config["quick_think_llm"], 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): def get_global_news_openai(curr_date, look_back_days=7, limit=5):
config = get_config() config = get_config()
client = OpenAI(base_url=config["backend_url"]) client = get_openai_client()
response = client.responses.create( response = client.responses.create(
model=config["quick_think_llm"], 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): def get_fundamentals_openai(ticker, curr_date):
config = get_config() config = get_config()
client = OpenAI(base_url=config["backend_url"]) client = get_openai_client()
response = client.responses.create( response = client.responses.create(
model=config["quick_think_llm"], model=config["quick_think_llm"],
@ -146,7 +163,7 @@ def get_fundamentals_openai(ticker, curr_date):
def get_whitepaper_openai(symbol): def get_whitepaper_openai(symbol):
config = get_config() config = get_config()
client = OpenAI(base_url=config["backend_url"]) client = get_openai_client()
response = client.responses.create( response = client.responses.create(
model=config["quick_think_llm"], model=config["quick_think_llm"],
@ -177,4 +194,4 @@ def get_whitepaper_openai(symbol):
store=True, store=True,
) )
return response.output[1].content[0].text return response.output[1].content[0].text

View File

@ -1,16 +1,7 @@
import requests import requests
from typing import Annotated, List, Dict from typing import Annotated, List
import os
from tradingagents.dataflows.config import get_config 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 # This is for single indicator, unused for now but kept for reference
def get_crypto_stats_indicators_window( def get_crypto_stats_indicators_window(
symbol: Annotated[str, "ticker symbol of the coin/asset"], 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())}" return f"Error: Indicator '{indicator}' is not supported. Please choose from: {list(supported_indicators.keys())}"
config = get_config() config = get_config()
base_url = config["tool_providers"].get("TAAPI_BASE_URL", "https://api.taapi.io") base_url = config["external"].get("TAAPI_BASE_URL", "https://api.taapi.io")
api_key = get_api_key() 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 # Set backtrack as requested
backtrack = look_back_days 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())}" return f"Error: Indicators {invalid_indicators} are not supported. Please choose from: {list(supported_indicators.keys())}"
config = get_config() config = get_config()
base_url = config["tool_providers"].get("TAAPI_BASE_URL", "https://api.taapi.io") base_url = config["external"].get("TAAPI_BASE_URL", "https://api.taapi.io")
api_key = get_api_key() 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 # Construct the bulk API URL
url = f"{base_url}/bulk" url = f"{base_url}/bulk"

View File

@ -1,13 +1,19 @@
import asyncio import asyncio
from telethon import TelegramClient from telethon import TelegramClient
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
import os from tradingagents.dataflows.config import get_config
def get_api_credentials(): def get_api_credentials():
api_id = int(os.getenv("TELEGRAM_API_ID", "")) """Retrieve Telegram API credentials from environment variables."""
api_hash = os.getenv("TELEGRAM_API_HASH", "") config = get_config()
session_name = os.getenv("TELEGRAM_SESSION_NAME", "") api_id = config["external"]["TELEGRAM_API_ID"]
return api_id, api_hash, session_name 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): 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') start_date_str = start_date.strftime('%Y-%m-%d')
end_date_str = end_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)) return asyncio.run(_get_channel_history_async(start_date_str, end_date_str))

View File

@ -1,6 +1,10 @@
import os import os
DEFAULT_CONFIG = { DEFAULT_CONFIG = {
# App config
"APP_HOST": "localhost",
"APP_PORT": 8000,
# Directory settings
"project_dir": os.path.abspath(os.path.join(os.path.dirname(__file__), ".")), "project_dir": os.path.abspath(os.path.join(os.path.dirname(__file__), ".")),
"results_dir": os.getenv("TRADINGAGENTS_RESULTS_DIR", "./results"), "results_dir": os.getenv("TRADINGAGENTS_RESULTS_DIR", "./results"),
"data_dir": "/Users/yluo/Documents/Code/ScAI/FR1-data", "data_dir": "/Users/yluo/Documents/Code/ScAI/FR1-data",
@ -9,10 +13,10 @@ DEFAULT_CONFIG = {
"dataflows/data_cache", "dataflows/data_cache",
), ),
# LLM settings # LLM settings
"llm_provider": "openai", "llm_provider": os.getenv("LLM_PROVIDER", "openai"),
"deep_think_llm": "o4-mini", "deep_think_llm": os.getenv("DEEP_THINK_LLM", "gpt-4o-mini"),
"quick_think_llm": "gpt-4o-mini", "quick_think_llm": os.getenv("QUICK_THINK_LLM", "gpt-4o-mini"),
"backend_url": "https://api.openai.com/v1", "backend_url": os.getenv("BACKEND_URL","https://api.openai.com/v1"),
# Debate and discussion settings # Debate and discussion settings
"max_debate_rounds": 1, "max_debate_rounds": 1,
"max_risk_discuss_rounds": 1, "max_risk_discuss_rounds": 1,
@ -29,7 +33,6 @@ DEFAULT_CONFIG = {
}, },
# Tool-level configuration (takes precedence over category-level) # Tool-level configuration (takes precedence over category-level)
"tool_vendors": { "tool_vendors": {
# Example: "get_stock_data": "alpha_vantage", # Override category default
"get_global_news": "telegram" # Override category default "get_global_news": "telegram" # Override category default
}, },
# Tool provider settings # Tool provider settings
@ -37,8 +40,21 @@ DEFAULT_CONFIG = {
"TAAPI_BASE_URL": os.getenv("TAAPI_BASE_URL", "https://api.taapi.io"), "TAAPI_BASE_URL": os.getenv("TAAPI_BASE_URL", "https://api.taapi.io"),
}, },
"external": { "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_BASE_URL": os.getenv("BYBIT_BASE_URL", "https://api-demo.bybit.com"),
"BYBIT_API_KEY": os.getenv("BYBIT_API_KEY", ""), "BYBIT_API_KEY": os.getenv("BYBIT_API_KEY", ""),
"BYBIT_API_SECRET": os.getenv("BYBIT_API_SECRET", ""), "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", ""),
},
"redis": {
"REDIS_HOST": os.getenv("REDIS_HOST", "localhost"),
"REDIS_PORT": int(os.getenv("REDIS_PORT", 6379)),
"REDIS_PASSWORD": os.getenv("REDIS_PASSWORD", "defaultpassword"),
"REDIS_DB": int(os.getenv("REDIS_DB", 0)),
},
} }

34
tradingagents/external/redis/client.py vendored Normal file
View File

@ -0,0 +1,34 @@
from redis import Redis, ConnectionPool
from redis.backoff import ExponentialBackoff
from redis.retry import Retry
from tradingagents.dataflows.config import get_config
_client = None
def get_redis_client() -> Redis:
"""Get or create Redis client with lazy initialization."""
global _client
if _client is None:
try:
config = get_config()
retry = Retry(ExponentialBackoff(), retries=5)
pool = ConnectionPool(
host=config["redis"]["REDIS_HOST"],
port=config["redis"]["REDIS_PORT"],
password=config["redis"]["REDIS_PASSWORD"],
db=config["redis"]["REDIS_DB"],
decode_responses=True,
socket_connect_timeout=5,
socket_timeout=5,
health_check_interval=10,
retry=retry,
)
print("INFO: Initializing Redis client")
_client = Redis(connection_pool=pool)
print("INFO: Redis client initialized successfully")
except Exception as e:
print(f"ERROR: Failed to initialize Redis client: {e}")
raise
return _client

18
tradingagents/external/redis/repo.py vendored Normal file
View File

@ -0,0 +1,18 @@
from tradingagents.external.redis.client import get_redis_client
redis = get_redis_client()
class RedisRepo:
def get(self, key: str):
return redis.get(key)
def set(self, key: str, value: str, ex: int | None = None):
return redis.set(key, value, ex=ex)
def delete(self, key: str):
return redis.delete(key)
def exists(self, key: str) -> bool:
return redis.exists(key) == 1
redis_repo = RedisRepo()

View File

@ -21,7 +21,6 @@ class Propagator:
"""Create the initial state for the agent graph.""" """Create the initial state for the agent graph."""
return { return {
"messages": [("human", ticker)], "messages": [("human", ticker)],
"company_of_interest": ticker,
"ticker_of_interest": ticker, "ticker_of_interest": ticker,
"trade_date": str(trade_date), "trade_date": str(trade_date),
"investment_debate_state": InvestDebateState( "investment_debate_state": InvestDebateState(

View File

@ -215,7 +215,7 @@ class TradingAgentsGraph:
def _log_state(self, trade_date, final_state): def _log_state(self, trade_date, final_state):
"""Log the final state to a JSON file.""" """Log the final state to a JSON file."""
self.log_states_dict[str(trade_date)] = { self.log_states_dict[str(trade_date)] = {
"company_of_interest": final_state["company_of_interest"], "ticker_of_interest": final_state["ticker_of_interest"],
"trade_date": final_state["trade_date"], "trade_date": final_state["trade_date"],
"market_report": final_state["market_report"], "market_report": final_state["market_report"],
"sentiment_report": final_state["sentiment_report"], "sentiment_report": final_state["sentiment_report"],

105
webapp.py Normal file
View File

@ -0,0 +1,105 @@
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import uvicorn
from datetime import datetime
import asyncio
# Import your trading agents
from tradingagents.graph.trading_graph import TradingAgentsGraph
from tradingagents.dataflows.config import get_config
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
config = get_config()
# Create FastAPI app instance
app = FastAPI(
title="TradingAgents API",
description="API for TradingAgents financial trading framework",
version="0.1.0"
)
# Pydantic models for request/response
class TradingRequest(BaseModel):
symbol: str
date: str
class TradingResponse(BaseModel):
symbol: str
date: str
decision: dict
timestamp: str
status: str
# Initialize trading agent once at startup
def create_trading_agent():
"""Create trading agent with fixed configuration"""
return TradingAgentsGraph(debug=True, config=config)
# Create the trading agent instance once
trading_agent = create_trading_agent()
@app.get("/")
async def root():
"""Root endpoint"""
return {"message": "Welcome to TradingAgents API"}
@app.get("/ping")
async def ping():
"""Simple ping endpoint that returns pong"""
return {"message": "pong"}
@app.get("/health")
async def health_check():
"""Health check endpoint"""
return {
"status": "healthy",
"timestamp": datetime.now().isoformat(),
"service": "tradingagents-api"
}
@app.post("/trading/analyze", response_model=TradingResponse)
async def analyze_trading_decision(request: TradingRequest):
"""
Analyze trading decision for a given symbol and date
Example usage:
POST /trading/analyze
{
"symbol": "NVDA",
"date": "2024-05-10"
}
"""
try:
# Run the analysis (this might take a while, so we run it in a thread pool)
def run_analysis():
_, decision = trading_agent.propagate(request.symbol, request.date)
return decision
# Run in thread pool to avoid blocking
loop = asyncio.get_event_loop()
decision = await loop.run_in_executor(None, run_analysis)
return TradingResponse(
symbol=request.symbol,
date=request.date,
decision=decision,
timestamp=datetime.now().isoformat(),
status="success"
)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Trading analysis failed: {str(e)}")
if __name__ == "__main__":
# Run the server
uvicorn.run(
"webapp:app",
host=config.get("APP_HOST", "localhost"),
port=config.get("APP_PORT", 8000),
reload=True,
log_level="info"
)