Skip to content

Commit 13b826a

Browse files
authored
Merge pull request #245 from TauricResearch/feat/tooloptim
Y Finance Tools Optimizations
2 parents 32be17c + b2ef960 commit 13b826a

File tree

5 files changed

+222
-13
lines changed

5 files changed

+222
-13
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ cp .env.example .env
127127
# Edit .env with your actual API keys
128128
```
129129

130-
**Note:** The default configuration uses [Alpha Vantage](https://www.alphavantage.co/) for fundamental and news data. You can get a free API key from their website, or upgrade to [Alpha Vantage Premium](https://www.alphavantage.co/premium/) for higher rate limits and more stable access. If you prefer to use OpenAI for these data sources instead, you can modify the data vendor settings in `tradingagents/default_config.py`.
130+
**Note:** We are happy to partner with Alpha Vantage to provide robust API support for TradingAgents. You can get a free AlphaVantage API [here](https://www.alphavantage.co/support/#api-key), TradingAgents-sourced requests also have increased rate limits to 60 requests per minute with no daily limits. Typically the quota is sufficient for performing complex tasks with TradingAgents thanks to Alpha Vantage’s open-source support program. If you prefer to use OpenAI for these data sources instead, you can modify the data vendor settings in `tradingagents/default_config.py`.
131131

132132
### CLI Usage
133133

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ dependencies = [
1212
"eodhd>=1.0.32",
1313
"feedparser>=6.0.11",
1414
"finnhub-python>=2.4.23",
15+
"grip>=4.6.2",
1516
"langchain-anthropic>=0.3.15",
1617
"langchain-experimental>=0.3.4",
1718
"langchain-google-genai>=2.1.5",

test.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import time
2+
from tradingagents.dataflows.y_finance import get_YFin_data_online, get_stock_stats_indicators_window, get_balance_sheet as get_yfinance_balance_sheet, get_cashflow as get_yfinance_cashflow, get_income_statement as get_yfinance_income_statement, get_insider_transactions as get_yfinance_insider_transactions
3+
4+
print("Testing optimized implementation with 30-day lookback:")
5+
start_time = time.time()
6+
result = get_stock_stats_indicators_window("AAPL", "macd", "2024-11-01", 30)
7+
end_time = time.time()
8+
9+
print(f"Execution time: {end_time - start_time:.2f} seconds")
10+
print(f"Result length: {len(result)} characters")
11+
print(result)

tradingagents/dataflows/y_finance.py

Lines changed: 119 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -137,16 +137,42 @@ def get_stock_stats_indicators_window(
137137
curr_date_dt = datetime.strptime(curr_date, "%Y-%m-%d")
138138
before = curr_date_dt - relativedelta(days=look_back_days)
139139

140-
# online gathering only
141-
ind_string = ""
142-
while curr_date_dt >= before:
143-
indicator_value = get_stockstats_indicator(
144-
symbol, indicator, curr_date_dt.strftime("%Y-%m-%d")
145-
)
146-
147-
ind_string += f"{curr_date_dt.strftime('%Y-%m-%d')}: {indicator_value}\n"
148-
149-
curr_date_dt = curr_date_dt - relativedelta(days=1)
140+
# Optimized: Get stock data once and calculate indicators for all dates
141+
try:
142+
indicator_data = _get_stock_stats_bulk(symbol, indicator, curr_date)
143+
144+
# Generate the date range we need
145+
current_dt = curr_date_dt
146+
date_values = []
147+
148+
while current_dt >= before:
149+
date_str = current_dt.strftime('%Y-%m-%d')
150+
151+
# Look up the indicator value for this date
152+
if date_str in indicator_data:
153+
indicator_value = indicator_data[date_str]
154+
else:
155+
indicator_value = "N/A: Not a trading day (weekend or holiday)"
156+
157+
date_values.append((date_str, indicator_value))
158+
current_dt = current_dt - relativedelta(days=1)
159+
160+
# Build the result string
161+
ind_string = ""
162+
for date_str, value in date_values:
163+
ind_string += f"{date_str}: {value}\n"
164+
165+
except Exception as e:
166+
print(f"Error getting bulk stockstats data: {e}")
167+
# Fallback to original implementation if bulk method fails
168+
ind_string = ""
169+
curr_date_dt = datetime.strptime(curr_date, "%Y-%m-%d")
170+
while curr_date_dt >= before:
171+
indicator_value = get_stockstats_indicator(
172+
symbol, indicator, curr_date_dt.strftime("%Y-%m-%d")
173+
)
174+
ind_string += f"{curr_date_dt.strftime('%Y-%m-%d')}: {indicator_value}\n"
175+
curr_date_dt = curr_date_dt - relativedelta(days=1)
150176

151177
result_str = (
152178
f"## {indicator} values from {before.strftime('%Y-%m-%d')} to {end_date}:\n\n"
@@ -158,6 +184,89 @@ def get_stock_stats_indicators_window(
158184
return result_str
159185

160186

187+
def _get_stock_stats_bulk(
188+
symbol: Annotated[str, "ticker symbol of the company"],
189+
indicator: Annotated[str, "technical indicator to calculate"],
190+
curr_date: Annotated[str, "current date for reference"]
191+
) -> dict:
192+
"""
193+
Optimized bulk calculation of stock stats indicators.
194+
Fetches data once and calculates indicator for all available dates.
195+
Returns dict mapping date strings to indicator values.
196+
"""
197+
from .config import get_config
198+
import pandas as pd
199+
from stockstats import wrap
200+
import os
201+
202+
config = get_config()
203+
online = config["data_vendors"]["technical_indicators"] != "local"
204+
205+
if not online:
206+
# Local data path
207+
try:
208+
data = pd.read_csv(
209+
os.path.join(
210+
config.get("data_cache_dir", "data"),
211+
f"{symbol}-YFin-data-2015-01-01-2025-03-25.csv",
212+
)
213+
)
214+
df = wrap(data)
215+
except FileNotFoundError:
216+
raise Exception("Stockstats fail: Yahoo Finance data not fetched yet!")
217+
else:
218+
# Online data fetching with caching
219+
today_date = pd.Timestamp.today()
220+
curr_date_dt = pd.to_datetime(curr_date)
221+
222+
end_date = today_date
223+
start_date = today_date - pd.DateOffset(years=15)
224+
start_date_str = start_date.strftime("%Y-%m-%d")
225+
end_date_str = end_date.strftime("%Y-%m-%d")
226+
227+
os.makedirs(config["data_cache_dir"], exist_ok=True)
228+
229+
data_file = os.path.join(
230+
config["data_cache_dir"],
231+
f"{symbol}-YFin-data-{start_date_str}-{end_date_str}.csv",
232+
)
233+
234+
if os.path.exists(data_file):
235+
data = pd.read_csv(data_file)
236+
data["Date"] = pd.to_datetime(data["Date"])
237+
else:
238+
data = yf.download(
239+
symbol,
240+
start=start_date_str,
241+
end=end_date_str,
242+
multi_level_index=False,
243+
progress=False,
244+
auto_adjust=True,
245+
)
246+
data = data.reset_index()
247+
data.to_csv(data_file, index=False)
248+
249+
df = wrap(data)
250+
df["Date"] = df["Date"].dt.strftime("%Y-%m-%d")
251+
252+
# Calculate the indicator for all rows at once
253+
df[indicator] # This triggers stockstats to calculate the indicator
254+
255+
# Create a dictionary mapping date strings to indicator values
256+
result_dict = {}
257+
for _, row in df.iterrows():
258+
date_str = row["Date"]
259+
indicator_value = row[indicator]
260+
261+
# Handle NaN/None values
262+
if pd.isna(indicator_value):
263+
result_dict[date_str] = "N/A"
264+
else:
265+
result_dict[date_str] = str(indicator_value)
266+
267+
return result_dict
268+
269+
161270
def get_stockstats_indicator(
162271
symbol: Annotated[str, "ticker symbol of the company"],
163272
indicator: Annotated[str, "technical indicator to get the analysis and report of"],

uv.lock

Lines changed: 90 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)