InNoobWeTrust commited on
Commit
cf1b1b4
·
1 Parent(s): b69e760

build: transition to uv project

Browse files
Files changed (6) hide show
  1. .gitignore +2 -1
  2. .python-version +1 -0
  3. pkgx.yml +1 -0
  4. pyproject.toml +17 -0
  5. requirements.txt +1 -1
  6. streamlit_app.py +183 -108
.gitignore CHANGED
@@ -164,4 +164,5 @@ cython_debug/
164
  #.idea/
165
 
166
 
167
- .DS_Store
 
 
164
  #.idea/
165
 
166
 
167
+ .DS_Store
168
+ uv.lock
.python-version ADDED
@@ -0,0 +1 @@
 
 
1
+ 3.10
pkgx.yml CHANGED
@@ -1,2 +1,3 @@
1
  dependencies:
2
  nushell.sh: ^0
 
 
1
  dependencies:
2
  nushell.sh: ^0
3
+ uv: ^0
pyproject.toml ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "crypto-etf-tracker"
3
+ version = "0.1.0"
4
+ description = "Add your description here"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ dependencies = [
8
+ "altair>=5.5.0",
9
+ "cloudscraper>=1.2.71",
10
+ "lxml>=5.3.1",
11
+ "pandas>=2.2.3",
12
+ "pygwalker>=0.4.9.14",
13
+ "streamlit>=1.43.2",
14
+ "vega>=4.1.0",
15
+ "workalendar>=17.0.0",
16
+ "yfinance[nospam,repair]==0.2.54",
17
+ ]
requirements.txt CHANGED
@@ -6,4 +6,4 @@ altair
6
  vega
7
  workalendar
8
  pygwalker
9
- cloudscraper
 
6
  vega
7
  workalendar
8
  pygwalker
9
+ cloudscraper
streamlit_app.py CHANGED
@@ -1,3 +1,5 @@
 
 
1
  import pandas as pd
2
 
3
  import streamlit as st
@@ -17,6 +19,7 @@ alt.renderers.set_embed_options(theme="dark")
17
  def fetch_asset(asset):
18
  return fetch(asset)
19
 
 
20
  def gen_charts(asset, chart_size={"width": 560, "height": 150}):
21
  # Gen data
22
  data = fetch_asset(asset)
@@ -28,122 +31,180 @@ def gen_charts(asset, chart_size={"width": 560, "height": 150}):
28
  cum_flow_total = data.cum_flow_total
29
 
30
  # Create bindings for interval selection
31
- scale_selection = alt.selection_interval(encodings=["x"],bind="scales")
32
 
33
  # Line chart of price
34
  price = (
35
- alt.Chart(price)
36
- .mark_line()
37
- .encode(
38
- x=alt.X("Date:T", axis=alt.Axis(tickCount={"interval": "month", "step": 1}), title=""),
39
- y=alt.Y("Price:Q").scale(zero=False),
40
- color=alt.value("crimson"),
41
- )
42
- ).add_params(scale_selection).properties(
43
- width=chart_size["width"],
44
- height=chart_size["height"],
 
 
 
 
 
 
 
 
45
  )
46
 
47
  trading_vol_individual = (
48
- alt.Chart(etf_volumes)
49
- .transform_fold(
50
- etf_volumes.drop(columns="Date").columns.to_list(), as_=["Funds", "Volume"]
51
- )
52
- .mark_line()
53
- .encode(
54
- x=alt.X("Date:T", axis=alt.Axis(tickCount={"interval": "month", "step": 1}), title=""),
55
- y=alt.Y("Volume:Q", title="Trading Volume Individual"),
56
- color="Funds:N",
57
- )
58
- ).add_params(scale_selection).properties(
59
- width=chart_size["width"],
60
- height=chart_size["height"],
 
 
 
 
 
 
 
 
 
61
  )
62
  trading_vol_total = (
63
- alt.Chart(etf_volumes)
64
- .transform_fold(
65
- etf_volumes.drop(columns="Date").columns.to_list(), as_=["Funds", "Volume"]
66
- )
67
- .mark_rule()
68
- .encode(
69
- x=alt.X("Date:T", axis=alt.Axis(tickCount={"interval": "month", "step": 1}), title=""),
70
- y=alt.Y("sum(Volume):Q", title="Trading Volume Total"),
71
- color=alt.value("teal"),
72
- )
73
- ).add_params(scale_selection).properties(
74
- width=chart_size["width"],
75
- height=chart_size["height"],
 
 
 
 
 
 
 
 
 
76
  )
77
 
78
  # Net flow individual
79
  net_flow_individual = (
80
- alt.Chart(etf_flow_individual)
81
- .transform_fold(
82
- etf_flow_individual.drop(columns="Date").columns.to_list(),
83
- as_=["Funds", "Net Flow"],
84
- )
85
- .mark_line()
86
- .encode(
87
- x=alt.X("Date:T", axis=alt.Axis(tickCount={"interval": "month", "step": 1}), title=""),
88
- y=alt.Y("Net Flow:Q", title="Net Flow Individual"),
89
- color="Funds:N",
90
- )
91
- ).add_params(scale_selection).properties(
92
- width=chart_size["width"],
93
- height=chart_size["height"],
 
 
 
 
 
 
 
 
94
  )
95
  net_flow_total = (
96
- alt.Chart(etf_flow_total)
97
- .mark_rule()
98
- .encode(
99
- x=alt.X("Date:T", axis=alt.Axis(tickCount={"interval": "month", "step": 1}), title=""),
100
- y=alt.Y("Total:Q", title="Net Flow Total"),
101
- color=alt.condition(
102
- alt.datum.Total > 0,
103
- alt.value("seagreen"), # The positive color
104
- alt.value("orangered"), # The negative color
105
- ),
106
- )
107
- ).add_params(scale_selection).properties(
108
- width=chart_size["width"],
109
- height=chart_size["height"],
 
 
 
 
 
 
 
 
110
  )
111
 
112
  # Stacking area chart of flow from individual funds
113
  cum_flow_individual = (
114
- alt.Chart(cum_flow_individual)
115
- .transform_fold(
116
- cum_flow_individual.drop(columns="Date").columns.to_list(),
117
- as_=["Funds", "Net Flow"],
118
- )
119
- .mark_area()
120
- .encode(
121
- x=alt.X("Date:T", axis=alt.Axis(tickCount={"interval": "month", "step": 1}), title=""),
122
- y=alt.Y("Net Flow:Q", title="Cumulative Flow Individual"),
123
- color=alt.Color("Funds:N", scale=alt.Scale(scheme="tableau20")),
124
- )
125
- ).add_params(scale_selection).properties(
126
- width=chart_size["width"],
127
- height=chart_size["height"],
 
 
 
 
 
 
 
 
128
  )
129
 
130
  # Area chart for cumulative flow
131
  cum_flow_total = (
132
- alt.Chart(cum_flow_total)
133
- .transform_calculate(
134
- negative="datum.Total < 0",
135
- )
136
- .mark_area()
137
- .encode(
138
- x=alt.X("Date:T", axis=alt.Axis(tickCount={"interval": "month", "step": 1}), title=""),
139
- y=alt.Y("Total:Q", title="Cumulative Flow Total", impute={"value": 0}),
140
- color=alt.Color(
141
- "negative:N", title="Negative Flow", scale=alt.Scale(scheme="set2")
142
- ),
143
- )
144
- ).add_params(scale_selection).properties(
145
- width=chart_size["width"],
146
- height=chart_size["height"],
 
 
 
 
 
 
 
 
147
  )
148
 
149
  return SimpleNamespace(
@@ -156,27 +217,33 @@ def gen_charts(asset, chart_size={"width": 560, "height": 150}):
156
  cum_flow_total=cum_flow_total,
157
  )
158
 
 
159
  def asset_charts(asset: str, chart_size={"width": "container", "height": 150}):
160
  charts = gen_charts(asset, chart_size)
161
 
162
  # Vertical concat the charts in each asset into single column of that asset
163
  all_charts = (
164
- charts.price
165
- & charts.trading_vol_individual
166
- & charts.trading_vol_total
167
- & charts.net_flow_individual
168
- & charts.net_flow_total
169
- & charts.cum_flow_individual
170
- & charts.cum_flow_total
171
- ).resolve_scale(
172
- color="independent",
173
- ).properties(
174
- title=f"{asset} ETF",
 
 
 
 
175
  )
176
 
177
  return all_charts
178
 
179
- if __name__ == "__main__":
 
180
  # Set page config
181
  st.set_page_config(layout="wide", page_icon="📈")
182
  # Initialize pygwalker communication
@@ -196,8 +263,12 @@ if __name__ == "__main__":
196
  eth = fetch_asset("ETH")
197
 
198
  with dashboard_tab:
199
- btc_charts = asset_charts("BTC", chart_size={"width": "container", "height": 150})
200
- eth_charts = asset_charts("ETH", chart_size={"width": "container", "height": 150})
 
 
 
 
201
  # Display charts
202
  btc_chart_col, eth_chart_col = st.columns(2)
203
  with btc_chart_col:
@@ -239,3 +310,7 @@ if __name__ == "__main__":
239
  df = pd.concat([btc_price, eth_price])
240
  df.Date = df.Date.astype(str)
241
  StreamlitRenderer(df).explorer()
 
 
 
 
 
1
+ #!/usr/bin/env -S pkgx [email protected] uv run -- streamlit run
2
+
3
  import pandas as pd
4
 
5
  import streamlit as st
 
19
  def fetch_asset(asset):
20
  return fetch(asset)
21
 
22
+
23
  def gen_charts(asset, chart_size={"width": 560, "height": 150}):
24
  # Gen data
25
  data = fetch_asset(asset)
 
31
  cum_flow_total = data.cum_flow_total
32
 
33
  # Create bindings for interval selection
34
+ scale_selection = alt.selection_interval(encodings=["x"], bind="scales")
35
 
36
  # Line chart of price
37
  price = (
38
+ (
39
+ alt.Chart(price)
40
+ .mark_line()
41
+ .encode(
42
+ x=alt.X(
43
+ "Date:T",
44
+ axis=alt.Axis(tickCount={"interval": "month", "step": 1}),
45
+ title="",
46
+ ),
47
+ y=alt.Y("Price:Q").scale(zero=False),
48
+ color=alt.value("crimson"),
49
+ )
50
+ )
51
+ .add_params(scale_selection)
52
+ .properties(
53
+ width=chart_size["width"],
54
+ height=chart_size["height"],
55
+ )
56
  )
57
 
58
  trading_vol_individual = (
59
+ (
60
+ alt.Chart(etf_volumes)
61
+ .transform_fold(
62
+ etf_volumes.drop(columns="Date").columns.to_list(),
63
+ as_=["Funds", "Volume"],
64
+ )
65
+ .mark_line()
66
+ .encode(
67
+ x=alt.X(
68
+ "Date:T",
69
+ axis=alt.Axis(tickCount={"interval": "month", "step": 1}),
70
+ title="",
71
+ ),
72
+ y=alt.Y("Volume:Q", title="Trading Volume Individual"),
73
+ color="Funds:N",
74
+ )
75
+ )
76
+ .add_params(scale_selection)
77
+ .properties(
78
+ width=chart_size["width"],
79
+ height=chart_size["height"],
80
+ )
81
  )
82
  trading_vol_total = (
83
+ (
84
+ alt.Chart(etf_volumes)
85
+ .transform_fold(
86
+ etf_volumes.drop(columns="Date").columns.to_list(),
87
+ as_=["Funds", "Volume"],
88
+ )
89
+ .mark_rule()
90
+ .encode(
91
+ x=alt.X(
92
+ "Date:T",
93
+ axis=alt.Axis(tickCount={"interval": "month", "step": 1}),
94
+ title="",
95
+ ),
96
+ y=alt.Y("sum(Volume):Q", title="Trading Volume Total"),
97
+ color=alt.value("teal"),
98
+ )
99
+ )
100
+ .add_params(scale_selection)
101
+ .properties(
102
+ width=chart_size["width"],
103
+ height=chart_size["height"],
104
+ )
105
  )
106
 
107
  # Net flow individual
108
  net_flow_individual = (
109
+ (
110
+ alt.Chart(etf_flow_individual)
111
+ .transform_fold(
112
+ etf_flow_individual.drop(columns="Date").columns.to_list(),
113
+ as_=["Funds", "Net Flow"],
114
+ )
115
+ .mark_line()
116
+ .encode(
117
+ x=alt.X(
118
+ "Date:T",
119
+ axis=alt.Axis(tickCount={"interval": "month", "step": 1}),
120
+ title="",
121
+ ),
122
+ y=alt.Y("Net Flow:Q", title="Net Flow Individual"),
123
+ color="Funds:N",
124
+ )
125
+ )
126
+ .add_params(scale_selection)
127
+ .properties(
128
+ width=chart_size["width"],
129
+ height=chart_size["height"],
130
+ )
131
  )
132
  net_flow_total = (
133
+ (
134
+ alt.Chart(etf_flow_total)
135
+ .mark_rule()
136
+ .encode(
137
+ x=alt.X(
138
+ "Date:T",
139
+ axis=alt.Axis(tickCount={"interval": "month", "step": 1}),
140
+ title="",
141
+ ),
142
+ y=alt.Y("Total:Q", title="Net Flow Total"),
143
+ color=alt.condition(
144
+ alt.datum.Total > 0,
145
+ alt.value("seagreen"), # The positive color
146
+ alt.value("orangered"), # The negative color
147
+ ),
148
+ )
149
+ )
150
+ .add_params(scale_selection)
151
+ .properties(
152
+ width=chart_size["width"],
153
+ height=chart_size["height"],
154
+ )
155
  )
156
 
157
  # Stacking area chart of flow from individual funds
158
  cum_flow_individual = (
159
+ (
160
+ alt.Chart(cum_flow_individual)
161
+ .transform_fold(
162
+ cum_flow_individual.drop(columns="Date").columns.to_list(),
163
+ as_=["Funds", "Net Flow"],
164
+ )
165
+ .mark_area()
166
+ .encode(
167
+ x=alt.X(
168
+ "Date:T",
169
+ axis=alt.Axis(tickCount={"interval": "month", "step": 1}),
170
+ title="",
171
+ ),
172
+ y=alt.Y("Net Flow:Q", title="Cumulative Flow Individual"),
173
+ color=alt.Color("Funds:N", scale=alt.Scale(scheme="tableau20")),
174
+ )
175
+ )
176
+ .add_params(scale_selection)
177
+ .properties(
178
+ width=chart_size["width"],
179
+ height=chart_size["height"],
180
+ )
181
  )
182
 
183
  # Area chart for cumulative flow
184
  cum_flow_total = (
185
+ (
186
+ alt.Chart(cum_flow_total)
187
+ .transform_calculate(
188
+ negative="datum.Total < 0",
189
+ )
190
+ .mark_area()
191
+ .encode(
192
+ x=alt.X(
193
+ "Date:T",
194
+ axis=alt.Axis(tickCount={"interval": "month", "step": 1}),
195
+ title="",
196
+ ),
197
+ y=alt.Y("Total:Q", title="Cumulative Flow Total", impute={"value": 0}),
198
+ color=alt.Color(
199
+ "negative:N", title="Negative Flow", scale=alt.Scale(scheme="set2")
200
+ ),
201
+ )
202
+ )
203
+ .add_params(scale_selection)
204
+ .properties(
205
+ width=chart_size["width"],
206
+ height=chart_size["height"],
207
+ )
208
  )
209
 
210
  return SimpleNamespace(
 
217
  cum_flow_total=cum_flow_total,
218
  )
219
 
220
+
221
  def asset_charts(asset: str, chart_size={"width": "container", "height": 150}):
222
  charts = gen_charts(asset, chart_size)
223
 
224
  # Vertical concat the charts in each asset into single column of that asset
225
  all_charts = (
226
+ (
227
+ charts.price
228
+ & charts.trading_vol_individual
229
+ & charts.trading_vol_total
230
+ & charts.net_flow_individual
231
+ & charts.net_flow_total
232
+ & charts.cum_flow_individual
233
+ & charts.cum_flow_total
234
+ )
235
+ .resolve_scale(
236
+ color="independent",
237
+ )
238
+ .properties(
239
+ title=f"{asset} ETF",
240
+ )
241
  )
242
 
243
  return all_charts
244
 
245
+
246
+ def app():
247
  # Set page config
248
  st.set_page_config(layout="wide", page_icon="📈")
249
  # Initialize pygwalker communication
 
263
  eth = fetch_asset("ETH")
264
 
265
  with dashboard_tab:
266
+ btc_charts = asset_charts(
267
+ "BTC", chart_size={"width": "container", "height": 150}
268
+ )
269
+ eth_charts = asset_charts(
270
+ "ETH", chart_size={"width": "container", "height": 150}
271
+ )
272
  # Display charts
273
  btc_chart_col, eth_chart_col = st.columns(2)
274
  with btc_chart_col:
 
310
  df = pd.concat([btc_price, eth_price])
311
  df.Date = df.Date.astype(str)
312
  StreamlitRenderer(df).explorer()
313
+
314
+
315
+ if __name__ == "__main__":
316
+ app()