InNoobWeTrust
commited on
Commit
β’
4b6a96c
1
Parent(s):
c5f202a
fix: asset price issue with latest yfinance
Browse files- .gitignore +4 -1
- df.py +32 -20
- requirements.txt +1 -1
- streamlit_app.py +67 -54
.gitignore
CHANGED
@@ -161,4 +161,7 @@ cython_debug/
|
|
161 |
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
162 |
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
163 |
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
164 |
-
#.idea/
|
|
|
|
|
|
|
|
161 |
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
162 |
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
163 |
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
164 |
+
#.idea/
|
165 |
+
|
166 |
+
|
167 |
+
.DS_Store
|
df.py
CHANGED
@@ -1,7 +1,6 @@
|
|
1 |
import pandas as pd
|
2 |
import pytz
|
3 |
import yfinance as yf
|
4 |
-
import streamlit as st
|
5 |
|
6 |
from typing import List
|
7 |
from types import SimpleNamespace
|
@@ -47,7 +46,9 @@ def extract_date_index(df):
|
|
47 |
def fetch_btc_etf():
|
48 |
# Get Bitcoin spot ETF history
|
49 |
btc_etf_flow = pd.read_html(
|
50 |
-
"https://farside.co.uk
|
|
|
|
|
51 |
)[0]
|
52 |
# Remove summary lines
|
53 |
btc_etf_flow = btc_etf_flow.iloc[:-4]
|
@@ -57,11 +58,12 @@ def fetch_btc_etf():
|
|
57 |
btc_etf_flow, btc_etf_flow_original = clean_etf_data(btc_etf_flow)
|
58 |
|
59 |
return SimpleNamespace(
|
60 |
-
flow
|
61 |
-
orig
|
62 |
-
funds
|
63 |
)
|
64 |
|
|
|
65 |
def fetch_eth_etf():
|
66 |
# Get Ethereum spot ETF history
|
67 |
eth_etf_flow = pd.read_html(
|
@@ -90,28 +92,34 @@ def fetch_eth_etf():
|
|
90 |
eth_etf_flow, eth_etf_flow_original = clean_etf_data(eth_etf_flow)
|
91 |
|
92 |
return SimpleNamespace(
|
93 |
-
flow
|
94 |
-
orig
|
95 |
-
funds
|
96 |
)
|
97 |
|
|
|
98 |
def fetch_etf_volumes(funds: List[str], start_time=None):
|
99 |
etf_volumes = pd.DataFrame()
|
100 |
for fund in funds:
|
101 |
etf_volumes[fund] = yf.download(
|
102 |
-
str(fund),
|
103 |
-
|
|
|
|
|
|
|
104 |
etf_volumes = extract_date_index(etf_volumes)
|
105 |
|
106 |
return etf_volumes
|
107 |
|
|
|
108 |
def fetch_asset_price(ticker: str, start_time=None):
|
109 |
-
price = yf.download(ticker, interval
|
110 |
price = extract_date_index(price)
|
|
|
111 |
|
112 |
return price
|
113 |
|
114 |
-
|
115 |
def fetch(asset):
|
116 |
if asset == "BTC":
|
117 |
df = fetch_btc_etf()
|
@@ -119,21 +127,25 @@ def fetch(asset):
|
|
119 |
df = fetch_eth_etf()
|
120 |
|
121 |
etf_flow, etf_funds = df.flow, df.funds
|
122 |
-
tz = pytz.timezone(
|
|
|
|
|
|
|
123 |
start_time = tz.localize(etf_flow.Date[0])
|
124 |
-
etf_volumes = fetch_etf_volumes(etf_funds, start_time
|
125 |
-
price = fetch_asset_price(f
|
126 |
-
price.rename(columns={'Close': 'Price'}, inplace=True)
|
127 |
|
128 |
etf_flow_individual = etf_flow.drop(columns="Total")
|
129 |
etf_flow_total = etf_flow[["Date", "Total"]]
|
130 |
|
131 |
cum_flow_individual = etf_flow_individual.drop(columns="Date").cumsum()
|
132 |
cum_flow_individual["Date"] = etf_flow_individual.Date
|
133 |
-
cum_flow_total = pd.DataFrame(
|
134 |
-
|
135 |
-
|
136 |
-
|
|
|
|
|
137 |
|
138 |
return SimpleNamespace(
|
139 |
etf_flow=etf_flow,
|
|
|
1 |
import pandas as pd
|
2 |
import pytz
|
3 |
import yfinance as yf
|
|
|
4 |
|
5 |
from typing import List
|
6 |
from types import SimpleNamespace
|
|
|
46 |
def fetch_btc_etf():
|
47 |
# Get Bitcoin spot ETF history
|
48 |
btc_etf_flow = pd.read_html(
|
49 |
+
"https://farside.co.uk/bitcoin-etf-flow-all-data/",
|
50 |
+
attrs={"class": "etf"},
|
51 |
+
skiprows=[1],
|
52 |
)[0]
|
53 |
# Remove summary lines
|
54 |
btc_etf_flow = btc_etf_flow.iloc[:-4]
|
|
|
58 |
btc_etf_flow, btc_etf_flow_original = clean_etf_data(btc_etf_flow)
|
59 |
|
60 |
return SimpleNamespace(
|
61 |
+
flow=btc_etf_flow,
|
62 |
+
orig=btc_etf_flow_original,
|
63 |
+
funds=btc_etf_funds,
|
64 |
)
|
65 |
|
66 |
+
|
67 |
def fetch_eth_etf():
|
68 |
# Get Ethereum spot ETF history
|
69 |
eth_etf_flow = pd.read_html(
|
|
|
92 |
eth_etf_flow, eth_etf_flow_original = clean_etf_data(eth_etf_flow)
|
93 |
|
94 |
return SimpleNamespace(
|
95 |
+
flow=eth_etf_flow,
|
96 |
+
orig=eth_etf_flow_original,
|
97 |
+
funds=eth_etf_funds,
|
98 |
)
|
99 |
|
100 |
+
|
101 |
def fetch_etf_volumes(funds: List[str], start_time=None):
|
102 |
etf_volumes = pd.DataFrame()
|
103 |
for fund in funds:
|
104 |
etf_volumes[fund] = yf.download(
|
105 |
+
str(fund),
|
106 |
+
interval="1d",
|
107 |
+
period="max",
|
108 |
+
start=start_time,
|
109 |
+
)["Volume"]
|
110 |
etf_volumes = extract_date_index(etf_volumes)
|
111 |
|
112 |
return etf_volumes
|
113 |
|
114 |
+
|
115 |
def fetch_asset_price(ticker: str, start_time=None):
|
116 |
+
price = yf.download(ticker, interval="1d", period="max", start=start_time)["Close"]
|
117 |
price = extract_date_index(price)
|
118 |
+
price.rename(columns={"Close": "Price"}, inplace=True)
|
119 |
|
120 |
return price
|
121 |
|
122 |
+
|
123 |
def fetch(asset):
|
124 |
if asset == "BTC":
|
125 |
df = fetch_btc_etf()
|
|
|
127 |
df = fetch_eth_etf()
|
128 |
|
129 |
etf_flow, etf_funds = df.flow, df.funds
|
130 |
+
tz = pytz.timezone("America/New_York")
|
131 |
+
|
132 |
+
etf_flow, etf_funds = df.flow, df.funds
|
133 |
+
tz = pytz.timezone("America/New_York")
|
134 |
start_time = tz.localize(etf_flow.Date[0])
|
135 |
+
etf_volumes = fetch_etf_volumes(etf_funds, start_time=start_time)
|
136 |
+
price = fetch_asset_price(f"{asset}-USD", start_time=start_time)
|
|
|
137 |
|
138 |
etf_flow_individual = etf_flow.drop(columns="Total")
|
139 |
etf_flow_total = etf_flow[["Date", "Total"]]
|
140 |
|
141 |
cum_flow_individual = etf_flow_individual.drop(columns="Date").cumsum()
|
142 |
cum_flow_individual["Date"] = etf_flow_individual.Date
|
143 |
+
cum_flow_total = pd.DataFrame(
|
144 |
+
{
|
145 |
+
"Date": etf_flow_total.Date,
|
146 |
+
"Total": etf_flow_total.Total.cumsum(),
|
147 |
+
}
|
148 |
+
)
|
149 |
|
150 |
return SimpleNamespace(
|
151 |
etf_flow=etf_flow,
|
requirements.txt
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
streamlit
|
2 |
pandas
|
3 |
-
yfinance
|
4 |
altair
|
5 |
vega
|
6 |
workalendar
|
|
|
1 |
streamlit
|
2 |
pandas
|
3 |
+
yfinance[nospam,repair]==0.2.47
|
4 |
altair
|
5 |
vega
|
6 |
workalendar
|
streamlit_app.py
CHANGED
@@ -2,17 +2,23 @@ import pandas as pd
|
|
2 |
|
3 |
import streamlit as st
|
4 |
import altair as alt
|
5 |
-
|
6 |
from pygwalker.api.streamlit import StreamlitRenderer, init_streamlit_comm
|
7 |
|
8 |
from types import SimpleNamespace
|
9 |
|
10 |
from df import fetch
|
11 |
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
|
13 |
def gen_charts(asset, chart_size={"width": 560, "height": 300}):
|
14 |
# Gen data
|
15 |
-
data =
|
16 |
etf_volumes = data.etf_volumes
|
17 |
price = data.price
|
18 |
etf_flow_individual = data.etf_flow_individual
|
@@ -32,8 +38,8 @@ def gen_charts(asset, chart_size={"width": 560, "height": 300}):
|
|
32 |
color="Funds:N",
|
33 |
)
|
34 |
).properties(
|
35 |
-
width=chart_size[
|
36 |
-
height=chart_size[
|
37 |
)
|
38 |
trading_vol_total_fig = (
|
39 |
alt.Chart(etf_volumes)
|
@@ -42,13 +48,13 @@ def gen_charts(asset, chart_size={"width": 560, "height": 300}):
|
|
42 |
)
|
43 |
.mark_rule()
|
44 |
.encode(
|
45 |
-
x=alt.X("Date:T", axis=alt.Axis(tickCount="day", title=
|
46 |
y=alt.Y("sum(Volume):Q", title="Total Volume"),
|
47 |
color=alt.value("teal"),
|
48 |
)
|
49 |
).properties(
|
50 |
-
width=chart_size[
|
51 |
-
height=chart_size[
|
52 |
)
|
53 |
# Combine trading volume and average trading volume
|
54 |
trading_vol_fig = trading_vol_total_fig & trading_vol_fig
|
@@ -70,14 +76,14 @@ def gen_charts(asset, chart_size={"width": 560, "height": 300}):
|
|
70 |
color="Funds:N",
|
71 |
)
|
72 |
).properties(
|
73 |
-
width=chart_size[
|
74 |
-
height=chart_size[
|
75 |
)
|
76 |
net_flow_total_fig = (
|
77 |
alt.Chart(etf_flow_total)
|
78 |
.mark_rule()
|
79 |
.encode(
|
80 |
-
x=alt.X("Date:T", axis=alt.Axis(tickCount="day", title=
|
81 |
y=alt.Y("Total:Q"),
|
82 |
color=alt.condition(
|
83 |
alt.datum.Total > 0,
|
@@ -86,11 +92,13 @@ def gen_charts(asset, chart_size={"width": 560, "height": 300}):
|
|
86 |
),
|
87 |
)
|
88 |
).properties(
|
89 |
-
width=chart_size[
|
90 |
-
height=chart_size[
|
91 |
)
|
92 |
net_flow_individual_fig = net_flow_total_fig & net_flow_individual_fig
|
93 |
-
net_flow_individual_fig = net_flow_individual_fig.resolve_scale(
|
|
|
|
|
94 |
title=f"{asset} ETF net flow of individual funds",
|
95 |
)
|
96 |
|
@@ -98,7 +106,7 @@ def gen_charts(asset, chart_size={"width": 560, "height": 300}):
|
|
98 |
alt.Chart(etf_flow_total)
|
99 |
.mark_rule()
|
100 |
.encode(
|
101 |
-
x=alt.X("Date:T", axis=alt.Axis(tickCount="day", title=
|
102 |
y=alt.Y("Total:Q"),
|
103 |
color=alt.condition(
|
104 |
alt.datum.Total > 0,
|
@@ -107,8 +115,8 @@ def gen_charts(asset, chart_size={"width": 560, "height": 300}):
|
|
107 |
),
|
108 |
)
|
109 |
).properties(
|
110 |
-
width=chart_size[
|
111 |
-
height=chart_size[
|
112 |
)
|
113 |
# Line chart of price
|
114 |
price_fig = (
|
@@ -120,12 +128,12 @@ def gen_charts(asset, chart_size={"width": 560, "height": 300}):
|
|
120 |
color=alt.value("crimson"),
|
121 |
)
|
122 |
).properties(
|
123 |
-
width=chart_size[
|
124 |
-
height=chart_size[
|
125 |
)
|
126 |
|
127 |
net_flow_total_fig = net_flow_total_fig & price_fig
|
128 |
-
net_flow_total_fig = net_flow_total_fig.resolve_scale(x=
|
129 |
title=f"{asset} ETF net flow total vs asset price",
|
130 |
)
|
131 |
|
@@ -138,16 +146,18 @@ def gen_charts(asset, chart_size={"width": 560, "height": 300}):
|
|
138 |
)
|
139 |
.mark_area()
|
140 |
.encode(
|
141 |
-
x=alt.X("Date:T", axis=alt.Axis(tickCount="day", title=
|
142 |
y=alt.Y("Net Flow:Q"),
|
143 |
color=alt.Color("Funds:N", scale=alt.Scale(scheme="tableau20")),
|
144 |
)
|
145 |
).properties(
|
146 |
-
width=chart_size[
|
147 |
-
height=chart_size[
|
148 |
)
|
149 |
cum_flow_individual_net_fig = cum_flow_individual_net_fig & price_fig
|
150 |
-
cum_flow_individual_net_fig = cum_flow_individual_net_fig.resolve_scale(
|
|
|
|
|
151 |
title=f"{asset} ETF cumulative flow of individual funds vs asset price",
|
152 |
)
|
153 |
|
@@ -159,18 +169,18 @@ def gen_charts(asset, chart_size={"width": 560, "height": 300}):
|
|
159 |
)
|
160 |
.mark_area()
|
161 |
.encode(
|
162 |
-
x=alt.X("Date:T", axis=alt.Axis(tickCount="day", title=
|
163 |
y=alt.Y("Total:Q", impute={"value": 0}),
|
164 |
color=alt.Color(
|
165 |
"negative:N", title="Negative Flow", scale=alt.Scale(scheme="set2")
|
166 |
),
|
167 |
)
|
168 |
).properties(
|
169 |
-
width=chart_size[
|
170 |
-
height=chart_size[
|
171 |
)
|
172 |
cum_flow_total_fig = cum_flow_total_fig & price_fig
|
173 |
-
cum_flow_total_fig = cum_flow_total_fig.resolve_scale(x=
|
174 |
title=f"{asset} ETF cumulative flow total vs asset price",
|
175 |
)
|
176 |
|
@@ -182,7 +192,8 @@ def gen_charts(asset, chart_size={"width": 560, "height": 300}):
|
|
182 |
cum_flow_total_fig=cum_flow_total_fig,
|
183 |
)
|
184 |
|
185 |
-
|
|
|
186 |
charts = gen_charts(asset, chart_size)
|
187 |
# Vertical concat the charts in each asset into single column of that asset
|
188 |
all_charts = (
|
@@ -195,31 +206,33 @@ def asset_charts(asset: str, chart_size={"width": 'container', "height": 300}):
|
|
195 |
|
196 |
return all_charts
|
197 |
|
|
|
198 |
def compound_chart(chart_size={"width": 560, "height": 300}):
|
199 |
-
all_charts_btc = asset_charts(
|
200 |
-
all_charts_eth = asset_charts(
|
201 |
# Horizontal concat the charts for btc and eth
|
202 |
all_charts = (all_charts_btc | all_charts_eth).resolve_scale(color="independent")
|
203 |
|
204 |
return all_charts
|
205 |
|
206 |
-
|
207 |
if __name__ == "__main__":
|
208 |
# Set page config
|
209 |
st.set_page_config(layout="wide", page_icon="π")
|
210 |
# Initialize pygwalker communication
|
211 |
init_streamlit_comm()
|
212 |
|
213 |
-
dashboard_tab, single_view, flow_tab, volume_tab, price_tab = st.tabs(
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
|
|
|
|
220 |
|
221 |
-
btc =
|
222 |
-
eth =
|
223 |
|
224 |
with dashboard_tab:
|
225 |
chart = compound_chart(chart_size={"width": 560, "height": 300})
|
@@ -227,33 +240,33 @@ if __name__ == "__main__":
|
|
227 |
st.altair_chart(chart, use_container_width=True)
|
228 |
with single_view:
|
229 |
asset = st.selectbox(
|
230 |
-
|
231 |
-
(
|
232 |
)
|
233 |
-
charts = gen_charts(asset, chart_size={"width":
|
234 |
-
st.altair_chart(charts.trading_vol_fig, use_container_width
|
235 |
-
st.altair_chart(charts.net_flow_individual_fig, use_container_width
|
236 |
-
st.altair_chart(charts.net_flow_total_fig, use_container_width
|
237 |
-
st.altair_chart(charts.cum_flow_individual_net_fig, use_container_width
|
238 |
-
st.altair_chart(charts.cum_flow_total_fig, use_container_width
|
239 |
with flow_tab:
|
240 |
btc_flow, eth_flow = btc.etf_flow, eth.etf_flow
|
241 |
-
btc_flow[
|
242 |
-
eth_flow[
|
243 |
df = pd.concat([btc_flow, eth_flow])
|
244 |
df.Date = df.Date.astype(str)
|
245 |
StreamlitRenderer(df).explorer()
|
246 |
with volume_tab:
|
247 |
btc_volume, eth_volume = btc.etf_volumes, eth.etf_volumes
|
248 |
-
btc_volume[
|
249 |
-
eth_volume[
|
250 |
df = pd.concat([btc_volume, eth_volume])
|
251 |
df.Date = df.Date.astype(str)
|
252 |
StreamlitRenderer(df).explorer()
|
253 |
with price_tab:
|
254 |
btc_price, eth_price = btc.price, eth.price
|
255 |
-
btc_price[
|
256 |
-
eth_price[
|
257 |
df = pd.concat([btc_price, eth_price])
|
258 |
df.Date = df.Date.astype(str)
|
259 |
StreamlitRenderer(df).explorer()
|
|
|
2 |
|
3 |
import streamlit as st
|
4 |
import altair as alt
|
5 |
+
|
6 |
from pygwalker.api.streamlit import StreamlitRenderer, init_streamlit_comm
|
7 |
|
8 |
from types import SimpleNamespace
|
9 |
|
10 |
from df import fetch
|
11 |
|
12 |
+
alt.renderers.set_embed_options(theme="dark")
|
13 |
+
|
14 |
+
|
15 |
+
@st.cache_data(ttl="30m")
|
16 |
+
def fetch_asset(asset):
|
17 |
+
return fetch(asset)
|
18 |
|
19 |
def gen_charts(asset, chart_size={"width": 560, "height": 300}):
|
20 |
# Gen data
|
21 |
+
data = fetch_asset(asset)
|
22 |
etf_volumes = data.etf_volumes
|
23 |
price = data.price
|
24 |
etf_flow_individual = data.etf_flow_individual
|
|
|
38 |
color="Funds:N",
|
39 |
)
|
40 |
).properties(
|
41 |
+
width=chart_size["width"],
|
42 |
+
height=chart_size["height"] / 2,
|
43 |
)
|
44 |
trading_vol_total_fig = (
|
45 |
alt.Chart(etf_volumes)
|
|
|
48 |
)
|
49 |
.mark_rule()
|
50 |
.encode(
|
51 |
+
x=alt.X("Date:T", axis=alt.Axis(tickCount="day", title="", labels=False)),
|
52 |
y=alt.Y("sum(Volume):Q", title="Total Volume"),
|
53 |
color=alt.value("teal"),
|
54 |
)
|
55 |
).properties(
|
56 |
+
width=chart_size["width"],
|
57 |
+
height=chart_size["height"] / 2,
|
58 |
)
|
59 |
# Combine trading volume and average trading volume
|
60 |
trading_vol_fig = trading_vol_total_fig & trading_vol_fig
|
|
|
76 |
color="Funds:N",
|
77 |
)
|
78 |
).properties(
|
79 |
+
width=chart_size["width"],
|
80 |
+
height=chart_size["height"] / 2,
|
81 |
)
|
82 |
net_flow_total_fig = (
|
83 |
alt.Chart(etf_flow_total)
|
84 |
.mark_rule()
|
85 |
.encode(
|
86 |
+
x=alt.X("Date:T", axis=alt.Axis(tickCount="day", title="", labels=False)),
|
87 |
y=alt.Y("Total:Q"),
|
88 |
color=alt.condition(
|
89 |
alt.datum.Total > 0,
|
|
|
92 |
),
|
93 |
)
|
94 |
).properties(
|
95 |
+
width=chart_size["width"],
|
96 |
+
height=chart_size["height"] / 2,
|
97 |
)
|
98 |
net_flow_individual_fig = net_flow_total_fig & net_flow_individual_fig
|
99 |
+
net_flow_individual_fig = net_flow_individual_fig.resolve_scale(
|
100 |
+
x="shared"
|
101 |
+
).properties(
|
102 |
title=f"{asset} ETF net flow of individual funds",
|
103 |
)
|
104 |
|
|
|
106 |
alt.Chart(etf_flow_total)
|
107 |
.mark_rule()
|
108 |
.encode(
|
109 |
+
x=alt.X("Date:T", axis=alt.Axis(tickCount="day", title="", labels=False)),
|
110 |
y=alt.Y("Total:Q"),
|
111 |
color=alt.condition(
|
112 |
alt.datum.Total > 0,
|
|
|
115 |
),
|
116 |
)
|
117 |
).properties(
|
118 |
+
width=chart_size["width"],
|
119 |
+
height=chart_size["height"] / 2,
|
120 |
)
|
121 |
# Line chart of price
|
122 |
price_fig = (
|
|
|
128 |
color=alt.value("crimson"),
|
129 |
)
|
130 |
).properties(
|
131 |
+
width=chart_size["width"],
|
132 |
+
height=chart_size["height"] / 2,
|
133 |
)
|
134 |
|
135 |
net_flow_total_fig = net_flow_total_fig & price_fig
|
136 |
+
net_flow_total_fig = net_flow_total_fig.resolve_scale(x="shared").properties(
|
137 |
title=f"{asset} ETF net flow total vs asset price",
|
138 |
)
|
139 |
|
|
|
146 |
)
|
147 |
.mark_area()
|
148 |
.encode(
|
149 |
+
x=alt.X("Date:T", axis=alt.Axis(tickCount="day", title="", labels=False)),
|
150 |
y=alt.Y("Net Flow:Q"),
|
151 |
color=alt.Color("Funds:N", scale=alt.Scale(scheme="tableau20")),
|
152 |
)
|
153 |
).properties(
|
154 |
+
width=chart_size["width"],
|
155 |
+
height=chart_size["height"] / 2,
|
156 |
)
|
157 |
cum_flow_individual_net_fig = cum_flow_individual_net_fig & price_fig
|
158 |
+
cum_flow_individual_net_fig = cum_flow_individual_net_fig.resolve_scale(
|
159 |
+
x="shared"
|
160 |
+
).properties(
|
161 |
title=f"{asset} ETF cumulative flow of individual funds vs asset price",
|
162 |
)
|
163 |
|
|
|
169 |
)
|
170 |
.mark_area()
|
171 |
.encode(
|
172 |
+
x=alt.X("Date:T", axis=alt.Axis(tickCount="day", title="", labels=False)),
|
173 |
y=alt.Y("Total:Q", impute={"value": 0}),
|
174 |
color=alt.Color(
|
175 |
"negative:N", title="Negative Flow", scale=alt.Scale(scheme="set2")
|
176 |
),
|
177 |
)
|
178 |
).properties(
|
179 |
+
width=chart_size["width"],
|
180 |
+
height=chart_size["height"] / 2,
|
181 |
)
|
182 |
cum_flow_total_fig = cum_flow_total_fig & price_fig
|
183 |
+
cum_flow_total_fig = cum_flow_total_fig.resolve_scale(x="shared").properties(
|
184 |
title=f"{asset} ETF cumulative flow total vs asset price",
|
185 |
)
|
186 |
|
|
|
192 |
cum_flow_total_fig=cum_flow_total_fig,
|
193 |
)
|
194 |
|
195 |
+
|
196 |
+
def asset_charts(asset: str, chart_size={"width": "container", "height": 300}):
|
197 |
charts = gen_charts(asset, chart_size)
|
198 |
# Vertical concat the charts in each asset into single column of that asset
|
199 |
all_charts = (
|
|
|
206 |
|
207 |
return all_charts
|
208 |
|
209 |
+
|
210 |
def compound_chart(chart_size={"width": 560, "height": 300}):
|
211 |
+
all_charts_btc = asset_charts("BTC", chart_size)
|
212 |
+
all_charts_eth = asset_charts("ETH", chart_size)
|
213 |
# Horizontal concat the charts for btc and eth
|
214 |
all_charts = (all_charts_btc | all_charts_eth).resolve_scale(color="independent")
|
215 |
|
216 |
return all_charts
|
217 |
|
|
|
218 |
if __name__ == "__main__":
|
219 |
# Set page config
|
220 |
st.set_page_config(layout="wide", page_icon="π")
|
221 |
# Initialize pygwalker communication
|
222 |
init_streamlit_comm()
|
223 |
|
224 |
+
dashboard_tab, single_view, flow_tab, volume_tab, price_tab = st.tabs(
|
225 |
+
[
|
226 |
+
"Dashboard",
|
227 |
+
"View Single ETF",
|
228 |
+
"Explore ETF Flow",
|
229 |
+
"Explore ETF Volume",
|
230 |
+
"Explore ETF Asset Price",
|
231 |
+
]
|
232 |
+
)
|
233 |
|
234 |
+
btc = fetch_asset("BTC")
|
235 |
+
eth = fetch_asset("ETH")
|
236 |
|
237 |
with dashboard_tab:
|
238 |
chart = compound_chart(chart_size={"width": 560, "height": 300})
|
|
|
240 |
st.altair_chart(chart, use_container_width=True)
|
241 |
with single_view:
|
242 |
asset = st.selectbox(
|
243 |
+
"Asset to view",
|
244 |
+
("BTC", "ETH"),
|
245 |
)
|
246 |
+
charts = gen_charts(asset, chart_size={"width": "container", "height": 600})
|
247 |
+
st.altair_chart(charts.trading_vol_fig, use_container_width=True)
|
248 |
+
st.altair_chart(charts.net_flow_individual_fig, use_container_width=True)
|
249 |
+
st.altair_chart(charts.net_flow_total_fig, use_container_width=True)
|
250 |
+
st.altair_chart(charts.cum_flow_individual_net_fig, use_container_width=True)
|
251 |
+
st.altair_chart(charts.cum_flow_total_fig, use_container_width=True)
|
252 |
with flow_tab:
|
253 |
btc_flow, eth_flow = btc.etf_flow, eth.etf_flow
|
254 |
+
btc_flow["Asset"] = "BTC"
|
255 |
+
eth_flow["Asset"] = "ETH"
|
256 |
df = pd.concat([btc_flow, eth_flow])
|
257 |
df.Date = df.Date.astype(str)
|
258 |
StreamlitRenderer(df).explorer()
|
259 |
with volume_tab:
|
260 |
btc_volume, eth_volume = btc.etf_volumes, eth.etf_volumes
|
261 |
+
btc_volume["Asset"] = "BTC"
|
262 |
+
eth_volume["Asset"] = "ETH"
|
263 |
df = pd.concat([btc_volume, eth_volume])
|
264 |
df.Date = df.Date.astype(str)
|
265 |
StreamlitRenderer(df).explorer()
|
266 |
with price_tab:
|
267 |
btc_price, eth_price = btc.price, eth.price
|
268 |
+
btc_price["Asset"] = "BTC"
|
269 |
+
eth_price["Asset"] = "ETH"
|
270 |
df = pd.concat([btc_price, eth_price])
|
271 |
df.Date = df.Date.astype(str)
|
272 |
StreamlitRenderer(df).explorer()
|