Matthew-Anyiam commited on
Commit
29d192d
·
1 Parent(s): ae9f782

v0.2: 2-tool architecture (eugene_sec + eugene_economics), clean demo

Browse files
Files changed (3) hide show
  1. app.py +72 -431
  2. eugene_server.py +191 -0
  3. requirements.txt +5 -3
app.py CHANGED
@@ -1,448 +1,89 @@
1
  """
2
- Eugene Intelligence — HuggingFace Demo
3
- Thin UI wrapper around eugene/ package
4
  """
5
-
6
  import gradio as gr
7
- import requests
8
- import os
9
-
10
- # Import from eugene package
11
- from eugene.tools.institutional import company as get_company
12
-
13
- SEC_USER_AGENT = os.environ.get("SEC_USER_AGENT", "Eugene Intelligence (matthew@eugeneintelligence.com)")
14
- SEC_HEADERS = {"User-Agent": SEC_USER_AGENT}
15
-
16
-
17
- def fmt_currency(val):
18
- if val is None:
19
- return "N/A"
20
- if abs(val) >= 1e12:
21
- return f"${val/1e12:.2f}T"
22
- if abs(val) >= 1e9:
23
- return f"${val/1e9:.2f}B"
24
- if abs(val) >= 1e6:
25
- return f"${val/1e6:.1f}M"
26
- return f"${val:,.2f}"
27
-
28
-
29
- # ============================================
30
- # FLAGSHIP: Full Company Analysis
31
- # ============================================
32
-
33
- def analyze_full(ticker, show_json=False):
34
- """One killer workflow: Ticker → Statements → Ratios → Citations"""
35
- if not ticker.strip():
36
- return "Enter a ticker"
37
- ticker = ticker.strip().upper()
38
-
39
- # Fetch all data
40
- prices = get_company(ticker, "prices")
41
- fin = get_company(ticker, "financials")
42
- health = get_company(ticker, "health")
43
-
44
- # JSON output for agents
45
- if show_json:
46
- import json
47
- data = {"ticker": ticker, "prices": prices, "financials": fin, "health": health}
48
- return f"```json\n{json.dumps(data, indent=2, default=str)}\n```"
49
-
50
- output = f"# {ticker} — Full Analysis\n\n"
51
-
52
- # 1. Prices
53
- if prices.get("status") == "success":
54
- data = prices.get("data", {})
55
- meta = prices.get("metadata", {})
56
- output += f"## Quote\n"
57
- output += f"**{meta.get('name', ticker)}** | {meta.get('exchange', '')}\n\n"
58
- output += "| Metric | Value |\n|--------|-------|\n"
59
- output += f"| Price | {data.get('price', {}).get('formatted', 'N/A')} |\n"
60
- output += f"| Change | {data.get('change', {}).get('formatted', 'N/A')} ({data.get('change_percent', {}).get('formatted', 'N/A')}) |\n"
61
- output += f"| Market Cap | {data.get('market_cap', {}).get('formatted', 'N/A')} |\n"
62
- output += f"| 52W Range | {data.get('year_low', {}).get('formatted', 'N/A')} - {data.get('year_high', {}).get('formatted', 'N/A')} |\n\n"
63
-
64
- # 2. Financials
65
- if fin.get("status") in ["success", "partial"]:
66
- data = fin.get("data", {})
67
- meta = fin.get("metadata", {})
68
- output += f"## SEC Financials (FY{meta.get('fiscal_year', 'N/A')})\n"
69
- output += f"*Filed: {meta.get('filed_date', 'N/A')} | CIK: {meta.get('cik', 'N/A')}*\n\n"
70
- output += "| Metric | Value | SEC Concept | Period |\n|--------|-------|-------------|--------|\n"
71
-
72
- for key in ["revenue", "net_income", "total_assets", "total_liabilities", "stockholders_equity", "cash"]:
73
- metric = data.get(key, {})
74
- concept = metric.get('sec_concept', 'N/A')
75
- if concept and len(concept) > 25:
76
- concept = concept[:25] + "..."
77
- output += f"| {key.replace('_', ' ').title()} | {metric.get('formatted', 'N/A')} | `{concept}` | {metric.get('period_end', 'N/A')} |\n"
78
-
79
- if fin.get("warnings"):
80
- output += f"\n⚠️ *{', '.join(fin.get('warnings'))}*\n"
81
- output += "\n"
82
-
83
- # 3. Health Ratios
84
- if health.get("status") in ["success", "partial"]:
85
- data = health.get("data", {})
86
- output += "## Financial Health\n\n"
87
- output += "| Ratio | Value |\n|-------|-------|\n"
88
-
89
- for key in ["roe", "roa", "net_margin", "debt_to_equity", "debt_to_assets"]:
90
- metric = data.get(key, {})
91
- output += f"| {key.replace('_', ' ').upper()} | {metric.get('formatted', 'N/A')} |\n"
92
- output += "\n"
93
-
94
- # 4. Source Citation
95
- output += "---\n"
96
- output += "### Data Sources\n"
97
- output += f"- **Prices**: Financial Modeling Prep (real-time)\n"
98
- output += f"- **Financials**: SEC XBRL ([View 10-K](https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&CIK={fin.get('metadata', {}).get('cik', '')}&type=10-K))\n"
99
- output += f"- **Retrieved**: {prices.get('retrieved_at', 'N/A')[:19] if prices.get('retrieved_at') else 'N/A'}\n\n"
100
- output += "*Disclaimer: For informational purposes only. Not financial advice.*"
101
-
102
- return output
103
-
104
-
105
- # ============================================
106
- # Individual Tabs (use institutional tools)
107
- # ============================================
108
 
109
- def analyze_prices(ticker):
110
- if not ticker.strip():
111
- return "Enter a ticker"
112
- result = get_company(ticker.strip().upper(), "prices")
113
-
114
- if result.get("status") == "error":
115
- return f"❌ {result.get('error', {}).get('message', 'Error')}"
116
-
117
- data = result.get("data", {})
118
- meta = result.get("metadata", {})
119
-
120
- output = f"# {meta.get('name', ticker)} ({ticker.upper()})\n\n"
121
- output += "| Metric | Value |\n|--------|-------|\n"
122
- output += f"| Price | {data.get('price', {}).get('formatted', 'N/A')} |\n"
123
- output += f"| Change | {data.get('change', {}).get('formatted', 'N/A')} ({data.get('change_percent', {}).get('formatted', 'N/A')}) |\n"
124
- output += f"| Volume | {data.get('volume', {}).get('formatted', 'N/A')} |\n"
125
- output += f"| Market Cap | {data.get('market_cap', {}).get('formatted', 'N/A')} |\n"
126
- output += f"| Day Range | {data.get('day_low', {}).get('formatted', 'N/A')} - {data.get('day_high', {}).get('formatted', 'N/A')} |\n"
127
- output += f"| 52W Range | {data.get('year_low', {}).get('formatted', 'N/A')} - {data.get('year_high', {}).get('formatted', 'N/A')} |\n\n"
128
- output += f"*Source: {result.get('source', {}).get('name', 'FMP')} | Retrieved: {result.get('retrieved_at', 'N/A')[:19]}*"
129
-
130
- return output
131
 
 
 
132
 
133
- def analyze_financials(ticker):
134
- if not ticker.strip():
135
- return "Enter a ticker"
136
- result = get_company(ticker.strip().upper(), "financials")
137
-
138
- if result.get("status") == "error":
139
- return f"❌ {result.get('error', {}).get('message', 'Error')}"
140
-
141
- data = result.get("data", {})
142
- meta = result.get("metadata", {})
143
-
144
- output = f"# {ticker.upper()} — SEC Financials\n\n"
145
- output += f"**CIK:** {meta.get('cik', 'N/A')} | **Fiscal Year:** {meta.get('fiscal_year', 'N/A')} | **Filed:** {meta.get('filed_date', 'N/A')}\n\n"
146
- output += "| Metric | Value | SEC Concept | Period |\n|--------|-------|-------------|--------|\n"
147
-
148
- for key, label in [("revenue", "Revenue"), ("net_income", "Net Income"), ("total_assets", "Total Assets"),
149
- ("total_liabilities", "Total Liabilities"), ("stockholders_equity", "Equity"), ("cash", "Cash")]:
150
- metric = data.get(key, {})
151
- concept = metric.get('sec_concept', '-')
152
- if concept and len(concept) > 25:
153
- concept = concept[:25] + "..."
154
- output += f"| {label} | {metric.get('formatted', 'N/A')} | `{concept}` | {metric.get('period_end', '-')} |\n"
155
-
156
- if result.get("warnings"):
157
- output += f"\n⚠️ {', '.join(result.get('warnings'))}\n"
158
-
159
- output += f"\n*Source: {result.get('source', {}).get('name', 'SEC XBRL')}*\n"
160
- output += f"*[View on SEC](https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&CIK={meta.get('cik', '')}&type=10-K)*"
161
-
162
- return output
163
-
164
-
165
- def analyze_health(ticker):
166
- if not ticker.strip():
167
- return "Enter a ticker"
168
- result = get_company(ticker.strip().upper(), "health")
169
-
170
- if result.get("status") == "error":
171
- return f"❌ {result.get('error', {}).get('message', 'Error')}"
172
-
173
- data = result.get("data", {})
174
-
175
- output = f"# {ticker.upper()} — Financial Health\n\n"
176
- output += "| Ratio | Value |\n|-------|-------|\n"
177
-
178
- for key in ["roe", "roa", "net_margin", "debt_to_equity", "debt_to_assets", "equity_ratio"]:
179
- metric = data.get(key, {})
180
- output += f"| {key.replace('_', ' ').upper()} | {metric.get('formatted', 'N/A')} |\n"
181
-
182
- output += f"\n*Source: SEC XBRL (calculated) | Period: {result.get('period', 'N/A')}*"
183
-
184
- return output
185
-
186
-
187
- def analyze_earnings(ticker):
188
- if not ticker.strip():
189
- return "Enter a ticker"
190
- result = get_company(ticker.strip().upper(), "earnings")
191
-
192
- if result.get("status") == "error":
193
- return f"❌ {result.get('error', {}).get('message', 'Error')}"
194
-
195
- data = result.get("data", {})
196
- eps_list = data.get("eps_history", [])
197
-
198
- output = f"# {ticker.upper()} — Earnings History\n\n"
199
- output += "| Period | EPS | Filed |\n|--------|-----|-------|\n"
200
-
201
- for e in eps_list:
202
- output += f"| {e.get('period_end', 'N/A')} | {e.get('eps', {}).get('formatted', 'N/A')} | {e.get('filed_date', 'N/A')} |\n"
203
-
204
- output += f"\n*Source: SEC XBRL ({data.get('concept_used', 'EPS')})*"
205
-
206
- return output
207
-
208
-
209
- def analyze_insider(ticker, days=365):
210
- if not ticker.strip():
211
- return "Enter a ticker"
212
- result = get_company(ticker.strip().upper(), "insider")
213
-
214
- if result.get("status") == "error":
215
- return f"❌ {result.get('error', {}).get('message', 'Error')}"
216
-
217
- data = result.get("data", {})
218
- trades = data.get("trades", [])
219
-
220
- output = f"# {ticker.upper()} — Insider Activity\n\n"
221
- output += f"**Form 4 Filings:** {data.get('total_filings', 0)}\n\n"
222
-
223
- if trades:
224
- output += "| Filed | Filer |\n|-------|-------|\n"
225
- for t in trades[:15]:
226
- output += f"| {t.get('filed_date', 'N/A')} | {t.get('filer', 'N/A')[:40] if t.get('filer') else 'N/A'} |\n"
227
-
228
- output += f"\n*Source: SEC EDGAR Form 4*"
229
-
230
- return output
231
-
232
-
233
- # ============================================
234
- # Economic / Regulatory (direct API calls)
235
- # ============================================
236
-
237
- def show_treasury():
238
  try:
239
- FRED_API_KEY = os.environ.get("FRED_API_KEY")
240
- if not FRED_API_KEY:
241
- return "❌ FRED_API_KEY not configured"
242
-
243
- yields = {}
244
- labels = {"DGS1": "1 Year", "DGS2": "2 Year", "DGS5": "5 Year", "DGS10": "10 Year", "DGS30": "30 Year"}
245
-
246
- for series in labels.keys():
247
- r = requests.get(f"https://api.stlouisfed.org/fred/series/observations",
248
- params={"series_id": series, "api_key": FRED_API_KEY,
249
- "file_type": "json", "limit": 1, "sort_order": "desc"}, timeout=10)
250
- data = r.json()
251
- if "observations" in data and data["observations"]:
252
- yields[series] = data["observations"][0].get("value")
253
-
254
- output = "# Treasury Yields\n\n"
255
- output += "| Maturity | Yield |\n|----------|-------|\n"
256
- for series, label in labels.items():
257
- if series in yields:
258
- output += f"| {label} | {yields[series]}% |\n"
259
- output += f"\n*Source: FRED*"
260
- return output
261
  except Exception as e:
262
- return f"❌ Error: {str(e)}"
263
-
264
 
265
- def show_economic(category):
266
  try:
267
- FRED_API_KEY = os.environ.get("FRED_API_KEY")
268
- if not FRED_API_KEY:
269
- return "❌ FRED_API_KEY not configured"
270
-
271
- indicators = {
272
- "inflation": {"CPIAUCSL": "CPI", "CPILFESL": "Core CPI", "PCEPI": "PCE", "PCEPILFE": "Core PCE"},
273
- "employment": {"UNRATE": "Unemployment", "PAYEMS": "Payrolls", "ICSA": "Jobless Claims"},
274
- "gdp": {"GDP": "Nominal GDP", "GDPC1": "Real GDP", "A191RL1Q225SBEA": "GDP Growth %"},
275
- "housing": {"HOUST": "Housing Starts", "MORTGAGE30US": "30Y Mortgage", "CSUSHPISA": "Case-Shiller"},
276
- }
277
-
278
- if category not in indicators:
279
- return f"❌ Unknown category"
280
-
281
- output = f"# Economic Data: {category.title()}\n\n"
282
- output += "| Indicator | Value | Date |\n|-----------|-------|------|\n"
283
-
284
- for series, name in indicators[category].items():
285
- r = requests.get(f"https://api.stlouisfed.org/fred/series/observations",
286
- params={"series_id": series, "api_key": FRED_API_KEY,
287
- "file_type": "json", "limit": 1, "sort_order": "desc"}, timeout=10)
288
- data = r.json()
289
- if "observations" in data and data["observations"]:
290
- obs = data["observations"][0]
291
- output += f"| {name} | {obs.get('value')} | {obs.get('date')} |\n"
292
-
293
- output += f"\n*Source: FRED*"
294
- return output
295
  except Exception as e:
296
- return f"❌ Error: {str(e)}"
297
 
 
 
 
 
298
 
299
- def show_forex():
300
- try:
301
- r = requests.get("https://api.frankfurter.app/latest?from=USD", timeout=10)
302
- data = r.json()
303
-
304
- output = "# Forex Rates (USD Base)\n\n"
305
- output += "| Currency | Rate |\n|----------|------|\n"
306
- for curr in ["EUR", "GBP", "JPY", "CHF", "CAD", "AUD", "CNY"]:
307
- if curr in data.get("rates", {}):
308
- output += f"| USD/{curr} | {data['rates'][curr]} |\n"
309
- output += f"\n*Source: ECB | Date: {data.get('date')}*"
310
- return output
311
- except Exception as e:
312
- return f"❌ Error: {str(e)}"
313
-
314
-
315
- def show_debt():
316
- try:
317
- r = requests.get("https://api.fiscaldata.treasury.gov/services/api/fiscal_service/v2/accounting/od/debt_to_penny",
318
- params={"sort": "-record_date", "page[size]": 5}, timeout=15)
319
- data = r.json()
320
-
321
- output = "# US National Debt\n\n"
322
- output += "| Date | Total Debt |\n|------|------------|\n"
323
- for record in data.get("data", [])[:5]:
324
- debt = float(record.get("tot_pub_debt_out_amt", 0))
325
- output += f"| {record.get('record_date')} | ${debt/1e12:.2f}T |\n"
326
- output += f"\n*Source: Treasury FiscalData*"
327
- return output
328
- except Exception as e:
329
- return f"❌ Error: {str(e)}"
330
-
331
-
332
- def show_sec_news():
333
- try:
334
- import feedparser
335
- feed = feedparser.parse("https://www.sec.gov/news/pressreleases.rss")
336
- output = "# SEC Press Releases\n\n"
337
- for entry in feed.entries[:10]:
338
- output += f"**[{entry.get('title', '')}]({entry.get('link', '')})**\n*{entry.get('published', '')}*\n\n"
339
- output += f"*Source: SEC RSS*"
340
- return output
341
- except Exception as e:
342
- return f"❌ Error: {str(e)}"
343
-
344
-
345
- def show_fed():
346
- try:
347
- import feedparser
348
- feed = feedparser.parse("https://www.federalreserve.gov/feeds/speeches.xml")
349
- output = "# Federal Reserve Speeches\n\n"
350
- for entry in feed.entries[:10]:
351
- output += f"**[{entry.get('title', '')}]({entry.get('link', '')})**\n*{entry.get('published', '')}*\n\n"
352
- output += f"*Source: Federal Reserve*"
353
- return output
354
- except Exception as e:
355
- return f"❌ Error: {str(e)}"
356
-
357
-
358
- # ============================================
359
- # GRADIO APP
360
- # ============================================
361
-
362
- with gr.Blocks(title="Eugene Intelligence") as demo:
363
  gr.Markdown("""
364
- # 🏛️ Eugene Intelligence
365
- **Data Infrastructure for AI Agents**
366
-
367
- [GitHub](https://github.com/Matthew-Anyiam/eugene-data-labs)
368
  """)
369
-
370
- with gr.Tabs():
371
- # FLAGSHIP TAB
372
- with gr.TabItem("🔍 Full Analysis"):
373
- gr.Markdown("*One ticker → Complete financial picture with citations*")
374
- fa_input = gr.Textbox(label="Ticker", placeholder="AAPL")
375
- fa_json = gr.Checkbox(label="Show JSON (for agents)", value=False)
376
- fa_btn = gr.Button("Analyze", variant="primary")
377
- fa_output = gr.Markdown()
378
- fa_btn.click(analyze_full, inputs=[fa_input, fa_json], outputs=fa_output)
379
-
380
- # Company Data
381
- with gr.TabItem("📊 Quote"):
382
- c_input = gr.Textbox(label="Ticker", placeholder="AAPL")
383
- c_btn = gr.Button("Get Quote", variant="primary")
384
- c_output = gr.Markdown()
385
- c_btn.click(analyze_prices, inputs=c_input, outputs=c_output)
386
-
387
- with gr.TabItem("📈 Financials"):
388
- f_input = gr.Textbox(label="Ticker", placeholder="AAPL")
389
- f_btn = gr.Button("Get Financials", variant="primary")
390
- f_output = gr.Markdown()
391
- f_btn.click(analyze_financials, inputs=f_input, outputs=f_output)
392
-
393
- with gr.TabItem("📉 Health"):
394
- h_input = gr.Textbox(label="Ticker", placeholder="AAPL")
395
- h_btn = gr.Button("Get Health", variant="primary")
396
- h_output = gr.Markdown()
397
- h_btn.click(analyze_health, inputs=h_input, outputs=h_output)
398
-
399
- with gr.TabItem("💰 Earnings"):
400
- e_input = gr.Textbox(label="Ticker", placeholder="NVDA")
401
- e_btn = gr.Button("Get Earnings", variant="primary")
402
- e_output = gr.Markdown()
403
- e_btn.click(analyze_earnings, inputs=e_input, outputs=e_output)
404
-
405
- with gr.TabItem("🕵️ Insider"):
406
- with gr.Row():
407
- i_ticker = gr.Textbox(label="Ticker", placeholder="TSLA")
408
- i_days = gr.Slider(minimum=30, maximum=730, value=365, label="Days")
409
- i_btn = gr.Button("Get Activity", variant="primary")
410
- i_output = gr.Markdown()
411
- i_btn.click(analyze_insider, inputs=[i_ticker, i_days], outputs=i_output)
412
-
413
- # Economic Data
414
- with gr.TabItem("💵 Treasury"):
415
- t_btn = gr.Button("Get Yields", variant="primary")
416
- t_output = gr.Markdown()
417
- t_btn.click(show_treasury, outputs=t_output)
418
-
419
- with gr.TabItem("📉 Economic"):
420
- eco_cat = gr.Dropdown(choices=["inflation", "employment", "gdp", "housing"], value="inflation", label="Category")
421
- eco_btn = gr.Button("Get Data", variant="primary")
422
- eco_output = gr.Markdown()
423
- eco_btn.click(show_economic, inputs=eco_cat, outputs=eco_output)
424
-
425
- with gr.TabItem("💱 Forex"):
426
- fx_btn = gr.Button("Get Rates", variant="primary")
427
- fx_output = gr.Markdown()
428
- fx_btn.click(show_forex, outputs=fx_output)
429
-
430
- with gr.TabItem("🏦 Debt"):
431
- debt_btn = gr.Button("Get Debt", variant="primary")
432
- debt_output = gr.Markdown()
433
- debt_btn.click(show_debt, outputs=debt_output)
434
-
435
- # Regulatory
436
- with gr.TabItem("📰 SEC"):
437
- n_btn = gr.Button("Get News", variant="primary")
438
- n_output = gr.Markdown()
439
- n_btn.click(show_sec_news, outputs=n_output)
440
-
441
- with gr.TabItem("🏛️ Fed"):
442
- fed_btn = gr.Button("Get Speeches", variant="primary")
443
- fed_output = gr.Markdown()
444
- fed_btn.click(show_fed, outputs=fed_output)
445
-
446
- gr.Markdown("---\n*Eugene Intelligence — Data Infrastructure for AI Agents*")
447
 
448
  demo.launch()
 
1
  """
2
+ Eugene Intelligence — Financial Context for AI
3
+ Demo: 2 tools, complete coverage, every number traced to source.
4
  """
 
5
  import gradio as gr
6
+ import sys, os, json
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
9
+ from eugene_server import eugene_sec, eugene_economics
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
+ def fmt(result):
12
+ return json.dumps(result, indent=2, default=str)
13
 
14
+ def query_sec(ticker, extract):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  try:
16
+ return fmt(eugene_sec(ticker.strip(), extract))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  except Exception as e:
18
+ return fmt({"error": str(e)})
 
19
 
20
+ def query_econ(category):
21
  try:
22
+ return fmt(eugene_economics(category))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  except Exception as e:
24
+ return fmt({"error": str(e)})
25
 
26
+ EXAMPLES_SEC = [
27
+ ["AAPL", "financials"], ["NVDA", "prices"], ["TSLA", "insider"],
28
+ ["BA", "health"], ["JPM", "earnings"], ["MSFT", "profile"],
29
+ ]
30
 
31
+ with gr.Blocks(title="Eugene Intelligence", theme=gr.themes.Base()) as demo:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  gr.Markdown("""
33
+ # Eugene Intelligence
34
+ **Financial context for AI. The data layer for agents that need to get finance right.**
35
+
36
+ 2 tools. 10+ data types. Every number traced to its SEC filing, XBRL tag, and accession number.
37
  """)
38
+
39
+ with gr.Tab("eugene_sec"):
40
+ gr.Markdown("All company and SEC data in one call.")
41
+ with gr.Row():
42
+ ticker = gr.Textbox(label="Ticker", value="AAPL", scale=1)
43
+ extract = gr.Dropdown(
44
+ choices=["financials", "prices", "profile", "health", "earnings",
45
+ "insider", "institutional", "filings", "estimates", "news"],
46
+ value="financials", label="Extract", scale=1,
47
+ )
48
+ sec_btn = gr.Button("Get Data", variant="primary", scale=1)
49
+ sec_out = gr.Code(label="Response (JSON)", language="json")
50
+ sec_btn.click(query_sec, inputs=[ticker, extract], outputs=sec_out)
51
+ gr.Examples(EXAMPLES_SEC, inputs=[ticker, extract], label="Try these")
52
+
53
+ with gr.Tab("eugene_economics"):
54
+ gr.Markdown("All economic data from FRED in one call.")
55
+ with gr.Row():
56
+ category = gr.Dropdown(
57
+ choices=["treasury", "inflation", "employment", "gdp", "housing",
58
+ "consumer", "manufacturing", "rates", "money", "all"],
59
+ value="treasury", label="Category", scale=1,
60
+ )
61
+ econ_btn = gr.Button("Get Data", variant="primary", scale=1)
62
+ econ_out = gr.Code(label="Response (JSON)", language="json")
63
+ econ_btn.click(query_econ, inputs=[category], outputs=econ_out)
64
+
65
+ with gr.Tab("Use Eugene"):
66
+ gr.Markdown("""
67
+ ### API
68
+ ```bash
69
+ pip install eugene-intelligence
70
+ python eugene_server.py
71
+ curl "http://localhost:8000/v1/sec/AAPL?extract=financials"
72
+ ```
73
+
74
+ ### MCP Server (Claude, Cursor)
75
+ ```bash
76
+ python eugene_server.py --mode mcp
77
+ ```
78
+
79
+ ### Python
80
+ ```python
81
+ from eugene_server import eugene_sec, eugene_economics
82
+ financials = eugene_sec("AAPL", "financials")
83
+ yields = eugene_economics("treasury")
84
+ ```
85
+
86
+ **GitHub**: [Matthew-Anyiam/eugene-data-labs](https://github.com/Matthew-Anyiam/eugene-data-labs)
87
+ """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
 
89
  demo.launch()
eugene_server.py ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Eugene Intelligence — Unified Data Layer
3
+ 2 tools: eugene_sec + eugene_economics
4
+ Works as both FastAPI and MCP server.
5
+ """
6
+ import sys, os, logging
7
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
8
+ logging.basicConfig(level=logging.INFO)
9
+ logger = logging.getLogger('eugene')
10
+
11
+ # =============================================
12
+ # CORE: eugene_sec
13
+ # =============================================
14
+
15
+ SEC_EXTRACTS = [
16
+ 'prices', 'profile', 'financials', 'health', 'earnings',
17
+ 'insider', 'institutional', 'filings', 'estimates', 'news'
18
+ ]
19
+
20
+ def eugene_sec(ticker: str, extract: str = 'financials', filing_type: str = None, institution: str = None, limit: int = 10) -> dict:
21
+ """
22
+ All SEC and company data in one call.
23
+
24
+ Args:
25
+ ticker: Stock symbol (AAPL, NVDA, TSLA, JPM, etc.)
26
+ extract: What to get — prices | profile | financials | health | earnings | insider | institutional | filings | estimates | news
27
+ filing_type: For filings — 10-K, 10-Q, 8-K (optional)
28
+ institution: For institutional — BERKSHIRE, BLACKROCK, etc. (optional)
29
+ limit: Number of results (default 10)
30
+ """
31
+ ticker = ticker.upper().strip()
32
+ try:
33
+ if extract == 'prices':
34
+ from eugene.sources.fmp import get_stock_price
35
+ return get_stock_price(ticker)
36
+ elif extract == 'profile':
37
+ from eugene.sources.fmp import get_company_profile
38
+ return get_company_profile(ticker)
39
+ elif extract == 'financials':
40
+ from eugene.sources.xbrl import XBRLClient
41
+ return XBRLClient().get_financials(ticker).to_dict()
42
+ elif extract == 'health':
43
+ from eugene.agents.health import HealthMonitor
44
+ from eugene.config import Config
45
+ result = HealthMonitor(Config()).analyze(ticker)
46
+ return result.to_dict() if hasattr(result, 'to_dict') else result
47
+ elif extract == 'earnings':
48
+ from eugene.sources.fmp import get_earnings
49
+ return get_earnings(ticker)
50
+ elif extract == 'insider':
51
+ from eugene.sources.insider import get_insider_transactions
52
+ return get_insider_transactions(ticker)
53
+ elif extract == 'institutional':
54
+ if institution:
55
+ from eugene.sources.holdings_13f import get_whale_holdings
56
+ return get_whale_holdings(institution)
57
+ from eugene.sources.holdings_13f import get_whale_holdings
58
+ return get_whale_holdings(ticker)
59
+ elif extract == 'filings':
60
+ from eugene.sources.edgar import EDGARClient
61
+ from eugene.config import Config
62
+ client = EDGARClient(Config())
63
+ filings = client.get_filings(ticker, filing_type=filing_type, limit=limit)
64
+ return {'ticker': ticker, 'filings': [{'type': f.filing_type, 'date': f.filing_date, 'accession': f.accession_number, 'url': f.filing_url} for f in filings], 'source': 'SEC EDGAR'}
65
+ elif extract == 'estimates':
66
+ from eugene.sources.fmp import get_analyst_estimates
67
+ return get_analyst_estimates(ticker)
68
+ elif extract == 'news':
69
+ from eugene.sources.fmp import get_stock_news
70
+ return get_stock_news(ticker, limit)
71
+ else:
72
+ return {'error': f'Unknown extract: {extract}', 'valid': SEC_EXTRACTS}
73
+ except Exception as e:
74
+ logger.error(f'eugene_sec({ticker}, {extract}) failed: {e}')
75
+ return {'ticker': ticker, 'extract': extract, 'error': str(e)}
76
+
77
+
78
+ # =============================================
79
+ # CORE: eugene_economics
80
+ # =============================================
81
+
82
+ ECON_CATEGORIES = [
83
+ 'inflation', 'employment', 'gdp', 'housing', 'consumer',
84
+ 'manufacturing', 'rates', 'money', 'treasury', 'all'
85
+ ]
86
+
87
+ def eugene_economics(category: str = 'all', series: str = None) -> dict:
88
+ """
89
+ All economic and market data in one call.
90
+
91
+ Args:
92
+ category: inflation | employment | gdp | housing | consumer | manufacturing | rates | money | treasury | all
93
+ series: Specific FRED series ID (e.g. GDP, UNRATE, CPIAUCSL) — overrides category
94
+ """
95
+ try:
96
+ if series:
97
+ from eugene.sources.fred import get_economic_data
98
+ return get_economic_data(series)
99
+ if category == 'treasury':
100
+ from eugene.sources.fred import get_economic_data
101
+ yields = {}
102
+ for s in ['DGS1', 'DGS2', 'DGS5', 'DGS10', 'DGS30']:
103
+ data = get_economic_data(s)
104
+ if 'observations' in data and data['observations']:
105
+ yields[s] = {'rate': data['observations'][-1].get('value'), 'date': data['observations'][-1].get('date')}
106
+ return {'type': 'treasury_yields', 'yields': yields, 'source': 'FRED'}
107
+ from eugene.sources.fred import get_latest_indicators
108
+ return get_latest_indicators(category)
109
+ except Exception as e:
110
+ logger.error(f'eugene_economics({category}) failed: {e}')
111
+ return {'category': category, 'error': str(e)}
112
+
113
+
114
+ # =============================================
115
+ # FastAPI Server
116
+ # =============================================
117
+
118
+ def create_api():
119
+ from fastapi import FastAPI
120
+ from fastapi.middleware.cors import CORSMiddleware
121
+
122
+ app = FastAPI(
123
+ title='Eugene Intelligence API',
124
+ description='Financial context for AI agents. 2 endpoints. Every number traced to source.',
125
+ version='0.2.0',
126
+ docs_url='/',
127
+ )
128
+ app.add_middleware(CORSMiddleware, allow_origins=['*'], allow_methods=['*'], allow_headers=['*'])
129
+
130
+ @app.get('/v1/sec/{ticker}')
131
+ def sec_endpoint(ticker: str, extract: str = 'financials', filing_type: str = None, institution: str = None, limit: int = 10):
132
+ return eugene_sec(ticker, extract, filing_type, institution, limit)
133
+
134
+ @app.get('/v1/economics')
135
+ def economics_endpoint(category: str = 'all', series: str = None):
136
+ return eugene_economics(category, series)
137
+
138
+ @app.get('/v1/economics/{category}')
139
+ def economics_category_endpoint(category: str, series: str = None):
140
+ return eugene_economics(category, series)
141
+
142
+ @app.get('/health')
143
+ def health():
144
+ return {'status': 'ok', 'service': 'eugene-intelligence', 'version': '0.2.0', 'tools': ['eugene_sec', 'eugene_economics']}
145
+
146
+ return app
147
+
148
+
149
+ # =============================================
150
+ # MCP Server
151
+ # =============================================
152
+
153
+ def create_mcp():
154
+ from mcp.server.fastmcp import FastMCP
155
+
156
+ mcp = FastMCP(
157
+ name='eugene-intelligence',
158
+ instructions='Eugene Intelligence: Financial context for AI agents. 2 tools: eugene_sec (all company/SEC data) and eugene_economics (all FRED/macro data). Every number traced to source.',
159
+ )
160
+
161
+ @mcp.tool()
162
+ def sec(ticker: str, extract: str = 'financials', filing_type: str = None, institution: str = None, limit: int = 10) -> dict:
163
+ """All SEC and company data. extract: prices|profile|financials|health|earnings|insider|institutional|filings|estimates|news"""
164
+ return eugene_sec(ticker, extract, filing_type, institution, limit)
165
+
166
+ @mcp.tool()
167
+ def economics(category: str = 'all', series: str = None) -> dict:
168
+ """All economic data from FRED. category: inflation|employment|gdp|housing|consumer|manufacturing|rates|money|treasury|all"""
169
+ return eugene_economics(category, series)
170
+
171
+ return mcp
172
+
173
+
174
+ # =============================================
175
+ # Entry point
176
+ # =============================================
177
+
178
+ if __name__ == '__main__':
179
+ import argparse
180
+ parser = argparse.ArgumentParser(description='Eugene Intelligence')
181
+ parser.add_argument('--mode', choices=['api', 'mcp'], default='api', help='Run as API server or MCP server')
182
+ parser.add_argument('--port', type=int, default=8000)
183
+ args = parser.parse_args()
184
+
185
+ if args.mode == 'mcp':
186
+ mcp = create_mcp()
187
+ mcp.run()
188
+ else:
189
+ import uvicorn
190
+ app = create_api()
191
+ uvicorn.run(app, host='0.0.0.0', port=args.port)
requirements.txt CHANGED
@@ -1,4 +1,6 @@
1
- gradio==4.44.1
2
- huggingface_hub==0.25.0
3
  requests
4
- feedparser
 
 
 
 
1
+ gradio>=4.44.0
 
2
  requests
3
+ fredapi
4
+ mcp
5
+ fastapi
6
+ uvicorn