Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,161 +1,209 @@
|
|
1 |
import gradio as gr
|
2 |
-
import torch
|
3 |
import pandas as pd
|
|
|
4 |
import json
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
T5Tokenizer
|
11 |
-
)
|
12 |
-
|
13 |
-
class FinancialAnalyzer:
|
14 |
def __init__(self):
|
15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
try:
|
17 |
-
|
18 |
-
self.tiny_tokenizer = AutoTokenizer.from_pretrained("TinyLlama/TinyLlama-1.1B-Chat-v1.0")
|
19 |
-
self.tiny_model = AutoModelForCausalLM.from_pretrained("TinyLlama/TinyLlama-1.1B-Chat-v1.0")
|
20 |
|
21 |
-
#
|
22 |
-
|
23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
|
25 |
-
self.device = "cpu"
|
26 |
-
self._move_models_to_device()
|
27 |
-
print("Models loaded successfully!")
|
28 |
except Exception as e:
|
29 |
-
|
30 |
-
raise
|
31 |
|
32 |
-
def
|
33 |
-
|
34 |
-
|
|
|
|
|
|
|
|
|
|
|
35 |
|
36 |
-
def
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
elif
|
45 |
-
|
46 |
-
return df.to_string()
|
47 |
-
elif file_path.endswith('.md'):
|
48 |
-
with open(file_path, 'r') as f:
|
49 |
-
return f.read()
|
50 |
-
else:
|
51 |
-
return "Unsupported file format. Please upload CSV, Excel, or Markdown files."
|
52 |
-
except Exception as e:
|
53 |
-
return f"Error processing file: {str(e)}"
|
54 |
|
55 |
-
|
56 |
-
|
57 |
-
# Read file contents
|
58 |
-
balance_sheet = self.read_file_content(balance_sheet_path)
|
59 |
-
income_statement = self.read_file_content(income_statement_path)
|
60 |
-
|
61 |
-
if "Error" in balance_sheet or "Error" in income_statement:
|
62 |
-
return "Error processing files. Please check the file format and content."
|
63 |
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
1. Key financial metrics
|
75 |
-
2. Growth trends
|
76 |
-
3. Risk analysis
|
77 |
-
4. Recommendations</human>
|
78 |
-
|
79 |
-
<assistant>Here's my analysis:"""
|
80 |
-
|
81 |
-
# Tokenize input
|
82 |
-
inputs = self.tiny_tokenizer(
|
83 |
-
prompt,
|
84 |
-
return_tensors="pt",
|
85 |
-
truncation=True,
|
86 |
-
max_length=2048
|
87 |
-
).to(self.device)
|
88 |
-
|
89 |
-
# Generate response
|
90 |
-
outputs = self.tiny_model.generate(
|
91 |
-
inputs["input_ids"],
|
92 |
-
do_sample=True,
|
93 |
-
temperature=0.7,
|
94 |
-
top_p=0.95,
|
95 |
-
repetition_penalty=1.2,
|
96 |
-
max_length=2048 # Total length including input
|
97 |
-
)
|
98 |
-
|
99 |
-
# Decode response
|
100 |
-
analysis = self.tiny_tokenizer.decode(outputs[0], skip_special_tokens=True)
|
101 |
|
102 |
-
|
103 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
104 |
|
105 |
-
|
106 |
-
|
107 |
-
"Analysis": analysis,
|
108 |
-
"Sentiment": sentiment,
|
109 |
-
"Note": "Analysis based on financial statements from 2021-2025"
|
110 |
-
}
|
111 |
|
112 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
113 |
|
114 |
-
|
115 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
116 |
|
117 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
118 |
try:
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
).to(self.device)
|
126 |
|
127 |
-
|
128 |
-
|
129 |
-
labels = ['negative', 'neutral', 'positive']
|
130 |
|
131 |
-
|
132 |
-
|
133 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
134 |
}
|
|
|
|
|
|
|
135 |
except Exception as e:
|
136 |
-
return f"Error in
|
137 |
|
138 |
def create_interface():
|
139 |
-
analyzer =
|
140 |
|
141 |
iface = gr.Interface(
|
142 |
-
fn=analyzer.
|
143 |
inputs=[
|
144 |
-
gr.File(
|
145 |
-
|
146 |
-
type="filepath"
|
147 |
-
),
|
148 |
-
gr.File(
|
149 |
-
label="Upload Income Statement (CSV, Excel, or Markdown)",
|
150 |
-
type="filepath"
|
151 |
-
)
|
152 |
],
|
153 |
-
outputs=gr.Textbox(
|
154 |
-
|
155 |
-
|
156 |
-
),
|
157 |
-
title="Financial Statement Analyzer",
|
158 |
-
description="Upload your financial statements (Balance Sheet and Income Statement) to get AI-powered insights and analysis."
|
159 |
)
|
160 |
|
161 |
return iface
|
|
|
1 |
import gradio as gr
|
|
|
2 |
import pandas as pd
|
3 |
+
import numpy as np
|
4 |
import json
|
5 |
+
import chromadb
|
6 |
+
from chromadb.config import Settings
|
7 |
+
from datetime import datetime
|
8 |
+
|
9 |
+
class FastFinancialAnalyzer:
|
|
|
|
|
|
|
|
|
10 |
def __init__(self):
|
11 |
+
# Initialize ChromaDB
|
12 |
+
self.client = chromadb.Client(Settings(anonymized_telemetry=False))
|
13 |
+
|
14 |
+
# Create financial metrics collection
|
15 |
+
self.collection = self.client.create_collection(
|
16 |
+
name="financial_metrics_" + datetime.now().strftime("%Y%m%d_%H%M%S")
|
17 |
+
)
|
18 |
+
|
19 |
+
# Initialize ratio benchmarks
|
20 |
+
self.initialize_ratio_benchmarks()
|
21 |
+
|
22 |
+
def initialize_ratio_benchmarks(self):
|
23 |
+
"""Initialize benchmark ratios for comparison"""
|
24 |
+
self.benchmarks = {
|
25 |
+
"liquidity_ratios": {
|
26 |
+
"current_ratio": {"good": 2.0, "warning": 1.0},
|
27 |
+
"quick_ratio": {"good": 1.0, "warning": 0.5}
|
28 |
+
},
|
29 |
+
"profitability_ratios": {
|
30 |
+
"gross_margin": {"good": 0.4, "warning": 0.2},
|
31 |
+
"net_margin": {"good": 0.1, "warning": 0.05}
|
32 |
+
},
|
33 |
+
"efficiency_ratios": {
|
34 |
+
"inventory_turnover": {"good": 4, "warning": 2},
|
35 |
+
"asset_turnover": {"good": 0.5, "warning": 0.25}
|
36 |
+
}
|
37 |
+
}
|
38 |
+
|
39 |
+
def calculate_ratios(self, balance_sheet_df, income_stmt_df):
|
40 |
+
"""Calculate key financial ratios"""
|
41 |
try:
|
42 |
+
ratios = {}
|
|
|
|
|
43 |
|
44 |
+
# Clean numeric data
|
45 |
+
for df in [balance_sheet_df, income_stmt_df]:
|
46 |
+
for col in df.select_dtypes(include=['object']).columns:
|
47 |
+
df[col] = pd.to_numeric(df[col].astype(str).str.replace(r'[^\d.-]', ''), errors='coerce')
|
48 |
+
|
49 |
+
# Calculate ratios for each year
|
50 |
+
years = [col for col in balance_sheet_df.columns if str(col).isdigit()]
|
51 |
+
for year in years:
|
52 |
+
ratios[year] = {
|
53 |
+
"liquidity": {
|
54 |
+
"current_ratio": balance_sheet_df.loc[balance_sheet_df['Account'] == 'Total_Current_Assets', year].values[0] /
|
55 |
+
balance_sheet_df.loc[balance_sheet_df['Account'] == 'Total_Current_Liabilities', year].values[0],
|
56 |
+
"quick_ratio": (balance_sheet_df.loc[balance_sheet_df['Account'] == 'Total_Current_Assets', year].values[0] -
|
57 |
+
balance_sheet_df.loc[balance_sheet_df['Account'] == 'Inventory', year].values[0]) /
|
58 |
+
balance_sheet_df.loc[balance_sheet_df['Account'] == 'Total_Current_Liabilities', year].values[0]
|
59 |
+
},
|
60 |
+
"profitability": {
|
61 |
+
"gross_margin": income_stmt_df.loc[income_stmt_df['Revenue Items'] == 'Gross Profit', year].values[0] /
|
62 |
+
income_stmt_df.loc[income_stmt_df['Revenue Items'] == 'Total Net Revenue', year].values[0],
|
63 |
+
"net_margin": income_stmt_df.loc[income_stmt_df['Revenue Items'] == 'Net Earnings', year].values[0] /
|
64 |
+
income_stmt_df.loc[income_stmt_df['Revenue Items'] == 'Total Net Revenue', year].values[0]
|
65 |
+
},
|
66 |
+
"growth": {
|
67 |
+
"revenue_growth": None if year == years[0] else
|
68 |
+
(income_stmt_df.loc[income_stmt_df['Revenue Items'] == 'Total Net Revenue', year].values[0] -
|
69 |
+
income_stmt_df.loc[income_stmt_df['Revenue Items'] == 'Total Net Revenue', str(int(year)-1)].values[0]) /
|
70 |
+
income_stmt_df.loc[income_stmt_df['Revenue Items'] == 'Total Net Revenue', str(int(year)-1)].values[0] * 100
|
71 |
+
}
|
72 |
+
}
|
73 |
+
|
74 |
+
return ratios
|
75 |
|
|
|
|
|
|
|
76 |
except Exception as e:
|
77 |
+
return f"Error calculating ratios: {str(e)}"
|
|
|
78 |
|
79 |
+
def analyze_trends(self, ratios):
|
80 |
+
"""Analyze financial trends"""
|
81 |
+
trends = {
|
82 |
+
"liquidity": self.analyze_ratio_trend("current_ratio", ratios),
|
83 |
+
"profitability": self.analyze_ratio_trend("net_margin", ratios),
|
84 |
+
"growth": self.analyze_revenue_growth(ratios)
|
85 |
+
}
|
86 |
+
return trends
|
87 |
|
88 |
+
def analyze_ratio_trend(self, ratio_name, ratios):
|
89 |
+
"""Analyze trend for a specific ratio"""
|
90 |
+
values = []
|
91 |
+
years = sorted(ratios.keys())
|
92 |
+
|
93 |
+
for year in years:
|
94 |
+
if ratio_name in ratios[year].get("liquidity", {}):
|
95 |
+
values.append(ratios[year]["liquidity"][ratio_name])
|
96 |
+
elif ratio_name in ratios[year].get("profitability", {}):
|
97 |
+
values.append(ratios[year]["profitability"][ratio_name])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
98 |
|
99 |
+
if not values:
|
100 |
+
return "No data available"
|
|
|
|
|
|
|
|
|
|
|
|
|
101 |
|
102 |
+
trend = np.polyfit(range(len(values)), values, 1)[0]
|
103 |
+
|
104 |
+
if trend > 0.05:
|
105 |
+
return "Strong upward trend"
|
106 |
+
elif trend > 0:
|
107 |
+
return "Slight upward trend"
|
108 |
+
elif trend > -0.05:
|
109 |
+
return "Stable"
|
110 |
+
else:
|
111 |
+
return "Downward trend"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
112 |
|
113 |
+
def analyze_revenue_growth(self, ratios):
|
114 |
+
"""Analyze revenue growth trend"""
|
115 |
+
growth_rates = []
|
116 |
+
years = sorted(ratios.keys())[1:] # Skip first year as it won't have growth rate
|
117 |
+
|
118 |
+
for year in years:
|
119 |
+
if ratios[year]["growth"]["revenue_growth"] is not None:
|
120 |
+
growth_rates.append(ratios[year]["growth"]["revenue_growth"])
|
121 |
|
122 |
+
if not growth_rates:
|
123 |
+
return "No growth data available"
|
|
|
|
|
|
|
|
|
124 |
|
125 |
+
avg_growth = np.mean(growth_rates)
|
126 |
+
if avg_growth > 10:
|
127 |
+
return f"Strong growth (avg {avg_growth:.1f}%)"
|
128 |
+
elif avg_growth > 0:
|
129 |
+
return f"Moderate growth (avg {avg_growth:.1f}%)"
|
130 |
+
else:
|
131 |
+
return f"Declining growth (avg {avg_growth:.1f}%)"
|
132 |
|
133 |
+
def generate_insights(self, ratios, trends):
|
134 |
+
"""Generate actionable insights"""
|
135 |
+
insights = []
|
136 |
+
|
137 |
+
# Liquidity insights
|
138 |
+
current_ratio = ratios[max(ratios.keys())]["liquidity"]["current_ratio"]
|
139 |
+
if current_ratio < self.benchmarks["liquidity_ratios"]["current_ratio"]["warning"]:
|
140 |
+
insights.append("ALERT: Liquidity needs immediate attention")
|
141 |
+
elif current_ratio < self.benchmarks["liquidity_ratios"]["current_ratio"]["good"]:
|
142 |
+
insights.append("WATCH: Liquidity is below ideal levels")
|
143 |
+
|
144 |
+
# Profitability insights
|
145 |
+
net_margin = ratios[max(ratios.keys())]["profitability"]["net_margin"]
|
146 |
+
if net_margin > self.benchmarks["profitability_ratios"]["net_margin"]["good"]:
|
147 |
+
insights.append("STRONG: Excellent profit margins")
|
148 |
+
elif net_margin < self.benchmarks["profitability_ratios"]["net_margin"]["warning"]:
|
149 |
+
insights.append("ALERT: Profit margins need improvement")
|
150 |
|
151 |
+
# Growth insights
|
152 |
+
if "growth" in trends:
|
153 |
+
if "Strong" in trends["growth"]:
|
154 |
+
insights.append("POSITIVE: Strong revenue growth trend")
|
155 |
+
elif "Declining" in trends["growth"]:
|
156 |
+
insights.append("WATCH: Revenue growth is slowing")
|
157 |
+
|
158 |
+
return insights
|
159 |
+
|
160 |
+
def analyze_financials(self, balance_sheet_file, income_stmt_file):
|
161 |
+
"""Main analysis function"""
|
162 |
try:
|
163 |
+
# Read files
|
164 |
+
balance_sheet_df = pd.read_csv(balance_sheet_file)
|
165 |
+
income_stmt_df = pd.read_csv(income_stmt_file)
|
166 |
+
|
167 |
+
# Calculate ratios
|
168 |
+
ratios = self.calculate_ratios(balance_sheet_df, income_stmt_df)
|
|
|
169 |
|
170 |
+
# Analyze trends
|
171 |
+
trends = self.analyze_trends(ratios)
|
|
|
172 |
|
173 |
+
# Generate insights
|
174 |
+
insights = self.generate_insights(ratios, trends)
|
175 |
+
|
176 |
+
# Prepare comprehensive analysis
|
177 |
+
analysis = {
|
178 |
+
"Financial Ratios": ratios,
|
179 |
+
"Trend Analysis": trends,
|
180 |
+
"Key Insights": insights,
|
181 |
+
"Summary": {
|
182 |
+
"Latest Year Analysis": {
|
183 |
+
"Current Ratio": f"{ratios[max(ratios.keys())]['liquidity']['current_ratio']:.2f}",
|
184 |
+
"Net Margin": f"{ratios[max(ratios.keys())]['profitability']['net_margin']:.2%}",
|
185 |
+
"Revenue Growth": f"{ratios[max(ratios.keys())]['growth']['revenue_growth']:.2f}%" if ratios[max(ratios.keys())]['growth']['revenue_growth'] else "N/A"
|
186 |
+
}
|
187 |
+
}
|
188 |
}
|
189 |
+
|
190 |
+
return json.dumps(analysis, indent=2)
|
191 |
+
|
192 |
except Exception as e:
|
193 |
+
return f"Error in analysis: {str(e)}"
|
194 |
|
195 |
def create_interface():
|
196 |
+
analyzer = FastFinancialAnalyzer()
|
197 |
|
198 |
iface = gr.Interface(
|
199 |
+
fn=analyzer.analyze_financials,
|
200 |
inputs=[
|
201 |
+
gr.File(label="Balance Sheet (CSV)", type="filepath"),
|
202 |
+
gr.File(label="Income Statement (CSV)", type="filepath")
|
|
|
|
|
|
|
|
|
|
|
|
|
203 |
],
|
204 |
+
outputs=gr.Textbox(label="Analysis Results", lines=20),
|
205 |
+
title="Fast Financial Statement Analyzer",
|
206 |
+
description="Upload financial statements for instant analysis with ratio calculations and trend detection."
|
|
|
|
|
|
|
207 |
)
|
208 |
|
209 |
return iface
|