sigma-terminal 2.0.2__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.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.2.0.dist-info}/WHEEL +0 -0
sigma/charts.py
ADDED
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
"""Chart generation for Sigma using Plotly."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import tempfile
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
import pandas as pd
|
|
9
|
+
import plotly.graph_objects as go
|
|
10
|
+
from plotly.subplots import make_subplots
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Chart theme
|
|
14
|
+
SIGMA_THEME = {
|
|
15
|
+
"bg_color": "#0a0a0f",
|
|
16
|
+
"paper_color": "#0a0a0f",
|
|
17
|
+
"grid_color": "#1a1a2e",
|
|
18
|
+
"text_color": "#e4e4e7",
|
|
19
|
+
"accent": "#3b82f6",
|
|
20
|
+
"positive": "#22c55e",
|
|
21
|
+
"negative": "#ef4444",
|
|
22
|
+
"neutral": "#6b7280",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def create_candlestick_chart(
|
|
27
|
+
symbol: str,
|
|
28
|
+
data: pd.DataFrame,
|
|
29
|
+
title: Optional[str] = None,
|
|
30
|
+
show_volume: bool = True,
|
|
31
|
+
show_sma: bool = True,
|
|
32
|
+
sma_periods: list = [20, 50],
|
|
33
|
+
) -> str:
|
|
34
|
+
"""Create a candlestick chart with optional indicators."""
|
|
35
|
+
|
|
36
|
+
if show_volume:
|
|
37
|
+
fig = make_subplots(
|
|
38
|
+
rows=2, cols=1,
|
|
39
|
+
shared_xaxes=True,
|
|
40
|
+
vertical_spacing=0.03,
|
|
41
|
+
row_heights=[0.7, 0.3],
|
|
42
|
+
subplot_titles=[None, None],
|
|
43
|
+
)
|
|
44
|
+
else:
|
|
45
|
+
fig = make_subplots(rows=1, cols=1)
|
|
46
|
+
|
|
47
|
+
# Candlestick chart
|
|
48
|
+
fig.add_trace(
|
|
49
|
+
go.Candlestick(
|
|
50
|
+
x=data.index,
|
|
51
|
+
open=data["Open"],
|
|
52
|
+
high=data["High"],
|
|
53
|
+
low=data["Low"],
|
|
54
|
+
close=data["Close"],
|
|
55
|
+
name=symbol.upper(),
|
|
56
|
+
increasing_line_color=SIGMA_THEME["positive"],
|
|
57
|
+
decreasing_line_color=SIGMA_THEME["negative"],
|
|
58
|
+
),
|
|
59
|
+
row=1, col=1
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Add SMAs
|
|
63
|
+
if show_sma:
|
|
64
|
+
colors = ["#f59e0b", "#8b5cf6", "#06b6d4"]
|
|
65
|
+
for i, period in enumerate(sma_periods):
|
|
66
|
+
if len(data) >= period:
|
|
67
|
+
sma = data["Close"].rolling(period).mean()
|
|
68
|
+
fig.add_trace(
|
|
69
|
+
go.Scatter(
|
|
70
|
+
x=data.index,
|
|
71
|
+
y=sma,
|
|
72
|
+
name=f"SMA {period}",
|
|
73
|
+
line=dict(color=colors[i % len(colors)], width=1),
|
|
74
|
+
),
|
|
75
|
+
row=1, col=1
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Volume
|
|
79
|
+
if show_volume and "Volume" in data.columns:
|
|
80
|
+
colors = [SIGMA_THEME["positive"] if data["Close"].iloc[i] >= data["Open"].iloc[i]
|
|
81
|
+
else SIGMA_THEME["negative"] for i in range(len(data))]
|
|
82
|
+
|
|
83
|
+
fig.add_trace(
|
|
84
|
+
go.Bar(
|
|
85
|
+
x=data.index,
|
|
86
|
+
y=data["Volume"],
|
|
87
|
+
name="Volume",
|
|
88
|
+
marker_color=colors,
|
|
89
|
+
opacity=0.7,
|
|
90
|
+
),
|
|
91
|
+
row=2, col=1
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# Layout
|
|
95
|
+
chart_title = title or f"{symbol.upper()} Price Chart"
|
|
96
|
+
_apply_layout(fig, chart_title, show_volume)
|
|
97
|
+
|
|
98
|
+
return _save_chart(fig, f"{symbol}_candlestick")
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def create_line_chart(
|
|
102
|
+
symbol: str,
|
|
103
|
+
data: pd.DataFrame,
|
|
104
|
+
title: Optional[str] = None,
|
|
105
|
+
show_volume: bool = False,
|
|
106
|
+
) -> str:
|
|
107
|
+
"""Create a line chart."""
|
|
108
|
+
|
|
109
|
+
if show_volume:
|
|
110
|
+
fig = make_subplots(
|
|
111
|
+
rows=2, cols=1,
|
|
112
|
+
shared_xaxes=True,
|
|
113
|
+
vertical_spacing=0.03,
|
|
114
|
+
row_heights=[0.7, 0.3],
|
|
115
|
+
)
|
|
116
|
+
else:
|
|
117
|
+
fig = make_subplots(rows=1, cols=1)
|
|
118
|
+
|
|
119
|
+
# Price line
|
|
120
|
+
fig.add_trace(
|
|
121
|
+
go.Scatter(
|
|
122
|
+
x=data.index,
|
|
123
|
+
y=data["Close"],
|
|
124
|
+
name=symbol.upper(),
|
|
125
|
+
line=dict(color=SIGMA_THEME["accent"], width=2),
|
|
126
|
+
fill="tozeroy",
|
|
127
|
+
fillcolor="rgba(59, 130, 246, 0.1)",
|
|
128
|
+
),
|
|
129
|
+
row=1, col=1
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Volume
|
|
133
|
+
if show_volume and "Volume" in data.columns:
|
|
134
|
+
fig.add_trace(
|
|
135
|
+
go.Bar(
|
|
136
|
+
x=data.index,
|
|
137
|
+
y=data["Volume"],
|
|
138
|
+
name="Volume",
|
|
139
|
+
marker_color=SIGMA_THEME["neutral"],
|
|
140
|
+
opacity=0.5,
|
|
141
|
+
),
|
|
142
|
+
row=2, col=1
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
chart_title = title or f"{symbol.upper()} Price"
|
|
146
|
+
_apply_layout(fig, chart_title, show_volume)
|
|
147
|
+
|
|
148
|
+
return _save_chart(fig, f"{symbol}_line")
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def create_comparison_chart(
|
|
152
|
+
symbols: list,
|
|
153
|
+
data_dict: dict,
|
|
154
|
+
title: Optional[str] = None,
|
|
155
|
+
normalize: bool = True,
|
|
156
|
+
) -> str:
|
|
157
|
+
"""Create a comparison chart for multiple symbols."""
|
|
158
|
+
|
|
159
|
+
fig = go.Figure()
|
|
160
|
+
|
|
161
|
+
colors = ["#3b82f6", "#22c55e", "#f59e0b", "#ef4444", "#8b5cf6", "#06b6d4"]
|
|
162
|
+
|
|
163
|
+
for i, symbol in enumerate(symbols):
|
|
164
|
+
if symbol not in data_dict:
|
|
165
|
+
continue
|
|
166
|
+
|
|
167
|
+
data = data_dict[symbol]
|
|
168
|
+
|
|
169
|
+
if normalize:
|
|
170
|
+
values = (data["Close"] / data["Close"].iloc[0] - 1) * 100
|
|
171
|
+
y_label = "Return (%)"
|
|
172
|
+
else:
|
|
173
|
+
values = data["Close"]
|
|
174
|
+
y_label = "Price ($)"
|
|
175
|
+
|
|
176
|
+
fig.add_trace(
|
|
177
|
+
go.Scatter(
|
|
178
|
+
x=data.index,
|
|
179
|
+
y=values,
|
|
180
|
+
name=symbol.upper(),
|
|
181
|
+
line=dict(color=colors[i % len(colors)], width=2),
|
|
182
|
+
)
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
chart_title = title or "Stock Comparison"
|
|
186
|
+
_apply_layout(fig, chart_title, False)
|
|
187
|
+
fig.update_yaxes(title_text=y_label if normalize else "Price ($)")
|
|
188
|
+
|
|
189
|
+
return _save_chart(fig, "comparison")
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def create_technical_chart(
|
|
193
|
+
symbol: str,
|
|
194
|
+
data: pd.DataFrame,
|
|
195
|
+
indicators: list = ["rsi", "macd"],
|
|
196
|
+
title: Optional[str] = None,
|
|
197
|
+
) -> str:
|
|
198
|
+
"""Create a chart with technical indicators."""
|
|
199
|
+
|
|
200
|
+
num_indicators = len(indicators)
|
|
201
|
+
heights = [0.5] + [0.25 / max(1, num_indicators)] * num_indicators + [0.25]
|
|
202
|
+
|
|
203
|
+
fig = make_subplots(
|
|
204
|
+
rows=num_indicators + 2,
|
|
205
|
+
cols=1,
|
|
206
|
+
shared_xaxes=True,
|
|
207
|
+
vertical_spacing=0.03,
|
|
208
|
+
row_heights=heights,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
row = 1
|
|
212
|
+
|
|
213
|
+
# Candlestick
|
|
214
|
+
fig.add_trace(
|
|
215
|
+
go.Candlestick(
|
|
216
|
+
x=data.index,
|
|
217
|
+
open=data["Open"],
|
|
218
|
+
high=data["High"],
|
|
219
|
+
low=data["Low"],
|
|
220
|
+
close=data["Close"],
|
|
221
|
+
name=symbol.upper(),
|
|
222
|
+
increasing_line_color=SIGMA_THEME["positive"],
|
|
223
|
+
decreasing_line_color=SIGMA_THEME["negative"],
|
|
224
|
+
),
|
|
225
|
+
row=row, col=1
|
|
226
|
+
)
|
|
227
|
+
row += 1
|
|
228
|
+
|
|
229
|
+
# Add indicators
|
|
230
|
+
for indicator in indicators:
|
|
231
|
+
if indicator.lower() == "rsi":
|
|
232
|
+
rsi = _calculate_rsi(data["Close"])
|
|
233
|
+
fig.add_trace(
|
|
234
|
+
go.Scatter(x=data.index, y=rsi, name="RSI", line=dict(color=SIGMA_THEME["accent"])),
|
|
235
|
+
row=row, col=1
|
|
236
|
+
)
|
|
237
|
+
fig.add_hline(y=70, line_dash="dash", line_color=SIGMA_THEME["negative"], row=row, col=1)
|
|
238
|
+
fig.add_hline(y=30, line_dash="dash", line_color=SIGMA_THEME["positive"], row=row, col=1)
|
|
239
|
+
fig.update_yaxes(range=[0, 100], row=row, col=1)
|
|
240
|
+
row += 1
|
|
241
|
+
|
|
242
|
+
elif indicator.lower() == "macd":
|
|
243
|
+
macd, signal, hist = _calculate_macd(data["Close"])
|
|
244
|
+
colors = [SIGMA_THEME["positive"] if h >= 0 else SIGMA_THEME["negative"] for h in hist]
|
|
245
|
+
|
|
246
|
+
fig.add_trace(
|
|
247
|
+
go.Bar(x=data.index, y=hist, name="MACD Hist", marker_color=colors, opacity=0.5),
|
|
248
|
+
row=row, col=1
|
|
249
|
+
)
|
|
250
|
+
fig.add_trace(
|
|
251
|
+
go.Scatter(x=data.index, y=macd, name="MACD", line=dict(color=SIGMA_THEME["accent"])),
|
|
252
|
+
row=row, col=1
|
|
253
|
+
)
|
|
254
|
+
fig.add_trace(
|
|
255
|
+
go.Scatter(x=data.index, y=signal, name="Signal", line=dict(color="#f59e0b")),
|
|
256
|
+
row=row, col=1
|
|
257
|
+
)
|
|
258
|
+
row += 1
|
|
259
|
+
|
|
260
|
+
# Volume
|
|
261
|
+
if "Volume" in data.columns:
|
|
262
|
+
colors = [SIGMA_THEME["positive"] if data["Close"].iloc[i] >= data["Open"].iloc[i]
|
|
263
|
+
else SIGMA_THEME["negative"] for i in range(len(data))]
|
|
264
|
+
fig.add_trace(
|
|
265
|
+
go.Bar(x=data.index, y=data["Volume"], name="Volume", marker_color=colors, opacity=0.7),
|
|
266
|
+
row=row, col=1
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
chart_title = title or f"{symbol.upper()} Technical Analysis"
|
|
270
|
+
_apply_layout(fig, chart_title, True)
|
|
271
|
+
|
|
272
|
+
return _save_chart(fig, f"{symbol}_technical")
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def create_performance_chart(
|
|
276
|
+
equity_curve: list,
|
|
277
|
+
title: str = "Portfolio Performance",
|
|
278
|
+
) -> str:
|
|
279
|
+
"""Create a performance/equity curve chart."""
|
|
280
|
+
|
|
281
|
+
fig = go.Figure()
|
|
282
|
+
|
|
283
|
+
x = list(range(len(equity_curve)))
|
|
284
|
+
|
|
285
|
+
# Equity curve
|
|
286
|
+
fig.add_trace(
|
|
287
|
+
go.Scatter(
|
|
288
|
+
x=x,
|
|
289
|
+
y=equity_curve,
|
|
290
|
+
name="Portfolio Value",
|
|
291
|
+
line=dict(color=SIGMA_THEME["accent"], width=2),
|
|
292
|
+
fill="tozeroy",
|
|
293
|
+
fillcolor="rgba(59, 130, 246, 0.1)",
|
|
294
|
+
)
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
# Starting value reference line
|
|
298
|
+
fig.add_hline(
|
|
299
|
+
y=equity_curve[0],
|
|
300
|
+
line_dash="dash",
|
|
301
|
+
line_color=SIGMA_THEME["neutral"],
|
|
302
|
+
annotation_text=f"Start: ${equity_curve[0]:,.0f}",
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
_apply_layout(fig, title, False)
|
|
306
|
+
fig.update_xaxes(title_text="Trading Days")
|
|
307
|
+
fig.update_yaxes(title_text="Portfolio Value ($)")
|
|
308
|
+
|
|
309
|
+
return _save_chart(fig, "performance")
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def create_sector_chart(sector_data: dict) -> str:
|
|
313
|
+
"""Create a sector performance chart."""
|
|
314
|
+
|
|
315
|
+
sectors = list(sector_data.keys())
|
|
316
|
+
values = list(sector_data.values())
|
|
317
|
+
|
|
318
|
+
colors = [SIGMA_THEME["positive"] if v >= 0 else SIGMA_THEME["negative"] for v in values]
|
|
319
|
+
|
|
320
|
+
fig = go.Figure()
|
|
321
|
+
|
|
322
|
+
fig.add_trace(
|
|
323
|
+
go.Bar(
|
|
324
|
+
x=sectors,
|
|
325
|
+
y=values,
|
|
326
|
+
marker_color=colors,
|
|
327
|
+
text=[f"{v:+.2f}%" for v in values],
|
|
328
|
+
textposition="outside",
|
|
329
|
+
)
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
_apply_layout(fig, "Sector Performance", False)
|
|
333
|
+
fig.update_yaxes(title_text="Return (%)")
|
|
334
|
+
|
|
335
|
+
return _save_chart(fig, "sectors")
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def _calculate_rsi(prices: pd.Series, period: int = 14) -> pd.Series:
|
|
339
|
+
"""Calculate RSI indicator."""
|
|
340
|
+
delta = prices.diff()
|
|
341
|
+
gain = (delta.where(delta > 0, 0)).rolling(period).mean()
|
|
342
|
+
loss = (-delta.where(delta < 0, 0)).rolling(period).mean()
|
|
343
|
+
rs = gain / loss
|
|
344
|
+
return 100 - (100 / (1 + rs))
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def _calculate_macd(prices: pd.Series, fast: int = 12, slow: int = 26, signal: int = 9):
|
|
348
|
+
"""Calculate MACD indicator."""
|
|
349
|
+
ema_fast = prices.ewm(span=fast).mean()
|
|
350
|
+
ema_slow = prices.ewm(span=slow).mean()
|
|
351
|
+
macd = ema_fast - ema_slow
|
|
352
|
+
macd_signal = macd.ewm(span=signal).mean()
|
|
353
|
+
macd_hist = macd - macd_signal
|
|
354
|
+
return macd, macd_signal, macd_hist
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def _apply_layout(fig: go.Figure, title: str, has_volume: bool):
|
|
358
|
+
"""Apply Sigma theme to chart."""
|
|
359
|
+
fig.update_layout(
|
|
360
|
+
title=dict(
|
|
361
|
+
text=title,
|
|
362
|
+
font=dict(size=18, color=SIGMA_THEME["text_color"]),
|
|
363
|
+
x=0.5,
|
|
364
|
+
),
|
|
365
|
+
paper_bgcolor=SIGMA_THEME["paper_color"],
|
|
366
|
+
plot_bgcolor=SIGMA_THEME["bg_color"],
|
|
367
|
+
font=dict(color=SIGMA_THEME["text_color"], family="SF Mono, Menlo, monospace"),
|
|
368
|
+
xaxis=dict(
|
|
369
|
+
gridcolor=SIGMA_THEME["grid_color"],
|
|
370
|
+
showgrid=True,
|
|
371
|
+
zeroline=False,
|
|
372
|
+
),
|
|
373
|
+
yaxis=dict(
|
|
374
|
+
gridcolor=SIGMA_THEME["grid_color"],
|
|
375
|
+
showgrid=True,
|
|
376
|
+
zeroline=False,
|
|
377
|
+
title_text="Price ($)",
|
|
378
|
+
),
|
|
379
|
+
legend=dict(
|
|
380
|
+
bgcolor="rgba(0,0,0,0)",
|
|
381
|
+
font=dict(color=SIGMA_THEME["text_color"]),
|
|
382
|
+
orientation="h",
|
|
383
|
+
yanchor="bottom",
|
|
384
|
+
y=1.02,
|
|
385
|
+
xanchor="right",
|
|
386
|
+
x=1,
|
|
387
|
+
),
|
|
388
|
+
margin=dict(l=60, r=40, t=80, b=40),
|
|
389
|
+
xaxis_rangeslider_visible=False,
|
|
390
|
+
hovermode="x unified",
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def _save_chart(fig: go.Figure, name: str) -> str:
|
|
395
|
+
"""Save chart to file and return path."""
|
|
396
|
+
|
|
397
|
+
# Create charts directory
|
|
398
|
+
charts_dir = os.path.expanduser("~/.sigma/charts")
|
|
399
|
+
os.makedirs(charts_dir, exist_ok=True)
|
|
400
|
+
|
|
401
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
402
|
+
filename = f"{name}_{timestamp}.png"
|
|
403
|
+
filepath = os.path.join(charts_dir, filename)
|
|
404
|
+
|
|
405
|
+
fig.write_image(filepath, width=1200, height=800, scale=2)
|
|
406
|
+
|
|
407
|
+
return filepath
|