sigma-terminal 3.3.0__py3-none-any.whl → 3.3.1__py3-none-any.whl
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.
- sigma/__init__.py +2 -2
- sigma/app.py +3 -3
- sigma/cli.py +5 -4
- sigma/config.py +4 -4
- sigma/setup.py +17 -3
- sigma/tools.py +431 -5
- sigma_terminal-3.3.1.dist-info/METADATA +444 -0
- {sigma_terminal-3.3.0.dist-info → sigma_terminal-3.3.1.dist-info}/RECORD +11 -11
- sigma_terminal-3.3.0.dist-info/METADATA +0 -583
- {sigma_terminal-3.3.0.dist-info → sigma_terminal-3.3.1.dist-info}/WHEEL +0 -0
- {sigma_terminal-3.3.0.dist-info → sigma_terminal-3.3.1.dist-info}/entry_points.txt +0 -0
- {sigma_terminal-3.3.0.dist-info → sigma_terminal-3.3.1.dist-info}/licenses/LICENSE +0 -0
sigma/__init__.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Sigma v3.3.
|
|
2
|
+
Sigma v3.3.1 - Finance Research Agent
|
|
3
3
|
|
|
4
4
|
An elite finance research agent combining:
|
|
5
5
|
- Multi-provider AI (Google Gemini, OpenAI, Anthropic, Groq, xAI, Ollama)
|
|
@@ -12,7 +12,7 @@ An elite finance research agent combining:
|
|
|
12
12
|
- Monitoring, alerts, and watchlists
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
__version__ = "3.3.
|
|
15
|
+
__version__ = "3.3.1"
|
|
16
16
|
__author__ = "Sigma Team"
|
|
17
17
|
|
|
18
18
|
# Core functionality
|
sigma/app.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Sigma v3.3.
|
|
1
|
+
"""Sigma v3.3.1 - Finance Research Agent."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import os
|
|
@@ -23,7 +23,7 @@ from .tools import TOOLS, execute_tool
|
|
|
23
23
|
from .backtest import run_backtest, get_available_strategies, BACKTEST_TOOL
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
__version__ = "3.3.
|
|
26
|
+
__version__ = "3.3.1"
|
|
27
27
|
SIGMA = "σ"
|
|
28
28
|
|
|
29
29
|
# Common stock tickers for recognition
|
|
@@ -60,7 +60,7 @@ WELCOME_BANNER = """
|
|
|
60
60
|
[bold blue]███████║██║╚██████╔╝██║ ╚═╝ ██║██║ ██║[/bold blue]
|
|
61
61
|
[bold blue]╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝[/bold blue]
|
|
62
62
|
|
|
63
|
-
[bold cyan]Finance Research Agent[/bold cyan] [dim]v3.3.
|
|
63
|
+
[bold cyan]Finance Research Agent[/bold cyan] [dim]v3.3.1 | Native macOS[/dim]
|
|
64
64
|
"""
|
|
65
65
|
|
|
66
66
|
SYSTEM_PROMPT = """You are Sigma, a Finance Research Agent. You provide comprehensive market analysis, trading strategies, and investment insights.
|
sigma/cli.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
"""CLI entry point for Sigma v3.3.
|
|
1
|
+
"""CLI entry point for Sigma v3.3.1."""
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
4
|
import json
|
|
5
5
|
import sys
|
|
6
|
+
import time
|
|
6
7
|
from typing import Optional
|
|
7
8
|
|
|
8
9
|
from rich.console import Console
|
|
@@ -16,7 +17,7 @@ from .config import (
|
|
|
16
17
|
)
|
|
17
18
|
|
|
18
19
|
|
|
19
|
-
__version__ = "3.3.
|
|
20
|
+
__version__ = "3.3.1"
|
|
20
21
|
|
|
21
22
|
console = Console()
|
|
22
23
|
|
|
@@ -31,7 +32,7 @@ def show_banner():
|
|
|
31
32
|
[bold white]███████║██║╚██████╔╝██║ ╚═╝ ██║██║ ██║[/bold white]
|
|
32
33
|
[bold white]╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝[/bold white]
|
|
33
34
|
|
|
34
|
-
[dim]v3.3.
|
|
35
|
+
[dim]v3.3.1[/dim] [bold cyan]σ[/bold cyan] [bold]Finance Research Agent[/bold]
|
|
35
36
|
"""
|
|
36
37
|
console.print(banner)
|
|
37
38
|
|
|
@@ -40,7 +41,7 @@ def main():
|
|
|
40
41
|
"""Main CLI entry point."""
|
|
41
42
|
parser = argparse.ArgumentParser(
|
|
42
43
|
prog="sigma",
|
|
43
|
-
description="Sigma v3.3.
|
|
44
|
+
description="Sigma v3.3.1 - Finance Research Agent",
|
|
44
45
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
45
46
|
epilog="""
|
|
46
47
|
Examples:
|
sigma/config.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Configuration management for Sigma v3.3.
|
|
1
|
+
"""Configuration management for Sigma v3.3.1."""
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
import shutil
|
|
@@ -11,7 +11,7 @@ from pydantic import Field
|
|
|
11
11
|
from pydantic_settings import BaseSettings
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
__version__ = "3.3.
|
|
14
|
+
__version__ = "3.3.1"
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class LLMProvider(str, Enum):
|
|
@@ -231,8 +231,8 @@ class Settings(BaseSettings):
|
|
|
231
231
|
lean_enabled: bool = Field(default=False, alias="LEAN_ENABLED")
|
|
232
232
|
|
|
233
233
|
# Data API keys
|
|
234
|
-
alpha_vantage_api_key: str = "6ER128DD3NQUPTVC"
|
|
235
|
-
exa_api_key: Optional[str] = None
|
|
234
|
+
alpha_vantage_api_key: str = Field(default="6ER128DD3NQUPTVC", alias="ALPHA_VANTAGE_API_KEY")
|
|
235
|
+
exa_api_key: Optional[str] = Field(default=None, alias="EXA_API_KEY")
|
|
236
236
|
|
|
237
237
|
class Config:
|
|
238
238
|
env_file = str(CONFIG_FILE)
|
sigma/setup.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Sigma v3.3.
|
|
1
|
+
"""Sigma v3.3.1 - Setup Wizard."""
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
import sys
|
|
@@ -25,7 +25,7 @@ from .config import (
|
|
|
25
25
|
)
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
__version__ = "3.3.
|
|
28
|
+
__version__ = "3.3.1"
|
|
29
29
|
SIGMA = "σ"
|
|
30
30
|
console = Console()
|
|
31
31
|
|
|
@@ -38,7 +38,7 @@ BANNER = """
|
|
|
38
38
|
[bold blue]███████║██║╚██████╔╝██║ ╚═╝ ██║██║ ██║[/bold blue]
|
|
39
39
|
[bold blue]╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝[/bold blue]
|
|
40
40
|
|
|
41
|
-
[bold cyan]σ Finance Research Agent[/bold cyan] [dim]- Setup Wizard v3.3.
|
|
41
|
+
[bold cyan]σ Finance Research Agent[/bold cyan] [dim]- Setup Wizard v3.3.1[/dim]
|
|
42
42
|
"""
|
|
43
43
|
|
|
44
44
|
|
|
@@ -342,6 +342,20 @@ class SetupWizard:
|
|
|
342
342
|
console.print(f"[cyan]{SIGMA}[/cyan] LEAN configured: {lean_path}")
|
|
343
343
|
else:
|
|
344
344
|
console.print("[dim]Skipping LEAN setup. You can configure it later.[/dim]")
|
|
345
|
+
|
|
346
|
+
console.print()
|
|
347
|
+
|
|
348
|
+
# Exa API Key (Optional)
|
|
349
|
+
console.print("[bold]Exa Search (Optional)[/bold]")
|
|
350
|
+
console.print("[dim]Enables financial news search, SEC filings, and earnings transcripts.[/dim]")
|
|
351
|
+
console.print()
|
|
352
|
+
|
|
353
|
+
if Confirm.ask("Configure Exa? (financial news search, SEC filings)", default=False):
|
|
354
|
+
console.print("[dim]Get key: https://exa.ai[/dim]")
|
|
355
|
+
exa_key = Prompt.ask("Exa API key", password=True, default="")
|
|
356
|
+
if exa_key:
|
|
357
|
+
save_setting("exa_api_key", exa_key)
|
|
358
|
+
console.print(f"[cyan]{SIGMA}[/cyan] Exa configured")
|
|
345
359
|
|
|
346
360
|
def _show_summary(self):
|
|
347
361
|
"""Show summary."""
|
sigma/tools.py
CHANGED
|
@@ -122,10 +122,17 @@ def get_financial_statements(symbol: str, statement: str = "income") -> dict:
|
|
|
122
122
|
else:
|
|
123
123
|
data[str(idx)] = f"${val:,.0f}"
|
|
124
124
|
|
|
125
|
+
# Get period from column
|
|
126
|
+
col = df.columns[0]
|
|
127
|
+
if hasattr(col, 'date'):
|
|
128
|
+
period_str = str(col.date()) # type: ignore[union-attr]
|
|
129
|
+
else:
|
|
130
|
+
period_str = str(col)
|
|
131
|
+
|
|
125
132
|
return {
|
|
126
133
|
"symbol": symbol.upper(),
|
|
127
134
|
"statement_type": statement,
|
|
128
|
-
"period":
|
|
135
|
+
"period": period_str,
|
|
129
136
|
"data": data
|
|
130
137
|
}
|
|
131
138
|
except Exception as e:
|
|
@@ -139,11 +146,11 @@ def get_analyst_recommendations(symbol: str) -> dict:
|
|
|
139
146
|
|
|
140
147
|
# Get recommendations
|
|
141
148
|
recs = ticker.recommendations
|
|
142
|
-
|
|
149
|
+
rec_summary = {}
|
|
150
|
+
if isinstance(recs, pd.DataFrame) and not recs.empty:
|
|
143
151
|
recent = recs.tail(10)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
rec_summary = {}
|
|
152
|
+
if "To Grade" in recent.columns:
|
|
153
|
+
rec_summary = recent["To Grade"].value_counts().to_dict()
|
|
147
154
|
|
|
148
155
|
# Get info for targets
|
|
149
156
|
info = ticker.info
|
|
@@ -431,6 +438,325 @@ def get_sector_performance() -> dict:
|
|
|
431
438
|
return {"sectors": results, "timestamp": datetime.now().isoformat()}
|
|
432
439
|
|
|
433
440
|
|
|
441
|
+
# ============================================================================
|
|
442
|
+
# ALPHA VANTAGE TOOLS (Economic Data, Intraday, News)
|
|
443
|
+
# ============================================================================
|
|
444
|
+
|
|
445
|
+
def _get_alpha_vantage_key() -> Optional[str]:
|
|
446
|
+
"""Get Alpha Vantage API key from config."""
|
|
447
|
+
try:
|
|
448
|
+
from .config import get_settings
|
|
449
|
+
return get_settings().alpha_vantage_api_key
|
|
450
|
+
except:
|
|
451
|
+
return None
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def get_economic_indicators(indicator: str = "GDP") -> dict:
|
|
455
|
+
"""Get economic indicators from Alpha Vantage (GDP, inflation, unemployment, etc.)."""
|
|
456
|
+
api_key = _get_alpha_vantage_key()
|
|
457
|
+
if not api_key:
|
|
458
|
+
return {"error": "Alpha Vantage API key not configured. Set ALPHA_VANTAGE_API_KEY in ~/.sigma/config.env"}
|
|
459
|
+
|
|
460
|
+
import requests
|
|
461
|
+
|
|
462
|
+
indicator_map = {
|
|
463
|
+
"GDP": "REAL_GDP",
|
|
464
|
+
"INFLATION": "INFLATION",
|
|
465
|
+
"UNEMPLOYMENT": "UNEMPLOYMENT",
|
|
466
|
+
"INTEREST_RATE": "FEDERAL_FUNDS_RATE",
|
|
467
|
+
"CPI": "CPI",
|
|
468
|
+
"RETAIL_SALES": "RETAIL_SALES",
|
|
469
|
+
"NONFARM_PAYROLL": "NONFARM_PAYROLL",
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
av_indicator = indicator_map.get(indicator.upper(), indicator.upper())
|
|
473
|
+
|
|
474
|
+
try:
|
|
475
|
+
url = f"https://www.alphavantage.co/query?function={av_indicator}&apikey={api_key}"
|
|
476
|
+
response = requests.get(url, timeout=10)
|
|
477
|
+
data = response.json()
|
|
478
|
+
|
|
479
|
+
if "Error Message" in data:
|
|
480
|
+
return {"error": data["Error Message"]}
|
|
481
|
+
|
|
482
|
+
if "data" in data:
|
|
483
|
+
# Return most recent data points
|
|
484
|
+
recent = data["data"][:12] # Last 12 periods
|
|
485
|
+
return {
|
|
486
|
+
"indicator": indicator.upper(),
|
|
487
|
+
"name": data.get("name", indicator),
|
|
488
|
+
"unit": data.get("unit", ""),
|
|
489
|
+
"data": [{"date": d["date"], "value": d["value"]} for d in recent]
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
return {"error": "No data returned", "raw": data}
|
|
493
|
+
except Exception as e:
|
|
494
|
+
return {"error": str(e)}
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
def get_intraday_data(symbol: str, interval: str = "5min") -> dict:
|
|
498
|
+
"""Get intraday price data from Alpha Vantage."""
|
|
499
|
+
api_key = _get_alpha_vantage_key()
|
|
500
|
+
if not api_key:
|
|
501
|
+
return {"error": "Alpha Vantage API key not configured. Set ALPHA_VANTAGE_API_KEY in ~/.sigma/config.env"}
|
|
502
|
+
|
|
503
|
+
import requests
|
|
504
|
+
|
|
505
|
+
valid_intervals = ["1min", "5min", "15min", "30min", "60min"]
|
|
506
|
+
if interval not in valid_intervals:
|
|
507
|
+
return {"error": f"Invalid interval. Use: {valid_intervals}"}
|
|
508
|
+
|
|
509
|
+
try:
|
|
510
|
+
url = f"https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol={symbol}&interval={interval}&apikey={api_key}"
|
|
511
|
+
response = requests.get(url, timeout=10)
|
|
512
|
+
data = response.json()
|
|
513
|
+
|
|
514
|
+
if "Error Message" in data:
|
|
515
|
+
return {"error": data["Error Message"]}
|
|
516
|
+
|
|
517
|
+
time_series_key = f"Time Series ({interval})"
|
|
518
|
+
if time_series_key not in data:
|
|
519
|
+
return {"error": "No data returned. Check symbol or API limits.", "raw": data}
|
|
520
|
+
|
|
521
|
+
# Get last 20 candles
|
|
522
|
+
series = data[time_series_key]
|
|
523
|
+
candles = []
|
|
524
|
+
for timestamp, values in list(series.items())[:20]:
|
|
525
|
+
candles.append({
|
|
526
|
+
"timestamp": timestamp,
|
|
527
|
+
"open": float(values["1. open"]),
|
|
528
|
+
"high": float(values["2. high"]),
|
|
529
|
+
"low": float(values["3. low"]),
|
|
530
|
+
"close": float(values["4. close"]),
|
|
531
|
+
"volume": int(values["5. volume"])
|
|
532
|
+
})
|
|
533
|
+
|
|
534
|
+
return {
|
|
535
|
+
"symbol": symbol.upper(),
|
|
536
|
+
"interval": interval,
|
|
537
|
+
"candles": candles
|
|
538
|
+
}
|
|
539
|
+
except Exception as e:
|
|
540
|
+
return {"error": str(e)}
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
def get_market_news(tickers: str = "", topics: str = "") -> dict:
|
|
544
|
+
"""Get market news and sentiment from Alpha Vantage."""
|
|
545
|
+
api_key = _get_alpha_vantage_key()
|
|
546
|
+
if not api_key:
|
|
547
|
+
return {"error": "Alpha Vantage API key not configured. Set ALPHA_VANTAGE_API_KEY in ~/.sigma/config.env"}
|
|
548
|
+
|
|
549
|
+
import requests
|
|
550
|
+
|
|
551
|
+
try:
|
|
552
|
+
url = f"https://www.alphavantage.co/query?function=NEWS_SENTIMENT&apikey={api_key}"
|
|
553
|
+
if tickers:
|
|
554
|
+
url += f"&tickers={tickers}"
|
|
555
|
+
if topics:
|
|
556
|
+
url += f"&topics={topics}"
|
|
557
|
+
|
|
558
|
+
response = requests.get(url, timeout=15)
|
|
559
|
+
data = response.json()
|
|
560
|
+
|
|
561
|
+
if "Error Message" in data:
|
|
562
|
+
return {"error": data["Error Message"]}
|
|
563
|
+
|
|
564
|
+
feed = data.get("feed", [])[:10] # Get top 10 news items
|
|
565
|
+
|
|
566
|
+
articles = []
|
|
567
|
+
for item in feed:
|
|
568
|
+
articles.append({
|
|
569
|
+
"title": item.get("title", ""),
|
|
570
|
+
"source": item.get("source", ""),
|
|
571
|
+
"time": item.get("time_published", ""),
|
|
572
|
+
"summary": item.get("summary", "")[:300] + "..." if item.get("summary") else "",
|
|
573
|
+
"sentiment": item.get("overall_sentiment_label", ""),
|
|
574
|
+
"sentiment_score": item.get("overall_sentiment_score", 0),
|
|
575
|
+
"tickers": [t["ticker"] for t in item.get("ticker_sentiment", [])[:3]]
|
|
576
|
+
})
|
|
577
|
+
|
|
578
|
+
return {
|
|
579
|
+
"articles": articles,
|
|
580
|
+
"query": {"tickers": tickers, "topics": topics}
|
|
581
|
+
}
|
|
582
|
+
except Exception as e:
|
|
583
|
+
return {"error": str(e)}
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
# ============================================================================
|
|
587
|
+
# EXA SEARCH TOOLS (Financial News, SEC Filings)
|
|
588
|
+
# ============================================================================
|
|
589
|
+
|
|
590
|
+
def _get_exa_key() -> Optional[str]:
|
|
591
|
+
"""Get Exa API key from config."""
|
|
592
|
+
try:
|
|
593
|
+
from .config import get_settings
|
|
594
|
+
return get_settings().exa_api_key
|
|
595
|
+
except:
|
|
596
|
+
return None
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
def search_financial_news(query: str, num_results: int = 5) -> dict:
|
|
600
|
+
"""Search for financial news using Exa."""
|
|
601
|
+
api_key = _get_exa_key()
|
|
602
|
+
if not api_key:
|
|
603
|
+
return {"error": "Exa API key not configured. Set EXA_API_KEY in ~/.sigma/config.env"}
|
|
604
|
+
|
|
605
|
+
import requests
|
|
606
|
+
|
|
607
|
+
try:
|
|
608
|
+
headers = {
|
|
609
|
+
"x-api-key": api_key,
|
|
610
|
+
"Content-Type": "application/json"
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
payload = {
|
|
614
|
+
"query": query,
|
|
615
|
+
"num_results": num_results,
|
|
616
|
+
"use_autoprompt": True,
|
|
617
|
+
"type": "neural",
|
|
618
|
+
"include_domains": [
|
|
619
|
+
"reuters.com", "bloomberg.com", "wsj.com", "cnbc.com",
|
|
620
|
+
"marketwatch.com", "ft.com", "seekingalpha.com", "yahoo.com/finance"
|
|
621
|
+
]
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
response = requests.post(
|
|
625
|
+
"https://api.exa.ai/search",
|
|
626
|
+
headers=headers,
|
|
627
|
+
json=payload,
|
|
628
|
+
timeout=15
|
|
629
|
+
)
|
|
630
|
+
|
|
631
|
+
if response.status_code != 200:
|
|
632
|
+
return {"error": f"Exa API error: {response.status_code}", "details": response.text}
|
|
633
|
+
|
|
634
|
+
data = response.json()
|
|
635
|
+
|
|
636
|
+
results = []
|
|
637
|
+
for item in data.get("results", []):
|
|
638
|
+
results.append({
|
|
639
|
+
"title": item.get("title", ""),
|
|
640
|
+
"url": item.get("url", ""),
|
|
641
|
+
"published": item.get("publishedDate", ""),
|
|
642
|
+
"score": item.get("score", 0)
|
|
643
|
+
})
|
|
644
|
+
|
|
645
|
+
return {
|
|
646
|
+
"query": query,
|
|
647
|
+
"results": results
|
|
648
|
+
}
|
|
649
|
+
except Exception as e:
|
|
650
|
+
return {"error": str(e)}
|
|
651
|
+
|
|
652
|
+
|
|
653
|
+
def search_sec_filings(company: str, filing_type: str = "10-K", num_results: int = 3) -> dict:
|
|
654
|
+
"""Search for SEC filings using Exa."""
|
|
655
|
+
api_key = _get_exa_key()
|
|
656
|
+
if not api_key:
|
|
657
|
+
return {"error": "Exa API key not configured. Set EXA_API_KEY in ~/.sigma/config.env"}
|
|
658
|
+
|
|
659
|
+
import requests
|
|
660
|
+
|
|
661
|
+
try:
|
|
662
|
+
headers = {
|
|
663
|
+
"x-api-key": api_key,
|
|
664
|
+
"Content-Type": "application/json"
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
query = f"{company} {filing_type} SEC filing site:sec.gov"
|
|
668
|
+
|
|
669
|
+
payload = {
|
|
670
|
+
"query": query,
|
|
671
|
+
"num_results": num_results,
|
|
672
|
+
"use_autoprompt": True,
|
|
673
|
+
"type": "neural",
|
|
674
|
+
"include_domains": ["sec.gov"]
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
response = requests.post(
|
|
678
|
+
"https://api.exa.ai/search",
|
|
679
|
+
headers=headers,
|
|
680
|
+
json=payload,
|
|
681
|
+
timeout=15
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
if response.status_code != 200:
|
|
685
|
+
return {"error": f"Exa API error: {response.status_code}"}
|
|
686
|
+
|
|
687
|
+
data = response.json()
|
|
688
|
+
|
|
689
|
+
results = []
|
|
690
|
+
for item in data.get("results", []):
|
|
691
|
+
results.append({
|
|
692
|
+
"title": item.get("title", ""),
|
|
693
|
+
"url": item.get("url", ""),
|
|
694
|
+
"published": item.get("publishedDate", "")
|
|
695
|
+
})
|
|
696
|
+
|
|
697
|
+
return {
|
|
698
|
+
"company": company,
|
|
699
|
+
"filing_type": filing_type,
|
|
700
|
+
"results": results
|
|
701
|
+
}
|
|
702
|
+
except Exception as e:
|
|
703
|
+
return {"error": str(e)}
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
def search_earnings_transcripts(company: str, num_results: int = 3) -> dict:
|
|
707
|
+
"""Search for earnings call transcripts using Exa."""
|
|
708
|
+
api_key = _get_exa_key()
|
|
709
|
+
if not api_key:
|
|
710
|
+
return {"error": "Exa API key not configured. Set EXA_API_KEY in ~/.sigma/config.env"}
|
|
711
|
+
|
|
712
|
+
import requests
|
|
713
|
+
|
|
714
|
+
try:
|
|
715
|
+
headers = {
|
|
716
|
+
"x-api-key": api_key,
|
|
717
|
+
"Content-Type": "application/json"
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
query = f"{company} earnings call transcript Q4 2025"
|
|
721
|
+
|
|
722
|
+
payload = {
|
|
723
|
+
"query": query,
|
|
724
|
+
"num_results": num_results,
|
|
725
|
+
"use_autoprompt": True,
|
|
726
|
+
"type": "neural",
|
|
727
|
+
"include_domains": [
|
|
728
|
+
"seekingalpha.com", "fool.com", "reuters.com"
|
|
729
|
+
]
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
response = requests.post(
|
|
733
|
+
"https://api.exa.ai/search",
|
|
734
|
+
headers=headers,
|
|
735
|
+
json=payload,
|
|
736
|
+
timeout=15
|
|
737
|
+
)
|
|
738
|
+
|
|
739
|
+
if response.status_code != 200:
|
|
740
|
+
return {"error": f"Exa API error: {response.status_code}"}
|
|
741
|
+
|
|
742
|
+
data = response.json()
|
|
743
|
+
|
|
744
|
+
results = []
|
|
745
|
+
for item in data.get("results", []):
|
|
746
|
+
results.append({
|
|
747
|
+
"title": item.get("title", ""),
|
|
748
|
+
"url": item.get("url", ""),
|
|
749
|
+
"published": item.get("publishedDate", "")
|
|
750
|
+
})
|
|
751
|
+
|
|
752
|
+
return {
|
|
753
|
+
"company": company,
|
|
754
|
+
"results": results
|
|
755
|
+
}
|
|
756
|
+
except Exception as e:
|
|
757
|
+
return {"error": str(e)}
|
|
758
|
+
|
|
759
|
+
|
|
434
760
|
# ============================================================================
|
|
435
761
|
# TOOL DEFINITIONS FOR LLM
|
|
436
762
|
# ============================================================================
|
|
@@ -590,6 +916,98 @@ TOOLS = [
|
|
|
590
916
|
}
|
|
591
917
|
}
|
|
592
918
|
},
|
|
919
|
+
# Alpha Vantage tools
|
|
920
|
+
{
|
|
921
|
+
"type": "function",
|
|
922
|
+
"function": {
|
|
923
|
+
"name": "get_economic_indicators",
|
|
924
|
+
"description": "Get economic indicators like GDP, inflation, unemployment, interest rates, CPI",
|
|
925
|
+
"parameters": {
|
|
926
|
+
"type": "object",
|
|
927
|
+
"properties": {
|
|
928
|
+
"indicator": {"type": "string", "enum": ["GDP", "INFLATION", "UNEMPLOYMENT", "INTEREST_RATE", "CPI", "RETAIL_SALES", "NONFARM_PAYROLL"], "description": "Economic indicator to retrieve"}
|
|
929
|
+
},
|
|
930
|
+
"required": ["indicator"]
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
},
|
|
934
|
+
{
|
|
935
|
+
"type": "function",
|
|
936
|
+
"function": {
|
|
937
|
+
"name": "get_intraday_data",
|
|
938
|
+
"description": "Get intraday price data with 1min, 5min, 15min, 30min, or 60min candles",
|
|
939
|
+
"parameters": {
|
|
940
|
+
"type": "object",
|
|
941
|
+
"properties": {
|
|
942
|
+
"symbol": {"type": "string", "description": "Stock ticker symbol"},
|
|
943
|
+
"interval": {"type": "string", "enum": ["1min", "5min", "15min", "30min", "60min"], "description": "Candle interval", "default": "5min"}
|
|
944
|
+
},
|
|
945
|
+
"required": ["symbol"]
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
},
|
|
949
|
+
{
|
|
950
|
+
"type": "function",
|
|
951
|
+
"function": {
|
|
952
|
+
"name": "get_market_news",
|
|
953
|
+
"description": "Get market news and sentiment for specific tickers or topics",
|
|
954
|
+
"parameters": {
|
|
955
|
+
"type": "object",
|
|
956
|
+
"properties": {
|
|
957
|
+
"tickers": {"type": "string", "description": "Comma-separated ticker symbols (e.g., AAPL,MSFT)"},
|
|
958
|
+
"topics": {"type": "string", "description": "Topics like: earnings, ipo, mergers, technology, finance"}
|
|
959
|
+
},
|
|
960
|
+
"required": []
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
},
|
|
964
|
+
# Exa Search tools
|
|
965
|
+
{
|
|
966
|
+
"type": "function",
|
|
967
|
+
"function": {
|
|
968
|
+
"name": "search_financial_news",
|
|
969
|
+
"description": "Search for financial news articles from major sources (Bloomberg, Reuters, WSJ, etc.)",
|
|
970
|
+
"parameters": {
|
|
971
|
+
"type": "object",
|
|
972
|
+
"properties": {
|
|
973
|
+
"query": {"type": "string", "description": "Search query for financial news"},
|
|
974
|
+
"num_results": {"type": "integer", "description": "Number of results (1-10)", "default": 5}
|
|
975
|
+
},
|
|
976
|
+
"required": ["query"]
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
},
|
|
980
|
+
{
|
|
981
|
+
"type": "function",
|
|
982
|
+
"function": {
|
|
983
|
+
"name": "search_sec_filings",
|
|
984
|
+
"description": "Search for SEC filings (10-K, 10-Q, 8-K, etc.) for a company",
|
|
985
|
+
"parameters": {
|
|
986
|
+
"type": "object",
|
|
987
|
+
"properties": {
|
|
988
|
+
"company": {"type": "string", "description": "Company name or ticker"},
|
|
989
|
+
"filing_type": {"type": "string", "enum": ["10-K", "10-Q", "8-K", "S-1", "DEF 14A"], "description": "Type of SEC filing", "default": "10-K"},
|
|
990
|
+
"num_results": {"type": "integer", "description": "Number of results", "default": 3}
|
|
991
|
+
},
|
|
992
|
+
"required": ["company"]
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
},
|
|
996
|
+
{
|
|
997
|
+
"type": "function",
|
|
998
|
+
"function": {
|
|
999
|
+
"name": "search_earnings_transcripts",
|
|
1000
|
+
"description": "Search for earnings call transcripts",
|
|
1001
|
+
"parameters": {
|
|
1002
|
+
"type": "object",
|
|
1003
|
+
"properties": {
|
|
1004
|
+
"company": {"type": "string", "description": "Company name or ticker"},
|
|
1005
|
+
"num_results": {"type": "integer", "description": "Number of results", "default": 3}
|
|
1006
|
+
},
|
|
1007
|
+
"required": ["company"]
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
},
|
|
593
1011
|
]
|
|
594
1012
|
|
|
595
1013
|
|
|
@@ -606,6 +1024,14 @@ TOOL_FUNCTIONS = {
|
|
|
606
1024
|
"compare_stocks": compare_stocks,
|
|
607
1025
|
"get_market_overview": get_market_overview,
|
|
608
1026
|
"get_sector_performance": get_sector_performance,
|
|
1027
|
+
# Alpha Vantage
|
|
1028
|
+
"get_economic_indicators": get_economic_indicators,
|
|
1029
|
+
"get_intraday_data": get_intraday_data,
|
|
1030
|
+
"get_market_news": get_market_news,
|
|
1031
|
+
# Exa Search
|
|
1032
|
+
"search_financial_news": search_financial_news,
|
|
1033
|
+
"search_sec_filings": search_sec_filings,
|
|
1034
|
+
"search_earnings_transcripts": search_earnings_transcripts,
|
|
609
1035
|
}
|
|
610
1036
|
|
|
611
1037
|
|