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 CHANGED
@@ -1,5 +1,5 @@
1
1
  """
2
- Sigma v3.3.0 - Finance Research Agent
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.0"
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.0 - Finance Research Agent."""
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.0"
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.0 | Native macOS[/dim]
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.0."""
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.0"
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.0[/dim] [bold cyan]σ[/bold cyan] [bold]Finance Research Agent[/bold]
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.0 - Finance Research Agent",
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.0."""
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.0"
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" # Built-in free key
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.0 - Setup Wizard."""
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.0"
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.0[/dim]
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": str(df.columns[0].date()) if hasattr(df.columns[0], 'date') else str(df.columns[0]),
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
- if recs is not None and not recs.empty:
149
+ rec_summary = {}
150
+ if isinstance(recs, pd.DataFrame) and not recs.empty:
143
151
  recent = recs.tail(10)
144
- rec_summary = recent["To Grade"].value_counts().to_dict() if "To Grade" in recent.columns else {}
145
- else:
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