axiom-coding-agent-setup 1.0.9 → 1.0.11
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.
- package/.agents/skills/agent-browser/SKILL.md +55 -0
- package/.agents/skills/developing-with-streamlit/SKILL.md +220 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-chat-ui/SKILL.md +195 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/SKILL.md +233 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/packaged-components.md +212 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/state-sync.md +149 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/theme-css-variables.md +243 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/troubleshooting.md +121 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-dashboards/SKILL.md +147 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-multipage-apps/SKILL.md +218 -0
- package/.agents/skills/developing-with-streamlit/skills/choosing-streamlit-selection-widgets/SKILL.md +139 -0
- package/.agents/skills/developing-with-streamlit/skills/connecting-streamlit-to-snowflake/SKILL.md +188 -0
- package/.agents/skills/developing-with-streamlit/skills/creating-streamlit-themes/SKILL.md +486 -0
- package/.agents/skills/developing-with-streamlit/skills/displaying-streamlit-data/SKILL.md +199 -0
- package/.agents/skills/developing-with-streamlit/skills/improving-streamlit-design/SKILL.md +191 -0
- package/.agents/skills/developing-with-streamlit/skills/optimizing-streamlit-performance/SKILL.md +323 -0
- package/.agents/skills/developing-with-streamlit/skills/organizing-streamlit-code/SKILL.md +91 -0
- package/.agents/skills/developing-with-streamlit/skills/setting-up-streamlit-environment/SKILL.md +128 -0
- package/.agents/skills/developing-with-streamlit/skills/using-streamlit-cli/SKILL.md +165 -0
- package/.agents/skills/developing-with-streamlit/skills/using-streamlit-custom-components/SKILL.md +170 -0
- package/.agents/skills/developing-with-streamlit/skills/using-streamlit-layouts/SKILL.md +229 -0
- package/.agents/skills/developing-with-streamlit/skills/using-streamlit-markdown/SKILL.md +207 -0
- package/.agents/skills/developing-with-streamlit/skills/using-streamlit-session-state/SKILL.md +144 -0
- package/.agents/skills/developing-with-streamlit/templates/.gitattributes +3 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/README.md +188 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-companies/pyproject.toml +12 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-companies/streamlit_app.py +365 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute/pyproject.toml +12 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute/streamlit_app.py +461 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/.streamlit/secrets.toml.example +20 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/pyproject.toml +11 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/snowflake.yml +18 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/streamlit_app.py +527 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-feature-usage/pyproject.toml +12 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-feature-usage/streamlit_app.py +307 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics/pyproject.toml +12 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics/streamlit_app.py +426 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/.streamlit/secrets.toml.example +20 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/pyproject.toml +11 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/snowflake.yml +18 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/streamlit_app.py +463 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-seattle-weather/pyproject.toml +12 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-seattle-weather/streamlit_app.py +252 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers/pyproject.toml +12 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers/streamlit_app.py +342 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/.streamlit/secrets.toml.example +20 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/pyproject.toml +11 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/snowflake.yml +18 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/streamlit_app.py +384 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/README.md +128 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/dracula.toml +39 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/github.toml +37 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/minimal.toml +39 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/nord.toml +39 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/snowflake.toml +42 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/solarized-light.toml +38 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/spotify.toml +34 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/stripe.toml +35 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_shared/streamlit_app.py +336 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_templates/pyproject.toml.tmpl +12 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/dracula/.streamlit/config.toml +39 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/dracula/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/dracula/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/github/.streamlit/config.toml +37 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/github/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/github/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/manage.py +332 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/minimal/.streamlit/config.toml +39 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/minimal/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/minimal/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/nord/.streamlit/config.toml +39 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/nord/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/nord/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/snowflake/.streamlit/config.toml +42 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/snowflake/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/snowflake/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/solarized-light/.streamlit/config.toml +38 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/solarized-light/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/solarized-light/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/spotify/.streamlit/config.toml +34 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/spotify/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/spotify/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/stripe/.streamlit/config.toml +35 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/stripe/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/stripe/streamlit_app.py +337 -0
- package/.agents/skills/fastapi/SKILL.md +436 -0
- package/.agents/skills/fastapi/references/dependencies.md +142 -0
- package/.agents/skills/fastapi/references/other-tools.md +76 -0
- package/.agents/skills/fastapi/references/streaming.md +105 -0
- package/.agents/skills/frontend-design/LICENSE.txt +177 -0
- package/.agents/skills/frontend-design/SKILL.md +42 -0
- package/.agents/skills/gradio/SKILL.md +84 -0
- package/.agents/skills/gradio/references/api-signatures.md +115 -0
- package/.agents/skills/gradio/references/event-listeners.md +130 -0
- package/.agents/skills/gradio/references/examples.md +613 -0
- package/.agents/skills/ui-ux-pro-max/SKILL.md +659 -0
- package/.agents/skills/ui-ux-pro-max/data +1 -0
- package/.agents/skills/ui-ux-pro-max/scripts +1 -0
- package/.agents/stack.md +187 -187
- package/.env.axiom +8 -0
- package/AGENTS.md +2 -1
- package/README.md +19 -3
- package/bin/cli.js +11 -1
- package/opencode.json +12 -4
- package/package.json +1 -1
- package/skills-lock.json +36 -0
- package/.agents/skills/n8n-patterns/SKILL.md +0 -272
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import streamlit as st
|
|
2
|
+
import altair as alt
|
|
3
|
+
import vega_datasets
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
full_df = vega_datasets.data("seattle_weather")
|
|
7
|
+
|
|
8
|
+
st.set_page_config(
|
|
9
|
+
# Title and icon for the browser's tab bar:
|
|
10
|
+
page_title="Seattle Weather",
|
|
11
|
+
page_icon=":mostly_sunny:",
|
|
12
|
+
# Make the content take up the width of the page:
|
|
13
|
+
layout="wide",
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
"""
|
|
18
|
+
# Seattle Weather
|
|
19
|
+
|
|
20
|
+
Let's explore the [classic Seattle Weather
|
|
21
|
+
dataset](https://altair-viz.github.io/case_studies/exploring-weather.html)!
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
"" # Add a little vertical space. Same as st.write("").
|
|
25
|
+
""
|
|
26
|
+
|
|
27
|
+
"""
|
|
28
|
+
## 2015 Summary
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
""
|
|
32
|
+
|
|
33
|
+
df_2015 = full_df[full_df["date"].dt.year == 2015]
|
|
34
|
+
df_2014 = full_df[full_df["date"].dt.year == 2014]
|
|
35
|
+
|
|
36
|
+
max_temp_2015 = df_2015["temp_max"].max()
|
|
37
|
+
max_temp_2014 = df_2014["temp_max"].max()
|
|
38
|
+
|
|
39
|
+
min_temp_2015 = df_2015["temp_min"].min()
|
|
40
|
+
min_temp_2014 = df_2014["temp_min"].min()
|
|
41
|
+
|
|
42
|
+
max_wind_2015 = df_2015["wind"].max()
|
|
43
|
+
max_wind_2014 = df_2014["wind"].max()
|
|
44
|
+
|
|
45
|
+
min_wind_2015 = df_2015["wind"].min()
|
|
46
|
+
min_wind_2014 = df_2014["wind"].min()
|
|
47
|
+
|
|
48
|
+
max_prec_2015 = df_2015["precipitation"].max()
|
|
49
|
+
max_prec_2014 = df_2014["precipitation"].max()
|
|
50
|
+
|
|
51
|
+
min_prec_2015 = df_2015["precipitation"].min()
|
|
52
|
+
min_prec_2014 = df_2014["precipitation"].min()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
with st.container(horizontal=True, gap="medium"):
|
|
56
|
+
cols = st.columns(2, gap="medium", width=300)
|
|
57
|
+
|
|
58
|
+
with cols[0]:
|
|
59
|
+
st.metric(
|
|
60
|
+
"Max temperature",
|
|
61
|
+
f"{max_temp_2015:0.1f}C",
|
|
62
|
+
delta=f"{max_temp_2015 - max_temp_2014:0.1f}C",
|
|
63
|
+
width="content",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
with cols[1]:
|
|
67
|
+
st.metric(
|
|
68
|
+
"Min temperature",
|
|
69
|
+
f"{min_temp_2015:0.1f}C",
|
|
70
|
+
delta=f"{min_temp_2015 - min_temp_2014:0.1f}C",
|
|
71
|
+
width="content",
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
cols = st.columns(2, gap="medium", width=300)
|
|
75
|
+
|
|
76
|
+
with cols[0]:
|
|
77
|
+
st.metric(
|
|
78
|
+
"Max precipitation",
|
|
79
|
+
f"{max_prec_2015:0.1f}mm",
|
|
80
|
+
delta=f"{max_prec_2015 - max_prec_2014:0.1f}mm",
|
|
81
|
+
width="content",
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
with cols[1]:
|
|
85
|
+
st.metric(
|
|
86
|
+
"Min precipitation",
|
|
87
|
+
f"{min_prec_2015:0.1f}mm",
|
|
88
|
+
delta=f"{min_prec_2015 - min_prec_2014:0.1f}mm",
|
|
89
|
+
width="content",
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
cols = st.columns(2, gap="medium", width=300)
|
|
93
|
+
|
|
94
|
+
with cols[0]:
|
|
95
|
+
st.metric(
|
|
96
|
+
"Max wind",
|
|
97
|
+
f"{max_wind_2015:0.1f}m/s",
|
|
98
|
+
delta=f"{max_wind_2015 - max_wind_2014:0.1f}m/s",
|
|
99
|
+
width="content",
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
with cols[1]:
|
|
103
|
+
st.metric(
|
|
104
|
+
"Min wind",
|
|
105
|
+
f"{min_wind_2015:0.1f}m/s",
|
|
106
|
+
delta=f"{min_wind_2015 - min_wind_2014:0.1f}m/s",
|
|
107
|
+
width="content",
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
weather_icons = {
|
|
111
|
+
"sun": "sunny",
|
|
112
|
+
"snow": "weather_snowy",
|
|
113
|
+
"rain": "rainy",
|
|
114
|
+
"fog": "foggy",
|
|
115
|
+
"drizzle": "rainy",
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
cols = st.columns(2, gap="large")
|
|
119
|
+
|
|
120
|
+
with cols[0]:
|
|
121
|
+
weather_name = (
|
|
122
|
+
full_df["weather"].value_counts().head(1).reset_index()["weather"][0]
|
|
123
|
+
)
|
|
124
|
+
st.metric(
|
|
125
|
+
"Most common weather",
|
|
126
|
+
f":material/{weather_icons[weather_name]}: {weather_name.upper()}",
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
with cols[1]:
|
|
130
|
+
weather_name = (
|
|
131
|
+
full_df["weather"].value_counts().tail(1).reset_index()["weather"][0]
|
|
132
|
+
)
|
|
133
|
+
st.metric(
|
|
134
|
+
"Least common weather",
|
|
135
|
+
f":material/{weather_icons[weather_name]}: {weather_name.upper()}",
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
""
|
|
139
|
+
""
|
|
140
|
+
|
|
141
|
+
"""
|
|
142
|
+
## Compare different years
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
YEARS = full_df["date"].dt.year.unique()
|
|
146
|
+
selected_years = st.pills(
|
|
147
|
+
"Years to compare", YEARS, default=YEARS, selection_mode="multi"
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
if not selected_years:
|
|
151
|
+
st.warning("You must select at least 1 year.", icon=":material/warning:")
|
|
152
|
+
|
|
153
|
+
df = full_df[full_df["date"].dt.year.isin(selected_years)]
|
|
154
|
+
|
|
155
|
+
cols = st.columns([3, 1])
|
|
156
|
+
|
|
157
|
+
with cols[0].container(border=True, height="stretch"):
|
|
158
|
+
"### 🌡️ Temperature"
|
|
159
|
+
|
|
160
|
+
st.altair_chart(
|
|
161
|
+
alt.Chart(df)
|
|
162
|
+
.mark_bar(width=1)
|
|
163
|
+
.encode(
|
|
164
|
+
alt.X("monthdate(date):T").title("date"),
|
|
165
|
+
alt.Y("temp_max:Q").title("temperature range (C)"),
|
|
166
|
+
alt.Y2("temp_min:Q"),
|
|
167
|
+
alt.Color("year(date):N").title("year"),
|
|
168
|
+
alt.XOffset("year(date):N"),
|
|
169
|
+
tooltip=[
|
|
170
|
+
alt.Tooltip("monthdate(date):T", title="Date"),
|
|
171
|
+
alt.Tooltip("temp_max:Q", title="Max Temp (C)"),
|
|
172
|
+
alt.Tooltip("temp_min:Q", title="Min Temp (C)"),
|
|
173
|
+
alt.Tooltip("year(date):N", title="Year"),
|
|
174
|
+
],
|
|
175
|
+
)
|
|
176
|
+
.configure_legend(orient="bottom")
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
with cols[1].container(border=True, height="stretch"):
|
|
180
|
+
"### Weather distribution"
|
|
181
|
+
|
|
182
|
+
st.altair_chart(
|
|
183
|
+
alt.Chart(df)
|
|
184
|
+
.mark_arc()
|
|
185
|
+
.encode(
|
|
186
|
+
alt.Theta("count()"),
|
|
187
|
+
alt.Color("weather:N"),
|
|
188
|
+
)
|
|
189
|
+
.configure_legend(orient="bottom")
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
cols = st.columns(2)
|
|
194
|
+
|
|
195
|
+
with cols[0].container(border=True, height="stretch"):
|
|
196
|
+
"### 💨 Wind"
|
|
197
|
+
|
|
198
|
+
# Prepare data for st.line_chart - pivot by year
|
|
199
|
+
wind_df = df.copy()
|
|
200
|
+
wind_df["month_day"] = wind_df["date"].dt.strftime("%m-%d")
|
|
201
|
+
wind_df["year"] = wind_df["date"].dt.year
|
|
202
|
+
|
|
203
|
+
# Calculate 14-day rolling average per year
|
|
204
|
+
wind_pivot = wind_df.pivot_table(
|
|
205
|
+
index="month_day",
|
|
206
|
+
columns="year",
|
|
207
|
+
values="wind",
|
|
208
|
+
aggfunc="mean"
|
|
209
|
+
).sort_index()
|
|
210
|
+
|
|
211
|
+
st.line_chart(wind_pivot, height=300)
|
|
212
|
+
|
|
213
|
+
with cols[1].container(border=True, height="stretch"):
|
|
214
|
+
"### 🌧️ Precipitation"
|
|
215
|
+
|
|
216
|
+
st.altair_chart(
|
|
217
|
+
alt.Chart(df)
|
|
218
|
+
.mark_bar()
|
|
219
|
+
.encode(
|
|
220
|
+
alt.X("month(date):O").title("month"),
|
|
221
|
+
alt.Y("sum(precipitation):Q").title("precipitation (mm)"),
|
|
222
|
+
alt.Color("year(date):N").title("year"),
|
|
223
|
+
tooltip=[
|
|
224
|
+
alt.Tooltip("month(date):O", title="Month"),
|
|
225
|
+
alt.Tooltip("sum(precipitation):Q", title="Precipitation (mm)"),
|
|
226
|
+
alt.Tooltip("year(date):N", title="Year"),
|
|
227
|
+
],
|
|
228
|
+
)
|
|
229
|
+
.configure_legend(orient="bottom")
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
cols = st.columns(2)
|
|
233
|
+
|
|
234
|
+
with cols[0].container(border=True, height="stretch"):
|
|
235
|
+
"### Monthly weather breakdown"
|
|
236
|
+
""
|
|
237
|
+
|
|
238
|
+
st.altair_chart(
|
|
239
|
+
alt.Chart(df)
|
|
240
|
+
.mark_bar()
|
|
241
|
+
.encode(
|
|
242
|
+
alt.X("month(date):O", title="month"),
|
|
243
|
+
alt.Y("count():Q", title="days").stack("normalize"),
|
|
244
|
+
alt.Color("weather:N"),
|
|
245
|
+
)
|
|
246
|
+
.configure_legend(orient="bottom")
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
with cols[1].container(border=True, height="stretch"):
|
|
250
|
+
"### Raw data"
|
|
251
|
+
|
|
252
|
+
st.dataframe(df)
|
package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers/pyproject.toml
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "dashboard-stock-peers"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "Stock peer analysis dashboard: easily compare stocks against others in their peer group"
|
|
5
|
+
requires-python = ">=3.11"
|
|
6
|
+
dependencies = [
|
|
7
|
+
"altair>=5.5.0",
|
|
8
|
+
"pandas>=2.2.3",
|
|
9
|
+
"snowflake-connector-python>=3.3.0",
|
|
10
|
+
"streamlit[snowflake]>=1.54.0",
|
|
11
|
+
"yfinance>=0.2.55",
|
|
12
|
+
]
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import streamlit as st
|
|
2
|
+
import yfinance as yf
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import altair as alt
|
|
5
|
+
|
|
6
|
+
st.set_page_config(
|
|
7
|
+
page_title="Stock peer analysis dashboard",
|
|
8
|
+
page_icon=":chart_with_upwards_trend:",
|
|
9
|
+
layout="wide",
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
"""
|
|
13
|
+
# :material/query_stats: Stock peer analysis
|
|
14
|
+
|
|
15
|
+
Easily compare stocks against others in their peer group.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
"" # Add some space.
|
|
19
|
+
|
|
20
|
+
cols = st.columns([1, 3])
|
|
21
|
+
# Will declare right cell later to avoid showing it when no data.
|
|
22
|
+
|
|
23
|
+
STOCKS = [
|
|
24
|
+
"AAPL",
|
|
25
|
+
"ABBV",
|
|
26
|
+
"ACN",
|
|
27
|
+
"ADBE",
|
|
28
|
+
"ADP",
|
|
29
|
+
"AMD",
|
|
30
|
+
"AMGN",
|
|
31
|
+
"AMT",
|
|
32
|
+
"AMZN",
|
|
33
|
+
"APD",
|
|
34
|
+
"AVGO",
|
|
35
|
+
"AXP",
|
|
36
|
+
"BA",
|
|
37
|
+
"BK",
|
|
38
|
+
"BKNG",
|
|
39
|
+
"BMY",
|
|
40
|
+
"BRK.B",
|
|
41
|
+
"BSX",
|
|
42
|
+
"C",
|
|
43
|
+
"CAT",
|
|
44
|
+
"CI",
|
|
45
|
+
"CL",
|
|
46
|
+
"CMCSA",
|
|
47
|
+
"COST",
|
|
48
|
+
"CRM",
|
|
49
|
+
"CSCO",
|
|
50
|
+
"CVX",
|
|
51
|
+
"DE",
|
|
52
|
+
"DHR",
|
|
53
|
+
"DIS",
|
|
54
|
+
"DUK",
|
|
55
|
+
"ELV",
|
|
56
|
+
"EOG",
|
|
57
|
+
"EQR",
|
|
58
|
+
"FDX",
|
|
59
|
+
"GD",
|
|
60
|
+
"GE",
|
|
61
|
+
"GILD",
|
|
62
|
+
"GOOG",
|
|
63
|
+
"GOOGL",
|
|
64
|
+
"HD",
|
|
65
|
+
"HON",
|
|
66
|
+
"HUM",
|
|
67
|
+
"IBM",
|
|
68
|
+
"ICE",
|
|
69
|
+
"INTC",
|
|
70
|
+
"ISRG",
|
|
71
|
+
"JNJ",
|
|
72
|
+
"JPM",
|
|
73
|
+
"KO",
|
|
74
|
+
"LIN",
|
|
75
|
+
"LLY",
|
|
76
|
+
"LMT",
|
|
77
|
+
"LOW",
|
|
78
|
+
"MA",
|
|
79
|
+
"MCD",
|
|
80
|
+
"MDLZ",
|
|
81
|
+
"META",
|
|
82
|
+
"MMC",
|
|
83
|
+
"MO",
|
|
84
|
+
"MRK",
|
|
85
|
+
"MSFT",
|
|
86
|
+
"NEE",
|
|
87
|
+
"NFLX",
|
|
88
|
+
"NKE",
|
|
89
|
+
"NOW",
|
|
90
|
+
"NVDA",
|
|
91
|
+
"ORCL",
|
|
92
|
+
"PEP",
|
|
93
|
+
"PFE",
|
|
94
|
+
"PG",
|
|
95
|
+
"PLD",
|
|
96
|
+
"PM",
|
|
97
|
+
"PSA",
|
|
98
|
+
"REGN",
|
|
99
|
+
"RTX",
|
|
100
|
+
"SBUX",
|
|
101
|
+
"SCHW",
|
|
102
|
+
"SLB",
|
|
103
|
+
"SO",
|
|
104
|
+
"SPGI",
|
|
105
|
+
"T",
|
|
106
|
+
"TJX",
|
|
107
|
+
"TMO",
|
|
108
|
+
"TSLA",
|
|
109
|
+
"TXN",
|
|
110
|
+
"UNH",
|
|
111
|
+
"UNP",
|
|
112
|
+
"UPS",
|
|
113
|
+
"V",
|
|
114
|
+
"VZ",
|
|
115
|
+
"WFC",
|
|
116
|
+
"WM",
|
|
117
|
+
"WMT",
|
|
118
|
+
"XOM",
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
DEFAULT_STOCKS = ["AAPL", "MSFT", "GOOGL", "NVDA", "AMZN", "TSLA", "META"]
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def stocks_to_str(stocks):
|
|
125
|
+
return ",".join(stocks)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
if "tickers_input" not in st.session_state:
|
|
129
|
+
st.session_state.tickers_input = st.query_params.get(
|
|
130
|
+
"stocks", stocks_to_str(DEFAULT_STOCKS)
|
|
131
|
+
).split(",")
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
# Callback to update query param when input changes
|
|
135
|
+
def update_query_param():
|
|
136
|
+
if st.session_state.tickers_input:
|
|
137
|
+
st.query_params["stocks"] = stocks_to_str(st.session_state.tickers_input)
|
|
138
|
+
else:
|
|
139
|
+
st.query_params.pop("stocks", None)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
top_left_cell = cols[0].container(
|
|
143
|
+
border=True, height="stretch", vertical_alignment="center"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
with top_left_cell:
|
|
147
|
+
# Selectbox for stock tickers
|
|
148
|
+
tickers = st.multiselect(
|
|
149
|
+
"Stock tickers",
|
|
150
|
+
options=sorted(set(STOCKS) | set(st.session_state.tickers_input)),
|
|
151
|
+
default=st.session_state.tickers_input,
|
|
152
|
+
placeholder="Choose stocks to compare. Example: NVDA",
|
|
153
|
+
accept_new_options=True,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# Time horizon selector
|
|
157
|
+
horizon_map = {
|
|
158
|
+
"1 Month": "1mo",
|
|
159
|
+
"3 Months": "3mo",
|
|
160
|
+
"6 Months": "6mo",
|
|
161
|
+
"1 Year": "1y",
|
|
162
|
+
"5 Years": "5y",
|
|
163
|
+
"10 Years": "10y",
|
|
164
|
+
"20 Years": "20y",
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
with top_left_cell:
|
|
168
|
+
# Buttons for picking time horizon
|
|
169
|
+
horizon = st.pills(
|
|
170
|
+
"Time horizon",
|
|
171
|
+
options=list(horizon_map.keys()),
|
|
172
|
+
default="6 Months",
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
tickers = [t.upper() for t in tickers]
|
|
176
|
+
|
|
177
|
+
# Update query param when text input changes
|
|
178
|
+
if tickers:
|
|
179
|
+
st.query_params["stocks"] = stocks_to_str(tickers)
|
|
180
|
+
else:
|
|
181
|
+
# Clear the param if input is empty
|
|
182
|
+
st.query_params.pop("stocks", None)
|
|
183
|
+
|
|
184
|
+
if not tickers:
|
|
185
|
+
top_left_cell.info("Pick some stocks to compare", icon=":material/info:")
|
|
186
|
+
st.stop()
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
right_cell = cols[1].container(
|
|
190
|
+
border=True, height="stretch", vertical_alignment="center"
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@st.cache_resource(show_spinner=False, ttl="6h")
|
|
195
|
+
def load_data(tickers, period):
|
|
196
|
+
tickers_obj = yf.Tickers(tickers)
|
|
197
|
+
data = tickers_obj.history(period=period)
|
|
198
|
+
if data is None:
|
|
199
|
+
raise RuntimeError("YFinance returned no data.")
|
|
200
|
+
return data["Close"]
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
# Load the data
|
|
204
|
+
try:
|
|
205
|
+
data = load_data(tickers, horizon_map[horizon])
|
|
206
|
+
except yf.exceptions.YFRateLimitError as e:
|
|
207
|
+
st.warning("YFinance is rate-limiting us :(\nTry again later.")
|
|
208
|
+
load_data.clear() # Remove the bad cache entry.
|
|
209
|
+
st.stop()
|
|
210
|
+
|
|
211
|
+
empty_columns = data.columns[data.isna().all()].tolist()
|
|
212
|
+
|
|
213
|
+
if empty_columns:
|
|
214
|
+
st.error(f"Error loading data for the tickers: {', '.join(empty_columns)}.")
|
|
215
|
+
st.stop()
|
|
216
|
+
|
|
217
|
+
# Normalize prices (start at 1)
|
|
218
|
+
normalized = data.div(data.iloc[0])
|
|
219
|
+
|
|
220
|
+
latest_norm_values = {normalized[ticker].iat[-1]: ticker for ticker in tickers}
|
|
221
|
+
max_norm_value = max(latest_norm_values.items())
|
|
222
|
+
min_norm_value = min(latest_norm_values.items())
|
|
223
|
+
|
|
224
|
+
bottom_left_cell = cols[0].container(
|
|
225
|
+
border=True, height="stretch", vertical_alignment="center"
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
with bottom_left_cell:
|
|
229
|
+
cols = st.columns(2)
|
|
230
|
+
cols[0].metric(
|
|
231
|
+
"Best stock",
|
|
232
|
+
max_norm_value[1],
|
|
233
|
+
delta=f"{round(max_norm_value[0] * 100)}%",
|
|
234
|
+
width="content",
|
|
235
|
+
)
|
|
236
|
+
cols[1].metric(
|
|
237
|
+
"Worst stock",
|
|
238
|
+
min_norm_value[1],
|
|
239
|
+
delta=f"{round(min_norm_value[0] * 100)}%",
|
|
240
|
+
width="content",
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
# Plot normalized prices
|
|
245
|
+
with right_cell:
|
|
246
|
+
st.altair_chart(
|
|
247
|
+
alt.Chart(
|
|
248
|
+
normalized.reset_index().melt(
|
|
249
|
+
id_vars=["Date"], var_name="Stock", value_name="Normalized price"
|
|
250
|
+
)
|
|
251
|
+
)
|
|
252
|
+
.mark_line()
|
|
253
|
+
.encode(
|
|
254
|
+
alt.X("Date:T"),
|
|
255
|
+
alt.Y("Normalized price:Q").scale(zero=False),
|
|
256
|
+
alt.Color("Stock:N"),
|
|
257
|
+
)
|
|
258
|
+
.properties(height=400)
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
""
|
|
262
|
+
""
|
|
263
|
+
|
|
264
|
+
# Plot individual stock vs peer average
|
|
265
|
+
"""
|
|
266
|
+
## Individual stocks vs peer average
|
|
267
|
+
|
|
268
|
+
For the analysis below, the "peer average" when analyzing stock X always
|
|
269
|
+
excludes X itself.
|
|
270
|
+
"""
|
|
271
|
+
|
|
272
|
+
if len(tickers) <= 1:
|
|
273
|
+
st.warning("Pick 2 or more tickers to compare them")
|
|
274
|
+
st.stop()
|
|
275
|
+
|
|
276
|
+
NUM_COLS = 4
|
|
277
|
+
cols = st.columns(NUM_COLS)
|
|
278
|
+
|
|
279
|
+
for i, ticker in enumerate(tickers):
|
|
280
|
+
# Calculate peer average (excluding current stock)
|
|
281
|
+
peers = normalized.drop(columns=[ticker])
|
|
282
|
+
peer_avg = peers.mean(axis=1)
|
|
283
|
+
|
|
284
|
+
# Create DataFrame with peer average.
|
|
285
|
+
plot_data = pd.DataFrame(
|
|
286
|
+
{
|
|
287
|
+
"Date": normalized.index,
|
|
288
|
+
ticker: normalized[ticker],
|
|
289
|
+
"Peer average": peer_avg,
|
|
290
|
+
}
|
|
291
|
+
).melt(id_vars=["Date"], var_name="Series", value_name="Price")
|
|
292
|
+
|
|
293
|
+
chart = (
|
|
294
|
+
alt.Chart(plot_data)
|
|
295
|
+
.mark_line()
|
|
296
|
+
.encode(
|
|
297
|
+
alt.X("Date:T"),
|
|
298
|
+
alt.Y("Price:Q").scale(zero=False),
|
|
299
|
+
alt.Color(
|
|
300
|
+
"Series:N",
|
|
301
|
+
scale=alt.Scale(domain=[ticker, "Peer average"], range=["red", "gray"]),
|
|
302
|
+
legend=alt.Legend(orient="bottom"),
|
|
303
|
+
),
|
|
304
|
+
alt.Tooltip(["Date", "Series", "Price"]),
|
|
305
|
+
)
|
|
306
|
+
.properties(title=f"{ticker} vs peer average", height=300)
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
cell = cols[(i * 2) % NUM_COLS].container(border=True)
|
|
310
|
+
cell.write("")
|
|
311
|
+
cell.altair_chart(chart)
|
|
312
|
+
|
|
313
|
+
# Create Delta chart
|
|
314
|
+
plot_data = pd.DataFrame(
|
|
315
|
+
{
|
|
316
|
+
"Date": normalized.index,
|
|
317
|
+
"Delta": normalized[ticker] - peer_avg,
|
|
318
|
+
}
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
chart = (
|
|
322
|
+
alt.Chart(plot_data)
|
|
323
|
+
.mark_area()
|
|
324
|
+
.encode(
|
|
325
|
+
alt.X("Date:T"),
|
|
326
|
+
alt.Y("Delta:Q").scale(zero=False),
|
|
327
|
+
)
|
|
328
|
+
.properties(title=f"{ticker} minus peer average", height=300)
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
cell = cols[(i * 2 + 1) % NUM_COLS].container(border=True)
|
|
332
|
+
cell.write("")
|
|
333
|
+
cell.altair_chart(chart)
|
|
334
|
+
|
|
335
|
+
""
|
|
336
|
+
""
|
|
337
|
+
|
|
338
|
+
"""
|
|
339
|
+
## Raw data
|
|
340
|
+
"""
|
|
341
|
+
|
|
342
|
+
data
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Copy this file to secrets.toml and fill in your credentials
|
|
2
|
+
# NEVER commit secrets.toml to version control — it contains sensitive
|
|
3
|
+
# credentials that would be exposed in your repository.
|
|
4
|
+
#
|
|
5
|
+
# To find your account and host values, run:
|
|
6
|
+
# snow connection list
|
|
7
|
+
# Look for the "account" and "host" fields in your connection config.
|
|
8
|
+
|
|
9
|
+
[connections.snowflake]
|
|
10
|
+
account = "<YOUR_ACCOUNT>" # e.g., "ORGNAME-ACCTNAME" from `snow connection list`
|
|
11
|
+
host = "<YOUR_HOST>" # e.g., "myaccount.snowflakecomputing.com" (required for private link / internal accounts)
|
|
12
|
+
user = "<YOUR_USER>"
|
|
13
|
+
authenticator = "externalbrowser" # Opens browser for SSO login
|
|
14
|
+
warehouse = "<YOUR_WAREHOUSE>"
|
|
15
|
+
database = "<YOUR_DATABASE>"
|
|
16
|
+
schema = "<YOUR_SCHEMA>"
|
|
17
|
+
|
|
18
|
+
# Alternative: use password instead of SSO (less secure)
|
|
19
|
+
# password = "<YOUR_PASSWORD>"
|
|
20
|
+
# Remove the authenticator line above if using password
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "dashboard-stock-peers-snowflake"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "Stock peer analysis dashboard with Snowflake connection"
|
|
5
|
+
requires-python = ">=3.11"
|
|
6
|
+
dependencies = [
|
|
7
|
+
"altair>=5.5.0",
|
|
8
|
+
"pandas>=2.2.3",
|
|
9
|
+
"snowflake-connector-python>=3.3.0",
|
|
10
|
+
"streamlit[snowflake]>=1.54.0",
|
|
11
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
definition_version: 2
|
|
2
|
+
entities:
|
|
3
|
+
DASHBOARD_STOCK_PEERS_SNOWFLAKE:
|
|
4
|
+
type: streamlit
|
|
5
|
+
identifier:
|
|
6
|
+
name: DASHBOARD_STOCK_PEERS_SNOWFLAKE
|
|
7
|
+
database: <FROM_CONNECTION> # Use: snow connection list
|
|
8
|
+
schema: <FROM_CONNECTION>
|
|
9
|
+
query_warehouse: <FROM_CONNECTION>
|
|
10
|
+
runtime_name: SYSTEM$ST_CONTAINER_RUNTIME_PY3_11
|
|
11
|
+
# List available: SHOW EXTERNAL ACCESS INTEGRATIONS;
|
|
12
|
+
# Common: PYPI_ACCESS_INTEGRATION (verify exists)
|
|
13
|
+
external_access_integrations:
|
|
14
|
+
- <YOUR_PYPI_INTEGRATION>
|
|
15
|
+
main_file: streamlit_app.py
|
|
16
|
+
artifacts:
|
|
17
|
+
- streamlit_app.py
|
|
18
|
+
- pyproject.toml
|