Update src/streamlit_app.py
Browse files- src/streamlit_app.py +329 -37
src/streamlit_app.py
CHANGED
@@ -1,40 +1,332 @@
|
|
1 |
-
import
|
2 |
-
import
|
3 |
import pandas as pd
|
|
|
4 |
import streamlit as st
|
5 |
|
6 |
-
""
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import requests
|
3 |
import pandas as pd
|
4 |
+
import plotly.graph_objects as go
|
5 |
import streamlit as st
|
6 |
|
7 |
+
st.set_page_config(layout="wide")
|
8 |
+
|
9 |
+
# you need to save your FMP API Key into the respective environment variable to let it work
|
10 |
+
apikey = os.environ["FMP_API_KEY"]
|
11 |
+
|
12 |
+
def parse_json(url):
|
13 |
+
resp = requests.get(url)
|
14 |
+
resp.raise_for_status()
|
15 |
+
return pd.DataFrame(resp.json())
|
16 |
+
|
17 |
+
def fmt(val):
|
18 |
+
if abs(val) >= 1e9: return f"${val/1e9:.1f}B"
|
19 |
+
if abs(val) >= 1e6: return f"${val/1e6:.1f}M"
|
20 |
+
if abs(val) >= 1e3: return f"${val/1e3:.0f}K"
|
21 |
+
return f"${val:.0f}"
|
22 |
+
|
23 |
+
def draw_balance_sankey(balance_sheet, symbol, height, font_size):
|
24 |
+
# define all Sankey flows (left is source, right is target)
|
25 |
+
flows = [
|
26 |
+
# Current Assets
|
27 |
+
("Cash and Cash Equivalents", "Total Current Assets", balance_sheet["cashAndCashEquivalents"]),
|
28 |
+
("Short-Term Investments", "Total Current Assets", balance_sheet["shortTermInvestments"]),
|
29 |
+
("Net Receivables", "Total Current Assets", balance_sheet["netReceivables"]),
|
30 |
+
("Inventory", "Total Current Assets", balance_sheet["inventory"]),
|
31 |
+
("Prepaids", "Total Current Assets", balance_sheet["prepaids"]),
|
32 |
+
# NOTE: Other Current Assets may sometimes overlap with some of the above lines
|
33 |
+
("Other Current Assets", "Total Current Assets", balance_sheet["otherCurrentAssets"]),
|
34 |
+
|
35 |
+
# Non-Current Assets
|
36 |
+
("Property, Plant and Equipment, Net", "Total Non-Current Assets", balance_sheet["propertyPlantEquipmentNet"]),
|
37 |
+
("Goodwill", "Total Non-Current Assets", balance_sheet["goodwill"]),
|
38 |
+
("Intangible Assets", "Total Non-Current Assets", balance_sheet["intangibleAssets"]),
|
39 |
+
("Long-Term Investments", "Total Non-Current Assets", balance_sheet["longTermInvestments"]),
|
40 |
+
("Tax Assets", "Total Non-Current Assets", balance_sheet["taxAssets"]),
|
41 |
+
("Other Non-Current Assets", "Total Non-Current Assets", balance_sheet["otherNonCurrentAssets"]),
|
42 |
+
|
43 |
+
# ... to Total Assets
|
44 |
+
("Total Current Assets", "Total Assets", balance_sheet["totalCurrentAssets"]),
|
45 |
+
("Total Non-Current Assets", "Total Assets", balance_sheet["totalNonCurrentAssets"]),
|
46 |
+
|
47 |
+
# Total Assets to ...
|
48 |
+
("Total Assets", "Total Liabilities", balance_sheet["totalLiabilities"]),
|
49 |
+
("Total Assets", "Total Stockholders' Equity", balance_sheet["totalStockholdersEquity"]),
|
50 |
+
|
51 |
+
# Current Liabilities
|
52 |
+
("Total Liabilities", "Total Current Liabilities", balance_sheet["totalCurrentLiabilities"]),
|
53 |
+
("Total Current Liabilities", "Tax Payables", balance_sheet["taxPayables"]),
|
54 |
+
("Total Current Liabilities", "Short-Term Debt", balance_sheet["shortTermDebt"]),
|
55 |
+
("Total Current Liabilities", "Capital Lease Obligations (Current)", balance_sheet["capitalLeaseObligationsCurrent"]),
|
56 |
+
("Total Current Liabilities", "Deferred Revenue (Current)", balance_sheet["deferredRevenue"]),
|
57 |
+
("Total Current Liabilities", "Other Current Liabilities", balance_sheet["otherCurrentLiabilities"]),
|
58 |
+
("Total Current Liabilities", "Accounts Payable", balance_sheet["accountPayables"]),
|
59 |
+
("Total Current Liabilities", "Other Payables", balance_sheet["otherPayables"]),
|
60 |
+
("Total Current Liabilities", "Accrued Expenses", balance_sheet["accruedExpenses"]),
|
61 |
+
|
62 |
+
# Non-Current Liabilities
|
63 |
+
("Total Liabilities", "Total Non-Current Liabilities", balance_sheet["totalNonCurrentLiabilities"]),
|
64 |
+
("Total Non-Current Liabilities", "Long-Term Debt", balance_sheet["longTermDebt"]),
|
65 |
+
("Total Non-Current Liabilities", "Capital Lease Obligations (Non-Current)", balance_sheet["capitalLeaseObligationsNonCurrent"]),
|
66 |
+
("Total Non-Current Liabilities", "Deferred Revenue (Non-Current)", balance_sheet["deferredRevenueNonCurrent"]),
|
67 |
+
("Total Non-Current Liabilities", "Deferred Tax Liabilities (Non-Current)", balance_sheet["deferredTaxLiabilitiesNonCurrent"]),
|
68 |
+
("Total Non-Current Liabilities", "Other Non-Current Liabilities", balance_sheet["otherNonCurrentLiabilities"]),
|
69 |
+
|
70 |
+
# Equity
|
71 |
+
("Total Stockholders' Equity", "Common Stock", balance_sheet["commonStock"]),
|
72 |
+
("Total Stockholders' Equity", "Retained Earnings", balance_sheet["retainedEarnings"]),
|
73 |
+
("Total Stockholders' Equity", "Accumulated Other Comprehensive Income (Loss)", balance_sheet["accumulatedOtherComprehensiveIncomeLoss"]),
|
74 |
+
("Total Stockholders' Equity", "Additional Paid-In Capital", balance_sheet["additionalPaidInCapital"]),
|
75 |
+
("Total Stockholders' Equity", "Other Stockholders' Equity", balance_sheet["otherTotalStockholdersEquity"]),
|
76 |
+
]
|
77 |
+
|
78 |
+
|
79 |
+
# need to adjust flow to make negative values easier to read
|
80 |
+
adjusted_flows = []
|
81 |
+
for src, tgt, val in flows:
|
82 |
+
if val >= 0:
|
83 |
+
# positive: keep direction, color green
|
84 |
+
adjusted_flows.append((src, tgt, val, 'rgba(50,200,50,0.6)'))
|
85 |
+
else:
|
86 |
+
# negative: reverse direction, color red
|
87 |
+
adjusted_flows.append((tgt, src, -val, 'rgba(200,50,50,0.6)'))
|
88 |
+
|
89 |
+
# for labelling later, first we store the source and target names
|
90 |
+
labels = []
|
91 |
+
for src, tgt, _ in flows:
|
92 |
+
if src not in labels: labels.append(src)
|
93 |
+
if tgt not in labels: labels.append(tgt)
|
94 |
+
|
95 |
+
# map label to its actual balance‐sheet value for annotation
|
96 |
+
node_values = {
|
97 |
+
"Cash and Cash Equivalents": balance_sheet["cashAndCashEquivalents"],
|
98 |
+
"Short-Term Investments": balance_sheet["shortTermInvestments"],
|
99 |
+
"Net Receivables": balance_sheet["netReceivables"],
|
100 |
+
"Inventory": balance_sheet["inventory"],
|
101 |
+
"Prepaids": balance_sheet["prepaids"],
|
102 |
+
"Other Current Assets": balance_sheet["otherCurrentAssets"],
|
103 |
+
"Total Current Assets": balance_sheet["totalCurrentAssets"],
|
104 |
+
"Property, Plant and Equipment, Net": balance_sheet["propertyPlantEquipmentNet"],
|
105 |
+
"Goodwill": balance_sheet["goodwill"],
|
106 |
+
"Intangible Assets": balance_sheet["intangibleAssets"],
|
107 |
+
"Long-Term Investments": balance_sheet["longTermInvestments"],
|
108 |
+
"Tax Assets": balance_sheet["taxAssets"],
|
109 |
+
"Other Non-Current Assets": balance_sheet["otherNonCurrentAssets"],
|
110 |
+
"Total Non-Current Assets": balance_sheet["totalNonCurrentAssets"],
|
111 |
+
"Total Assets": balance_sheet["totalAssets"],
|
112 |
+
|
113 |
+
"Total Liabilities": balance_sheet["totalLiabilities"],
|
114 |
+
"Total Current Liabilities": balance_sheet["totalCurrentLiabilities"],
|
115 |
+
"Tax Payables": balance_sheet["taxPayables"],
|
116 |
+
"Short-Term Debt": balance_sheet["shortTermDebt"],
|
117 |
+
"Capital Lease Obligations (Current)": balance_sheet["capitalLeaseObligationsCurrent"],
|
118 |
+
"Deferred Revenue (Current)": balance_sheet["deferredRevenue"],
|
119 |
+
"Other Current Liabilities": balance_sheet["otherCurrentLiabilities"],
|
120 |
+
|
121 |
+
"Accounts Payable": balance_sheet["accountPayables"],
|
122 |
+
"Other Payables": balance_sheet["otherPayables"],
|
123 |
+
"Accrued Expenses": balance_sheet["accruedExpenses"],
|
124 |
+
|
125 |
+
"Total Non-Current Liabilities": balance_sheet["totalNonCurrentLiabilities"],
|
126 |
+
"Long-Term Debt": balance_sheet["longTermDebt"],
|
127 |
+
"Capital Lease Obligations (Non-Current)": balance_sheet["capitalLeaseObligationsNonCurrent"],
|
128 |
+
"Deferred Revenue (Non-Current)": balance_sheet["deferredRevenueNonCurrent"],
|
129 |
+
"Deferred Tax Liabilities (Non-Current)": balance_sheet["deferredTaxLiabilitiesNonCurrent"],
|
130 |
+
"Other Non-Current Liabilities": balance_sheet["otherNonCurrentLiabilities"],
|
131 |
+
|
132 |
+
"Total Stockholders' Equity": balance_sheet["totalStockholdersEquity"],
|
133 |
+
"Common Stock": balance_sheet["commonStock"],
|
134 |
+
"Retained Earnings": balance_sheet["retainedEarnings"],
|
135 |
+
"Accumulated Other Comprehensive Income (Loss)": balance_sheet["accumulatedOtherComprehensiveIncomeLoss"],
|
136 |
+
"Additional Paid-In Capital": balance_sheet["additionalPaidInCapital"],
|
137 |
+
"Other Stockholders' Equity": balance_sheet["otherTotalStockholdersEquity"],
|
138 |
+
}
|
139 |
+
|
140 |
+
# for formatting, annotate labels with $ amounts and take care of billions, millions, thousands
|
141 |
+
def fmt(val):
|
142 |
+
if abs(val) >= 1e9: return f"${val/1e9:.1f}B"
|
143 |
+
if abs(val) >= 1e6: return f"${val/1e6:.1f}M"
|
144 |
+
if abs(val) >= 1e3: return f"${val/1e3:.0f}K"
|
145 |
+
return f"${val:.0f}"
|
146 |
+
|
147 |
+
# put the sorce and target values in labels
|
148 |
+
labels = []
|
149 |
+
for s, t, _, _ in adjusted_flows:
|
150 |
+
if s not in labels: labels.append(s)
|
151 |
+
if t not in labels: labels.append(t)
|
152 |
+
|
153 |
+
idx = {label:i for i,label in enumerate(labels)}
|
154 |
+
source = [ idx[s] for s, t, _, _ in adjusted_flows ] # index of sources for sankey input
|
155 |
+
target = [ idx[t] for s, t, _, _ in adjusted_flows ] # index of target for sankey input
|
156 |
+
value = [ v for _, _, v, _ in adjusted_flows ]
|
157 |
+
colors = [ c for _, _, _, c in adjusted_flows ]
|
158 |
+
|
159 |
+
label_with_values = []
|
160 |
+
for label in labels:
|
161 |
+
val = node_values[label]
|
162 |
+
base = label.replace(" (Current)", "")\
|
163 |
+
.replace(" (Non-Current)", "") # saves some printing space
|
164 |
+
if val < 0:
|
165 |
+
base += " [NEGATIVE]" # just to make negatives more obvious in the label
|
166 |
+
label_with_values.append(f"{base} ({fmt(val)})")
|
167 |
+
|
168 |
+
fig = go.Figure(go.Sankey(
|
169 |
+
arrangement="snap",
|
170 |
+
node = dict(label=label_with_values, pad=15, thickness=20),
|
171 |
+
link = dict(source=source, target=target, value=value, color=colors)
|
172 |
+
))
|
173 |
+
fig.update_layout(
|
174 |
+
title_text=f"Balance Sheet Sankey — {symbol}",
|
175 |
+
height=height,
|
176 |
+
font_size=font_size
|
177 |
+
)
|
178 |
+
return fig
|
179 |
+
|
180 |
+
def draw_income_sankey(income_statement, symbol, height, font_size):
|
181 |
+
flows = [
|
182 |
+
# Revenue─
|
183 |
+
("Revenue", "Cost of Revenue", income_statement["costOfRevenue"]),
|
184 |
+
("Revenue", "Gross Profit", income_statement["grossProfit"]),
|
185 |
+
|
186 |
+
# Gross Profit
|
187 |
+
("Gross Profit", "Operating Income", income_statement["operatingIncome"]),
|
188 |
+
("Gross Profit", "Operating Expenses", income_statement["operatingExpenses"]),
|
189 |
+
|
190 |
+
# Operating Expenses
|
191 |
+
("Operating Expenses", "Research & Development Expenses", income_statement["researchAndDevelopmentExpenses"]),
|
192 |
+
# ("Operating Expenses", "General & Administrative Expenses", income_statement["generalAndAdministrativeExpenses"]), # already in SG&A
|
193 |
+
# ("Operating Expenses", "Selling & Marketing Expenses", income_statement["sellingAndMarketingExpenses"]), # already in SG&A
|
194 |
+
("Operating Expenses", "SG&A Expenses", income_statement["sellingGeneralAndAdministrativeExpenses"]),
|
195 |
+
("Operating Expenses", "Other Operating Expenses", income_statement["otherExpenses"]),
|
196 |
+
|
197 |
+
# Pretax Income
|
198 |
+
("Pretax Income", "Income Tax Expense", income_statement["incomeTaxExpense"]),
|
199 |
+
("Pretax Income", "Net Income", income_statement["netIncome"]),
|
200 |
+
("Pretax Income", "Interest Expense", income_statement["interestExpense"]),
|
201 |
+
# this value is recorded as negative in API, but we do not need to reverse the flow like in balance sheet
|
202 |
+
# because it decreases the pretax income so we put it together at the same side with all the tax expenses
|
203 |
+
("Pretax Income", "Non-Operating Income Excl. Interest", -income_statement["nonOperatingIncomeExcludingInterest"]),
|
204 |
+
("Pretax Income", "Total Other Income & Expenses Net", income_statement["totalOtherIncomeExpensesNet"]),
|
205 |
+
("Pretax Income", "Other Adjustments to Net Income", income_statement["otherAdjustmentsToNetIncome"]),
|
206 |
+
|
207 |
+
# Other Income that goes into Pretax Income
|
208 |
+
("Operating Income", "Pretax Income", income_statement["operatingIncome"]),
|
209 |
+
("Net Interest Income", "Pretax Income", income_statement["netInterestIncome"]),
|
210 |
+
("Interest Income", "Pretax Income", income_statement["interestIncome"]),
|
211 |
+
]
|
212 |
+
|
213 |
+
|
214 |
+
# need to adjust flow to make negative values easier to read
|
215 |
+
adjusted_flows = []
|
216 |
+
for src, tgt, val in flows:
|
217 |
+
if val >= 0:
|
218 |
+
# positive: keep direction, color green
|
219 |
+
adjusted_flows.append((src, tgt, val, 'rgba(50,200,50,0.6)'))
|
220 |
+
else:
|
221 |
+
# negative: reverse direction, color red
|
222 |
+
adjusted_flows.append((tgt, src, -val, 'rgba(200,50,50,0.6)'))
|
223 |
+
|
224 |
+
# for labelling later, first we store the source and target names
|
225 |
+
labels = []
|
226 |
+
for src, tgt, _ in flows:
|
227 |
+
if src not in labels: labels.append(src)
|
228 |
+
if tgt not in labels: labels.append(tgt)
|
229 |
+
|
230 |
+
# map label to its actual balance‐sheet value for annotation
|
231 |
+
node_values = {
|
232 |
+
"Revenue": income_statement["revenue"],
|
233 |
+
"Cost of Revenue": income_statement["costOfRevenue"],
|
234 |
+
"Gross Profit": income_statement["grossProfit"],
|
235 |
+
|
236 |
+
"Operating Income": income_statement["operatingIncome"],
|
237 |
+
"Operating Expenses": income_statement["operatingExpenses"],
|
238 |
+
"Research & Development Expenses": income_statement["researchAndDevelopmentExpenses"],
|
239 |
+
#"General & Administrative Expenses": income_statement["generalAndAdministrativeExpenses"], # already in SG&A
|
240 |
+
#"Selling & Marketing Expenses": income_statement["sellingAndMarketingExpenses"], # already in SG&A
|
241 |
+
"SG&A Expenses": income_statement["sellingGeneralAndAdministrativeExpenses"],
|
242 |
+
"Other Operating Expenses": income_statement["otherExpenses"],
|
243 |
+
|
244 |
+
"Net Interest Income": income_statement["netInterestIncome"],
|
245 |
+
"Interest Income": income_statement["interestIncome"],
|
246 |
+
"Interest Expense": income_statement["interestExpense"],
|
247 |
+
"Non-Operating Income Excl. Interest":-income_statement["nonOperatingIncomeExcludingInterest"],
|
248 |
+
"Total Other Income & Expenses Net": income_statement["totalOtherIncomeExpensesNet"],
|
249 |
+
|
250 |
+
"Pretax Income": income_statement["incomeBeforeTax"],
|
251 |
+
"Income Tax Expense": income_statement["incomeTaxExpense"],
|
252 |
+
"Net Income": income_statement["netIncome"],
|
253 |
+
"Other Adjustments to Net Income": income_statement["otherAdjustmentsToNetIncome"],
|
254 |
+
"Bottom Line Net Income": income_statement["bottomLineNetIncome"],
|
255 |
+
}
|
256 |
+
|
257 |
+
# for formatting, annotate labels with $ amounts and take care of billions, millions, thousands
|
258 |
+
def fmt(val):
|
259 |
+
if abs(val) >= 1e9: return f"${val/1e9:.1f}B"
|
260 |
+
if abs(val) >= 1e6: return f"${val/1e6:.1f}M"
|
261 |
+
if abs(val) >= 1e3: return f"${val/1e3:.0f}K"
|
262 |
+
return f"${val:.0f}"
|
263 |
+
|
264 |
+
# put the sorce and target values in labels
|
265 |
+
labels = []
|
266 |
+
for s, t, _, _ in adjusted_flows:
|
267 |
+
if s not in labels: labels.append(s)
|
268 |
+
if t not in labels: labels.append(t)
|
269 |
+
|
270 |
+
idx = {label:i for i,label in enumerate(labels)}
|
271 |
+
source = [ idx[s] for s, t, _, _ in adjusted_flows ] # index of sources for sankey input
|
272 |
+
target = [ idx[t] for s, t, _, _ in adjusted_flows ] # index of target for sankey input
|
273 |
+
value = [ v for _, _, v, _ in adjusted_flows ]
|
274 |
+
colors = [ c for _, _, _, c in adjusted_flows ]
|
275 |
+
|
276 |
+
label_with_values = []
|
277 |
+
for label in labels:
|
278 |
+
val = node_values[label]
|
279 |
+
base = label.replace(" (Current)", "")\
|
280 |
+
.replace(" (Non-Current)", "") # saves some printing space
|
281 |
+
if val < 0:
|
282 |
+
base += " [NEGATIVE]" # just to make negatives more obvious in the label
|
283 |
+
label_with_values.append(f"{base} ({fmt(val)})")
|
284 |
+
|
285 |
+
fig = go.Figure(go.Sankey(
|
286 |
+
arrangement="snap",
|
287 |
+
node = dict(label=label_with_values, pad=15, thickness=20),
|
288 |
+
link = dict(source=source, target=target, value=value, color=colors)
|
289 |
+
))
|
290 |
+
fig.update_layout(
|
291 |
+
title_text=f"Income Statement Sankey — {symbol}",
|
292 |
+
height=height,
|
293 |
+
font_size=font_size
|
294 |
+
)
|
295 |
+
return fig
|
296 |
+
|
297 |
+
st.title("Financial Sankeys")
|
298 |
+
|
299 |
+
symbol = st.sidebar.text_input("Ticker symbol", "AMZN").upper()
|
300 |
+
|
301 |
+
# sidebar controls
|
302 |
+
bs_height = st.sidebar.slider("Balance Sheet height", 500, 1500, 800)
|
303 |
+
bs_font = st.sidebar.slider("Balance Sheet font size", 5, 15, 10)
|
304 |
+
is_height = st.sidebar.slider("Income Statement height", 500, 1500, 600)
|
305 |
+
is_font = st.sidebar.slider("Income Statement font size", 5, 15, 10)
|
306 |
+
|
307 |
+
# where the data came from
|
308 |
+
st.sidebar.markdown("## [Financial Modeling Prep API](https://site.financialmodelingprep.com/?utm_source=medium&utm_medium=medium&utm_campaign=damian8)\
|
309 |
+
\n\nFinancial statements are obtained from the FinancialModelingPrep API, feel free to sign up\
|
310 |
+
[here](https://site.financialmodelingprep.com/?utm_source=medium&utm_medium=medium&utm_campaign=damian8)\
|
311 |
+
if you wish.")
|
312 |
+
|
313 |
+
if symbol:
|
314 |
+
# Balance Sheet
|
315 |
+
st.header(f"Balance Sheet — {symbol}")
|
316 |
+
try:
|
317 |
+
df_bs = parse_json(f"https://financialmodelingprep.com/stable/balance-sheet-statement?symbol={symbol}&apikey={apikey}")
|
318 |
+
balance_sheet = df_bs.iloc[0]
|
319 |
+
fig_bs = draw_balance_sankey(balance_sheet, symbol.upper(), bs_height, bs_font)
|
320 |
+
st.plotly_chart(fig_bs, use_container_width=True)
|
321 |
+
except Exception as e:
|
322 |
+
st.error(f"Failed to fetch balance sheet: {e}")
|
323 |
+
|
324 |
+
# Income Statement
|
325 |
+
st.header(f"Income Statement — {symbol}")
|
326 |
+
try:
|
327 |
+
df_is = parse_json(f"https://financialmodelingprep.com/stable/income-statement?symbol={symbol}&apikey={apikey}")
|
328 |
+
income_statement = df_is.iloc[0]
|
329 |
+
fig_is = draw_income_sankey(income_statement, symbol.upper(), is_height, is_font)
|
330 |
+
st.plotly_chart(fig_is, use_container_width=True)
|
331 |
+
except Exception as e:
|
332 |
+
st.error(f"Failed to fetch income statement: {e}")
|