axiom-coding-agent-setup 1.0.10 → 1.0.12

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.
Files changed (129) hide show
  1. package/.agents/skills/agent-browser/SKILL.md +55 -0
  2. package/.agents/skills/developing-with-streamlit/SKILL.md +220 -0
  3. package/.agents/skills/developing-with-streamlit/skills/building-streamlit-chat-ui/SKILL.md +195 -0
  4. package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/SKILL.md +233 -0
  5. package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/packaged-components.md +212 -0
  6. package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/state-sync.md +149 -0
  7. package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/theme-css-variables.md +243 -0
  8. package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/troubleshooting.md +121 -0
  9. package/.agents/skills/developing-with-streamlit/skills/building-streamlit-dashboards/SKILL.md +147 -0
  10. package/.agents/skills/developing-with-streamlit/skills/building-streamlit-multipage-apps/SKILL.md +218 -0
  11. package/.agents/skills/developing-with-streamlit/skills/choosing-streamlit-selection-widgets/SKILL.md +139 -0
  12. package/.agents/skills/developing-with-streamlit/skills/connecting-streamlit-to-snowflake/SKILL.md +188 -0
  13. package/.agents/skills/developing-with-streamlit/skills/creating-streamlit-themes/SKILL.md +486 -0
  14. package/.agents/skills/developing-with-streamlit/skills/displaying-streamlit-data/SKILL.md +199 -0
  15. package/.agents/skills/developing-with-streamlit/skills/improving-streamlit-design/SKILL.md +191 -0
  16. package/.agents/skills/developing-with-streamlit/skills/optimizing-streamlit-performance/SKILL.md +323 -0
  17. package/.agents/skills/developing-with-streamlit/skills/organizing-streamlit-code/SKILL.md +91 -0
  18. package/.agents/skills/developing-with-streamlit/skills/setting-up-streamlit-environment/SKILL.md +128 -0
  19. package/.agents/skills/developing-with-streamlit/skills/using-streamlit-cli/SKILL.md +165 -0
  20. package/.agents/skills/developing-with-streamlit/skills/using-streamlit-custom-components/SKILL.md +170 -0
  21. package/.agents/skills/developing-with-streamlit/skills/using-streamlit-layouts/SKILL.md +229 -0
  22. package/.agents/skills/developing-with-streamlit/skills/using-streamlit-markdown/SKILL.md +207 -0
  23. package/.agents/skills/developing-with-streamlit/skills/using-streamlit-session-state/SKILL.md +144 -0
  24. package/.agents/skills/developing-with-streamlit/templates/.gitattributes +3 -0
  25. package/.agents/skills/developing-with-streamlit/templates/apps/README.md +188 -0
  26. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-companies/pyproject.toml +12 -0
  27. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-companies/streamlit_app.py +365 -0
  28. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute/pyproject.toml +12 -0
  29. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute/streamlit_app.py +461 -0
  30. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/.streamlit/secrets.toml.example +20 -0
  31. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/pyproject.toml +11 -0
  32. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/snowflake.yml +18 -0
  33. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/streamlit_app.py +527 -0
  34. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-feature-usage/pyproject.toml +12 -0
  35. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-feature-usage/streamlit_app.py +307 -0
  36. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics/pyproject.toml +12 -0
  37. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics/streamlit_app.py +426 -0
  38. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/.streamlit/secrets.toml.example +20 -0
  39. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/pyproject.toml +11 -0
  40. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/snowflake.yml +18 -0
  41. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/streamlit_app.py +463 -0
  42. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-seattle-weather/pyproject.toml +12 -0
  43. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-seattle-weather/streamlit_app.py +252 -0
  44. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers/pyproject.toml +12 -0
  45. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers/streamlit_app.py +342 -0
  46. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/.streamlit/secrets.toml.example +20 -0
  47. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/pyproject.toml +11 -0
  48. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/snowflake.yml +18 -0
  49. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/streamlit_app.py +384 -0
  50. package/.agents/skills/developing-with-streamlit/templates/themes/README.md +128 -0
  51. package/.agents/skills/developing-with-streamlit/templates/themes/_configs/dracula.toml +39 -0
  52. package/.agents/skills/developing-with-streamlit/templates/themes/_configs/github.toml +37 -0
  53. package/.agents/skills/developing-with-streamlit/templates/themes/_configs/minimal.toml +39 -0
  54. package/.agents/skills/developing-with-streamlit/templates/themes/_configs/nord.toml +39 -0
  55. package/.agents/skills/developing-with-streamlit/templates/themes/_configs/snowflake.toml +42 -0
  56. package/.agents/skills/developing-with-streamlit/templates/themes/_configs/solarized-light.toml +38 -0
  57. package/.agents/skills/developing-with-streamlit/templates/themes/_configs/spotify.toml +34 -0
  58. package/.agents/skills/developing-with-streamlit/templates/themes/_configs/stripe.toml +35 -0
  59. package/.agents/skills/developing-with-streamlit/templates/themes/_shared/streamlit_app.py +336 -0
  60. package/.agents/skills/developing-with-streamlit/templates/themes/_templates/pyproject.toml.tmpl +12 -0
  61. package/.agents/skills/developing-with-streamlit/templates/themes/dracula/.streamlit/config.toml +39 -0
  62. package/.agents/skills/developing-with-streamlit/templates/themes/dracula/pyproject.toml +10 -0
  63. package/.agents/skills/developing-with-streamlit/templates/themes/dracula/streamlit_app.py +337 -0
  64. package/.agents/skills/developing-with-streamlit/templates/themes/github/.streamlit/config.toml +37 -0
  65. package/.agents/skills/developing-with-streamlit/templates/themes/github/pyproject.toml +10 -0
  66. package/.agents/skills/developing-with-streamlit/templates/themes/github/streamlit_app.py +337 -0
  67. package/.agents/skills/developing-with-streamlit/templates/themes/manage.py +332 -0
  68. package/.agents/skills/developing-with-streamlit/templates/themes/minimal/.streamlit/config.toml +39 -0
  69. package/.agents/skills/developing-with-streamlit/templates/themes/minimal/pyproject.toml +10 -0
  70. package/.agents/skills/developing-with-streamlit/templates/themes/minimal/streamlit_app.py +337 -0
  71. package/.agents/skills/developing-with-streamlit/templates/themes/nord/.streamlit/config.toml +39 -0
  72. package/.agents/skills/developing-with-streamlit/templates/themes/nord/pyproject.toml +10 -0
  73. package/.agents/skills/developing-with-streamlit/templates/themes/nord/streamlit_app.py +337 -0
  74. package/.agents/skills/developing-with-streamlit/templates/themes/snowflake/.streamlit/config.toml +42 -0
  75. package/.agents/skills/developing-with-streamlit/templates/themes/snowflake/pyproject.toml +10 -0
  76. package/.agents/skills/developing-with-streamlit/templates/themes/snowflake/streamlit_app.py +337 -0
  77. package/.agents/skills/developing-with-streamlit/templates/themes/solarized-light/.streamlit/config.toml +38 -0
  78. package/.agents/skills/developing-with-streamlit/templates/themes/solarized-light/pyproject.toml +10 -0
  79. package/.agents/skills/developing-with-streamlit/templates/themes/solarized-light/streamlit_app.py +337 -0
  80. package/.agents/skills/developing-with-streamlit/templates/themes/spotify/.streamlit/config.toml +34 -0
  81. package/.agents/skills/developing-with-streamlit/templates/themes/spotify/pyproject.toml +10 -0
  82. package/.agents/skills/developing-with-streamlit/templates/themes/spotify/streamlit_app.py +337 -0
  83. package/.agents/skills/developing-with-streamlit/templates/themes/stripe/.streamlit/config.toml +35 -0
  84. package/.agents/skills/developing-with-streamlit/templates/themes/stripe/pyproject.toml +10 -0
  85. package/.agents/skills/developing-with-streamlit/templates/themes/stripe/streamlit_app.py +337 -0
  86. package/.agents/skills/fastapi/SKILL.md +436 -0
  87. package/.agents/skills/fastapi/references/dependencies.md +142 -0
  88. package/.agents/skills/fastapi/references/other-tools.md +76 -0
  89. package/.agents/skills/fastapi/references/streaming.md +105 -0
  90. package/.agents/skills/frontend-design/LICENSE.txt +177 -0
  91. package/.agents/skills/frontend-design/SKILL.md +42 -0
  92. package/.agents/skills/gradio/SKILL.md +84 -0
  93. package/.agents/skills/gradio/references/api-signatures.md +115 -0
  94. package/.agents/skills/gradio/references/event-listeners.md +130 -0
  95. package/.agents/skills/gradio/references/examples.md +613 -0
  96. package/.agents/skills/project-design/SKILL.md +207 -0
  97. package/.agents/skills/project-design/references/ARCHITECTURE.md +641 -0
  98. package/.agents/skills/project-design/references/PROJECT_PLAN.md +316 -0
  99. package/.agents/skills/skill-creator/LICENSE.txt +202 -0
  100. package/.agents/skills/skill-creator/SKILL.md +485 -0
  101. package/.agents/skills/skill-creator/agents/analyzer.md +274 -0
  102. package/.agents/skills/skill-creator/agents/comparator.md +202 -0
  103. package/.agents/skills/skill-creator/agents/grader.md +223 -0
  104. package/.agents/skills/skill-creator/assets/eval_review.html +146 -0
  105. package/.agents/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  106. package/.agents/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  107. package/.agents/skills/skill-creator/references/schemas.md +430 -0
  108. package/.agents/skills/skill-creator/scripts/__init__.py +0 -0
  109. package/.agents/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  110. package/.agents/skills/skill-creator/scripts/generate_report.py +326 -0
  111. package/.agents/skills/skill-creator/scripts/improve_description.py +247 -0
  112. package/.agents/skills/skill-creator/scripts/package_skill.py +136 -0
  113. package/.agents/skills/skill-creator/scripts/quick_validate.py +103 -0
  114. package/.agents/skills/skill-creator/scripts/run_eval.py +310 -0
  115. package/.agents/skills/skill-creator/scripts/run_loop.py +328 -0
  116. package/.agents/skills/skill-creator/scripts/utils.py +47 -0
  117. package/.agents/skills/ui-ux-pro-max/SKILL.md +659 -0
  118. package/.agents/skills/ui-ux-pro-max/data +1 -0
  119. package/.agents/skills/ui-ux-pro-max/scripts +1 -0
  120. package/.env.axiom +8 -0
  121. package/AGENTS.md +2 -1
  122. package/README.md +20 -3
  123. package/bin/cli.js +12 -1
  124. package/opencode.json +7 -4
  125. package/package.json +1 -1
  126. package/plugin/oh-my-openagent.json +198 -0
  127. package/plugin/oh-my-openagent.md +49 -0
  128. package/skills-lock.json +42 -0
  129. 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)
@@ -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