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_API_KEY=bybit_api_key_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 local 3.12.7
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
python --version
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
python -m pip list
deactivate
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:
```bash
pip install -r requirements.txt
### Connect to Redis (Local)
```
docker-compose up -d
redis-cli -h localhost -p 6379 -a {REDIS_PASSWORD}
```
### 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",
"setuptools>=80.9.0",
"stockstats>=0.6.5",
"fastapi>=0.115.0",
"uvicorn[standard]>=0.32.0",
"tqdm>=4.67.1",
"tushare>=1.4.21",
"typing-extensions>=4.14.0",

View File

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

View File

@ -1,15 +1,11 @@
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.dataflows.config import get_config
def create_fundamentals_analyst(llm):
def fundamentals_analyst_node(state):
current_date = state["trade_date"]
ticker = state["ticker_of_interest"]
company_name = state["ticker_of_interest"]
tools = [
get_fundamentals,

View File

@ -1,9 +1,5 @@
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.dataflows.config import get_config
def create_news_analyst(llm):
def news_analyst_node(state):

View File

@ -1,15 +1,11 @@
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.dataflows.config import get_config
def create_social_media_analyst(llm):
def social_media_analyst_node(state):
current_date = state["trade_date"]
ticker = state["ticker_of_interest"]
coin_name = state["ticker_of_interest"]
tools = [
get_news,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,4 @@
import functools
import time
import json
def create_trader(llm, memory):
def trader_node(state, name):
@ -40,7 +37,9 @@ def create_trader(llm, memory):
messages = [
{
"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,
]

View File

@ -1,10 +1,7 @@
from typing import Annotated, Sequence
from datetime import date, timedelta, datetime
from typing_extensions import TypedDict, Optional
from langchain_openai import ChatOpenAI
from typing import Annotated
from typing_extensions import TypedDict
from tradingagents.agents import *
from langgraph.prebuilt import ToolNode
from langgraph.graph import END, StateGraph, START, MessagesState
from langgraph.graph import MessagesState
# Researcher team state
@ -48,7 +45,6 @@ class RiskDebateState(TypedDict):
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
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.client = OpenAI(base_url=config["backend_url"])
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):
"""Get OpenAI embedding for a text"""

View File

@ -1,20 +1,33 @@
from binance_common.configuration import ConfigurationRestAPI
from binance_common.constants import SPOT_REST_API_PROD_URL
from binance_sdk_spot.spot import Spot
import os
from datetime import datetime
import csv
import io
from tradingagents.dataflows.config import get_config
def get_api_key() -> str:
"""Retrieve the API key for Binance from environment variables."""
api_key = os.getenv("BINANCE_API_KEY")
if not api_key:
raise ValueError("BINANCE_API_KEY environment variable is not set.")
return api_key
_client = None
configuration = ConfigurationRestAPI(api_key=get_api_key(), base_path=SPOT_REST_API_PROD_URL)
client = Spot(config_rest_api=configuration)
def get_binance_client():
"""Get or create Binance client with lazy initialization."""
global _client
if _client is None:
try:
config = get_config()
api_key = config["external"].get("BINANCE_API_KEY", "")
if not api_key:
raise ValueError("BINANCE_API_KEY not found in configuration")
configuration = ConfigurationRestAPI(
api_key=api_key,
base_path=SPOT_REST_API_PROD_URL
)
_client = Spot(config_rest_api=configuration)
except Exception as e:
print(f"ERROR: Failed to initialize Binance client: {e}")
raise
return _client
def get_market_data(symbol: str, start_date: str, end_date: str):
"""Fetch market data for a given symbol from Binance. Get OHLCV data. interval is 1 day.
@ -36,6 +49,7 @@ def get_market_data(symbol: str, start_date: str, end_date: str):
# print(f"DEBUG: Fetching data for {formatted_symbol} from {start_date} to {end_date}")
try:
client = get_binance_client()
response = client.rest_api.klines(
symbol=formatted_symbol,
start_time=start_epoch,
@ -80,6 +94,8 @@ def get_market_data(symbol: str, start_date: str, end_date: str):
csv_string = output.getvalue()
output.close()
title = f"# Market Data for {symbol} from {start_date} to {end_date}\n\n"
csv_string = title + csv_string
return csv_string
except Exception as e:

View File

@ -1,5 +1,6 @@
import requests
from .alpha_vantage_common import API_BASE_URL
from tradingagents.dataflows.config import get_config
def get_market_cap() -> str:
"""
@ -8,8 +9,11 @@ def get_market_cap() -> str:
Returns:
str: Market capitalization percentage data for cryptocurrencies
"""
endpoint = f"https://api.coingecko.com/api/v3/global"
config = get_config()
api_base_url = config["external"].get("COIN_GECKO_API_BASE_URL", "https://api.coingecko.com/api/v3")
endpoint = f"{api_base_url}/global"
response = requests.get(endpoint)
print(f"DEBUG: CoinGecko API response status code: {response.status_code}")
response.raise_for_status()
data = response.json()
market_cap_pct = data.get("data", {}).get("market_cap_percentage", {})
@ -18,4 +22,4 @@ def get_market_cap() -> str:
for coin, percentage in market_cap_pct.items():
# Format each line as "Coin: XX.XX%"
result += f"- {coin.upper()}: {percentage:.2f}%\n"
return result
return result

View File

@ -1,10 +1,27 @@
from openai import OpenAI
from .config import get_config
_client = None
def get_openai_client():
"""Get or create OpenAI client with lazy initialization."""
global _client
if _client is None:
try:
config = get_config()
base_url = config.get("backend_url")
if not base_url:
raise ValueError("backend_url not found in configuration")
_client = OpenAI(base_url=base_url)
except Exception as e:
print(f"ERROR: Failed to initialize OpenAI client: {e}")
raise
return _client
def get_stock_news_openai(query, start_date, end_date):
config = get_config()
client = OpenAI(base_url=config["backend_url"])
client = get_openai_client()
response = client.responses.create(
model=config["quick_think_llm"],
@ -39,7 +56,7 @@ def get_stock_news_openai(query, start_date, end_date):
def get_crypto_news_openai(query, start_date, end_date):
config = get_config()
client = OpenAI(base_url=config["backend_url"])
client = get_openai_client()
response = client.responses.create(
model=config["quick_think_llm"],
@ -74,7 +91,7 @@ def get_crypto_news_openai(query, start_date, end_date):
def get_global_news_openai(curr_date, look_back_days=7, limit=5):
config = get_config()
client = OpenAI(base_url=config["backend_url"])
client = get_openai_client()
response = client.responses.create(
model=config["quick_think_llm"],
@ -110,7 +127,7 @@ def get_global_news_openai(curr_date, look_back_days=7, limit=5):
def get_fundamentals_openai(ticker, curr_date):
config = get_config()
client = OpenAI(base_url=config["backend_url"])
client = get_openai_client()
response = client.responses.create(
model=config["quick_think_llm"],
@ -146,7 +163,7 @@ def get_fundamentals_openai(ticker, curr_date):
def get_whitepaper_openai(symbol):
config = get_config()
client = OpenAI(base_url=config["backend_url"])
client = get_openai_client()
response = client.responses.create(
model=config["quick_think_llm"],
@ -177,4 +194,4 @@ def get_whitepaper_openai(symbol):
store=True,
)
return response.output[1].content[0].text
return response.output[1].content[0].text

View File

@ -1,16 +1,7 @@
import requests
from typing import Annotated, List, Dict
import os
from typing import Annotated, List
from tradingagents.dataflows.config import get_config
def get_api_key() -> str:
"""Retrieve the API key for TAAPI from environment variables."""
api_key = os.getenv("TAAPI_API_KEY")
if not api_key:
raise ValueError("TAAPI_API_KEY environment variable is not set.")
return api_key
# This is for single indicator, unused for now but kept for reference
def get_crypto_stats_indicators_window(
symbol: Annotated[str, "ticker symbol of the coin/asset"],
@ -58,8 +49,10 @@ def get_crypto_stats_indicators_window(
return f"Error: Indicator '{indicator}' is not supported. Please choose from: {list(supported_indicators.keys())}"
config = get_config()
base_url = config["tool_providers"].get("TAAPI_BASE_URL", "https://api.taapi.io")
api_key = get_api_key()
base_url = config["external"].get("TAAPI_BASE_URL", "https://api.taapi.io")
api_key = config["external"].get("TAAPI_API_KEY", "")
if not api_key:
return "Error: TAAPI_API_KEY is not set in the configuration."
# Set backtrack as requested
backtrack = look_back_days
@ -187,8 +180,10 @@ def get_crypto_stats_indicators(
return f"Error: Indicators {invalid_indicators} are not supported. Please choose from: {list(supported_indicators.keys())}"
config = get_config()
base_url = config["tool_providers"].get("TAAPI_BASE_URL", "https://api.taapi.io")
api_key = get_api_key()
base_url = config["external"].get("TAAPI_BASE_URL", "https://api.taapi.io")
api_key = config["external"].get("TAAPI_API_KEY", "")
if not api_key:
return "Error: TAAPI_API_KEY is not set in the configuration."
# Construct the bulk API URL
url = f"{base_url}/bulk"

View File

@ -1,13 +1,19 @@
import asyncio
from telethon import TelegramClient
from datetime import datetime, timedelta, timezone
import os
from tradingagents.dataflows.config import get_config
def get_api_credentials():
api_id = int(os.getenv("TELEGRAM_API_ID", ""))
api_hash = os.getenv("TELEGRAM_API_HASH", "")
session_name = os.getenv("TELEGRAM_SESSION_NAME", "")
return api_id, api_hash, session_name
"""Retrieve Telegram API credentials from environment variables."""
config = get_config()
api_id = config["external"]["TELEGRAM_API_ID"]
api_hash = config["external"]["TELEGRAM_API_HASH"]
session_name = config["external"]["TELEGRAM_SESSION_NAME"]
if not api_id or not api_hash or not session_name:
raise ValueError("Missing required Telegram credentials: TELEGRAM_API_ID, TELEGRAM_API_HASH, or TELEGRAM_SESSION_NAME")
return int(api_id), api_hash, session_name
async def _get_channel_history_async(start_date_str, end_date_str):
"""
@ -55,4 +61,4 @@ def get_crypto_news_telegram(curr_date, look_back_days=7, limit=100):
start_date_str = start_date.strftime('%Y-%m-%d')
end_date_str = end_date.strftime('%Y-%m-%d')
return asyncio.run(_get_channel_history_async(start_date_str, end_date_str))
return asyncio.run(_get_channel_history_async(start_date_str, end_date_str))

View File

@ -1,6 +1,10 @@
import os
DEFAULT_CONFIG = {
# App config
"APP_HOST": "localhost",
"APP_PORT": 8000,
# Directory settings
"project_dir": os.path.abspath(os.path.join(os.path.dirname(__file__), ".")),
"results_dir": os.getenv("TRADINGAGENTS_RESULTS_DIR", "./results"),
"data_dir": "/Users/yluo/Documents/Code/ScAI/FR1-data",
@ -9,10 +13,10 @@ DEFAULT_CONFIG = {
"dataflows/data_cache",
),
# LLM settings
"llm_provider": "openai",
"deep_think_llm": "o4-mini",
"quick_think_llm": "gpt-4o-mini",
"backend_url": "https://api.openai.com/v1",
"llm_provider": os.getenv("LLM_PROVIDER", "openai"),
"deep_think_llm": os.getenv("DEEP_THINK_LLM", "gpt-4o-mini"),
"quick_think_llm": os.getenv("QUICK_THINK_LLM", "gpt-4o-mini"),
"backend_url": os.getenv("BACKEND_URL","https://api.openai.com/v1"),
# Debate and discussion settings
"max_debate_rounds": 1,
"max_risk_discuss_rounds": 1,
@ -29,7 +33,6 @@ DEFAULT_CONFIG = {
},
# Tool-level configuration (takes precedence over category-level)
"tool_vendors": {
# Example: "get_stock_data": "alpha_vantage", # Override category default
"get_global_news": "telegram" # Override category default
},
# Tool provider settings
@ -37,8 +40,21 @@ DEFAULT_CONFIG = {
"TAAPI_BASE_URL": os.getenv("TAAPI_BASE_URL", "https://api.taapi.io"),
},
"external": {
"BINANCE_API_KEY": os.getenv("BINANCE_API_KEY", ""),
"TAAPI_BASE_URL": os.getenv("TAAPI_BASE_URL", "https://api.taapi.io"),
"TAAPI_API_KEY": os.getenv("TAAPI_API_KEY", ""),
"BYBIT_BASE_URL": os.getenv("BYBIT_BASE_URL", "https://api-demo.bybit.com"),
"BYBIT_API_KEY": os.getenv("BYBIT_API_KEY", ""),
"BYBIT_API_SECRET": os.getenv("BYBIT_API_SECRET", ""),
}
"COIN_GECKO_API_BASE_URL": os.getenv("COIN_GECKO_API_BASE_URL", "https://api.coingecko.com/api/v3"),
"TELEGRAM_API_ID": os.getenv("TELEGRAM_API_ID", ""),
"TELEGRAM_API_HASH": os.getenv("TELEGRAM_API_HASH", ""),
"TELEGRAM_SESSION_NAME": os.getenv("TELEGRAM_SESSION_NAME", ""),
},
"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."""
return {
"messages": [("human", ticker)],
"company_of_interest": ticker,
"ticker_of_interest": ticker,
"trade_date": str(trade_date),
"investment_debate_state": InvestDebateState(

View File

@ -215,7 +215,7 @@ class TradingAgentsGraph:
def _log_state(self, trade_date, final_state):
"""Log the final state to a JSON file."""
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"],
"market_report": final_state["market_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"
)