sigma-terminal 2.0.2__py3-none-any.whl → 3.3.0__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 +182 -6
- sigma/__main__.py +2 -2
- sigma/analytics/__init__.py +636 -0
- sigma/app.py +801 -892
- sigma/backtest.py +372 -0
- sigma/charts.py +407 -0
- sigma/cli.py +465 -0
- sigma/comparison.py +611 -0
- sigma/config.py +366 -0
- sigma/core/__init__.py +4 -17
- sigma/core/engine.py +493 -0
- sigma/core/intent.py +595 -0
- sigma/core/models.py +516 -125
- sigma/data/__init__.py +681 -0
- sigma/data/models.py +130 -0
- sigma/llm.py +639 -0
- sigma/monitoring.py +666 -0
- sigma/portfolio.py +697 -0
- sigma/reporting.py +658 -0
- sigma/robustness.py +675 -0
- sigma/setup.py +374 -403
- sigma/strategy.py +753 -0
- sigma/tools/backtest.py +23 -5
- sigma/tools.py +617 -0
- sigma/visualization.py +766 -0
- sigma_terminal-3.3.0.dist-info/METADATA +583 -0
- sigma_terminal-3.3.0.dist-info/RECORD +30 -0
- sigma_terminal-3.3.0.dist-info/entry_points.txt +6 -0
- sigma_terminal-3.3.0.dist-info/licenses/LICENSE +25 -0
- sigma/core/agent.py +0 -205
- sigma/core/config.py +0 -119
- sigma/core/llm.py +0 -794
- sigma/tools/__init__.py +0 -5
- sigma/tools/charts.py +0 -400
- sigma/tools/financial.py +0 -1457
- sigma/ui/__init__.py +0 -1
- sigma_terminal-2.0.2.dist-info/METADATA +0 -222
- sigma_terminal-2.0.2.dist-info/RECORD +0 -19
- sigma_terminal-2.0.2.dist-info/entry_points.txt +0 -2
- sigma_terminal-2.0.2.dist-info/licenses/LICENSE +0 -42
- {sigma_terminal-2.0.2.dist-info → sigma_terminal-3.3.0.dist-info}/WHEEL +0 -0
sigma/setup.py
CHANGED
|
@@ -1,455 +1,426 @@
|
|
|
1
|
-
"""Sigma
|
|
1
|
+
"""Sigma v3.3.0 - Setup Wizard."""
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
import sys
|
|
5
|
-
import
|
|
5
|
+
import subprocess
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from typing import Optional
|
|
8
8
|
|
|
9
9
|
from rich.console import Console
|
|
10
10
|
from rich.panel import Panel
|
|
11
11
|
from rich.prompt import Prompt, Confirm
|
|
12
|
-
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
13
12
|
from rich.table import Table
|
|
14
13
|
from rich.text import Text
|
|
15
|
-
from rich import box
|
|
16
14
|
|
|
15
|
+
from .config import (
|
|
16
|
+
get_settings,
|
|
17
|
+
save_api_key,
|
|
18
|
+
save_setting,
|
|
19
|
+
LLMProvider,
|
|
20
|
+
AVAILABLE_MODELS,
|
|
21
|
+
CONFIG_DIR,
|
|
22
|
+
detect_lean_installation,
|
|
23
|
+
detect_ollama,
|
|
24
|
+
install_lean_cli_sync,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
__version__ = "3.3.0"
|
|
29
|
+
SIGMA = "σ"
|
|
17
30
|
console = Console()
|
|
18
31
|
|
|
19
|
-
#
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
32
|
+
# Clean banner - just SIGMA, no SETU
|
|
33
|
+
BANNER = """
|
|
34
|
+
[bold blue]███████╗██╗ ██████╗ ███╗ ███╗ █████╗ [/bold blue]
|
|
35
|
+
[bold blue]██╔════╝██║██╔════╝ ████╗ ████║██╔══██╗[/bold blue]
|
|
36
|
+
[bold blue]███████╗██║██║ ███╗██╔████╔██║███████║[/bold blue]
|
|
37
|
+
[bold blue]╚════██║██║██║ ██║██║╚██╔╝██║██╔══██║[/bold blue]
|
|
38
|
+
[bold blue]███████║██║╚██████╔╝██║ ╚═╝ ██║██║ ██║[/bold blue]
|
|
39
|
+
[bold blue]╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝[/bold blue]
|
|
23
40
|
|
|
24
|
-
|
|
25
|
-
def is_setup_complete() -> bool:
|
|
26
|
-
"""Check if setup has been completed."""
|
|
27
|
-
return SETUP_COMPLETE_FILE.exists()
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def clear_screen():
|
|
31
|
-
"""Clear the terminal screen."""
|
|
32
|
-
os.system('cls' if os.name == 'nt' else 'clear')
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def print_logo():
|
|
36
|
-
"""Print the Sigma logo."""
|
|
37
|
-
logo = """
|
|
38
|
-
[bold bright_cyan]
|
|
39
|
-
███████╗██╗ ██████╗ ███╗ ███╗ █████╗
|
|
40
|
-
██╔════╝██║██╔════╝ ████╗ ████║██╔══██╗
|
|
41
|
-
███████╗██║██║ ███╗██╔████╔██║███████║
|
|
42
|
-
╚════██║██║██║ ██║██║╚██╔╝██║██╔══██║
|
|
43
|
-
███████║██║╚██████╔╝██║ ╚═╝ ██║██║ ██║
|
|
44
|
-
╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝
|
|
45
|
-
[/bold bright_cyan]
|
|
41
|
+
[bold cyan]σ Finance Research Agent[/bold cyan] [dim]- Setup Wizard v3.3.0[/dim]
|
|
46
42
|
"""
|
|
47
|
-
console.print(logo)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def animate_text(text: str, delay: float = 0.02):
|
|
51
|
-
"""Animate text character by character."""
|
|
52
|
-
for char in text:
|
|
53
|
-
console.print(char, end="", highlight=False)
|
|
54
|
-
time.sleep(delay)
|
|
55
|
-
console.print()
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def show_welcome():
|
|
59
|
-
"""Show the welcome screen."""
|
|
60
|
-
clear_screen()
|
|
61
|
-
print_logo()
|
|
62
|
-
|
|
63
|
-
console.print()
|
|
64
|
-
console.print(Panel(
|
|
65
|
-
"[bold]Welcome to Sigma[/bold]\n\n"
|
|
66
|
-
"[dim]The Institutional-Grade Financial Research Agent[/dim]\n\n"
|
|
67
|
-
"This setup wizard will help you configure Sigma for\n"
|
|
68
|
-
"optimal performance. It only takes about 2 minutes.",
|
|
69
|
-
title="[bold bright_green]Setup Wizard[/bold bright_green]",
|
|
70
|
-
border_style="bright_green",
|
|
71
|
-
padding=(1, 2)
|
|
72
|
-
))
|
|
73
|
-
console.print()
|
|
74
|
-
|
|
75
|
-
Prompt.ask("[dim]Press Enter to begin[/dim]", default="")
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def show_progress_step(step: int, total: int, title: str):
|
|
79
|
-
"""Show progress header."""
|
|
80
|
-
console.print()
|
|
81
|
-
console.print(f"[bold bright_cyan]Step {step}/{total}:[/bold bright_cyan] {title}")
|
|
82
|
-
console.print("[dim]" + "─" * 50 + "[/dim]")
|
|
83
|
-
console.print()
|
|
84
43
|
|
|
85
44
|
|
|
86
|
-
|
|
87
|
-
"""
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
45
|
+
class SetupWizard:
|
|
46
|
+
"""Interactive setup wizard."""
|
|
47
|
+
|
|
48
|
+
def __init__(self):
|
|
49
|
+
self.settings = get_settings()
|
|
50
|
+
self.providers = {
|
|
51
|
+
LLMProvider.GOOGLE: {
|
|
52
|
+
"name": "Google Gemini",
|
|
53
|
+
"models": AVAILABLE_MODELS.get("google", []),
|
|
54
|
+
"url": "https://aistudio.google.com/apikey",
|
|
55
|
+
"free": True,
|
|
56
|
+
"desc": "Fast, capable, free tier",
|
|
57
|
+
"recommended": True,
|
|
58
|
+
},
|
|
59
|
+
LLMProvider.OPENAI: {
|
|
60
|
+
"name": "OpenAI GPT",
|
|
61
|
+
"models": AVAILABLE_MODELS.get("openai", []),
|
|
62
|
+
"url": "https://platform.openai.com/api-keys",
|
|
63
|
+
"free": False,
|
|
64
|
+
"desc": "Industry standard",
|
|
65
|
+
},
|
|
66
|
+
LLMProvider.ANTHROPIC: {
|
|
67
|
+
"name": "Anthropic Claude",
|
|
68
|
+
"models": AVAILABLE_MODELS.get("anthropic", []),
|
|
69
|
+
"url": "https://console.anthropic.com/",
|
|
70
|
+
"free": False,
|
|
71
|
+
"desc": "Advanced reasoning",
|
|
72
|
+
},
|
|
73
|
+
LLMProvider.GROQ: {
|
|
74
|
+
"name": "Groq (Llama)",
|
|
75
|
+
"models": AVAILABLE_MODELS.get("groq", []),
|
|
76
|
+
"url": "https://console.groq.com/keys",
|
|
77
|
+
"free": True,
|
|
78
|
+
"desc": "Ultra-fast, free tier",
|
|
79
|
+
"recommended": True,
|
|
80
|
+
},
|
|
81
|
+
LLMProvider.XAI: {
|
|
82
|
+
"name": "xAI Grok",
|
|
83
|
+
"models": AVAILABLE_MODELS.get("xai", []),
|
|
84
|
+
"url": "https://console.x.ai/",
|
|
85
|
+
"free": False,
|
|
86
|
+
"desc": "X.com AI",
|
|
87
|
+
},
|
|
88
|
+
LLMProvider.OLLAMA: {
|
|
89
|
+
"name": "Ollama (Local)",
|
|
90
|
+
"models": AVAILABLE_MODELS.get("ollama", []),
|
|
91
|
+
"url": "https://ollama.ai/download",
|
|
92
|
+
"free": True,
|
|
93
|
+
"desc": "Run locally, no API key",
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
def run(self) -> bool:
|
|
98
|
+
"""Run setup wizard."""
|
|
99
|
+
console.clear()
|
|
100
|
+
console.print(BANNER)
|
|
127
101
|
console.print()
|
|
102
|
+
|
|
128
103
|
console.print(Panel(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
f"
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
104
|
+
"[bold]Welcome to Sigma Setup[/bold]\n\n"
|
|
105
|
+
"This wizard will configure Sigma for first use.\n"
|
|
106
|
+
f"Configuration stored in [cyan]~/.sigma/[/cyan]\n\n"
|
|
107
|
+
"[bold]Steps:[/bold]\n"
|
|
108
|
+
" 1. Choose AI provider\n"
|
|
109
|
+
" 2. Configure API key\n"
|
|
110
|
+
" 3. Select model\n"
|
|
111
|
+
" 4. Data settings\n"
|
|
112
|
+
" 5. Optional: Ollama, LEAN",
|
|
113
|
+
title=f"[cyan]{SIGMA} Setup[/cyan]",
|
|
114
|
+
border_style="blue",
|
|
137
115
|
))
|
|
138
116
|
console.print()
|
|
139
117
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
118
|
+
if not Confirm.ask("Ready to begin?", default=True):
|
|
119
|
+
console.print("[dim]Cancelled.[/dim]")
|
|
120
|
+
return False
|
|
121
|
+
|
|
122
|
+
console.print()
|
|
123
|
+
|
|
124
|
+
# Steps
|
|
125
|
+
self._setup_provider()
|
|
126
|
+
console.print()
|
|
127
|
+
self._setup_api_key()
|
|
128
|
+
console.print()
|
|
129
|
+
self._setup_model()
|
|
130
|
+
console.print()
|
|
131
|
+
self._setup_data()
|
|
132
|
+
console.print()
|
|
133
|
+
self._setup_integrations()
|
|
134
|
+
console.print()
|
|
135
|
+
self._show_summary()
|
|
136
|
+
|
|
137
|
+
return True
|
|
138
|
+
|
|
139
|
+
def _setup_provider(self):
|
|
140
|
+
"""Choose AI provider."""
|
|
141
|
+
console.print(Panel("[bold]Step 1: AI Provider[/bold]", border_style="blue"))
|
|
142
|
+
console.print()
|
|
143
|
+
|
|
144
|
+
providers = list(self.providers.keys())
|
|
145
|
+
|
|
146
|
+
for i, p in enumerate(providers, 1):
|
|
147
|
+
info = self.providers[p]
|
|
148
|
+
name = info["name"]
|
|
149
|
+
desc = info["desc"]
|
|
150
|
+
free = "[green]free[/green]" if info.get("free") else "[yellow]paid[/yellow]"
|
|
151
|
+
rec = " [cyan](recommended)[/cyan]" if info.get("recommended") else ""
|
|
152
|
+
console.print(f" {i}. [bold]{name}[/bold] - {desc} {free}{rec}")
|
|
153
|
+
|
|
154
|
+
console.print()
|
|
155
|
+
choice = Prompt.ask(
|
|
156
|
+
"Choose provider",
|
|
157
|
+
choices=[str(i) for i in range(1, len(providers) + 1)],
|
|
158
|
+
default="1"
|
|
143
159
|
)
|
|
160
|
+
|
|
161
|
+
provider = providers[int(choice) - 1]
|
|
162
|
+
save_setting("default_provider", provider.value)
|
|
163
|
+
self.settings.default_provider = provider
|
|
164
|
+
|
|
165
|
+
console.print(f"[cyan]{SIGMA}[/cyan] Provider: [bold]{self.providers[provider]['name']}[/bold]")
|
|
166
|
+
|
|
167
|
+
def _setup_api_key(self):
|
|
168
|
+
"""Configure API key."""
|
|
169
|
+
console.print(Panel("[bold]Step 2: API Key[/bold]", border_style="blue"))
|
|
170
|
+
console.print()
|
|
171
|
+
|
|
172
|
+
provider = self.settings.default_provider
|
|
173
|
+
info = self.providers[provider]
|
|
174
|
+
|
|
175
|
+
if provider == LLMProvider.OLLAMA:
|
|
176
|
+
console.print("[dim]Ollama runs locally - no API key needed.[/dim]")
|
|
177
|
+
if Confirm.ask("Is Ollama installed?", default=True):
|
|
178
|
+
console.print(f"[cyan]{SIGMA}[/cyan] Ollama configured")
|
|
179
|
+
else:
|
|
180
|
+
console.print(f"Install from: [bold]{info['url']}[/bold]")
|
|
181
|
+
return
|
|
182
|
+
|
|
183
|
+
# Check existing
|
|
184
|
+
key_attr = f"{provider.value}_api_key"
|
|
185
|
+
existing = getattr(self.settings, key_attr, None)
|
|
186
|
+
|
|
187
|
+
if existing:
|
|
188
|
+
masked = f"{existing[:8]}...{existing[-4:]}"
|
|
189
|
+
console.print(f"[dim]Existing key: {masked}[/dim]")
|
|
190
|
+
if not Confirm.ask("Replace?", default=False):
|
|
191
|
+
console.print(f"[cyan]{SIGMA}[/cyan] Keeping existing key")
|
|
192
|
+
return
|
|
193
|
+
|
|
194
|
+
console.print(f"Get key from: [bold]{info['url']}[/bold]")
|
|
195
|
+
console.print()
|
|
196
|
+
|
|
197
|
+
api_key = Prompt.ask("API key", password=True)
|
|
198
|
+
|
|
144
199
|
if api_key:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
200
|
+
save_api_key(provider, api_key)
|
|
201
|
+
setattr(self.settings, key_attr, api_key)
|
|
202
|
+
console.print(f"[cyan]{SIGMA}[/cyan] Key saved for {info['name']}")
|
|
203
|
+
else:
|
|
204
|
+
console.print("[yellow]Skipped[/yellow]")
|
|
205
|
+
|
|
206
|
+
def _setup_model(self):
|
|
207
|
+
"""Select model."""
|
|
208
|
+
console.print(Panel("[bold]Step 3: Model[/bold]", border_style="blue"))
|
|
209
|
+
console.print()
|
|
210
|
+
|
|
211
|
+
provider = self.settings.default_provider
|
|
212
|
+
models = self.providers[provider]["models"]
|
|
213
|
+
|
|
214
|
+
if not models:
|
|
215
|
+
console.print("[yellow]No models for this provider[/yellow]")
|
|
216
|
+
return
|
|
217
|
+
|
|
218
|
+
console.print("Available models:")
|
|
219
|
+
for i, m in enumerate(models, 1):
|
|
220
|
+
current = " [cyan](current)[/cyan]" if m == self.settings.default_model else ""
|
|
221
|
+
console.print(f" {i}. {m}{current}")
|
|
222
|
+
|
|
223
|
+
console.print()
|
|
224
|
+
choice = Prompt.ask(
|
|
225
|
+
"Choose model",
|
|
226
|
+
choices=[str(i) for i in range(1, len(models) + 1)],
|
|
227
|
+
default="1"
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
model = models[int(choice) - 1]
|
|
231
|
+
save_setting("default_model", model)
|
|
232
|
+
self.settings.default_model = model
|
|
233
|
+
|
|
234
|
+
console.print(f"[cyan]{SIGMA}[/cyan] Model: [bold]{model}[/bold]")
|
|
235
|
+
|
|
236
|
+
def _setup_data(self):
|
|
237
|
+
"""Data settings."""
|
|
238
|
+
console.print(Panel("[bold]Step 4: Data Settings[/bold]", border_style="blue"))
|
|
239
|
+
console.print()
|
|
240
|
+
|
|
241
|
+
console.print(f"Data stored in: [bold]~/.sigma[/bold]")
|
|
148
242
|
console.print()
|
|
243
|
+
|
|
244
|
+
# Output directory
|
|
245
|
+
default_out = os.path.expanduser("~/Documents/Sigma")
|
|
246
|
+
out_dir = Prompt.ask("Output directory", default=default_out)
|
|
247
|
+
|
|
248
|
+
Path(out_dir).mkdir(parents=True, exist_ok=True)
|
|
249
|
+
save_setting("output_dir", out_dir)
|
|
250
|
+
console.print(f"[cyan]{SIGMA}[/cyan] Output: {out_dir}")
|
|
251
|
+
|
|
252
|
+
# Cache
|
|
253
|
+
if Confirm.ask("Enable caching?", default=True):
|
|
254
|
+
save_setting("cache_enabled", "true")
|
|
255
|
+
console.print(f"[cyan]{SIGMA}[/cyan] Caching enabled")
|
|
256
|
+
else:
|
|
257
|
+
save_setting("cache_enabled", "false")
|
|
258
|
+
|
|
259
|
+
def _setup_integrations(self):
|
|
260
|
+
"""Optional integrations."""
|
|
261
|
+
console.print(Panel("[bold]Step 5: Integrations[/bold]", border_style="blue"))
|
|
262
|
+
console.print()
|
|
263
|
+
|
|
264
|
+
# Ollama detection
|
|
265
|
+
if self.settings.default_provider != LLMProvider.OLLAMA:
|
|
266
|
+
ollama_running, ollama_host = detect_ollama()
|
|
267
|
+
if ollama_running and ollama_host:
|
|
268
|
+
console.print(f"[green]✓[/green] Ollama detected at {ollama_host}")
|
|
269
|
+
if Confirm.ask("Enable Ollama as local fallback?", default=True):
|
|
270
|
+
save_setting("ollama_host", ollama_host)
|
|
271
|
+
console.print(f"[cyan]{SIGMA}[/cyan] Ollama enabled")
|
|
272
|
+
else:
|
|
273
|
+
if Confirm.ask("Setup Ollama for local fallback?", default=False):
|
|
274
|
+
console.print("[dim]Install: https://ollama.ai/download[/dim]")
|
|
275
|
+
console.print("[dim]Run: ollama pull llama3.2[/dim]")
|
|
276
|
+
|
|
277
|
+
console.print()
|
|
278
|
+
|
|
279
|
+
# LEAN auto-detection
|
|
280
|
+
lean_installed, lean_cli, lean_dir = detect_lean_installation()
|
|
281
|
+
|
|
282
|
+
if lean_installed:
|
|
283
|
+
console.print(f"[green]✓[/green] LEAN/QuantConnect detected!")
|
|
284
|
+
if lean_cli:
|
|
285
|
+
console.print(f" [dim]CLI: {lean_cli}[/dim]")
|
|
286
|
+
if lean_dir:
|
|
287
|
+
console.print(f" [dim]Directory: {lean_dir}[/dim]")
|
|
288
|
+
console.print()
|
|
289
|
+
|
|
290
|
+
if Confirm.ask("Enable LEAN integration?", default=True):
|
|
291
|
+
save_setting("lean_enabled", "true")
|
|
292
|
+
if lean_cli:
|
|
293
|
+
save_setting("lean_cli_path", lean_cli)
|
|
294
|
+
if lean_dir:
|
|
295
|
+
save_setting("lean_directory", lean_dir)
|
|
296
|
+
console.print(f"[cyan]{SIGMA}[/cyan] LEAN integration enabled")
|
|
297
|
+
else:
|
|
298
|
+
save_setting("lean_enabled", "false")
|
|
299
|
+
else:
|
|
300
|
+
console.print("[yellow]![/yellow] LEAN/QuantConnect not detected")
|
|
301
|
+
console.print("[dim]LEAN provides institutional-grade backtesting with QuantConnect's engine.[/dim]")
|
|
302
|
+
console.print()
|
|
303
|
+
|
|
304
|
+
lean_choice = Prompt.ask(
|
|
305
|
+
"Would you like to",
|
|
306
|
+
choices=["install", "manual", "skip"],
|
|
307
|
+
default="skip"
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
if lean_choice == "install":
|
|
311
|
+
console.print()
|
|
312
|
+
console.print(f"[cyan]{SIGMA}[/cyan] Installing LEAN CLI via pip...")
|
|
313
|
+
console.print("[dim]This may take a minute...[/dim]")
|
|
314
|
+
|
|
315
|
+
with console.status("[bold blue]Installing LEAN...[/bold blue]"):
|
|
316
|
+
success, message = install_lean_cli_sync()
|
|
317
|
+
|
|
318
|
+
if success:
|
|
319
|
+
console.print(f"[green]✓[/green] {message}")
|
|
320
|
+
save_setting("lean_enabled", "true")
|
|
321
|
+
save_setting("lean_cli_path", "lean")
|
|
322
|
+
|
|
323
|
+
# Verify installation
|
|
324
|
+
lean_installed, lean_cli, lean_dir = detect_lean_installation()
|
|
325
|
+
if lean_cli:
|
|
326
|
+
console.print(f"[cyan]{SIGMA}[/cyan] LEAN CLI ready: {lean_cli}")
|
|
327
|
+
else:
|
|
328
|
+
console.print(f"[red]✗[/red] {message}")
|
|
329
|
+
console.print("[dim]You can install manually later: pip install lean[/dim]")
|
|
330
|
+
|
|
331
|
+
elif lean_choice == "manual":
|
|
332
|
+
console.print()
|
|
333
|
+
console.print("[bold]Manual Installation Options:[/bold]")
|
|
334
|
+
console.print(" 1. [cyan]pip install lean[/cyan] - LEAN CLI (recommended)")
|
|
335
|
+
console.print(" 2. [cyan]https://github.com/QuantConnect/Lean[/cyan] - Full source")
|
|
336
|
+
console.print()
|
|
337
|
+
|
|
338
|
+
lean_path = Prompt.ask("Enter LEAN CLI path (or Enter to skip)", default="")
|
|
339
|
+
if lean_path:
|
|
340
|
+
save_setting("lean_enabled", "true")
|
|
341
|
+
save_setting("lean_cli_path", lean_path)
|
|
342
|
+
console.print(f"[cyan]{SIGMA}[/cyan] LEAN configured: {lean_path}")
|
|
343
|
+
else:
|
|
344
|
+
console.print("[dim]Skipping LEAN setup. You can configure it later.[/dim]")
|
|
345
|
+
|
|
346
|
+
def _show_summary(self):
|
|
347
|
+
"""Show summary."""
|
|
149
348
|
console.print(Panel(
|
|
150
|
-
"[bold]
|
|
151
|
-
"
|
|
152
|
-
"2. Run: ollama pull llama3.2\n"
|
|
153
|
-
"3. Ollama runs automatically in the background",
|
|
154
|
-
title="[bold yellow]Local AI Setup[/bold yellow]",
|
|
155
|
-
border_style="yellow"
|
|
349
|
+
"[bold green]Setup Complete![/bold green]",
|
|
350
|
+
border_style="green",
|
|
156
351
|
))
|
|
157
352
|
console.print()
|
|
158
|
-
Prompt.ask("[dim]Press Enter to continue[/dim]", default="")
|
|
159
|
-
|
|
160
|
-
return config
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
def setup_additional_providers(config: dict) -> dict:
|
|
164
|
-
"""Configure additional LLM providers."""
|
|
165
|
-
clear_screen()
|
|
166
|
-
print_logo()
|
|
167
|
-
show_progress_step(2, 4, "Additional AI Providers (Optional)")
|
|
168
|
-
|
|
169
|
-
console.print(Panel(
|
|
170
|
-
"You can add more AI providers to switch between them.\n"
|
|
171
|
-
"This is [bold]optional[/bold] - skip if you only need one provider.",
|
|
172
|
-
border_style="blue"
|
|
173
|
-
))
|
|
174
|
-
console.print()
|
|
175
|
-
|
|
176
|
-
if not Confirm.ask("[bold]Add additional AI providers?[/bold]", default=False):
|
|
177
|
-
return config
|
|
178
|
-
|
|
179
|
-
providers = [
|
|
180
|
-
("GOOGLE_API_KEY", "Google Gemini", "https://aistudio.google.com/apikey"),
|
|
181
|
-
("OPENAI_API_KEY", "OpenAI GPT-4", "https://platform.openai.com/api-keys"),
|
|
182
|
-
("ANTHROPIC_API_KEY", "Anthropic Claude", "https://console.anthropic.com/settings/keys"),
|
|
183
|
-
("GROQ_API_KEY", "Groq (Llama)", "https://console.groq.com/keys"),
|
|
184
|
-
("XAI_API_KEY", "xAI Grok", "https://console.x.ai/"),
|
|
185
|
-
]
|
|
186
|
-
|
|
187
|
-
for env_key, name, url in providers:
|
|
188
|
-
if env_key in config:
|
|
189
|
-
continue
|
|
190
353
|
|
|
354
|
+
table = Table(show_header=False, box=None)
|
|
355
|
+
table.add_column("", style="bold")
|
|
356
|
+
table.add_column("")
|
|
357
|
+
|
|
358
|
+
provider = getattr(self.settings.default_provider, 'value', str(self.settings.default_provider))
|
|
359
|
+
table.add_row("Provider", provider)
|
|
360
|
+
table.add_row("Model", self.settings.default_model)
|
|
361
|
+
|
|
362
|
+
console.print(table)
|
|
363
|
+
console.print()
|
|
364
|
+
console.print(f"Run [bold]sigma[/bold] to start!")
|
|
191
365
|
console.print()
|
|
192
|
-
if Confirm.ask(f"[bold]Add {name}?[/bold]", default=False):
|
|
193
|
-
console.print(f" [dim]Get key: {url}[/dim]")
|
|
194
|
-
api_key = Prompt.ask(f" [bold]API key[/bold]", password=True)
|
|
195
|
-
if api_key:
|
|
196
|
-
config[env_key] = api_key
|
|
197
|
-
console.print(f" [green]Added![/green]")
|
|
198
|
-
|
|
199
|
-
return config
|
|
200
366
|
|
|
201
367
|
|
|
202
|
-
def
|
|
203
|
-
"""
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
show_progress_step(3, 4, "Financial Data Providers (Optional)")
|
|
207
|
-
|
|
208
|
-
console.print(Panel(
|
|
209
|
-
"[bold]Sigma works great with free data from Yahoo Finance.[/bold]\n\n"
|
|
210
|
-
"For premium features, you can add professional data sources.\n"
|
|
211
|
-
"All of these are [bold]optional[/bold].",
|
|
212
|
-
border_style="blue"
|
|
213
|
-
))
|
|
214
|
-
console.print()
|
|
215
|
-
|
|
216
|
-
# Table of data providers
|
|
217
|
-
table = Table(box=box.ROUNDED, border_style="dim")
|
|
218
|
-
table.add_column("Provider", style="bold")
|
|
219
|
-
table.add_column("Features", style="dim")
|
|
220
|
-
table.add_column("Free Tier")
|
|
221
|
-
|
|
222
|
-
table.add_row("Financial Modeling Prep", "Fundamentals, SEC filings", "[green]Yes[/green]")
|
|
223
|
-
table.add_row("Polygon.io", "Real-time data, options", "[green]Yes[/green]")
|
|
224
|
-
table.add_row("Alpha Vantage", "Technical indicators", "[green]Yes[/green]")
|
|
225
|
-
table.add_row("Exa Search", "AI-powered news search", "[yellow]Limited[/yellow]")
|
|
226
|
-
|
|
227
|
-
console.print(table)
|
|
228
|
-
console.print()
|
|
229
|
-
|
|
230
|
-
if not Confirm.ask("[bold]Configure data providers?[/bold]", default=False):
|
|
231
|
-
return config
|
|
232
|
-
|
|
233
|
-
providers = [
|
|
234
|
-
("FMP_API_KEY", "Financial Modeling Prep", "https://financialmodelingprep.com/developer/docs/"),
|
|
235
|
-
("POLYGON_API_KEY", "Polygon.io", "https://polygon.io/dashboard/api-keys"),
|
|
236
|
-
("ALPHA_VANTAGE_API_KEY", "Alpha Vantage", "https://www.alphavantage.co/support/#api-key"),
|
|
237
|
-
("EXASEARCH_API_KEY", "Exa Search", "https://exa.ai/"),
|
|
238
|
-
]
|
|
239
|
-
|
|
240
|
-
for env_key, name, url in providers:
|
|
241
|
-
console.print()
|
|
242
|
-
if Confirm.ask(f"[bold]Add {name}?[/bold]", default=False):
|
|
243
|
-
console.print(f" [dim]Get key: {url}[/dim]")
|
|
244
|
-
api_key = Prompt.ask(f" [bold]API key[/bold]", password=True)
|
|
245
|
-
if api_key:
|
|
246
|
-
config[env_key] = api_key
|
|
247
|
-
console.print(f" [green]Added![/green]")
|
|
248
|
-
|
|
249
|
-
return config
|
|
368
|
+
def run_setup() -> bool:
|
|
369
|
+
"""Run the setup wizard."""
|
|
370
|
+
wizard = SetupWizard()
|
|
371
|
+
return wizard.run()
|
|
250
372
|
|
|
251
373
|
|
|
252
|
-
def
|
|
253
|
-
"""
|
|
254
|
-
|
|
255
|
-
print_logo()
|
|
256
|
-
show_progress_step(4, 4, "Preferences")
|
|
257
|
-
|
|
258
|
-
# Default model selection
|
|
259
|
-
provider = config.get("DEFAULT_PROVIDER", "google")
|
|
260
|
-
|
|
261
|
-
console.print(Panel(
|
|
262
|
-
"Configure your default settings.\n"
|
|
263
|
-
"You can change these anytime with /model and /mode commands.",
|
|
264
|
-
border_style="blue"
|
|
265
|
-
))
|
|
374
|
+
def quick_setup():
|
|
375
|
+
"""Quick setup for first-time users."""
|
|
376
|
+
console.print(BANNER)
|
|
266
377
|
console.print()
|
|
267
378
|
|
|
268
|
-
|
|
269
|
-
modes = [
|
|
270
|
-
("default", "Comprehensive - Uses all available tools"),
|
|
271
|
-
("technical", "Technical - Charts, indicators, price action"),
|
|
272
|
-
("fundamental", "Fundamental - Financials, ratios, valuations"),
|
|
273
|
-
("quant", "Quantitative - Predictions, backtesting"),
|
|
274
|
-
]
|
|
275
|
-
|
|
276
|
-
console.print("[bold]Default analysis mode:[/bold]")
|
|
277
|
-
for i, (key, desc) in enumerate(modes, 1):
|
|
278
|
-
console.print(f" {i}. {desc}")
|
|
379
|
+
console.print("[bold]Quick Setup[/bold]")
|
|
279
380
|
console.print()
|
|
280
381
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
)
|
|
286
|
-
config["DEFAULT_MODE"] = modes[int(mode_choice) - 1][0]
|
|
287
|
-
|
|
288
|
-
return config
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
def save_config(config: dict):
|
|
292
|
-
"""Save configuration to file."""
|
|
293
|
-
# Ensure config directory exists
|
|
294
|
-
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
295
|
-
|
|
296
|
-
# Write config file
|
|
297
|
-
with open(CONFIG_FILE, 'w') as f:
|
|
298
|
-
f.write("# Sigma Configuration\n")
|
|
299
|
-
f.write("# Generated by setup wizard\n")
|
|
300
|
-
f.write("# You can edit this file or run 'sigma --setup' again\n\n")
|
|
301
|
-
|
|
302
|
-
for key, value in config.items():
|
|
303
|
-
f.write(f"{key}={value}\n")
|
|
304
|
-
|
|
305
|
-
# Mark setup as complete
|
|
306
|
-
SETUP_COMPLETE_FILE.touch()
|
|
307
|
-
|
|
308
|
-
# Also create/update .env in current directory if it exists
|
|
309
|
-
cwd_env = Path.cwd() / ".env"
|
|
310
|
-
if cwd_env.exists() or Path.cwd().name == "sigma":
|
|
311
|
-
with open(cwd_env, 'a') as f:
|
|
312
|
-
f.write("\n# Added by Sigma setup\n")
|
|
313
|
-
for key, value in config.items():
|
|
314
|
-
if "API_KEY" in key:
|
|
315
|
-
f.write(f"{key}={value}\n")
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
def show_completion(config: dict):
|
|
319
|
-
"""Show completion screen."""
|
|
320
|
-
clear_screen()
|
|
321
|
-
print_logo()
|
|
322
|
-
|
|
323
|
-
console.print()
|
|
324
|
-
console.print(Panel(
|
|
325
|
-
"[bold bright_green]Setup Complete![/bold bright_green]\n\n"
|
|
326
|
-
"Sigma is ready to use. Here's what's configured:",
|
|
327
|
-
border_style="bright_green",
|
|
328
|
-
padding=(1, 2)
|
|
329
|
-
))
|
|
382
|
+
# Pick provider
|
|
383
|
+
console.print("Choose provider:")
|
|
384
|
+
console.print(" 1. [bold]Google Gemini[/bold] [green](free, recommended)[/green]")
|
|
385
|
+
console.print(" 2. [bold]Groq[/bold] [green](free, fast)[/green]")
|
|
386
|
+
console.print(" 3. [bold]Ollama[/bold] [green](local, no key)[/green]")
|
|
330
387
|
console.print()
|
|
331
388
|
|
|
332
|
-
|
|
333
|
-
table = Table(box=box.ROUNDED, border_style="green")
|
|
334
|
-
table.add_column("Setting", style="bold")
|
|
335
|
-
table.add_column("Value", style="cyan")
|
|
389
|
+
choice = Prompt.ask("Provider", choices=["1", "2", "3"], default="1")
|
|
336
390
|
|
|
337
|
-
|
|
338
|
-
"
|
|
339
|
-
"
|
|
340
|
-
"
|
|
341
|
-
"groq": "Groq (Llama)",
|
|
342
|
-
"xai": "xAI Grok",
|
|
343
|
-
"ollama": "Ollama (Local)"
|
|
391
|
+
providers = {
|
|
392
|
+
"1": ("google", "gemini-2.0-flash"),
|
|
393
|
+
"2": ("groq", "llama-3.3-70b-versatile"),
|
|
394
|
+
"3": ("ollama", "llama3.2"),
|
|
344
395
|
}
|
|
345
396
|
|
|
346
|
-
|
|
347
|
-
|
|
397
|
+
provider_key, model = providers[choice]
|
|
398
|
+
provider_name = {"google": "Google Gemini", "groq": "Groq", "ollama": "Ollama"}[provider_key]
|
|
348
399
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
400
|
+
if provider_key != "ollama":
|
|
401
|
+
urls = {
|
|
402
|
+
"google": "https://aistudio.google.com/apikey",
|
|
403
|
+
"groq": "https://console.groq.com/keys",
|
|
404
|
+
}
|
|
405
|
+
console.print(f"\nGet key from: [bold]{urls[provider_key]}[/bold]")
|
|
406
|
+
api_key = Prompt.ask("API key", password=True)
|
|
407
|
+
|
|
408
|
+
if api_key:
|
|
409
|
+
save_api_key(LLMProvider(provider_key), api_key)
|
|
352
410
|
|
|
353
|
-
|
|
411
|
+
save_setting("default_provider", provider_key)
|
|
412
|
+
save_setting("default_model", model)
|
|
354
413
|
|
|
355
414
|
console.print()
|
|
356
|
-
console.print(
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
" [cyan]sigma --help[/cyan] Show help\n"
|
|
360
|
-
" [cyan]sigma --setup[/cyan] Run setup again\n\n"
|
|
361
|
-
"[bold]Inside Sigma:[/bold]\n\n"
|
|
362
|
-
" [dim]Analyze NVDA stock[/dim]\n"
|
|
363
|
-
" [dim]Compare AAPL, MSFT, GOOGL[/dim]\n"
|
|
364
|
-
" [dim]/lean run TSLA macd_momentum[/dim]\n\n"
|
|
365
|
-
"[dim]Type /help for all commands[/dim]",
|
|
366
|
-
title="[bold bright_cyan]Getting Started[/bold bright_cyan]",
|
|
367
|
-
border_style="cyan",
|
|
368
|
-
padding=(1, 2)
|
|
369
|
-
))
|
|
415
|
+
console.print(f"[bold green]{SIGMA} Setup complete![/bold green]")
|
|
416
|
+
console.print(f"Provider: {provider_name}")
|
|
417
|
+
console.print(f"Model: {model}")
|
|
370
418
|
console.print()
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
def run_setup(force: bool = False) -> dict:
|
|
374
|
-
"""Run the setup wizard.
|
|
375
|
-
|
|
376
|
-
Args:
|
|
377
|
-
force: Force setup even if already complete
|
|
378
|
-
|
|
379
|
-
Returns:
|
|
380
|
-
Configuration dictionary
|
|
381
|
-
"""
|
|
382
|
-
if is_setup_complete() and not force:
|
|
383
|
-
return load_config()
|
|
384
|
-
|
|
385
|
-
try:
|
|
386
|
-
show_welcome()
|
|
387
|
-
|
|
388
|
-
config = {}
|
|
389
|
-
config = setup_llm_provider()
|
|
390
|
-
config = setup_additional_providers(config)
|
|
391
|
-
config = setup_data_providers(config)
|
|
392
|
-
config = setup_preferences(config)
|
|
393
|
-
|
|
394
|
-
# Save with progress
|
|
395
|
-
console.print()
|
|
396
|
-
with Progress(
|
|
397
|
-
SpinnerColumn(),
|
|
398
|
-
TextColumn("[bold]Saving configuration...[/bold]"),
|
|
399
|
-
console=console
|
|
400
|
-
) as progress:
|
|
401
|
-
task = progress.add_task("save", total=None)
|
|
402
|
-
save_config(config)
|
|
403
|
-
time.sleep(0.5)
|
|
404
|
-
|
|
405
|
-
show_completion(config)
|
|
406
|
-
|
|
407
|
-
Prompt.ask("\n[dim]Press Enter to start Sigma[/dim]", default="")
|
|
408
|
-
|
|
409
|
-
return config
|
|
410
|
-
|
|
411
|
-
except KeyboardInterrupt:
|
|
412
|
-
console.print("\n\n[yellow]Setup cancelled. Run 'sigma --setup' to try again.[/yellow]")
|
|
413
|
-
sys.exit(0)
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
def load_config() -> dict:
|
|
417
|
-
"""Load existing configuration."""
|
|
418
|
-
config = {}
|
|
419
|
-
|
|
420
|
-
if CONFIG_FILE.exists():
|
|
421
|
-
with open(CONFIG_FILE) as f:
|
|
422
|
-
for line in f:
|
|
423
|
-
line = line.strip()
|
|
424
|
-
if line and not line.startswith('#') and '=' in line:
|
|
425
|
-
key, value = line.split('=', 1)
|
|
426
|
-
config[key.strip()] = value.strip()
|
|
427
|
-
|
|
428
|
-
return config
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
def apply_config_to_env(config: dict):
|
|
432
|
-
"""Apply loaded config to environment variables."""
|
|
433
|
-
for key, value in config.items():
|
|
434
|
-
if key not in os.environ:
|
|
435
|
-
os.environ[key] = value
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
def ensure_setup() -> dict:
|
|
439
|
-
"""Ensure setup is complete, running wizard if needed.
|
|
440
|
-
|
|
441
|
-
Returns:
|
|
442
|
-
Configuration dictionary
|
|
443
|
-
"""
|
|
444
|
-
if is_setup_complete():
|
|
445
|
-
config = load_config()
|
|
446
|
-
apply_config_to_env(config)
|
|
447
|
-
return config
|
|
448
|
-
else:
|
|
449
|
-
config = run_setup()
|
|
450
|
-
apply_config_to_env(config)
|
|
451
|
-
return config
|
|
419
|
+
console.print("Run [bold]sigma[/bold] to start!")
|
|
452
420
|
|
|
453
421
|
|
|
454
422
|
if __name__ == "__main__":
|
|
455
|
-
|
|
423
|
+
if "--quick" in sys.argv:
|
|
424
|
+
quick_setup()
|
|
425
|
+
else:
|
|
426
|
+
run_setup()
|