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/tools/__init__.py DELETED
@@ -1,5 +0,0 @@
1
- """Tools module initialization."""
2
-
3
- from sigma.tools.financial import get_all_tools, execute_tool
4
-
5
- __all__ = ["get_all_tools", "execute_tool"]
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}"