Merge branch 'main' into profile-analyst
This commit is contained in:
commit
3ba1d5fee9
17
.env.example
17
.env.example
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
3.12.7
|
|
||||||
24
README.md
24
README.md
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
|
|
@ -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", "")
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"""
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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", {})
|
||||||
|
|
|
||||||
|
|
@ -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"],
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -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)),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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()
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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"],
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
)
|
||||||
Loading…
Reference in New Issue