tradingagents-finzdata 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 FinzData
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,83 @@
1
+ Metadata-Version: 2.4
2
+ Name: tradingagents-finzdata
3
+ Version: 0.1.0
4
+ Summary: FinzData as a data vendor for TradingAgents: point-in-time, survivorship-free prices, SEC statements as filed and macro without look-ahead.
5
+ Author-email: FinzData <info@finzdata.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://finzdata.com/tradingagents
8
+ Project-URL: Documentation, https://finzdata.com/docs
9
+ Project-URL: Source, https://github.com/FinzData/tradingagents-finzdata
10
+ Keywords: tradingagents,finzdata,market-data,backtesting,llm,agents,point-in-time,survivorship-bias
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Intended Audience :: Financial and Insurance Industry
14
+ Classifier: Topic :: Office/Business :: Financial :: Investment
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: finzdata>=0.1.14
19
+ Requires-Dist: pandas>=2.0
20
+ Provides-Extra: tradingagents
21
+ Requires-Dist: tradingagents; extra == "tradingagents"
22
+ Dynamic: license-file
23
+
24
+ # tradingagents-finzdata
25
+
26
+ FinzData as a data vendor for TradingAgents (https://github.com/TauricResearch/TradingAgents): point-in-time, survivorship-free data so your agents' backtests hold up.
27
+
28
+ ## Why FinzData
29
+
30
+ TradingAgents' default price vendor only knows stocks that still trade, so backtests silently drop the failures (survivorship bias). FinzData includes delisted stocks, point in time. Macro values are served as known on the analysis date (no look-ahead from later revisions). Statements as filed with the SEC.
31
+
32
+ ## Coverage
33
+
34
+ 16,000+ US stocks that have traded on NYSE and Nasdaq since January 2000, delisted included, point in time; 231 ETFs; 139 futures markets with every contract since 2007; GEX for single stocks, ETFs and the major indexes; COT since 1986; 13F; SEC fundamentals as filed; 18,000+ macro series with every vintage kept; crypto since 2015.
35
+
36
+ ## Install
37
+
38
+ `pip install tradingagents-finzdata` and set `FINZDATA_API_KEY` (free key, email only, 12 months of data: https://finzdata.com/account?ref=tradingagents ; full history from $19 per ticker or $195 for 5 years of the whole US market, yours to keep: https://finzdata.com/shop?ref=tradingagents).
39
+
40
+ ## Quick start
41
+
42
+ ```python
43
+ from tradingagents.graph.trading_graph import TradingAgentsGraph
44
+ from tradingagents_finzdata import register, finzdata_config
45
+ register() # adds "finzdata" as a vendor
46
+ config = finzdata_config() # prices, indicators, fundamentals and macro from FinzData
47
+ ta = TradingAgentsGraph(debug=True, config=config)
48
+ _, decision = ta.propagate("NVDA", "2024-05-10")
49
+ print(decision)
50
+ ```
51
+
52
+ ## Mix and match
53
+
54
+ Use FinzData as primary with fallback:
55
+
56
+ config["data_vendors"]["core_stock_apis"] = "finzdata,yfinance"
57
+
58
+ Tool-level override:
59
+
60
+ config["tool_vendors"]["get_macro_indicators"] = "finzdata"
61
+
62
+ ## Tool mapping
63
+
64
+ | TradingAgents tool | FinzData source |
65
+ |---|---|
66
+ | get_stock_data | daily bars |
67
+ | get_indicators | computed from FinzData bars: close_50_sma, close_200_sma, close_10_ema, macd, macds, macdh, rsi, boll, boll_ub, boll_lb, atr, vwma |
68
+ | get_balance_sheet / get_cashflow / get_income_statement | SEC statements as filed, as of the analysis date |
69
+ | get_fundamentals | profile + point-in-time valuation |
70
+ | get_insider_transactions | SEC Form 4 |
71
+ | get_macro_indicators | 18,000+ series, point in time, aliases cpi, core_cpi, pce, unemployment, payrolls, gdp, fed_funds, 10y_treasury, 2y_treasury, yield_curve, m2, initial_claims |
72
+
73
+ ## Errors
74
+
75
+ missing key -> vendor not configured (next vendor in your chain is used); history beyond your plan -> vendor unavailable with a link, never a fake "no data".
76
+
77
+ ## Also
78
+
79
+ The FinzData MCP server https://api.finzdata.com/mcp for Claude, ChatGPT and Cursor; docs https://finzdata.com/docs.
80
+
81
+ MIT licence. "FinzData is not affiliated with TauricResearch or the TradingAgents project." Support info@finzdata.com.
82
+
83
+ FinzData: built for AI, quants, data scientists and financial analysts who backtest. Clean data. Real insights. Real alpha.
@@ -0,0 +1,60 @@
1
+ # tradingagents-finzdata
2
+
3
+ FinzData as a data vendor for TradingAgents (https://github.com/TauricResearch/TradingAgents): point-in-time, survivorship-free data so your agents' backtests hold up.
4
+
5
+ ## Why FinzData
6
+
7
+ TradingAgents' default price vendor only knows stocks that still trade, so backtests silently drop the failures (survivorship bias). FinzData includes delisted stocks, point in time. Macro values are served as known on the analysis date (no look-ahead from later revisions). Statements as filed with the SEC.
8
+
9
+ ## Coverage
10
+
11
+ 16,000+ US stocks that have traded on NYSE and Nasdaq since January 2000, delisted included, point in time; 231 ETFs; 139 futures markets with every contract since 2007; GEX for single stocks, ETFs and the major indexes; COT since 1986; 13F; SEC fundamentals as filed; 18,000+ macro series with every vintage kept; crypto since 2015.
12
+
13
+ ## Install
14
+
15
+ `pip install tradingagents-finzdata` and set `FINZDATA_API_KEY` (free key, email only, 12 months of data: https://finzdata.com/account?ref=tradingagents ; full history from $19 per ticker or $195 for 5 years of the whole US market, yours to keep: https://finzdata.com/shop?ref=tradingagents).
16
+
17
+ ## Quick start
18
+
19
+ ```python
20
+ from tradingagents.graph.trading_graph import TradingAgentsGraph
21
+ from tradingagents_finzdata import register, finzdata_config
22
+ register() # adds "finzdata" as a vendor
23
+ config = finzdata_config() # prices, indicators, fundamentals and macro from FinzData
24
+ ta = TradingAgentsGraph(debug=True, config=config)
25
+ _, decision = ta.propagate("NVDA", "2024-05-10")
26
+ print(decision)
27
+ ```
28
+
29
+ ## Mix and match
30
+
31
+ Use FinzData as primary with fallback:
32
+
33
+ config["data_vendors"]["core_stock_apis"] = "finzdata,yfinance"
34
+
35
+ Tool-level override:
36
+
37
+ config["tool_vendors"]["get_macro_indicators"] = "finzdata"
38
+
39
+ ## Tool mapping
40
+
41
+ | TradingAgents tool | FinzData source |
42
+ |---|---|
43
+ | get_stock_data | daily bars |
44
+ | get_indicators | computed from FinzData bars: close_50_sma, close_200_sma, close_10_ema, macd, macds, macdh, rsi, boll, boll_ub, boll_lb, atr, vwma |
45
+ | get_balance_sheet / get_cashflow / get_income_statement | SEC statements as filed, as of the analysis date |
46
+ | get_fundamentals | profile + point-in-time valuation |
47
+ | get_insider_transactions | SEC Form 4 |
48
+ | get_macro_indicators | 18,000+ series, point in time, aliases cpi, core_cpi, pce, unemployment, payrolls, gdp, fed_funds, 10y_treasury, 2y_treasury, yield_curve, m2, initial_claims |
49
+
50
+ ## Errors
51
+
52
+ missing key -> vendor not configured (next vendor in your chain is used); history beyond your plan -> vendor unavailable with a link, never a fake "no data".
53
+
54
+ ## Also
55
+
56
+ The FinzData MCP server https://api.finzdata.com/mcp for Claude, ChatGPT and Cursor; docs https://finzdata.com/docs.
57
+
58
+ MIT licence. "FinzData is not affiliated with TauricResearch or the TradingAgents project." Support info@finzdata.com.
59
+
60
+ FinzData: built for AI, quants, data scientists and financial analysts who backtest. Clean data. Real insights. Real alpha.
@@ -0,0 +1,31 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "tradingagents-finzdata"
7
+ version = "0.1.0"
8
+ description = "FinzData as a data vendor for TradingAgents: point-in-time, survivorship-free prices, SEC statements as filed and macro without look-ahead."
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ authors = [{ name = "FinzData", email = "info@finzdata.com" }]
12
+ requires-python = ">=3.10"
13
+ dependencies = ["finzdata>=0.1.14", "pandas>=2.0"]
14
+ keywords = ["tradingagents", "finzdata", "market-data", "backtesting", "llm", "agents", "point-in-time", "survivorship-bias"]
15
+ classifiers = [
16
+ "License :: OSI Approved :: MIT License",
17
+ "Programming Language :: Python :: 3",
18
+ "Intended Audience :: Financial and Insurance Industry",
19
+ "Topic :: Office/Business :: Financial :: Investment",
20
+ ]
21
+
22
+ [project.optional-dependencies]
23
+ tradingagents = ["tradingagents"]
24
+
25
+ [project.urls]
26
+ Homepage = "https://finzdata.com/tradingagents"
27
+ Documentation = "https://finzdata.com/docs"
28
+ Source = "https://github.com/FinzData/tradingagents-finzdata"
29
+
30
+ [tool.setuptools]
31
+ packages = ["tradingagents_finzdata"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,460 @@
1
+ """
2
+ FinzData vendor for TradingAgents.
3
+
4
+ Usage:
5
+ from finzdata.integrations.tradingagents.tradingagents_finzdata import register, finzdata_config
6
+ register()
7
+ config = finzdata_config()
8
+ from tradingagents.graph import TradingAgentsGraph
9
+ graph = TradingAgentsGraph(config=config)
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import copy
15
+ import os
16
+ from datetime import date, datetime, timedelta
17
+ from typing import Any, Callable, Dict, List, Optional
18
+
19
+ import pandas as pd
20
+
21
+ VENDOR = "finzdata"
22
+ __version__ = "0.1.0"
23
+
24
+ _CLIENT: Optional[Any] = None
25
+
26
+
27
+ def set_client(client: Any) -> None:
28
+ """Set a custom FinzData client instance."""
29
+ global _CLIENT
30
+ _CLIENT = client
31
+
32
+
33
+ def _client() -> Any:
34
+ """Get or create the FinzData client."""
35
+ global _CLIENT
36
+ if _CLIENT is not None:
37
+ return _CLIENT
38
+ # Lazy import to avoid network at import time
39
+ import finzdata
40
+ _CLIENT = finzdata.Client()
41
+ return _CLIENT
42
+
43
+
44
+ def _call(fn: Callable[..., Any], *args: Any, **kwargs: Any) -> Any:
45
+ """Call a client method with exception mapping."""
46
+ try:
47
+ return fn(*args, **kwargs)
48
+ except ValueError:
49
+ raise # bad arguments (e.g. unknown indicator); VendorNotConfiguredError is also a ValueError
50
+ except Exception as e:
51
+ try:
52
+ from tradingagents.dataflows.errors import VendorError
53
+ if isinstance(e, VendorError):
54
+ raise # already a TradingAgents verdict (no data / unavailable / not configured)
55
+ except ImportError:
56
+ pass
57
+ # Map FinzError with status attribute
58
+ status = getattr(e, "status", None)
59
+ if status in (401, 403):
60
+ from tradingagents.dataflows.errors import VendorNotConfiguredError
61
+ raise VendorNotConfiguredError(
62
+ "FinzData needs an API key: set FINZDATA_API_KEY. Free key (email only, 12 months "
63
+ "of data): https://finzdata.com/account?ref=tradingagents"
64
+ ) from e
65
+ if status == 402:
66
+ from tradingagents.dataflows.errors import VendorUnavailableError
67
+ raise VendorUnavailableError(
68
+ "FinzData returned no rows for this range. Free accounts cover the last 12 months; "
69
+ "every US stock since 2000 (delisted included) is at "
70
+ "https://finzdata.com/shop?ref=tradingagents . This says nothing about the symbol."
71
+ ) from e
72
+ if status == 429 or (status is not None and status >= 500):
73
+ from tradingagents.dataflows.errors import VendorUnavailableError
74
+ raise VendorUnavailableError(f"FinzData unavailable: {e}") from e
75
+ # Network errors or other exceptions
76
+ from tradingagents.dataflows.errors import VendorUnavailableError
77
+ raise VendorUnavailableError(f"FinzData unavailable: {e}") from e
78
+
79
+
80
+ def get_stock_data(symbol: str, start_date: str, end_date: str) -> str:
81
+ """Get stock price data from FinzData."""
82
+ from tradingagents.dataflows.errors import NoMarketDataError, VendorUnavailableError
83
+
84
+ client = _client()
85
+ df = _call(client.prices, symbol, start=start_date, end=end_date)
86
+
87
+ if df is None or df.empty:
88
+ # Check if start_date is more than 365 days before today
89
+ try:
90
+ start_dt = datetime.strptime(start_date, "%Y-%m-%d").date()
91
+ if (date.today() - start_dt).days > 365:
92
+ raise VendorUnavailableError(
93
+ "FinzData returned no rows for this range. Free accounts cover the last 12 months; "
94
+ "every US stock since 2000 (delisted included) is at "
95
+ "https://finzdata.com/shop?ref=tradingagents . This says nothing about the symbol."
96
+ )
97
+ except ValueError:
98
+ pass
99
+ raise NoMarketDataError(symbol, symbol, f"no FinzData rows between {start_date} and {end_date}")
100
+
101
+ # Ensure date column is string YYYY-MM-DD
102
+ df = df.copy()
103
+ if "date" in df.columns:
104
+ df["date"] = pd.to_datetime(df["date"]).dt.strftime("%Y-%m-%d")
105
+ # Filter date <= end_date
106
+ df = df[df["date"] <= end_date]
107
+ # Round prices to 2 decimals
108
+ for col in ["open", "high", "low", "close"]:
109
+ if col in df.columns:
110
+ df[col] = df[col].round(2)
111
+ # Select and order columns
112
+ if "volume" in df.columns:
113
+ df["volume"] = pd.to_numeric(df["volume"], errors="coerce").round().astype("Int64")
114
+ cols = ["date", "open", "high", "low", "close", "volume"]
115
+ df = df[cols]
116
+ # Rename columns to match expected CSV header: Date,Open,High,Low,Close,Volume
117
+ df.columns = ["Date", "Open", "High", "Low", "Close", "Volume"]
118
+ # Header
119
+ header = (
120
+ f"# Stock data for {symbol.upper()} from {start_date} to {end_date} "
121
+ f"(FinzData: point in time, delisted included)\n"
122
+ f"# Total records: {len(df)}\n\n"
123
+ )
124
+ csv_data = df.to_csv(index=False, header=True)
125
+ return header + csv_data
126
+
127
+
128
+ def get_indicators(symbol: str, indicator: str, as_of_date: str, look_back_days: int) -> str:
129
+ """Get technical indicator values from FinzData."""
130
+ from tradingagents.dataflows.errors import NoMarketDataError, VendorUnavailableError
131
+
132
+ supported = {
133
+ "close_50_sma": "50-day Simple Moving Average of close",
134
+ "close_200_sma": "200-day Simple Moving Average of close",
135
+ "close_10_ema": "10-day Exponential Moving Average of close",
136
+ "macd": "MACD line (EMA 12 - EMA 26)",
137
+ "macds": "MACD signal line (9-day EMA of MACD)",
138
+ "macdh": "MACD histogram (MACD - signal)",
139
+ "rsi": "Relative Strength Index (14-day Wilder)",
140
+ "boll": "Bollinger Bands middle (20-day SMA)",
141
+ "boll_ub": "Bollinger Bands upper (20-day SMA + 2 std)",
142
+ "boll_lb": "Bollinger Bands lower (20-day SMA - 2 std)",
143
+ "atr": "Average True Range (14-day)",
144
+ "vwma": "Volume Weighted Moving Average (20-day)",
145
+ }
146
+
147
+ if indicator not in supported:
148
+ raise ValueError(
149
+ f"Unsupported indicator: {indicator}. Supported: {', '.join(sorted(supported.keys()))}"
150
+ )
151
+
152
+ client = _client()
153
+ as_of_dt = datetime.strptime(as_of_date, "%Y-%m-%d").date()
154
+ start_dt = as_of_dt - timedelta(days=look_back_days + 400)
155
+ start_str = start_dt.strftime("%Y-%m-%d")
156
+
157
+ df = _call(client.prices, symbol, start=start_str, end=as_of_date)
158
+
159
+ if df is None or df.empty:
160
+ raise NoMarketDataError(symbol, symbol, f"no FinzData rows for indicator calculation")
161
+
162
+ df = df.copy()
163
+ df["date"] = pd.to_datetime(df["date"])
164
+ df = df.sort_values("date")
165
+
166
+ close = df["close"]
167
+ high = df["high"]
168
+ low = df["low"]
169
+ volume = df["volume"]
170
+
171
+ # Compute indicators
172
+ if indicator == "close_50_sma":
173
+ values = close.rolling(50).mean()
174
+ elif indicator == "close_200_sma":
175
+ values = close.rolling(200).mean()
176
+ elif indicator == "close_10_ema":
177
+ values = close.ewm(span=10, adjust=False).mean()
178
+ elif indicator == "macd":
179
+ ema12 = close.ewm(span=12, adjust=False).mean()
180
+ ema26 = close.ewm(span=26, adjust=False).mean()
181
+ values = ema12 - ema26
182
+ elif indicator == "macds":
183
+ ema12 = close.ewm(span=12, adjust=False).mean()
184
+ ema26 = close.ewm(span=26, adjust=False).mean()
185
+ macd_line = ema12 - ema26
186
+ values = macd_line.ewm(span=9, adjust=False).mean()
187
+ elif indicator == "macdh":
188
+ ema12 = close.ewm(span=12, adjust=False).mean()
189
+ ema26 = close.ewm(span=26, adjust=False).mean()
190
+ macd_line = ema12 - ema26
191
+ signal = macd_line.ewm(span=9, adjust=False).mean()
192
+ values = macd_line - signal
193
+ elif indicator == "rsi":
194
+ delta = close.diff()
195
+ gain = delta.where(delta > 0, 0.0)
196
+ loss = -delta.where(delta < 0, 0.0)
197
+ avg_gain = gain.ewm(alpha=1/14, adjust=False).mean()
198
+ avg_loss = loss.ewm(alpha=1/14, adjust=False).mean()
199
+ rs = avg_gain / avg_loss
200
+ values = 100 - (100 / (1 + rs))
201
+ elif indicator == "boll":
202
+ values = close.rolling(20).mean()
203
+ elif indicator == "boll_ub":
204
+ sma20 = close.rolling(20).mean()
205
+ std20 = close.rolling(20).std()
206
+ values = sma20 + 2 * std20
207
+ elif indicator == "boll_lb":
208
+ sma20 = close.rolling(20).mean()
209
+ std20 = close.rolling(20).std()
210
+ values = sma20 - 2 * std20
211
+ elif indicator == "atr":
212
+ prev_close = close.shift(1)
213
+ tr1 = high - low
214
+ tr2 = (high - prev_close).abs()
215
+ tr3 = (low - prev_close).abs()
216
+ tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
217
+ values = tr.ewm(alpha=1/14, adjust=False).mean()
218
+ elif indicator == "vwma":
219
+ pv = close * volume
220
+ values = pv.rolling(20).sum() / volume.rolling(20).sum()
221
+ else:
222
+ raise ValueError(f"Unsupported indicator: {indicator}")
223
+
224
+ # Filter to look-back window (trading days only, newest first)
225
+ df["value"] = values
226
+ window_start = as_of_dt - timedelta(days=look_back_days) # warm-up rows above feed the calculation only
227
+ df = df[df["date"] >= pd.Timestamp(window_start)]
228
+ df = df[df["date"] <= pd.Timestamp(as_of_dt)]
229
+ df = df.dropna(subset=["value"])
230
+ df = df.sort_values("date", ascending=False)
231
+
232
+ lines = [f"{row['date'].strftime('%Y-%m-%d')}: {row['value']:.4f}" for _, row in df.iterrows()]
233
+
234
+ header = f"## {indicator} values from {window_start} to {as_of_date} (FinzData):\n\n"
235
+ body = "\n".join(lines)
236
+ footer = f"\n\n{supported[indicator]}"
237
+ return header + body + footer
238
+
239
+
240
+ def get_balance_sheet(ticker: str, freq: str = "quarterly", as_of_date: Optional[str] = None) -> str:
241
+ """Get balance sheet from FinzData."""
242
+ from tradingagents.dataflows.errors import NoMarketDataError
243
+
244
+ client = _client()
245
+ df = _call(client.statements, ticker, statement="balance", freq=freq, as_of=as_of_date)
246
+
247
+ if df is None or df.empty:
248
+ raise NoMarketDataError(ticker, ticker, f"no balance sheet data for {ticker}")
249
+
250
+ as_of_str = as_of_date or "today"
251
+ header = (
252
+ f"# Balance sheet for {ticker.upper()} ({freq}), as filed with the SEC, "
253
+ f"known as of {as_of_str} (FinzData)\n\n"
254
+ )
255
+ return header + df.to_csv(index=False)
256
+
257
+
258
+ def get_cashflow(ticker: str, freq: str = "quarterly", as_of_date: Optional[str] = None) -> str:
259
+ """Get cash flow statement from FinzData."""
260
+ from tradingagents.dataflows.errors import NoMarketDataError
261
+
262
+ client = _client()
263
+ df = _call(client.statements, ticker, statement="cashflow", freq=freq, as_of=as_of_date)
264
+
265
+ if df is None or df.empty:
266
+ raise NoMarketDataError(ticker, ticker, f"no cash flow data for {ticker}")
267
+
268
+ as_of_str = as_of_date or "today"
269
+ header = (
270
+ f"# Cash flow for {ticker.upper()} ({freq}), as filed with the SEC, "
271
+ f"known as of {as_of_str} (FinzData)\n\n"
272
+ )
273
+ return header + df.to_csv(index=False)
274
+
275
+
276
+ def get_income_statement(ticker: str, freq: str = "quarterly", as_of_date: Optional[str] = None) -> str:
277
+ """Get income statement from FinzData."""
278
+ from tradingagents.dataflows.errors import NoMarketDataError
279
+
280
+ client = _client()
281
+ df = _call(client.statements, ticker, statement="income", freq=freq, as_of=as_of_date)
282
+
283
+ if df is None or df.empty:
284
+ raise NoMarketDataError(ticker, ticker, f"no income statement data for {ticker}")
285
+
286
+ as_of_str = as_of_date or "today"
287
+ header = (
288
+ f"# Income statement for {ticker.upper()} ({freq}), as filed with the SEC, "
289
+ f"known as of {as_of_str} (FinzData)\n\n"
290
+ )
291
+ return header + df.to_csv(index=False)
292
+
293
+
294
+ def get_fundamentals(ticker: str, as_of_date: Optional[str] = None) -> str:
295
+ """Get company fundamentals from FinzData."""
296
+ from tradingagents.dataflows.errors import NoMarketDataError
297
+
298
+ client = _client()
299
+ profile_df = _call(client.profile, symbol=ticker)
300
+ valuation_df = _call(client.valuation, ticker, end=as_of_date)
301
+
302
+ if (profile_df is None or profile_df.empty) and (valuation_df is None or valuation_df.empty):
303
+ raise NoMarketDataError(ticker, ticker, f"no fundamentals data for {ticker}")
304
+
305
+ as_of_str = as_of_date or "today"
306
+ header = f"# Company fundamentals for {ticker.upper()} as of {as_of_str} (FinzData)\n\n"
307
+
308
+ lines = []
309
+ if profile_df is not None and not profile_df.empty:
310
+ row = profile_df.iloc[0]
311
+ for col in ["name", "sector", "industry"]:
312
+ if col in row and pd.notna(row[col]):
313
+ lines.append(f"{col}: {row[col]}")
314
+
315
+ if valuation_df is not None and not valuation_df.empty:
316
+ row = valuation_df.iloc[-1] # latest row
317
+ for col in valuation_df.columns:
318
+ if pd.notna(row[col]):
319
+ lines.append(f"{col}: {row[col]}")
320
+
321
+ return header + "\n".join(lines) + "\n"
322
+
323
+
324
+ def get_insider_transactions(ticker: str, as_of_date: Optional[str] = None) -> str:
325
+ """Get insider transactions from FinzData."""
326
+ from tradingagents.dataflows.errors import NoMarketDataError
327
+
328
+ client = _client()
329
+ df = _call(client.insider_transactions, ticker=ticker, end=as_of_date, limit=200)
330
+
331
+ if df is None or df.empty:
332
+ raise NoMarketDataError(ticker, ticker, f"no insider transactions for {ticker}")
333
+
334
+ if as_of_date and "filed" in df.columns: # point in time: only Form 4s already public on the analysis date
335
+ df = df[pd.to_datetime(df["filed"]) <= pd.Timestamp(as_of_date)]
336
+ if df.empty:
337
+ raise NoMarketDataError(ticker, ticker, f"no Form 4 filings for {ticker} public by {as_of_date}")
338
+ if "filed" in df.columns:
339
+ df = df.sort_values("filed", ascending=False)
340
+ df = df.head(50) # newest 50 rows
341
+ header = (f"# Insider transactions for {ticker.upper()} (FinzData, SEC Form 4)\n"
342
+ f"# Point in time: only filings public by {as_of_date or 'today'} (column 'filed')\n\n")
343
+ return header + df.to_csv(index=False)
344
+
345
+
346
+ def get_macro_indicators(
347
+ indicator: str,
348
+ as_of_date: str,
349
+ look_back_days: Optional[int] = None
350
+ ) -> str:
351
+ """Get macro indicators from FinzData."""
352
+ from tradingagents.dataflows.errors import NoMarketDataError, VendorUnavailableError
353
+
354
+ aliases = {
355
+ "cpi": "CPIAUCSL",
356
+ "core_cpi": "CPILFESL",
357
+ "pce": "PCEPI",
358
+ "unemployment": "UNRATE",
359
+ "payrolls": "PAYEMS",
360
+ "gdp": "GDP",
361
+ "fed_funds": "FEDFUNDS",
362
+ "10y_treasury": "DGS10",
363
+ "2y_treasury": "DGS2",
364
+ "yield_curve": "T10Y2Y",
365
+ "m2": "M2SL",
366
+ "initial_claims": "ICSA",
367
+ }
368
+
369
+ indicator_lower = indicator.lower()
370
+ series = aliases.get(indicator_lower, indicator.upper())
371
+
372
+ client = _client()
373
+ as_of_dt = datetime.strptime(as_of_date, "%Y-%m-%d").date()
374
+ lb = look_back_days if look_back_days is not None else 365
375
+ start_dt = as_of_dt - timedelta(days=lb)
376
+ start_str = start_dt.strftime("%Y-%m-%d")
377
+
378
+ df = _call(
379
+ client.macro,
380
+ series,
381
+ start=start_str,
382
+ end=as_of_date,
383
+ vintage="latest",
384
+ as_of=as_of_date,
385
+ )
386
+
387
+ if df is None or df.empty:
388
+ raise NoMarketDataError(series, series, f"no macro data for {series}")
389
+
390
+ df = df.copy()
391
+ if "date" in df.columns:
392
+ df["date"] = pd.to_datetime(df["date"])
393
+ df = df.sort_values("date", ascending=False)
394
+ df = df.head(24) # newest 24 rows
395
+
396
+ latest_row = df.iloc[0]
397
+ oldest_row = df.iloc[-1]
398
+ latest_val = latest_row.get("value", latest_row.iloc[-1])
399
+ latest_date = latest_row.get("date", latest_row.iloc[0])
400
+ oldest_val = oldest_row.get("value", oldest_row.iloc[-1])
401
+ change = latest_val - oldest_val
402
+
403
+ header = f"## {series} as known on {as_of_date} (FinzData, point in time)\n\n"
404
+ header += f"- Latest: {latest_val} ({latest_date.strftime('%Y-%m-%d') if hasattr(latest_date, 'strftime') else latest_date})\n"
405
+ header += f"- Change over window: {change}\n\n"
406
+ header += "| Date | Value |\n|---|---|\n"
407
+
408
+ rows = []
409
+ for _, row in df.iterrows():
410
+ d = row.get("date", row.iloc[0])
411
+ v = row.get("value", row.iloc[-1])
412
+ date_str = d.strftime("%Y-%m-%d") if hasattr(d, "strftime") else str(d)
413
+ rows.append(f"| {date_str} | {v} |")
414
+
415
+ return header + "\n".join(rows) + "\n"
416
+
417
+
418
+ def register() -> List[str]:
419
+ """Register FinzData vendor functions with TradingAgents router."""
420
+ from tradingagents.dataflows import router
421
+
422
+ methods = [
423
+ "get_stock_data",
424
+ "get_indicators",
425
+ "get_balance_sheet",
426
+ "get_cashflow",
427
+ "get_income_statement",
428
+ "get_fundamentals",
429
+ "get_insider_transactions",
430
+ "get_macro_indicators",
431
+ ]
432
+
433
+ for method in methods:
434
+ fn = globals()[method]
435
+ router.VENDOR_METHODS.setdefault(method, {})[VENDOR] = fn
436
+
437
+ return methods
438
+
439
+
440
+ def finzdata_config(base: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
441
+ """Create a TradingAgents config with FinzData as the data vendor."""
442
+ if base is not None:
443
+ config = copy.deepcopy(base)
444
+ else:
445
+ try:
446
+ from tradingagents.default_config import DEFAULT_CONFIG
447
+ config = copy.deepcopy(DEFAULT_CONFIG)
448
+ except Exception:
449
+ config = {}
450
+
451
+ config.setdefault("data_vendors", {})
452
+ config["data_vendors"]["core_stock_apis"] = "finzdata"
453
+ config["data_vendors"]["technical_indicators"] = "finzdata"
454
+ config["data_vendors"]["fundamental_data"] = "finzdata,sec_edgar"
455
+ config["data_vendors"]["macro_data"] = "finzdata"
456
+ # Insider trades route through the news category; FinzData's Form 4 rows carry the filing date, so they can be
457
+ # served point in time (TradingAgents withholds vendors that cannot).
458
+ config.setdefault("tool_vendors", {})["get_insider_transactions"] = "finzdata"
459
+
460
+ return config
@@ -0,0 +1,83 @@
1
+ Metadata-Version: 2.4
2
+ Name: tradingagents-finzdata
3
+ Version: 0.1.0
4
+ Summary: FinzData as a data vendor for TradingAgents: point-in-time, survivorship-free prices, SEC statements as filed and macro without look-ahead.
5
+ Author-email: FinzData <info@finzdata.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://finzdata.com/tradingagents
8
+ Project-URL: Documentation, https://finzdata.com/docs
9
+ Project-URL: Source, https://github.com/FinzData/tradingagents-finzdata
10
+ Keywords: tradingagents,finzdata,market-data,backtesting,llm,agents,point-in-time,survivorship-bias
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Intended Audience :: Financial and Insurance Industry
14
+ Classifier: Topic :: Office/Business :: Financial :: Investment
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: finzdata>=0.1.14
19
+ Requires-Dist: pandas>=2.0
20
+ Provides-Extra: tradingagents
21
+ Requires-Dist: tradingagents; extra == "tradingagents"
22
+ Dynamic: license-file
23
+
24
+ # tradingagents-finzdata
25
+
26
+ FinzData as a data vendor for TradingAgents (https://github.com/TauricResearch/TradingAgents): point-in-time, survivorship-free data so your agents' backtests hold up.
27
+
28
+ ## Why FinzData
29
+
30
+ TradingAgents' default price vendor only knows stocks that still trade, so backtests silently drop the failures (survivorship bias). FinzData includes delisted stocks, point in time. Macro values are served as known on the analysis date (no look-ahead from later revisions). Statements as filed with the SEC.
31
+
32
+ ## Coverage
33
+
34
+ 16,000+ US stocks that have traded on NYSE and Nasdaq since January 2000, delisted included, point in time; 231 ETFs; 139 futures markets with every contract since 2007; GEX for single stocks, ETFs and the major indexes; COT since 1986; 13F; SEC fundamentals as filed; 18,000+ macro series with every vintage kept; crypto since 2015.
35
+
36
+ ## Install
37
+
38
+ `pip install tradingagents-finzdata` and set `FINZDATA_API_KEY` (free key, email only, 12 months of data: https://finzdata.com/account?ref=tradingagents ; full history from $19 per ticker or $195 for 5 years of the whole US market, yours to keep: https://finzdata.com/shop?ref=tradingagents).
39
+
40
+ ## Quick start
41
+
42
+ ```python
43
+ from tradingagents.graph.trading_graph import TradingAgentsGraph
44
+ from tradingagents_finzdata import register, finzdata_config
45
+ register() # adds "finzdata" as a vendor
46
+ config = finzdata_config() # prices, indicators, fundamentals and macro from FinzData
47
+ ta = TradingAgentsGraph(debug=True, config=config)
48
+ _, decision = ta.propagate("NVDA", "2024-05-10")
49
+ print(decision)
50
+ ```
51
+
52
+ ## Mix and match
53
+
54
+ Use FinzData as primary with fallback:
55
+
56
+ config["data_vendors"]["core_stock_apis"] = "finzdata,yfinance"
57
+
58
+ Tool-level override:
59
+
60
+ config["tool_vendors"]["get_macro_indicators"] = "finzdata"
61
+
62
+ ## Tool mapping
63
+
64
+ | TradingAgents tool | FinzData source |
65
+ |---|---|
66
+ | get_stock_data | daily bars |
67
+ | get_indicators | computed from FinzData bars: close_50_sma, close_200_sma, close_10_ema, macd, macds, macdh, rsi, boll, boll_ub, boll_lb, atr, vwma |
68
+ | get_balance_sheet / get_cashflow / get_income_statement | SEC statements as filed, as of the analysis date |
69
+ | get_fundamentals | profile + point-in-time valuation |
70
+ | get_insider_transactions | SEC Form 4 |
71
+ | get_macro_indicators | 18,000+ series, point in time, aliases cpi, core_cpi, pce, unemployment, payrolls, gdp, fed_funds, 10y_treasury, 2y_treasury, yield_curve, m2, initial_claims |
72
+
73
+ ## Errors
74
+
75
+ missing key -> vendor not configured (next vendor in your chain is used); history beyond your plan -> vendor unavailable with a link, never a fake "no data".
76
+
77
+ ## Also
78
+
79
+ The FinzData MCP server https://api.finzdata.com/mcp for Claude, ChatGPT and Cursor; docs https://finzdata.com/docs.
80
+
81
+ MIT licence. "FinzData is not affiliated with TauricResearch or the TradingAgents project." Support info@finzdata.com.
82
+
83
+ FinzData: built for AI, quants, data scientists and financial analysts who backtest. Clean data. Real insights. Real alpha.
@@ -0,0 +1,9 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ tradingagents_finzdata/__init__.py
5
+ tradingagents_finzdata.egg-info/PKG-INFO
6
+ tradingagents_finzdata.egg-info/SOURCES.txt
7
+ tradingagents_finzdata.egg-info/dependency_links.txt
8
+ tradingagents_finzdata.egg-info/requires.txt
9
+ tradingagents_finzdata.egg-info/top_level.txt
@@ -0,0 +1,5 @@
1
+ finzdata>=0.1.14
2
+ pandas>=2.0
3
+
4
+ [tradingagents]
5
+ tradingagents
@@ -0,0 +1 @@
1
+ tradingagents_finzdata