Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -4,9 +4,7 @@ import pandas as pd
|
|
4 |
import torch
|
5 |
import logging
|
6 |
import gc
|
7 |
-
import re
|
8 |
from transformers import pipeline
|
9 |
-
from typing import Dict, List, Tuple, Optional
|
10 |
|
11 |
# Setup logging
|
12 |
logging.basicConfig(
|
@@ -19,13 +17,19 @@ logger = logging.getLogger(__name__)
|
|
19 |
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
|
20 |
logger.info(f"Using device: {DEVICE}")
|
21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
class FinancialDataExtractor:
|
23 |
-
"""
|
24 |
|
25 |
def __init__(self):
|
26 |
-
self.
|
27 |
|
28 |
-
def clean_number(self, value
|
29 |
"""Clean numeric values from financial statements"""
|
30 |
try:
|
31 |
if pd.isna(value) or value == '' or value == '-':
|
@@ -34,7 +38,7 @@ class FinancialDataExtractor:
|
|
34 |
return float(value)
|
35 |
|
36 |
# Remove currency symbols, spaces, commas
|
37 |
-
cleaned = str(value).replace('$', '').replace(',', '').strip()
|
38 |
# Handle parentheses for negative numbers
|
39 |
if '(' in cleaned and ')' in cleaned:
|
40 |
cleaned = '-' + cleaned.replace('(', '').replace(')', '')
|
@@ -42,74 +46,25 @@ class FinancialDataExtractor:
|
|
42 |
except:
|
43 |
return 0.0
|
44 |
|
45 |
-
def
|
46 |
-
"""
|
47 |
-
|
48 |
-
|
49 |
|
50 |
-
|
51 |
-
|
52 |
-
'Revenue': 0,
|
53 |
-
'Expenses': 0,
|
54 |
-
'Profit': 0,
|
55 |
-
'Details': {}
|
56 |
-
}
|
57 |
-
|
58 |
-
# Extract revenue
|
59 |
-
total_revenue = df[df.iloc[:, 0].str.contains('Total Net Revenue|Revenue', na=False, regex=True)].iloc[0][year]
|
60 |
-
metrics[year]['Revenue'] = self.clean_number(total_revenue)
|
61 |
-
|
62 |
-
# Extract expenses
|
63 |
-
total_expenses = df[df.iloc[:, 0].str.contains('Total Expenses', na=False)].iloc[0][year]
|
64 |
-
metrics[year]['Expenses'] = self.clean_number(total_expenses)
|
65 |
-
|
66 |
-
# Extract profit
|
67 |
-
net_income = df[df.iloc[:, 0].str.contains('Net Income|Net Earnings', na=False, regex=True)].iloc[-1][year]
|
68 |
-
metrics[year]['Profit'] = self.clean_number(net_income)
|
69 |
-
|
70 |
-
# Extract additional details
|
71 |
-
metrics[year]['Details'] = {
|
72 |
-
'COGS': self.clean_number(df[df.iloc[:, 0].str.contains('Cost of Goods Sold', na=False)].iloc[0][year]),
|
73 |
-
'Gross_Profit': self.clean_number(df[df.iloc[:, 0].str.contains('Gross Profit', na=False)].iloc[0][year]),
|
74 |
-
'Operating_Expenses': self.clean_number(total_expenses),
|
75 |
-
'EBIT': self.clean_number(df[df.iloc[:, 0].str.contains('Earnings Before Interest & Taxes', na=False)].iloc[0][year]),
|
76 |
-
'Interest_Expense': self.clean_number(df[df.iloc[:, 0].str.contains('Interest Expense', na=False)].iloc[0][year]),
|
77 |
-
'Tax_Expense': self.clean_number(df[df.iloc[:, 0].str.contains('Income Taxes', na=False)].iloc[0][year])
|
78 |
-
}
|
79 |
-
|
80 |
-
return metrics
|
81 |
-
|
82 |
-
def process_balance_sheet(self, df: pd.DataFrame) -> Dict:
|
83 |
-
"""Process balance sheet data"""
|
84 |
-
metrics = {}
|
85 |
-
years = [col for col in df.columns if str(col).isdigit()]
|
86 |
|
87 |
-
|
88 |
-
|
89 |
-
'Assets': 0,
|
90 |
-
'Liabilities': 0,
|
91 |
-
'Equity': 0,
|
92 |
-
'Details': {}
|
93 |
-
}
|
94 |
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
metrics[year]['Equity'] = self.clean_number(df[df.iloc[:, 0].str.contains("Shareholder's Equity", na=False)].iloc[-1][year])
|
99 |
|
100 |
-
|
101 |
-
metrics[year]['Details'] = {
|
102 |
-
'Current_Assets': self.clean_number(df[df.iloc[:, 0].str.contains('Total current assets', na=False)].iloc[0][year]),
|
103 |
-
'Fixed_Assets': self.clean_number(df[df.iloc[:, 0].str.contains('Property & Equipment', na=False)].iloc[0][year]),
|
104 |
-
'Current_Liabilities': self.clean_number(df[df.iloc[:, 0].str.contains('Total current liabilities', na=False)].iloc[0][year]),
|
105 |
-
'Long_Term_Debt': self.clean_number(df[df.iloc[:, 0].str.contains('Long-term debt', na=False)].iloc[0][year]),
|
106 |
-
'Retained_Earnings': self.clean_number(df[df.iloc[:, 0].str.contains('Retained Earnings', na=False)].iloc[0][year])
|
107 |
-
}
|
108 |
-
|
109 |
-
return metrics
|
110 |
|
111 |
class FinancialAnalyzer:
|
112 |
-
"""
|
113 |
|
114 |
def __init__(self):
|
115 |
self.extractor = FinancialDataExtractor()
|
@@ -140,185 +95,135 @@ class FinancialAnalyzer:
|
|
140 |
logger.error(f"Error loading models: {str(e)}")
|
141 |
raise
|
142 |
|
143 |
-
def
|
144 |
-
"""Calculate
|
145 |
-
|
146 |
-
|
147 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
148 |
|
149 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
150 |
'Profitability': {
|
151 |
-
'Gross_Margin': (income['
|
152 |
-
'Operating_Margin': (income['
|
153 |
-
'Net_Margin': (income['
|
|
|
|
|
154 |
},
|
155 |
'Liquidity': {
|
156 |
-
'Current_Ratio': balance['
|
157 |
-
'
|
158 |
-
'Cash_Ratio': 0 # Would need cash balance for this
|
159 |
},
|
160 |
-
'
|
161 |
-
'
|
162 |
-
'
|
163 |
-
'
|
164 |
-
},
|
165 |
-
'Efficiency': {
|
166 |
-
'Asset_Turnover': income['Revenue'] / balance['Assets'],
|
167 |
-
'ROE': income['Profit'] / balance['Equity'] * 100,
|
168 |
-
'ROA': income['Profit'] / balance['Assets'] * 100
|
169 |
}
|
170 |
}
|
171 |
-
|
172 |
-
return ratios
|
173 |
-
|
174 |
-
except Exception as e:
|
175 |
-
logger.error(f"Error calculating ratios: {str(e)}")
|
176 |
-
return {}
|
177 |
-
|
178 |
-
def generate_analysis_prompt(self, income_metrics: Dict, balance_metrics: Dict, ratios: Dict) -> str:
|
179 |
-
"""Generate analysis prompt for LLM"""
|
180 |
-
latest_year = max(income_metrics.keys())
|
181 |
-
earliest_year = min(income_metrics.keys())
|
182 |
|
183 |
-
|
184 |
-
revenue_growth = ((income_metrics[latest_year]['Revenue'] - income_metrics[earliest_year]['Revenue'])
|
185 |
-
/ income_metrics[earliest_year]['Revenue'] * 100)
|
186 |
-
profit_growth = ((income_metrics[latest_year]['Profit'] - income_metrics[earliest_year]['Profit'])
|
187 |
-
/ income_metrics[earliest_year]['Profit'] * 100)
|
188 |
-
asset_growth = ((balance_metrics[latest_year]['Assets'] - balance_metrics[earliest_year]['Assets'])
|
189 |
-
/ balance_metrics[earliest_year]['Assets'] * 100)
|
190 |
-
|
191 |
-
prompt = f"""[INST] As a financial analyst, provide a comprehensive analysis of this company:
|
192 |
-
|
193 |
-
Financial Performance ({earliest_year}-{latest_year}):
|
194 |
-
1. Growth Metrics:
|
195 |
-
- Revenue Growth: {revenue_growth:.1f}%
|
196 |
-
- Profit Growth: {profit_growth:.1f}%
|
197 |
-
- Asset Growth: {asset_growth:.1f}%
|
198 |
-
|
199 |
-
2. Latest Year Performance ({latest_year}):
|
200 |
-
- Revenue: ${income_metrics[latest_year]['Revenue']:,.0f}
|
201 |
-
- Net Profit: ${income_metrics[latest_year]['Profit']:,.0f}
|
202 |
-
- Total Assets: ${balance_metrics[latest_year]['Assets']:,.0f}
|
203 |
-
|
204 |
-
3. Key Ratios ({latest_year}):
|
205 |
-
- Profitability:
|
206 |
-
* Gross Margin: {ratios['Profitability']['Gross_Margin']:.1f}%
|
207 |
-
* Operating Margin: {ratios['Profitability']['Operating_Margin']:.1f}%
|
208 |
-
* Net Margin: {ratios['Profitability']['Net_Margin']:.1f}%
|
209 |
-
- Financial Health:
|
210 |
-
* Current Ratio: {ratios['Liquidity']['Current_Ratio']:.2f}
|
211 |
-
* Debt-to-Equity: {ratios['Solvency']['Debt_to_Equity']:.2f}
|
212 |
-
* ROE: {ratios['Efficiency']['ROE']:.1f}%
|
213 |
-
|
214 |
-
Provide:
|
215 |
-
1. Overall financial health assessment
|
216 |
-
2. Key performance insights and trends
|
217 |
-
3. Risk analysis and concerns
|
218 |
-
4. Specific strategic recommendations
|
219 |
-
[/INST]"""
|
220 |
-
|
221 |
-
return prompt
|
222 |
|
223 |
def analyze_financials(self, income_df: pd.DataFrame, balance_df: pd.DataFrame) -> str:
|
224 |
-
"""Generate
|
225 |
try:
|
226 |
-
# Extract
|
227 |
-
|
228 |
-
|
229 |
|
230 |
-
# Calculate
|
231 |
-
|
232 |
-
ratios = self.calculate_financial_ratios(income_metrics, balance_metrics, latest_year)
|
233 |
|
234 |
-
#
|
235 |
-
|
|
|
236 |
|
237 |
-
#
|
238 |
-
|
239 |
-
|
240 |
-
truncation=True
|
241 |
-
)[0]
|
242 |
|
243 |
-
# Generate analysis
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
244 |
analysis = self.analysis_model(
|
245 |
-
|
246 |
max_length=1500,
|
|
|
247 |
do_sample=True,
|
248 |
-
temperature=0.7
|
249 |
-
num_return_sequences=1
|
250 |
)[0]['generated_text']
|
251 |
|
252 |
-
# Format
|
253 |
-
output =
|
254 |
-
|
255 |
-
return output
|
256 |
-
|
257 |
-
except Exception as e:
|
258 |
-
logger.error(f"Analysis error: {str(e)}")
|
259 |
-
return f"Error in analysis: {str(e)}"
|
260 |
|
261 |
-
|
262 |
-
income_metrics: Dict, balance_metrics: Dict,
|
263 |
-
ratios: Dict, year: str) -> str:
|
264 |
-
"""Format the analysis response"""
|
265 |
-
try:
|
266 |
-
# Split analysis into sections
|
267 |
-
sections = analysis.split('\n\n')
|
268 |
-
|
269 |
-
# Format metrics section
|
270 |
-
metrics_section = f"""
|
271 |
-
### Key Financial Metrics ({year})
|
272 |
-
- Revenue: ${income_metrics[year]['Revenue']:,.0f}
|
273 |
-
- Net Profit: ${income_metrics[year]['Profit']:,.0f}
|
274 |
-
- Total Assets: ${balance_metrics[year]['Assets']:,.0f}
|
275 |
|
276 |
-
|
277 |
-
- Profitability
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
- Financial Health:
|
283 |
-
* Current Ratio: {ratios['Liquidity']['Current_Ratio']:.2f}
|
284 |
-
* Debt-to-Equity: {ratios['Solvency']['Debt_to_Equity']:.2f}
|
285 |
-
* Interest Coverage: {ratios['Solvency']['Interest_Coverage']:.2f}
|
286 |
-
|
287 |
-
- Efficiency:
|
288 |
-
* ROE: {ratios['Efficiency']['ROE']:.1f}%
|
289 |
-
* ROA: {ratios['Efficiency']['ROA']:.1f}%
|
290 |
-
* Asset Turnover: {ratios['Efficiency']['Asset_Turnover']:.2f}
|
291 |
-
"""
|
292 |
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
]
|
301 |
|
302 |
-
return
|
303 |
|
304 |
except Exception as e:
|
305 |
-
logger.error(f"
|
306 |
-
|
307 |
|
308 |
def analyze_statements(income_statement, balance_sheet):
|
309 |
"""Main function to analyze financial statements"""
|
310 |
try:
|
311 |
if not income_statement or not balance_sheet:
|
312 |
return "Please upload both Income Statement and Balance Sheet CSV files."
|
313 |
-
|
314 |
# Read files
|
315 |
income_df = pd.read_csv(income_statement.name)
|
316 |
balance_df = pd.read_csv(balance_sheet.name)
|
317 |
|
318 |
-
# Create analyzer
|
319 |
analyzer = FinancialAnalyzer()
|
320 |
-
|
321 |
-
# Generate analysis
|
322 |
result = analyzer.analyze_financials(income_df, balance_df)
|
323 |
|
324 |
# Clear memory
|
@@ -331,71 +236,33 @@ def analyze_statements(income_statement, balance_sheet):
|
|
331 |
return f"""Analysis Error: {str(e)}
|
332 |
|
333 |
Please ensure your CSV files:
|
334 |
-
1.
|
335 |
-
2.
|
336 |
-
3. Use consistent number formatting
|
337 |
-
4. Are not corrupted"""
|
338 |
|
339 |
-
# Create Gradio interface
|
340 |
iface = gr.Interface(
|
341 |
fn=analyze_statements,
|
342 |
inputs=[
|
343 |
-
gr.File(
|
344 |
-
|
345 |
-
file_types=[".csv"],
|
346 |
-
file_count="single"
|
347 |
-
),
|
348 |
-
gr.File(
|
349 |
-
label="Upload Balance Sheet (CSV)",
|
350 |
-
file_types=[".csv"],
|
351 |
-
file_count="single"
|
352 |
-
)
|
353 |
],
|
354 |
outputs=gr.Markdown(),
|
355 |
-
title="
|
356 |
-
description="""##
|
357 |
-
|
358 |
-
### Supported Financial Statement Formats:
|
359 |
-
|
360 |
-
#### Income Statement Requirements:
|
361 |
-
- Revenue/Sales information
|
362 |
-
- Cost and Expense details
|
363 |
-
- Profit/Income figures
|
364 |
-
- Operating metrics
|
365 |
-
- Period/Year information
|
366 |
-
|
367 |
-
#### Balance Sheet Requirements:
|
368 |
-
- Asset information (Current & Non-current)
|
369 |
-
- Liability details (Current & Long-term)
|
370 |
-
- Equity components
|
371 |
-
- Period/Year information
|
372 |
-
|
373 |
-
### Analysis Output Includes:
|
374 |
-
1. Overall Financial Health Assessment
|
375 |
-
2. Key Performance Metrics & Ratios
|
376 |
-
3. Trend Analysis
|
377 |
-
4. Risk Assessment
|
378 |
-
5. Strategic Recommendations
|
379 |
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
theme="default"
|
387 |
)
|
388 |
|
389 |
-
# Launch
|
390 |
if __name__ == "__main__":
|
391 |
try:
|
392 |
-
|
393 |
-
iface.queue()
|
394 |
-
iface.launch(
|
395 |
-
server_name="0.0.0.0",
|
396 |
-
server_port=7860,
|
397 |
-
share=False
|
398 |
-
)
|
399 |
except Exception as e:
|
400 |
logger.error(f"Launch error: {str(e)}")
|
401 |
sys.exit(1)
|
|
|
4 |
import torch
|
5 |
import logging
|
6 |
import gc
|
|
|
7 |
from transformers import pipeline
|
|
|
8 |
|
9 |
# Setup logging
|
10 |
logging.basicConfig(
|
|
|
17 |
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
|
18 |
logger.info(f"Using device: {DEVICE}")
|
19 |
|
20 |
+
def clear_gpu_memory():
|
21 |
+
"""Utility function to clear GPU memory"""
|
22 |
+
if DEVICE == "cuda":
|
23 |
+
torch.cuda.empty_cache()
|
24 |
+
gc.collect()
|
25 |
+
|
26 |
class FinancialDataExtractor:
|
27 |
+
"""Extract and clean financial data"""
|
28 |
|
29 |
def __init__(self):
|
30 |
+
self.logger = logger
|
31 |
|
32 |
+
def clean_number(self, value):
|
33 |
"""Clean numeric values from financial statements"""
|
34 |
try:
|
35 |
if pd.isna(value) or value == '' or value == '-':
|
|
|
38 |
return float(value)
|
39 |
|
40 |
# Remove currency symbols, spaces, commas
|
41 |
+
cleaned = str(value).replace('$', '').replace(',', '').replace('"', '').strip()
|
42 |
# Handle parentheses for negative numbers
|
43 |
if '(' in cleaned and ')' in cleaned:
|
44 |
cleaned = '-' + cleaned.replace('(', '').replace(')', '')
|
|
|
46 |
except:
|
47 |
return 0.0
|
48 |
|
49 |
+
def extract_data(self, df: pd.DataFrame) -> pd.DataFrame:
|
50 |
+
"""Extract and clean data from DataFrame"""
|
51 |
+
# Clean column names
|
52 |
+
df.columns = df.columns.str.strip()
|
53 |
|
54 |
+
# Get year columns
|
55 |
+
year_cols = [col for col in df.columns if str(col).isdigit()]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
|
57 |
+
if not year_cols:
|
58 |
+
raise ValueError("No year columns found in data")
|
|
|
|
|
|
|
|
|
|
|
59 |
|
60 |
+
# Clean numeric data
|
61 |
+
for col in year_cols:
|
62 |
+
df[col] = df[col].apply(self.clean_number)
|
|
|
63 |
|
64 |
+
return df, year_cols
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
|
66 |
class FinancialAnalyzer:
|
67 |
+
"""Financial analysis using small models"""
|
68 |
|
69 |
def __init__(self):
|
70 |
self.extractor = FinancialDataExtractor()
|
|
|
95 |
logger.error(f"Error loading models: {str(e)}")
|
96 |
raise
|
97 |
|
98 |
+
def calculate_metrics(self, income_df: pd.DataFrame, balance_df: pd.DataFrame, year_cols: list) -> dict:
|
99 |
+
"""Calculate financial metrics"""
|
100 |
+
metrics = {}
|
101 |
+
|
102 |
+
for year in year_cols:
|
103 |
+
# Income Statement metrics
|
104 |
+
income = {
|
105 |
+
'Revenue': income_df[income_df['Period'].str.contains('Total Net Revenue|Revenue', na=False, case=False)][year].iloc[0],
|
106 |
+
'COGS': income_df[income_df['Period'].str.contains('Cost of Goods Sold', na=False, case=False)][year].iloc[0],
|
107 |
+
'Operating_Expenses': income_df[income_df['Period'].str.contains('Total Expenses', na=False, case=False)][year].iloc[0],
|
108 |
+
'EBIT': income_df[income_df['Period'].str.contains('Earnings Before Interest & Taxes', na=False, case=False)][year].iloc[0],
|
109 |
+
'Net_Income': income_df[income_df['Period'].str.contains('Net Income|Net Earnings', na=False, case=False)][year].iloc[-1]
|
110 |
+
}
|
111 |
|
112 |
+
# Balance Sheet metrics
|
113 |
+
balance = {
|
114 |
+
'Total_Assets': balance_df[balance_df['Period'].str.contains('Total Assets', na=False, case=False)][year].iloc[0],
|
115 |
+
'Current_Assets': balance_df[balance_df['Period'].str.contains('Total current assets', na=False, case=False)][year].iloc[0],
|
116 |
+
'Total_Liabilities': balance_df[balance_df['Period'].str.contains('Total Liabilities', na=False, case=False)][year].iloc[0],
|
117 |
+
'Current_Liabilities': balance_df[balance_df['Period'].str.contains('Total current liabilities', na=False, case=False)][year].iloc[0],
|
118 |
+
'Equity': balance_df[balance_df['Period'].str.contains("Shareholder's Equity", na=False, case=False)][year].iloc[-1]
|
119 |
+
}
|
120 |
+
|
121 |
+
# Calculate ratios
|
122 |
+
metrics[year] = {
|
123 |
'Profitability': {
|
124 |
+
'Gross_Margin': ((income['Revenue'] - income['COGS']) / income['Revenue']) * 100,
|
125 |
+
'Operating_Margin': (income['EBIT'] / income['Revenue']) * 100,
|
126 |
+
'Net_Margin': (income['Net_Income'] / income['Revenue']) * 100,
|
127 |
+
'ROE': (income['Net_Income'] / balance['Equity']) * 100,
|
128 |
+
'ROA': (income['Net_Income'] / balance['Total_Assets']) * 100
|
129 |
},
|
130 |
'Liquidity': {
|
131 |
+
'Current_Ratio': balance['Current_Assets'] / balance['Current_Liabilities'],
|
132 |
+
'Working_Capital': balance['Current_Assets'] - balance['Current_Liabilities']
|
|
|
133 |
},
|
134 |
+
'Growth': {
|
135 |
+
'Revenue': income['Revenue'],
|
136 |
+
'Net_Income': income['Net_Income'],
|
137 |
+
'Total_Assets': balance['Total_Assets']
|
|
|
|
|
|
|
|
|
|
|
138 |
}
|
139 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
140 |
|
141 |
+
return metrics
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
142 |
|
143 |
def analyze_financials(self, income_df: pd.DataFrame, balance_df: pd.DataFrame) -> str:
|
144 |
+
"""Generate financial analysis"""
|
145 |
try:
|
146 |
+
# Extract and clean data
|
147 |
+
income_df, year_cols = self.extractor.extract_data(income_df)
|
148 |
+
balance_df, _ = self.extractor.extract_data(balance_df)
|
149 |
|
150 |
+
# Calculate metrics
|
151 |
+
metrics = self.calculate_metrics(income_df, balance_df, year_cols)
|
|
|
152 |
|
153 |
+
# Get latest and earliest years
|
154 |
+
latest_year = max(year_cols)
|
155 |
+
earliest_year = min(year_cols)
|
156 |
|
157 |
+
# Calculate growth
|
158 |
+
revenue_growth = ((metrics[latest_year]['Growth']['Revenue'] / metrics[earliest_year]['Growth']['Revenue']) - 1) * 100
|
159 |
+
profit_growth = ((metrics[latest_year]['Growth']['Net_Income'] / metrics[earliest_year]['Growth']['Net_Income']) - 1) * 100
|
|
|
|
|
160 |
|
161 |
+
# Generate analysis context
|
162 |
+
context = f"""Financial Analysis ({earliest_year}-{latest_year}):
|
163 |
+
|
164 |
+
Performance Metrics:
|
165 |
+
- Revenue Growth: {revenue_growth:.1f}%
|
166 |
+
- Profit Growth: {profit_growth:.1f}%
|
167 |
+
- Current Gross Margin: {metrics[latest_year]['Profitability']['Gross_Margin']:.1f}%
|
168 |
+
- Current Net Margin: {metrics[latest_year]['Profitability']['Net_Margin']:.1f}%
|
169 |
+
- ROE: {metrics[latest_year]['Profitability']['ROE']:.1f}%
|
170 |
+
- Current Ratio: {metrics[latest_year]['Liquidity']['Current_Ratio']:.2f}
|
171 |
+
|
172 |
+
Trends:
|
173 |
+
- Revenue has grown from ${metrics[earliest_year]['Growth']['Revenue']:,.0f} to ${metrics[latest_year]['Growth']['Revenue']:,.0f}
|
174 |
+
- Net Income has changed from ${metrics[earliest_year]['Growth']['Net_Income']:,.0f} to ${metrics[latest_year]['Growth']['Net_Income']:,.0f}
|
175 |
+
- Profitability margins show {('improving' if metrics[latest_year]['Profitability']['Net_Margin'] > metrics[earliest_year]['Profitability']['Net_Margin'] else 'declining')} trend"""
|
176 |
+
|
177 |
+
# Get sentiment
|
178 |
+
sentiment = self.sentiment_model(context[:512])[0]
|
179 |
+
|
180 |
+
# Generate detailed analysis
|
181 |
analysis = self.analysis_model(
|
182 |
+
f"[INST] As a financial analyst, provide a detailed analysis of this company:\n\n{context}\n\nInclude:\n1. Financial health assessment\n2. Key performance insights\n3. Strategic recommendations [/INST]",
|
183 |
max_length=1500,
|
184 |
+
num_return_sequences=1,
|
185 |
do_sample=True,
|
186 |
+
temperature=0.7
|
|
|
187 |
)[0]['generated_text']
|
188 |
|
189 |
+
# Format output
|
190 |
+
output = f"""# Financial Analysis Report
|
|
|
|
|
|
|
|
|
|
|
|
|
191 |
|
192 |
+
## Overall Sentiment: {sentiment['label'].upper()} ({sentiment['score']:.1%})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
193 |
|
194 |
+
## Key Performance Indicators ({latest_year})
|
195 |
+
- Gross Margin: {metrics[latest_year]['Profitability']['Gross_Margin']:.1f}%
|
196 |
+
- Operating Margin: {metrics[latest_year]['Profitability']['Operating_Margin']:.1f}%
|
197 |
+
- Net Margin: {metrics[latest_year]['Profitability']['Net_Margin']:.1f}%
|
198 |
+
- ROE: {metrics[latest_year]['Profitability']['ROE']:.1f}%
|
199 |
+
- Current Ratio: {metrics[latest_year]['Liquidity']['Current_Ratio']:.2f}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
200 |
|
201 |
+
## Performance Trends ({earliest_year}-{latest_year})
|
202 |
+
- Revenue Growth: {revenue_growth:.1f}%
|
203 |
+
- Profit Growth: {profit_growth:.1f}%
|
204 |
+
- Working Capital: ${metrics[latest_year]['Liquidity']['Working_Capital']:,.0f}
|
205 |
+
|
206 |
+
## Analysis
|
207 |
+
{analysis}"""
|
|
|
208 |
|
209 |
+
return output
|
210 |
|
211 |
except Exception as e:
|
212 |
+
logger.error(f"Analysis error: {str(e)}")
|
213 |
+
raise
|
214 |
|
215 |
def analyze_statements(income_statement, balance_sheet):
|
216 |
"""Main function to analyze financial statements"""
|
217 |
try:
|
218 |
if not income_statement or not balance_sheet:
|
219 |
return "Please upload both Income Statement and Balance Sheet CSV files."
|
220 |
+
|
221 |
# Read files
|
222 |
income_df = pd.read_csv(income_statement.name)
|
223 |
balance_df = pd.read_csv(balance_sheet.name)
|
224 |
|
225 |
+
# Create analyzer and process
|
226 |
analyzer = FinancialAnalyzer()
|
|
|
|
|
227 |
result = analyzer.analyze_financials(income_df, balance_df)
|
228 |
|
229 |
# Clear memory
|
|
|
236 |
return f"""Analysis Error: {str(e)}
|
237 |
|
238 |
Please ensure your CSV files:
|
239 |
+
1. Have clear year columns
|
240 |
+
2. Contain recognizable financial metrics
|
241 |
+
3. Use consistent number formatting"""
|
|
|
242 |
|
243 |
+
# Create Gradio interface
|
244 |
iface = gr.Interface(
|
245 |
fn=analyze_statements,
|
246 |
inputs=[
|
247 |
+
gr.File(label="Upload Income Statement (CSV)", file_types=[".csv"]),
|
248 |
+
gr.File(label="Upload Balance Sheet (CSV)", file_types=[".csv"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
249 |
],
|
250 |
outputs=gr.Markdown(),
|
251 |
+
title="Financial Statement Analyzer",
|
252 |
+
description="""## Financial Analysis Tool
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
253 |
|
254 |
+
Upload your financial statements to get:
|
255 |
+
- Performance Analysis
|
256 |
+
- Key Metrics & Ratios
|
257 |
+
- Trend Analysis
|
258 |
+
- Strategic Recommendations""",
|
259 |
+
examples=None
|
|
|
260 |
)
|
261 |
|
262 |
+
# Launch the interface
|
263 |
if __name__ == "__main__":
|
264 |
try:
|
265 |
+
iface.launch(server_name="0.0.0.0", server_port=7860)
|
|
|
|
|
|
|
|
|
|
|
|
|
266 |
except Exception as e:
|
267 |
logger.error(f"Launch error: {str(e)}")
|
268 |
sys.exit(1)
|