sigma-terminal 2.0.1__py3-none-any.whl → 3.2.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 +563 -898
- sigma/backtest.py +372 -0
- sigma/charts.py +407 -0
- sigma/cli.py +434 -0
- sigma/comparison.py +611 -0
- sigma/config.py +195 -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 +401 -0
- sigma/monitoring.py +666 -0
- sigma/portfolio.py +697 -0
- sigma/reporting.py +658 -0
- sigma/robustness.py +675 -0
- sigma/setup.py +305 -402
- sigma/strategy.py +753 -0
- sigma/tools/backtest.py +23 -5
- sigma/tools.py +617 -0
- sigma/visualization.py +766 -0
- sigma_terminal-3.2.0.dist-info/METADATA +298 -0
- sigma_terminal-3.2.0.dist-info/RECORD +30 -0
- sigma_terminal-3.2.0.dist-info/entry_points.txt +6 -0
- sigma_terminal-3.2.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.1.dist-info/METADATA +0 -222
- sigma_terminal-2.0.1.dist-info/RECORD +0 -19
- sigma_terminal-2.0.1.dist-info/entry_points.txt +0 -2
- sigma_terminal-2.0.1.dist-info/licenses/LICENSE +0 -42
- {sigma_terminal-2.0.1.dist-info → sigma_terminal-3.2.0.dist-info}/WHEEL +0 -0
sigma/tools/__init__.py
DELETED
sigma/tools/charts.py
DELETED
|
@@ -1,400 +0,0 @@
|
|
|
1
|
-
"""Beautiful terminal charts for Sigma."""
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
from datetime import datetime
|
|
5
|
-
from typing import Any, Optional
|
|
6
|
-
|
|
7
|
-
import plotext as plt
|
|
8
|
-
import yfinance as yf
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def create_price_chart(
|
|
12
|
-
symbol: str,
|
|
13
|
-
period: str = "3mo",
|
|
14
|
-
chart_type: str = "candle",
|
|
15
|
-
show_volume: bool = True,
|
|
16
|
-
theme: str = "dark",
|
|
17
|
-
) -> str:
|
|
18
|
-
"""Create a beautiful price chart in the terminal.
|
|
19
|
-
|
|
20
|
-
Args:
|
|
21
|
-
symbol: Stock ticker symbol
|
|
22
|
-
period: Time period (1d, 5d, 1mo, 3mo, 6mo, 1y, 2y, 5y, max)
|
|
23
|
-
chart_type: Chart type (candle, line, bar, area) - default is candle
|
|
24
|
-
show_volume: Whether to show volume subplot
|
|
25
|
-
theme: Color theme (dark, light, pro)
|
|
26
|
-
|
|
27
|
-
Returns:
|
|
28
|
-
ASCII chart string
|
|
29
|
-
"""
|
|
30
|
-
ticker = yf.Ticker(symbol.upper())
|
|
31
|
-
hist = ticker.history(period=period)
|
|
32
|
-
|
|
33
|
-
if hist.empty:
|
|
34
|
-
return f"No data available for {symbol}"
|
|
35
|
-
|
|
36
|
-
# Setup theme
|
|
37
|
-
if theme == "dark":
|
|
38
|
-
plt.theme("dark")
|
|
39
|
-
plt.canvas_color("black")
|
|
40
|
-
plt.axes_color("black")
|
|
41
|
-
elif theme == "pro":
|
|
42
|
-
plt.theme("pro")
|
|
43
|
-
else:
|
|
44
|
-
plt.theme("clear")
|
|
45
|
-
|
|
46
|
-
# Get data
|
|
47
|
-
dates = list(range(len(hist)))
|
|
48
|
-
closes = hist["Close"].tolist()
|
|
49
|
-
opens = hist["Open"].tolist()
|
|
50
|
-
highs = hist["High"].tolist()
|
|
51
|
-
lows = hist["Low"].tolist()
|
|
52
|
-
volumes = hist["Volume"].tolist()
|
|
53
|
-
|
|
54
|
-
# Date labels
|
|
55
|
-
date_labels = [d.strftime("%m/%d") for d in hist.index]
|
|
56
|
-
|
|
57
|
-
# Calculate change
|
|
58
|
-
if len(closes) > 1:
|
|
59
|
-
change = closes[-1] - closes[0]
|
|
60
|
-
change_pct = (change / closes[0]) * 100
|
|
61
|
-
color = "green" if change >= 0 else "red"
|
|
62
|
-
change_str = f"+{change:.2f} (+{change_pct:.1f}%)" if change >= 0 else f"{change:.2f} ({change_pct:.1f}%)"
|
|
63
|
-
else:
|
|
64
|
-
color = "white"
|
|
65
|
-
change_str = ""
|
|
66
|
-
|
|
67
|
-
plt.clear_figure()
|
|
68
|
-
plt.plotsize(150, 50) # Higher resolution
|
|
69
|
-
|
|
70
|
-
if show_volume:
|
|
71
|
-
plt.subplots(2, 1)
|
|
72
|
-
plt.subplot(1, 1)
|
|
73
|
-
|
|
74
|
-
# Chart title
|
|
75
|
-
info = ticker.info
|
|
76
|
-
name = info.get("shortName", symbol.upper())
|
|
77
|
-
current_price = closes[-1] if closes else 0
|
|
78
|
-
|
|
79
|
-
title = f" {name} ({symbol.upper()}) │ ${current_price:.2f} │ {change_str}"
|
|
80
|
-
plt.title(title)
|
|
81
|
-
|
|
82
|
-
# Plot based on type
|
|
83
|
-
if chart_type == "candle":
|
|
84
|
-
plt.candlestick(dates, {"Open": opens, "Close": closes, "High": highs, "Low": lows})
|
|
85
|
-
plt.xlabel("Date")
|
|
86
|
-
elif chart_type == "bar":
|
|
87
|
-
colors = ["green" if c >= o else "red" for c, o in zip(closes, opens)]
|
|
88
|
-
plt.bar(dates, closes, color=colors)
|
|
89
|
-
plt.xlabel("Date")
|
|
90
|
-
elif chart_type == "area":
|
|
91
|
-
plt.plot(dates, closes, color=color, fillx=True)
|
|
92
|
-
plt.xlabel("Date")
|
|
93
|
-
else: # line
|
|
94
|
-
plt.plot(dates, closes, color=color, marker="hd")
|
|
95
|
-
|
|
96
|
-
# Add moving averages
|
|
97
|
-
if len(closes) >= 20:
|
|
98
|
-
sma20 = []
|
|
99
|
-
for i in range(len(closes)):
|
|
100
|
-
if i >= 19:
|
|
101
|
-
sma20.append(sum(closes[i-19:i+1]) / 20)
|
|
102
|
-
else:
|
|
103
|
-
sma20.append(None)
|
|
104
|
-
valid_sma = [(i, v) for i, v in enumerate(sma20) if v is not None]
|
|
105
|
-
if valid_sma:
|
|
106
|
-
plt.plot([x[0] for x in valid_sma], [x[1] for x in valid_sma], color="cyan", marker="hd")
|
|
107
|
-
|
|
108
|
-
plt.ylabel("Price ($)")
|
|
109
|
-
|
|
110
|
-
# X-axis labels (every 5th date or so)
|
|
111
|
-
step = max(1, len(dates) // 8)
|
|
112
|
-
xticks = dates[::step]
|
|
113
|
-
xlabels = date_labels[::step]
|
|
114
|
-
plt.xticks(xticks, xlabels)
|
|
115
|
-
|
|
116
|
-
# Volume subplot
|
|
117
|
-
if show_volume:
|
|
118
|
-
plt.subplot(2, 1)
|
|
119
|
-
vol_colors = ["green" if closes[i] >= opens[i] else "red" for i in range(len(volumes))]
|
|
120
|
-
plt.bar(dates, [v / 1_000_000 for v in volumes], color=vol_colors)
|
|
121
|
-
plt.ylabel("Vol (M)")
|
|
122
|
-
plt.xticks(xticks, xlabels)
|
|
123
|
-
|
|
124
|
-
# Build the chart
|
|
125
|
-
chart_str = plt.build()
|
|
126
|
-
|
|
127
|
-
return chart_str
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
def create_comparison_chart(symbols: list[str], period: str = "3mo") -> str:
|
|
131
|
-
"""Create a comparison chart for multiple stocks.
|
|
132
|
-
|
|
133
|
-
Args:
|
|
134
|
-
symbols: List of stock symbols
|
|
135
|
-
period: Time period
|
|
136
|
-
|
|
137
|
-
Returns:
|
|
138
|
-
ASCII chart string
|
|
139
|
-
"""
|
|
140
|
-
plt.clear_figure()
|
|
141
|
-
plt.plotsize(150, 45) # Higher resolution
|
|
142
|
-
plt.theme("dark")
|
|
143
|
-
plt.canvas_color("black")
|
|
144
|
-
plt.axes_color("black")
|
|
145
|
-
|
|
146
|
-
colors = ["cyan", "magenta", "yellow", "green", "red", "blue"]
|
|
147
|
-
legend_items = []
|
|
148
|
-
|
|
149
|
-
for i, symbol in enumerate(symbols[:6]): # Max 6 symbols
|
|
150
|
-
ticker = yf.Ticker(symbol.upper())
|
|
151
|
-
hist = ticker.history(period=period)
|
|
152
|
-
|
|
153
|
-
if hist.empty:
|
|
154
|
-
continue
|
|
155
|
-
|
|
156
|
-
# Normalize to percentage change
|
|
157
|
-
closes = hist["Close"].tolist()
|
|
158
|
-
if closes:
|
|
159
|
-
base = closes[0]
|
|
160
|
-
normalized = [(c / base - 1) * 100 for c in closes]
|
|
161
|
-
dates = list(range(len(normalized)))
|
|
162
|
-
|
|
163
|
-
color = colors[i % len(colors)]
|
|
164
|
-
plt.plot(dates, normalized, color=color, marker="hd", label=symbol.upper())
|
|
165
|
-
legend_items.append(f"{symbol.upper()}")
|
|
166
|
-
|
|
167
|
-
plt.title(" Stock Comparison (% Change)")
|
|
168
|
-
plt.ylabel("Change (%)")
|
|
169
|
-
plt.xlabel("Days")
|
|
170
|
-
|
|
171
|
-
chart_str = plt.build()
|
|
172
|
-
return chart_str
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
def create_sector_chart() -> str:
|
|
176
|
-
"""Create a sector performance chart."""
|
|
177
|
-
sectors = {
|
|
178
|
-
"XLK": "Tech",
|
|
179
|
-
"XLF": "Fin",
|
|
180
|
-
"XLV": "Health",
|
|
181
|
-
"XLE": "Energy",
|
|
182
|
-
"XLI": "Indust",
|
|
183
|
-
"XLY": "Discr",
|
|
184
|
-
"XLP": "Staples",
|
|
185
|
-
"XLU": "Util",
|
|
186
|
-
"XLB": "Mater",
|
|
187
|
-
"XLRE": "RE",
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
plt.clear_figure()
|
|
191
|
-
plt.plotsize(120, 35) # Higher resolution
|
|
192
|
-
plt.theme("dark")
|
|
193
|
-
plt.canvas_color("black")
|
|
194
|
-
plt.axes_color("black")
|
|
195
|
-
|
|
196
|
-
names = []
|
|
197
|
-
changes = []
|
|
198
|
-
colors = []
|
|
199
|
-
|
|
200
|
-
for symbol, name in sectors.items():
|
|
201
|
-
try:
|
|
202
|
-
ticker = yf.Ticker(symbol)
|
|
203
|
-
info = ticker.info
|
|
204
|
-
if info:
|
|
205
|
-
price = info.get("regularMarketPrice", 0)
|
|
206
|
-
prev = info.get("previousClose", 0)
|
|
207
|
-
if price and prev:
|
|
208
|
-
change = ((price - prev) / prev) * 100
|
|
209
|
-
names.append(name)
|
|
210
|
-
changes.append(change)
|
|
211
|
-
colors.append("green" if change >= 0 else "red")
|
|
212
|
-
except:
|
|
213
|
-
continue
|
|
214
|
-
|
|
215
|
-
if not names:
|
|
216
|
-
return "Could not fetch sector data"
|
|
217
|
-
|
|
218
|
-
plt.bar(names, changes, color=colors, orientation="horizontal")
|
|
219
|
-
plt.title(" Sector Performance (% Daily Change)")
|
|
220
|
-
plt.xlabel("Change (%)")
|
|
221
|
-
|
|
222
|
-
chart_str = plt.build()
|
|
223
|
-
return chart_str
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
def create_rsi_chart(symbol: str, period: str = "3mo") -> str:
|
|
227
|
-
"""Create a price chart with RSI indicator."""
|
|
228
|
-
ticker = yf.Ticker(symbol.upper())
|
|
229
|
-
hist = ticker.history(period=period)
|
|
230
|
-
|
|
231
|
-
if hist.empty:
|
|
232
|
-
return f"No data available for {symbol}"
|
|
233
|
-
|
|
234
|
-
plt.clear_figure()
|
|
235
|
-
plt.plotsize(150, 50) # Higher resolution
|
|
236
|
-
plt.theme("dark")
|
|
237
|
-
plt.canvas_color("black")
|
|
238
|
-
plt.axes_color("black")
|
|
239
|
-
|
|
240
|
-
closes = hist["Close"].tolist()
|
|
241
|
-
dates = list(range(len(closes)))
|
|
242
|
-
|
|
243
|
-
# Calculate RSI
|
|
244
|
-
deltas = [closes[i] - closes[i-1] if i > 0 else 0 for i in range(len(closes))]
|
|
245
|
-
gains = [d if d > 0 else 0 for d in deltas]
|
|
246
|
-
losses = [-d if d < 0 else 0 for d in deltas]
|
|
247
|
-
|
|
248
|
-
# 14-period RSI
|
|
249
|
-
rsi_values = []
|
|
250
|
-
window = 14
|
|
251
|
-
for i in range(len(closes)):
|
|
252
|
-
if i < window:
|
|
253
|
-
rsi_values.append(50) # Neutral
|
|
254
|
-
else:
|
|
255
|
-
avg_gain = sum(gains[i-window+1:i+1]) / window
|
|
256
|
-
avg_loss = sum(losses[i-window+1:i+1]) / window
|
|
257
|
-
if avg_loss == 0:
|
|
258
|
-
rsi_values.append(100)
|
|
259
|
-
else:
|
|
260
|
-
rs = avg_gain / avg_loss
|
|
261
|
-
rsi_values.append(100 - (100 / (1 + rs)))
|
|
262
|
-
|
|
263
|
-
plt.subplots(2, 1)
|
|
264
|
-
|
|
265
|
-
# Price chart
|
|
266
|
-
plt.subplot(1, 1)
|
|
267
|
-
color = "green" if closes[-1] >= closes[0] else "red"
|
|
268
|
-
plt.plot(dates, closes, color=color, marker="hd")
|
|
269
|
-
plt.title(f" {symbol.upper()} Price & RSI")
|
|
270
|
-
plt.ylabel("Price ($)")
|
|
271
|
-
|
|
272
|
-
# RSI chart
|
|
273
|
-
plt.subplot(2, 1)
|
|
274
|
-
plt.plot(dates, rsi_values, color="magenta", marker="hd")
|
|
275
|
-
plt.hline(70, color="red")
|
|
276
|
-
plt.hline(30, color="green")
|
|
277
|
-
plt.ylabel("RSI")
|
|
278
|
-
plt.ylim(0, 100)
|
|
279
|
-
|
|
280
|
-
chart_str = plt.build()
|
|
281
|
-
return chart_str
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
def create_portfolio_pie(holdings: list[dict]) -> str:
|
|
285
|
-
"""Create a portfolio allocation pie chart."""
|
|
286
|
-
plt.clear_figure()
|
|
287
|
-
plt.theme("dark")
|
|
288
|
-
|
|
289
|
-
labels = []
|
|
290
|
-
values = []
|
|
291
|
-
|
|
292
|
-
total = 0
|
|
293
|
-
for h in holdings:
|
|
294
|
-
symbol = h.get("symbol", "")
|
|
295
|
-
shares = h.get("shares", 0)
|
|
296
|
-
|
|
297
|
-
try:
|
|
298
|
-
ticker = yf.Ticker(symbol.upper())
|
|
299
|
-
info = ticker.info
|
|
300
|
-
price = info.get("regularMarketPrice", 0)
|
|
301
|
-
value = price * shares
|
|
302
|
-
if value > 0:
|
|
303
|
-
labels.append(symbol.upper())
|
|
304
|
-
values.append(value)
|
|
305
|
-
total += value
|
|
306
|
-
except:
|
|
307
|
-
continue
|
|
308
|
-
|
|
309
|
-
if not labels:
|
|
310
|
-
return "Could not calculate portfolio"
|
|
311
|
-
|
|
312
|
-
# Convert to percentages
|
|
313
|
-
percentages = [v / total * 100 for v in values]
|
|
314
|
-
|
|
315
|
-
# Bar chart (pie not well supported in terminal)
|
|
316
|
-
colors = ["cyan", "magenta", "yellow", "green", "red", "blue"]
|
|
317
|
-
plt.bar(labels, percentages, color=colors[:len(labels)])
|
|
318
|
-
plt.title(f" Portfolio Allocation (${total:,.0f} total)")
|
|
319
|
-
plt.ylabel("% of Portfolio")
|
|
320
|
-
|
|
321
|
-
chart_str = plt.build()
|
|
322
|
-
return chart_str
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
def save_chart_to_file(
|
|
326
|
-
symbol: str,
|
|
327
|
-
period: str = "6mo",
|
|
328
|
-
filepath: Optional[str] = None,
|
|
329
|
-
) -> str:
|
|
330
|
-
"""Save a high-quality chart to HTML file using plotly."""
|
|
331
|
-
try:
|
|
332
|
-
import plotly.graph_objects as go # type: ignore
|
|
333
|
-
from plotly.subplots import make_subplots # type: ignore
|
|
334
|
-
except ImportError:
|
|
335
|
-
return "plotly not installed. Run: pip install plotly"
|
|
336
|
-
|
|
337
|
-
ticker = yf.Ticker(symbol.upper())
|
|
338
|
-
hist = ticker.history(period=period)
|
|
339
|
-
|
|
340
|
-
if hist.empty:
|
|
341
|
-
return f"No data for {symbol}"
|
|
342
|
-
|
|
343
|
-
# Create candlestick with volume
|
|
344
|
-
fig = make_subplots(
|
|
345
|
-
rows=2, cols=1,
|
|
346
|
-
shared_xaxes=True,
|
|
347
|
-
vertical_spacing=0.03,
|
|
348
|
-
row_heights=[0.7, 0.3]
|
|
349
|
-
)
|
|
350
|
-
|
|
351
|
-
# Candlestick
|
|
352
|
-
fig.add_trace(
|
|
353
|
-
go.Candlestick(
|
|
354
|
-
x=hist.index,
|
|
355
|
-
open=hist["Open"],
|
|
356
|
-
high=hist["High"],
|
|
357
|
-
low=hist["Low"],
|
|
358
|
-
close=hist["Close"],
|
|
359
|
-
name="OHLC"
|
|
360
|
-
),
|
|
361
|
-
row=1, col=1
|
|
362
|
-
)
|
|
363
|
-
|
|
364
|
-
# Volume
|
|
365
|
-
colors = ["green" if c >= o else "red"
|
|
366
|
-
for c, o in zip(hist["Close"], hist["Open"])]
|
|
367
|
-
fig.add_trace(
|
|
368
|
-
go.Bar(x=hist.index, y=hist["Volume"], marker_color=colors, name="Volume"),
|
|
369
|
-
row=2, col=1
|
|
370
|
-
)
|
|
371
|
-
|
|
372
|
-
# Add moving averages
|
|
373
|
-
hist["SMA20"] = hist["Close"].rolling(window=20).mean()
|
|
374
|
-
hist["SMA50"] = hist["Close"].rolling(window=50).mean()
|
|
375
|
-
|
|
376
|
-
fig.add_trace(
|
|
377
|
-
go.Scatter(x=hist.index, y=hist["SMA20"], line=dict(color="cyan", width=1), name="SMA20"),
|
|
378
|
-
row=1, col=1
|
|
379
|
-
)
|
|
380
|
-
fig.add_trace(
|
|
381
|
-
go.Scatter(x=hist.index, y=hist["SMA50"], line=dict(color="yellow", width=1), name="SMA50"),
|
|
382
|
-
row=1, col=1
|
|
383
|
-
)
|
|
384
|
-
|
|
385
|
-
info = ticker.info
|
|
386
|
-
name = info.get("shortName", symbol.upper())
|
|
387
|
-
|
|
388
|
-
fig.update_layout(
|
|
389
|
-
title=f"{name} ({symbol.upper()}) - {period} Chart",
|
|
390
|
-
template="plotly_dark",
|
|
391
|
-
xaxis_rangeslider_visible=False,
|
|
392
|
-
showlegend=True,
|
|
393
|
-
)
|
|
394
|
-
|
|
395
|
-
# Save
|
|
396
|
-
if filepath is None:
|
|
397
|
-
filepath = f"{symbol.lower()}_chart.html"
|
|
398
|
-
|
|
399
|
-
fig.write_html(filepath)
|
|
400
|
-
return f"Chart saved to {filepath}"
|