Delete app.py
Browse files
app.py
DELETED
@@ -1,584 +0,0 @@
|
|
1 |
-
import streamlit as st
|
2 |
-
import pandas as pd
|
3 |
-
import numpy as np
|
4 |
-
import plotly.express as px
|
5 |
-
import plotly.graph_objects as go
|
6 |
-
import google.generativeai as genai
|
7 |
-
import os
|
8 |
-
from datetime import datetime, timedelta
|
9 |
-
import base64
|
10 |
-
from io import BytesIO
|
11 |
-
|
12 |
-
# Initialize page configuration
|
13 |
-
st.set_page_config(
|
14 |
-
page_title="StartupFinancePilot",
|
15 |
-
page_icon="💰",
|
16 |
-
layout="wide",
|
17 |
-
initial_sidebar_state="expanded"
|
18 |
-
)
|
19 |
-
|
20 |
-
# Constants
|
21 |
-
DEFAULT_GROWTH_RATE = 0.08 # 8% monthly growth
|
22 |
-
DEFAULT_BURN_RATE = 85000 # $85,000 monthly burn
|
23 |
-
ENGINEER_SALARY = 10000 # $10,000 monthly cost per engineer ($120K/year)
|
24 |
-
DEFAULT_MARKETING_BUDGET = 10000 # $10,000 monthly marketing budget
|
25 |
-
|
26 |
-
# Sample data
|
27 |
-
@st.cache_data
|
28 |
-
def load_sample_data():
|
29 |
-
# TechHealth AI data
|
30 |
-
startup_data = {
|
31 |
-
"name": "TechHealth AI",
|
32 |
-
"stage": "Seed",
|
33 |
-
"founded": "18 months ago",
|
34 |
-
"employees": 12,
|
35 |
-
"last_funding": "$1.2M seed round 10 months ago",
|
36 |
-
"cash": 320000,
|
37 |
-
"burn_rate": 85000,
|
38 |
-
"revenue": 15000,
|
39 |
-
"growth_rate": 0.08
|
40 |
-
}
|
41 |
-
|
42 |
-
# Cash flow history
|
43 |
-
cash_flow_data = {
|
44 |
-
"Month": [f"Month {i}" for i in range(1, 11)],
|
45 |
-
"Revenue": [8000, 8500, 9200, 10000, 10800, 11700, 12600, 13600, 14700, 15800],
|
46 |
-
"Payroll": [60000, 60000, 62000, 62000, 65000, 65000, 70000, 70000, 75000, 75000],
|
47 |
-
"Marketing": [8000, 9000, 10000, 12000, 15000, 18000, 15000, 12000, 10000, 8000],
|
48 |
-
"Office": [5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000],
|
49 |
-
"Software": [3000, 3200, 3500, 3800, 4000, 4200, 4500, 4800, 5000, 5200],
|
50 |
-
"Travel": [2000, 1800, 2500, 3000, 4000, 4500, 3500, 3000, 2500, 2000],
|
51 |
-
"Legal": [1500, 1000, 800, 1200, 800, 2000, 1500, 1000, 3000, 1200],
|
52 |
-
"Misc": [1000, 1200, 1300, 1500, 1700, 1800, 2000, 2200, 2500, 2800]
|
53 |
-
}
|
54 |
-
|
55 |
-
# Add calculated fields
|
56 |
-
df = pd.DataFrame(cash_flow_data)
|
57 |
-
df["Total_Expenses"] = df[["Payroll", "Marketing", "Office", "Software", "Travel", "Legal", "Misc"]].sum(axis=1)
|
58 |
-
df["Net_Burn"] = df["Total_Expenses"] - df["Revenue"]
|
59 |
-
|
60 |
-
# Transaction data
|
61 |
-
transactions = pd.DataFrame([
|
62 |
-
{"Date": "2023-11-05", "Category": "Travel", "Vendor": "Caribbean Cruises", "Amount": 8500, "Description": "Team Retreat Planning", "Flag": "Suspicious"},
|
63 |
-
{"Date": "2023-11-12", "Category": "Marketing", "Vendor": "LuxuryGifts Inc", "Amount": 4200, "Description": "Client Appreciation", "Flag": "Suspicious"},
|
64 |
-
{"Date": "2023-11-22", "Category": "Office", "Vendor": "Premium Furniture", "Amount": 12000, "Description": "Office Upgrades", "Flag": "Suspicious"},
|
65 |
-
{"Date": "2023-11-28", "Category": "Consulting", "Vendor": "Strategic Vision LLC", "Amount": 7500, "Description": "Strategy Consulting", "Flag": "Suspicious"},
|
66 |
-
{"Date": "2023-12-05", "Category": "Software", "Vendor": "Personal Apple Store", "Amount": 3200, "Description": "Development Tools", "Flag": "Suspicious"},
|
67 |
-
{"Date": "2023-12-12", "Category": "Legal", "Vendor": "Anderson Brothers", "Amount": 5800, "Description": "Legal Services", "Flag": "Normal"},
|
68 |
-
{"Date": "2023-12-20", "Category": "Payroll", "Vendor": "November Payroll", "Amount": 75000, "Description": "Monthly Payroll", "Flag": "Normal"},
|
69 |
-
{"Date": "2023-12-22", "Category": "Marketing", "Vendor": "Google Ads", "Amount": 8000, "Description": "Ad Campaign", "Flag": "Normal"},
|
70 |
-
{"Date": "2023-12-25", "Category": "Office", "Vendor": "WeWork", "Amount": 5000, "Description": "Monthly Rent", "Flag": "Normal"},
|
71 |
-
{"Date": "2023-12-28", "Category": "Software", "Vendor": "AWS", "Amount": 5200, "Description": "Cloud Services", "Flag": "Normal"},
|
72 |
-
{"Date": "2024-01-05", "Category": "Travel", "Vendor": "Delta Airlines", "Amount": 1200, "Description": "Client Meeting Travel", "Flag": "Normal"},
|
73 |
-
{"Date": "2024-01-10", "Category": "Marketing", "Vendor": "Facebook Ads", "Amount": 4500, "Description": "Social Media Campaign", "Flag": "Normal"},
|
74 |
-
{"Date": "2024-01-15", "Category": "Software", "Vendor": "Atlassian", "Amount": 2800, "Description": "Development Tools", "Flag": "Normal"},
|
75 |
-
{"Date": "2024-01-20", "Category": "Payroll", "Vendor": "January Payroll", "Amount": 75000, "Description": "Monthly Payroll", "Flag": "Normal"},
|
76 |
-
{"Date": "2024-01-25", "Category": "Office", "Vendor": "WeWork", "Amount": 5000, "Description": "Monthly Rent", "Flag": "Normal"}
|
77 |
-
])
|
78 |
-
|
79 |
-
return startup_data, df, transactions
|
80 |
-
|
81 |
-
# Setup Gemini API (with API key from secrets)
|
82 |
-
def setup_genai():
|
83 |
-
if 'GOOGLE_API_KEY' in st.secrets:
|
84 |
-
genai.configure(api_key=st.secrets['GOOGLE_API_KEY'])
|
85 |
-
return True
|
86 |
-
else:
|
87 |
-
st.warning("Google API key not found. Please add it to the secrets.")
|
88 |
-
return False
|
89 |
-
|
90 |
-
# Financial modeling functions
|
91 |
-
def calculate_runway(initial_cash, monthly_burn, monthly_revenue, growth_rate, months=24):
|
92 |
-
"""Calculate runway based on current burn rate and revenue growth."""
|
93 |
-
dates = [datetime.now() + timedelta(days=30*i) for i in range(months)]
|
94 |
-
df = pd.DataFrame(index=dates, columns=['Cash', 'Revenue', 'Expenses', 'Net_Burn', 'Cumulative_Cash'])
|
95 |
-
|
96 |
-
current_cash = initial_cash
|
97 |
-
current_revenue = monthly_revenue
|
98 |
-
df.iloc[0, df.columns.get_loc('Cash')] = current_cash
|
99 |
-
df.iloc[0, df.columns.get_loc('Revenue')] = current_revenue
|
100 |
-
df.iloc[0, df.columns.get_loc('Expenses')] = monthly_burn
|
101 |
-
df.iloc[0, df.columns.get_loc('Net_Burn')] = monthly_burn - current_revenue
|
102 |
-
df.iloc[0, df.columns.get_loc('Cumulative_Cash')] = current_cash
|
103 |
-
|
104 |
-
runway_months = months
|
105 |
-
for i in range(1, months):
|
106 |
-
current_revenue = current_revenue * (1 + growth_rate)
|
107 |
-
net_burn = monthly_burn - current_revenue
|
108 |
-
current_cash = current_cash - net_burn
|
109 |
-
|
110 |
-
df.iloc[i, df.columns.get_loc('Cash')] = current_cash
|
111 |
-
df.iloc[i, df.columns.get_loc('Revenue')] = current_revenue
|
112 |
-
df.iloc[i, df.columns.get_loc('Expenses')] = monthly_burn
|
113 |
-
df.iloc[i, df.columns.get_loc('Net_Burn')] = net_burn
|
114 |
-
df.iloc[i, df.columns.get_loc('Cumulative_Cash')] = current_cash
|
115 |
-
|
116 |
-
if current_cash <= 0:
|
117 |
-
runway_months = i
|
118 |
-
break
|
119 |
-
|
120 |
-
return runway_months, df
|
121 |
-
|
122 |
-
def simulate_decision(initial_cash, monthly_burn, monthly_revenue, growth_rate,
|
123 |
-
new_expenses=0, new_hires=0, new_marketing=0, growth_impact=0):
|
124 |
-
"""Simulate the impact of a business decision on runway."""
|
125 |
-
# Calculate current runway
|
126 |
-
current_runway, current_df = calculate_runway(initial_cash, monthly_burn, monthly_revenue, growth_rate)
|
127 |
-
|
128 |
-
# Calculate additional expenses
|
129 |
-
additional_expenses = new_expenses + (new_hires * ENGINEER_SALARY) + new_marketing
|
130 |
-
|
131 |
-
# Calculate new runway
|
132 |
-
new_runway, new_df = calculate_runway(
|
133 |
-
initial_cash,
|
134 |
-
monthly_burn + additional_expenses,
|
135 |
-
monthly_revenue,
|
136 |
-
growth_rate + growth_impact
|
137 |
-
)
|
138 |
-
|
139 |
-
return current_runway, new_runway, current_df, new_df
|
140 |
-
|
141 |
-
def detect_suspicious_transactions(transactions_df):
|
142 |
-
"""Simple rule-based suspicious transaction detection."""
|
143 |
-
df = transactions_df.copy()
|
144 |
-
|
145 |
-
# Define thresholds for each category
|
146 |
-
category_thresholds = {
|
147 |
-
"Travel": 3000,
|
148 |
-
"Marketing": 10000,
|
149 |
-
"Office": 7000,
|
150 |
-
"Software": 6000,
|
151 |
-
"Consulting": 5000,
|
152 |
-
"Legal": 6000
|
153 |
-
}
|
154 |
-
|
155 |
-
# Define suspicious terms
|
156 |
-
suspicious_terms = ['luxury', 'cruise', 'premium', 'personal', 'gift']
|
157 |
-
|
158 |
-
# Add suspicious column
|
159 |
-
df['Suspicious'] = False
|
160 |
-
df['Reason'] = ""
|
161 |
-
|
162 |
-
# Check for suspicious patterns
|
163 |
-
for idx, row in df.iterrows():
|
164 |
-
reasons = []
|
165 |
-
|
166 |
-
# Check if amount exceeds category threshold
|
167 |
-
if row['Category'] in category_thresholds:
|
168 |
-
if row['Amount'] > category_thresholds[row['Category']]:
|
169 |
-
reasons.append(f"Amount exceeds typical spending for {row['Category']}")
|
170 |
-
|
171 |
-
# Check for suspicious vendors or descriptions
|
172 |
-
if any(term in str(row['Vendor']).lower() for term in suspicious_terms):
|
173 |
-
reasons.append(f"Vendor name contains suspicious term")
|
174 |
-
|
175 |
-
if any(term in str(row['Description']).lower() for term in suspicious_terms):
|
176 |
-
reasons.append(f"Description contains suspicious term")
|
177 |
-
|
178 |
-
# Mark as suspicious if any reasons
|
179 |
-
if reasons:
|
180 |
-
df.at[idx, 'Suspicious'] = True
|
181 |
-
df.at[idx, 'Reason'] = "; ".join(reasons)
|
182 |
-
|
183 |
-
return df
|
184 |
-
|
185 |
-
# Gemini prompt functions
|
186 |
-
def get_runway_analysis(financial_data):
|
187 |
-
"""Get runway analysis using Gemini."""
|
188 |
-
prompt = f"""
|
189 |
-
You are a financial advisor for startups. Analyze this startup's financial data:
|
190 |
-
- Current cash: ${financial_data['cash']}
|
191 |
-
- Monthly burn rate: ${financial_data['burn_rate']}
|
192 |
-
- Monthly revenue: ${financial_data['revenue']}
|
193 |
-
- Monthly growth rate: {financial_data['growth_rate'] * 100}%
|
194 |
-
|
195 |
-
Calculate and explain their runway, financial health, and recommendations in a concise paragraph.
|
196 |
-
"""
|
197 |
-
|
198 |
-
model = genai.GenerativeModel('gemini')
|
199 |
-
response = model.generate_content(prompt)
|
200 |
-
|
201 |
-
return response.text
|
202 |
-
|
203 |
-
def get_decision_analysis(question, financial_data):
|
204 |
-
"""Get analysis for a specific decision."""
|
205 |
-
prompt = f"""
|
206 |
-
You are a financial advisor for startups. A founder asks:
|
207 |
-
"{question}"
|
208 |
-
|
209 |
-
Here's their current financial situation:
|
210 |
-
- Current cash: ${financial_data['cash']}
|
211 |
-
- Monthly burn rate: ${financial_data['burn_rate']}
|
212 |
-
- Monthly revenue: ${financial_data['revenue']}
|
213 |
-
- Monthly growth rate: {financial_data['growth_rate'] * 100}%
|
214 |
-
|
215 |
-
Analyze how this decision would impact their runway and financial health.
|
216 |
-
Provide specific recommendations in 3-4 concise sentences.
|
217 |
-
"""
|
218 |
-
|
219 |
-
model = genai.GenerativeModel('gemini')
|
220 |
-
response = model.generate_content(prompt)
|
221 |
-
|
222 |
-
return response.text
|
223 |
-
|
224 |
-
def get_fraud_analysis(transactions_df):
|
225 |
-
"""Get analysis of potentially fraudulent transactions."""
|
226 |
-
suspicious_df = transactions_df[transactions_df['Suspicious']]
|
227 |
-
|
228 |
-
if len(suspicious_df) == 0:
|
229 |
-
return "No suspicious transactions detected."
|
230 |
-
|
231 |
-
transactions_text = suspicious_df.to_string(index=False)
|
232 |
-
|
233 |
-
prompt = f"""
|
234 |
-
You are a financial advisor specializing in startup spending oversight.
|
235 |
-
Review these flagged transactions:
|
236 |
-
|
237 |
-
{transactions_text}
|
238 |
-
|
239 |
-
Explain why these transactions might concern investors and what actions the startup should take.
|
240 |
-
Keep your response to 4-5 concise sentences focused on the most concerning issues.
|
241 |
-
"""
|
242 |
-
|
243 |
-
model = genai.GenerativeModel('gemini')
|
244 |
-
response = model.generate_content(prompt)
|
245 |
-
|
246 |
-
return response.text
|
247 |
-
|
248 |
-
def get_advisory_guidance(question, financial_data):
|
249 |
-
"""Get strategic guidance for a startup question."""
|
250 |
-
prompt = f"""
|
251 |
-
You are a strategic financial advisor for startups. A founder asks:
|
252 |
-
"{question}"
|
253 |
-
|
254 |
-
Here's their current financial situation:
|
255 |
-
- Stage: {financial_data['stage']}
|
256 |
-
- Current cash: ${financial_data['cash']}
|
257 |
-
- Monthly burn rate: ${financial_data['burn_rate']}
|
258 |
-
- Monthly revenue: ${financial_data['revenue']}
|
259 |
-
- Monthly growth rate: {financial_data['growth_rate'] * 100}%
|
260 |
-
- Last funding: {financial_data['last_funding']}
|
261 |
-
- Team size: {financial_data['employees']}
|
262 |
-
|
263 |
-
Provide specific, actionable advice addressing their question in 3-4 concise sentences.
|
264 |
-
Include timelines and metrics where relevant.
|
265 |
-
"""
|
266 |
-
|
267 |
-
model = genai.GenerativeModel('gemini')
|
268 |
-
response = model.generate_content(prompt)
|
269 |
-
|
270 |
-
return response.text
|
271 |
-
|
272 |
-
# UI Components
|
273 |
-
def create_sidebar():
|
274 |
-
"""Create sidebar with company profile and filters."""
|
275 |
-
st.sidebar.title("StartupFinancePilot")
|
276 |
-
st.sidebar.image("https://img.freepik.com/premium-vector/business-finance-analytics-logo-design-vector-template_67715-552.jpg", width=150)
|
277 |
-
|
278 |
-
# Company profile
|
279 |
-
startup_data, _, _ = load_sample_data()
|
280 |
-
|
281 |
-
st.sidebar.header("Company Profile")
|
282 |
-
st.sidebar.write(f"**{startup_data['name']}**")
|
283 |
-
st.sidebar.write(f"Stage: {startup_data['stage']}")
|
284 |
-
st.sidebar.write(f"Founded: {startup_data['founded']}")
|
285 |
-
st.sidebar.write(f"Employees: {startup_data['employees']}")
|
286 |
-
st.sidebar.write(f"Last Funding: {startup_data['last_funding']}")
|
287 |
-
|
288 |
-
# App navigation
|
289 |
-
st.sidebar.header("Navigation")
|
290 |
-
page = st.sidebar.radio("Go to", ["Financial Dashboard", "Decision Simulator", "Fund Monitoring", "Voice Advisory"])
|
291 |
-
|
292 |
-
return page
|
293 |
-
|
294 |
-
def render_financial_dashboard(startup_data, cash_flow_df):
|
295 |
-
"""Render financial dashboard page."""
|
296 |
-
st.title("Financial Dashboard")
|
297 |
-
|
298 |
-
# Key metrics
|
299 |
-
col1, col2, col3, col4 = st.columns(4)
|
300 |
-
|
301 |
-
# Calculate runway
|
302 |
-
runway_months, runway_df = calculate_runway(
|
303 |
-
startup_data['cash'],
|
304 |
-
startup_data['burn_rate'],
|
305 |
-
startup_data['revenue'],
|
306 |
-
startup_data['growth_rate']
|
307 |
-
)
|
308 |
-
|
309 |
-
with col1:
|
310 |
-
st.metric("Current Cash", f"${startup_data['cash']:,}")
|
311 |
-
with col2:
|
312 |
-
st.metric("Monthly Burn", f"${startup_data['burn_rate']:,}")
|
313 |
-
with col3:
|
314 |
-
st.metric("Monthly Revenue", f"${startup_data['revenue']:,}")
|
315 |
-
with col4:
|
316 |
-
st.metric("Runway", f"{runway_months} months")
|
317 |
-
|
318 |
-
# Financial charts
|
319 |
-
st.subheader("Financial Overview")
|
320 |
-
|
321 |
-
tab1, tab2, tab3 = st.tabs(["Runway Projection", "Revenue vs. Expenses", "Burn Rate Trend"])
|
322 |
-
|
323 |
-
with tab1:
|
324 |
-
# Runway projection chart
|
325 |
-
fig = px.line(runway_df.reset_index(), x='index', y='Cumulative_Cash',
|
326 |
-
title="Cash Runway Projection",
|
327 |
-
labels={'index': 'Date', 'Cumulative_Cash': 'Remaining Cash'})
|
328 |
-
fig.add_hline(y=0, line_dash="dash", line_color="red")
|
329 |
-
fig.update_layout(height=400)
|
330 |
-
st.plotly_chart(fig, use_container_width=True)
|
331 |
-
|
332 |
-
# Get analysis from Gemini
|
333 |
-
if setup_genai():
|
334 |
-
with st.expander("AI Financial Analysis"):
|
335 |
-
analysis = get_runway_analysis(startup_data)
|
336 |
-
st.write(analysis)
|
337 |
-
|
338 |
-
with tab2:
|
339 |
-
# Revenue vs Expenses chart
|
340 |
-
rev_exp_df = cash_flow_df.copy()
|
341 |
-
fig = px.bar(rev_exp_df, x='Month', y=['Revenue', 'Total_Expenses'],
|
342 |
-
title="Revenue vs. Expenses",
|
343 |
-
barmode='group',
|
344 |
-
labels={'value': 'Amount ($)', 'variable': 'Category'})
|
345 |
-
fig.update_layout(height=400)
|
346 |
-
st.plotly_chart(fig, use_container_width=True)
|
347 |
-
|
348 |
-
with tab3:
|
349 |
-
# Burn rate trend
|
350 |
-
fig = px.line(cash_flow_df, x='Month', y='Net_Burn',
|
351 |
-
title="Monthly Net Burn Trend",
|
352 |
-
labels={'Net_Burn': 'Net Burn ($)'})
|
353 |
-
fig.update_layout(height=400)
|
354 |
-
st.plotly_chart(fig, use_container_width=True)
|
355 |
-
|
356 |
-
# Expense breakdown
|
357 |
-
st.subheader("Expense Breakdown")
|
358 |
-
|
359 |
-
# Last month expenses
|
360 |
-
last_month = cash_flow_df.iloc[-1]
|
361 |
-
expense_categories = ['Payroll', 'Marketing', 'Office', 'Software', 'Travel', 'Legal', 'Misc']
|
362 |
-
expense_values = [last_month[cat] for cat in expense_categories]
|
363 |
-
|
364 |
-
fig = px.pie(values=expense_values, names=expense_categories,
|
365 |
-
title="Current Month Expense Breakdown")
|
366 |
-
fig.update_layout(height=400)
|
367 |
-
st.plotly_chart(fig, use_container_width=True)
|
368 |
-
|
369 |
-
def render_decision_simulator(startup_data):
|
370 |
-
"""Render decision simulator page."""
|
371 |
-
st.title("Decision Simulator")
|
372 |
-
|
373 |
-
st.write("Simulate the impact of business decisions on your startup's financial runway.")
|
374 |
-
|
375 |
-
# Decision input form
|
376 |
-
with st.form("decision_form"):
|
377 |
-
st.subheader("Enter Decision Parameters")
|
378 |
-
|
379 |
-
col1, col2 = st.columns(2)
|
380 |
-
|
381 |
-
with col1:
|
382 |
-
new_hires = st.number_input("New Engineering Hires", min_value=0, max_value=10, value=0,
|
383 |
-
help=f"Each engineer costs ${ENGINEER_SALARY:,} per month")
|
384 |
-
new_marketing = st.number_input("Additional Monthly Marketing Budget", min_value=0, max_value=50000, value=0, step=1000,
|
385 |
-
help="Additional marketing spend per month")
|
386 |
-
|
387 |
-
with col2:
|
388 |
-
other_expenses = st.number_input("Other Additional Monthly Expenses", min_value=0, max_value=50000, value=0, step=1000,
|
389 |
-
help="Any other additional monthly expenses")
|
390 |
-
growth_impact = st.slider("Estimated Impact on Monthly Growth Rate", min_value=0.0, max_value=0.10, value=0.0, step=0.01,
|
391 |
-
format="%.2f",
|
392 |
-
help="Estimated increase in monthly growth rate due to these investments")
|
393 |
-
|
394 |
-
question = st.text_area("Describe your decision scenario", height=100,
|
395 |
-
placeholder="E.g., We're considering hiring two more engineers and increasing our marketing budget...")
|
396 |
-
|
397 |
-
submitted = st.form_submit_button("Simulate Decision")
|
398 |
-
|
399 |
-
if submitted:
|
400 |
-
# Calculate current and new runway
|
401 |
-
current_runway, new_runway, current_df, new_df = simulate_decision(
|
402 |
-
startup_data['cash'],
|
403 |
-
startup_data['burn_rate'],
|
404 |
-
startup_data['revenue'],
|
405 |
-
startup_data['growth_rate'],
|
406 |
-
other_expenses,
|
407 |
-
new_hires,
|
408 |
-
new_marketing,
|
409 |
-
growth_impact
|
410 |
-
)
|
411 |
-
|
412 |
-
# Display results
|
413 |
-
st.subheader("Decision Impact")
|
414 |
-
|
415 |
-
# Summary metrics
|
416 |
-
col1, col2, col3 = st.columns(3)
|
417 |
-
|
418 |
-
with col1:
|
419 |
-
st.metric("Current Runway", f"{current_runway} months")
|
420 |
-
with col2:
|
421 |
-
st.metric("New Runway", f"{new_runway} months", delta=new_runway-current_runway)
|
422 |
-
with col3:
|
423 |
-
new_burn = startup_data['burn_rate'] + other_expenses + (new_hires * ENGINEER_SALARY) + new_marketing
|
424 |
-
st.metric("New Monthly Burn", f"${new_burn:,}", delta=new_burn-startup_data['burn_rate'], delta_color="inverse")
|
425 |
-
|
426 |
-
# Cash projection comparison
|
427 |
-
st.subheader("Cash Projection Comparison")
|
428 |
-
|
429 |
-
# Combine dataframes for comparison
|
430 |
-
current_df['Scenario'] = 'Current'
|
431 |
-
new_df['Scenario'] = 'After Decision'
|
432 |
-
|
433 |
-
combined_df = pd.concat([current_df, new_df])
|
434 |
-
combined_df = combined_df.reset_index()
|
435 |
-
combined_df = combined_df.rename(columns={'index': 'Date'})
|
436 |
-
|
437 |
-
# Plot comparison
|
438 |
-
fig = px.line(combined_df, x='Date', y='Cumulative_Cash', color='Scenario',
|
439 |
-
title="Cash Runway Comparison",
|
440 |
-
labels={'Cumulative_Cash': 'Remaining Cash'})
|
441 |
-
fig.add_hline(y=0, line_dash="dash", line_color="red")
|
442 |
-
fig.update_layout(height=400)
|
443 |
-
st.plotly_chart(fig, use_container_width=True)
|
444 |
-
|
445 |
-
# Get AI analysis
|
446 |
-
if setup_genai() and question:
|
447 |
-
st.subheader("AI Analysis")
|
448 |
-
analysis = get_decision_analysis(question, startup_data)
|
449 |
-
st.success(analysis)
|
450 |
-
|
451 |
-
def render_fund_monitoring(transactions_df):
|
452 |
-
"""Render fund monitoring page."""
|
453 |
-
st.title("Investor Fund Monitoring")
|
454 |
-
|
455 |
-
st.write("Monitor your startup's spending to maintain investor trust and ensure proper fund usage.")
|
456 |
-
|
457 |
-
# Process transactions to detect suspicious ones
|
458 |
-
processed_df = detect_suspicious_transactions(transactions_df)
|
459 |
-
|
460 |
-
# Summary metrics
|
461 |
-
total_transactions = len(processed_df)
|
462 |
-
suspicious_transactions = processed_df[processed_df['Suspicious']].copy()
|
463 |
-
suspicious_count = len(suspicious_transactions)
|
464 |
-
suspicious_amount = suspicious_transactions['Amount'].sum()
|
465 |
-
total_amount = processed_df['Amount'].sum()
|
466 |
-
|
467 |
-
col1, col2, col3 = st.columns(3)
|
468 |
-
|
469 |
-
with col1:
|
470 |
-
st.metric("Total Transactions", total_transactions)
|
471 |
-
with col2:
|
472 |
-
st.metric("Flagged Transactions", suspicious_count,
|
473 |
-
delta=f"{suspicious_count/total_transactions:.1%}" if total_transactions > 0 else "0%")
|
474 |
-
with col3:
|
475 |
-
st.metric("Flagged Amount", f"${suspicious_amount:,}",
|
476 |
-
delta=f"{suspicious_amount/total_amount:.1%}" if total_amount > 0 else "0%")
|
477 |
-
|
478 |
-
# Tabs for different views
|
479 |
-
tab1, tab2 = st.tabs(["Flagged Transactions", "All Transactions"])
|
480 |
-
|
481 |
-
with tab1:
|
482 |
-
if suspicious_count > 0:
|
483 |
-
st.dataframe(suspicious_transactions[['Date', 'Category', 'Vendor', 'Amount', 'Description', 'Reason']],
|
484 |
-
use_container_width=True)
|
485 |
-
|
486 |
-
# Get AI analysis of suspicious transactions
|
487 |
-
if setup_genai():
|
488 |
-
st.subheader("AI Fraud Analysis")
|
489 |
-
analysis = get_fraud_analysis(suspicious_transactions)
|
490 |
-
st.warning(analysis)
|
491 |
-
else:
|
492 |
-
st.success("No suspicious transactions detected.")
|
493 |
-
|
494 |
-
with tab2:
|
495 |
-
st.dataframe(processed_df[['Date', 'Category', 'Vendor', 'Amount', 'Description', 'Suspicious']],
|
496 |
-
use_container_width=True)
|
497 |
-
|
498 |
-
# Spending patterns
|
499 |
-
st.subheader("Spending Patterns")
|
500 |
-
|
501 |
-
# Category breakdown
|
502 |
-
category_spending = processed_df.groupby('Category')['Amount'].sum().reset_index()
|
503 |
-
|
504 |
-
fig = px.bar(category_spending, x='Category', y='Amount',
|
505 |
-
title="Spending by Category",
|
506 |
-
labels={'Amount': 'Total Spent ($)'})
|
507 |
-
fig.update_layout(height=400)
|
508 |
-
st.plotly_chart(fig, use_container_width=True)
|
509 |
-
|
510 |
-
# Time series of spending
|
511 |
-
processed_df['Date'] = pd.to_datetime(processed_df['Date'])
|
512 |
-
processed_df['Week'] = processed_df['Date'].dt.isocalendar().week
|
513 |
-
weekly_spending = processed_df.groupby(['Week', 'Category'])['Amount'].sum().reset_index()
|
514 |
-
|
515 |
-
fig = px.line(weekly_spending, x='Week', y='Amount', color='Category',
|
516 |
-
title="Weekly Spending Trends",
|
517 |
-
labels={'Amount': 'Amount Spent ($)'})
|
518 |
-
fig.update_layout(height=400)
|
519 |
-
st.plotly_chart(fig, use_container_width=True)
|
520 |
-
|
521 |
-
def render_voice_advisory(startup_data):
|
522 |
-
"""Render voice advisory page."""
|
523 |
-
st.title("Financial Advisory")
|
524 |
-
|
525 |
-
st.write("Get strategic financial guidance from our AI advisor.")
|
526 |
-
|
527 |
-
# Common financial questions
|
528 |
-
st.subheader("Select a Question or Ask Your Own")
|
529 |
-
|
530 |
-
common_questions = [
|
531 |
-
"Select a question...",
|
532 |
-
"How much runway do we have at our current burn rate?",
|
533 |
-
"If we hire two more engineers, how will it affect our runway?",
|
534 |
-
"Should we increase our marketing spend given our current growth rate?",
|
535 |
-
"Is our current burn rate sustainable given our revenue growth?",
|
536 |
-
"Should we consider raising a bridge round in the next 3 months?",
|
537 |
-
"What's our projected cash position at the end of next quarter?",
|
538 |
-
"Are there any concerning spending patterns in our recent expenses?",
|
539 |
-
"What metrics should we focus on improving before our next fundraise?"
|
540 |
-
]
|
541 |
-
|
542 |
-
selected_question = st.selectbox("Common Questions", common_questions)
|
543 |
-
|
544 |
-
custom_question = st.text_area("Or ask your own question", height=100,
|
545 |
-
placeholder="Enter your financial question here...")
|
546 |
-
|
547 |
-
question = custom_question if custom_question else (selected_question if selected_question != common_questions[0] else "")
|
548 |
-
|
549 |
-
if st.button("Get Advice") and question and setup_genai():
|
550 |
-
with st.spinner("Generating advisory response..."):
|
551 |
-
advice = get_advisory_guidance(question, startup_data)
|
552 |
-
|
553 |
-
# Display advice
|
554 |
-
st.subheader("Financial Advice")
|
555 |
-
st.info(advice)
|
556 |
-
|
557 |
-
# Note about voice generation
|
558 |
-
st.write("In the full version, this advice would be delivered as a voice response using ElevenLabs.")
|
559 |
-
|
560 |
-
# Add a sample conversation
|
561 |
-
with st.expander("View sample conversation"):
|
562 |
-
st.write("**You:** " + question)
|
563 |
-
st.write("**Financial Advisor:** " + advice)
|
564 |
-
|
565 |
-
# Main application
|
566 |
-
def main():
|
567 |
-
# Load sample data
|
568 |
-
startup_data, cash_flow_df, transactions_df = load_sample_data()
|
569 |
-
|
570 |
-
# Create sidebar and get selected page
|
571 |
-
page = create_sidebar()
|
572 |
-
|
573 |
-
# Render selected page
|
574 |
-
if page == "Financial Dashboard":
|
575 |
-
render_financial_dashboard(startup_data, cash_flow_df)
|
576 |
-
elif page == "Decision Simulator":
|
577 |
-
render_decision_simulator(startup_data)
|
578 |
-
elif page == "Fund Monitoring":
|
579 |
-
render_fund_monitoring(transactions_df)
|
580 |
-
elif page == "Voice Advisory":
|
581 |
-
render_voice_advisory(startup_data)
|
582 |
-
|
583 |
-
if __name__ == "__main__":
|
584 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|