walaa2022 commited on
Commit
558a79e
·
verified ·
1 Parent(s): 89c9c29

Rename financial-metrics-calculator.py to app.py

Browse files
Files changed (2) hide show
  1. app.py +328 -0
  2. financial-metrics-calculator.py +0 -165
app.py ADDED
@@ -0,0 +1,328 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import pandas as pd
3
+ import json
4
+ from transformers import (
5
+ AutoTokenizer,
6
+ AutoModelForCausalLM,
7
+ AutoModelForSequenceClassification
8
+ )
9
+ import torch
10
+ import numpy as np
11
+ import re
12
+
13
+ class FinancialDataset:
14
+ def __init__(self, texts, labels, tokenizer, max_length=512):
15
+ self.texts = texts
16
+ self.labels = labels
17
+ self.tokenizer = tokenizer
18
+ self.max_length = max_length
19
+
20
+ def __len__(self):
21
+ return len(self.texts)
22
+
23
+ def __getitem__(self, idx):
24
+ text = str(self.texts[idx])
25
+ inputs = self.tokenizer(
26
+ text,
27
+ truncation=True,
28
+ padding='max_length',
29
+ max_length=self.max_length,
30
+ return_tensors='pt'
31
+ )
32
+ return {
33
+ 'input_ids': inputs['input_ids'].squeeze(),
34
+ 'attention_mask': inputs['attention_mask'].squeeze(),
35
+ 'labels': torch.tensor(self.labels[idx], dtype=torch.long)
36
+ }
37
+
38
+ class FinancialAnalyzer:
39
+ def __init__(self):
40
+ print("Initializing Analyzer...")
41
+ self.last_metrics = {}
42
+ self.initialize_models()
43
+ print("Initialization complete!")
44
+
45
+ def initialize_models(self):
46
+ """Initialize TinyLlama model"""
47
+ try:
48
+ self.llama_tokenizer = AutoTokenizer.from_pretrained("TinyLlama/TinyLlama-1.1B-Chat-v1.0")
49
+ self.llama_model = AutoModelForCausalLM.from_pretrained("TinyLlama/TinyLlama-1.1B-Chat-v1.0")
50
+ self.llama_model.eval()
51
+ print("Models loaded successfully!")
52
+ except Exception as e:
53
+ print(f"Error initializing models: {str(e)}")
54
+ raise
55
+
56
+ def clean_number(self, value):
57
+ """Clean and convert numerical values"""
58
+ try:
59
+ if isinstance(value, str):
60
+ value = value.replace('$', '').replace(',', '').strip()
61
+ if '(' in value and ')' in value:
62
+ value = '-' + value.replace('(', '').replace(')', '')
63
+ return float(value or 0)
64
+ except:
65
+ return 0.0
66
+
67
+ def is_valid_markdown(self, file_path):
68
+ """Check if a file is a valid Markdown file"""
69
+ try:
70
+ with open(file_path, 'r') as f:
71
+ content = f.read()
72
+ return any(line.startswith('#') or '|' in line for line in content.split('\n'))
73
+ except:
74
+ return False
75
+
76
+ def parse_financial_data(self, content):
77
+ """Parse markdown content into structured data"""
78
+ try:
79
+ data = {}
80
+ current_section = ""
81
+ current_table = []
82
+ headers = None
83
+
84
+ for line in content.split('\n'):
85
+ if line.startswith('#'):
86
+ if current_table and headers:
87
+ data[current_section] = self.process_table(headers, current_table)
88
+ current_section = line.strip('# ')
89
+ current_table = []
90
+ headers = None
91
+ elif '|' in line:
92
+ if '-|-' not in line:
93
+ row = [cell.strip() for cell in line.split('|')[1:-1]]
94
+ if not headers:
95
+ headers = row
96
+ else:
97
+ current_table.append(row)
98
+
99
+ if current_table and headers:
100
+ data[current_section] = self.process_table(headers, current_table)
101
+
102
+ return data
103
+ except Exception as e:
104
+ print(f"Error parsing financial data: {str(e)}")
105
+ return {}
106
+
107
+ def process_table(self, headers, rows):
108
+ """Process table data into structured format"""
109
+ try:
110
+ processed_data = {}
111
+ for row in rows:
112
+ if len(row) == len(headers):
113
+ item_name = row[0].strip('*').strip()
114
+ processed_data[item_name] = {}
115
+ for i, value in enumerate(row[1:], 1):
116
+ processed_data[item_name][headers[i]] = self.clean_number(value)
117
+ return processed_data
118
+ except Exception as e:
119
+ print(f"Error processing table: {str(e)}")
120
+ return {}
121
+
122
+ def get_nested_value(self, data, section, key, year):
123
+ """Safely get nested dictionary value"""
124
+ try:
125
+ return data.get(section, {}).get(key, {}).get(str(year), 0)
126
+ except:
127
+ return 0
128
+
129
+ def calculate_metrics(self, income_data, balance_data):
130
+ """Calculate all CFI standard financial metrics"""
131
+ try:
132
+ metrics = {}
133
+
134
+ # 1. Gross Profit Margin Ratio
135
+ revenue = self.get_nested_value(income_data, "Revenue", "Total Net Revenue", "2025")
136
+ cogs = self.get_nested_value(income_data, "Operating Expenses", "Cost of Goods Sold", "2025")
137
+ gross_profit = revenue - cogs
138
+ metrics['gross_profit_margin'] = (gross_profit / revenue) * 100 if revenue != 0 else 0
139
+
140
+ # 2. Current Ratio
141
+ current_assets = self.get_nested_value(balance_data, "Assets", "Total Current Assets", "2025")
142
+ current_liabilities = self.get_nested_value(balance_data, "Liabilities", "Total Current Liabilities", "2025")
143
+ metrics['current_ratio'] = current_assets / current_liabilities if current_liabilities != 0 else 0
144
+
145
+ # 3. Debt Ratio
146
+ total_liabilities = self.get_nested_value(balance_data, "Liabilities", "Total Liabilities", "2025")
147
+ total_assets = self.get_nested_value(balance_data, "Assets", "Total Assets", "2025")
148
+ metrics['debt_ratio'] = (total_liabilities / total_assets) * 100 if total_assets != 0 else 0
149
+
150
+ # 4. Sustainable Growth Rate (SGR)
151
+ net_income = self.get_nested_value(income_data, "Net Income", "Total Net Income", "2025")
152
+ equity = self.get_nested_value(balance_data, "Equity", "Total Shareholders Equity", "2025")
153
+ dividends = self.get_nested_value(income_data, "Dividends", "Dividends Paid", "2025")
154
+
155
+ roe = (net_income / equity) * 100 if equity != 0 else 0
156
+ retention_ratio = (net_income - dividends) / net_income if net_income != 0 else 0
157
+ metrics['sgr'] = roe * retention_ratio / 100 if roe != 0 else 0
158
+
159
+ # 5. Accounts Receivable Turnover
160
+ accounts_receivable = self.get_nested_value(balance_data, "Assets", "Accounts Receivable", "2025")
161
+ metrics['ar_turnover'] = revenue / accounts_receivable if accounts_receivable != 0 else 0
162
+
163
+ # 6. Return on Equity (ROE)
164
+ metrics['roe'] = roe
165
+
166
+ # 7. Net Profit Margin
167
+ metrics['net_profit_margin'] = (net_income / revenue) * 100 if revenue != 0 else 0
168
+
169
+ # 8. Retained Earnings Ratio
170
+ retained_earnings = self.get_nested_value(balance_data, "Equity", "Retained Earnings", "2025")
171
+ metrics['retained_earnings_ratio'] = (retained_earnings / total_assets) * 100 if total_assets != 0 else 0
172
+
173
+ # Additional YoY metrics
174
+ revenue_2024 = self.get_nested_value(income_data, "Revenue", "Total Net Revenue", "2024")
175
+ metrics['revenue_growth'] = ((revenue / revenue_2024) - 1) * 100 if revenue_2024 != 0 else 0
176
+
177
+ return metrics
178
+ except Exception as e:
179
+ print(f"Error calculating metrics: {str(e)}")
180
+ return {}
181
+
182
+ def generate_analysis(self, metrics):
183
+ """Generate comprehensive analysis"""
184
+ try:
185
+ prompt = f"""[INST] As a financial analyst, provide a comprehensive analysis based on these metrics:
186
+
187
+ 1. Profitability:
188
+ - Gross Profit Margin: {metrics.get('gross_profit_margin', 0):.2f}%
189
+ - Net Profit Margin: {metrics.get('net_profit_margin', 0):.2f}%
190
+ - Return on Equity: {metrics.get('roe', 0):.2f}%
191
+
192
+ 2. Liquidity & Efficiency:
193
+ - Current Ratio: {metrics.get('current_ratio', 0):.2f}
194
+ - Accounts Receivable Turnover: {metrics.get('ar_turnover', 0):.2f}
195
+
196
+ 3. Financial Structure:
197
+ - Debt Ratio: {metrics.get('debt_ratio', 0):.2f}%
198
+ - Retained Earnings Ratio: {metrics.get('retained_earnings_ratio', 0):.2f}%
199
+
200
+ 4. Growth:
201
+ - Sustainable Growth Rate: {metrics.get('sgr', 0):.2f}%
202
+ - Revenue Growth (YoY): {metrics.get('revenue_growth', 0):.2f}%
203
+
204
+ Provide:
205
+ 1. Overall financial health assessment
206
+ 2. Key strengths and concerns
207
+ 3. Operational efficiency analysis
208
+ 4. Specific recommendations for improvement
209
+ [/INST]"""
210
+
211
+ inputs = self.llama_tokenizer(prompt, return_tensors="pt", truncation=True, max_length=2048)
212
+ outputs = self.llama_model.generate(
213
+ inputs["input_ids"],
214
+ max_new_tokens=1024,
215
+ min_new_tokens=200,
216
+ temperature=0.7,
217
+ top_p=0.95,
218
+ repetition_penalty=1.2,
219
+ length_penalty=1.5
220
+ )
221
+
222
+ analysis = self.llama_tokenizer.decode(outputs[0], skip_special_tokens=True)
223
+
224
+ if len(analysis.split()) < 100:
225
+ return self.generate_fallback_analysis(metrics)
226
+
227
+ return analysis
228
+
229
+ except Exception as e:
230
+ print(f"Error generating analysis: {str(e)}")
231
+ return self.generate_fallback_analysis(metrics)
232
+
233
+ def generate_fallback_analysis(self, metrics):
234
+ """Generate basic analysis when model fails"""
235
+ try:
236
+ analysis = f"""Financial Analysis Summary:
237
+
238
+ 1. Profitability Assessment:
239
+ - Gross Profit Margin: {metrics.get('gross_profit_margin', 0):.2f}%
240
+ ({self.interpret_metric('gross_profit_margin', metrics.get('gross_profit_margin', 0))})
241
+ - Net Profit Margin: {metrics.get('net_profit_margin', 0):.2f}%
242
+ ({self.interpret_metric('net_profit_margin', metrics.get('net_profit_margin', 0))})
243
+ - Return on Equity: {metrics.get('roe', 0):.2f}%
244
+ ({self.interpret_metric('roe', metrics.get('roe', 0))})
245
+
246
+ 2. Liquidity & Efficiency Analysis:
247
+ - Current Ratio: {metrics.get('current_ratio', 0):.2f}
248
+ ({self.interpret_metric('current_ratio', metrics.get('current_ratio', 0))})
249
+ - AR Turnover: {metrics.get('ar_turnover', 0):.2f}
250
+ ({self.interpret_metric('ar_turnover', metrics.get('ar_turnover', 0))})
251
+
252
+ 3. Financial Structure:
253
+ - Debt Ratio: {metrics.get('debt_ratio', 0):.2f}%
254
+ ({self.interpret_metric('debt_ratio', metrics.get('debt_ratio', 0))})
255
+ - Retained Earnings Ratio: {metrics.get('retained_earnings_ratio', 0):.2f}%
256
+ ({self.interpret_metric('retained_earnings_ratio', metrics.get('retained_earnings_ratio', 0))})
257
+
258
+ 4. Growth & Sustainability:
259
+ - Sustainable Growth Rate: {metrics.get('sgr', 0):.2f}%
260
+ ({self.interpret_metric('sgr', metrics.get('sgr', 0))})
261
+ - Revenue Growth: {metrics.get('revenue_growth', 0):.2f}%
262
+ ({self.interpret_metric('revenue_growth', metrics.get('revenue_growth', 0))})
263
+
264
+ {self.generate_recommendations(metrics)}"""
265
+ return analysis
266
+
267
+ except Exception as e:
268
+ return f"Error generating fallback analysis: {str(e)}"
269
+
270
+ def interpret_metric(self, metric_name, value):
271
+ """Interpret individual metrics based on CFI standards"""
272
+ interpretations = {
273
+ 'gross_profit_margin': lambda x: 'Strong' if x > 40 else 'Adequate' if x > 30 else 'Needs improvement',
274
+ 'current_ratio': lambda x: 'Strong' if x > 2 else 'Adequate' if x > 1 else 'Concerning',
275
+ 'debt_ratio': lambda x: 'Conservative' if x < 40 else 'Moderate' if x < 60 else 'High risk',
276
+ 'ar_turnover': lambda x: 'Excellent' if x > 8 else 'Good' if x > 4 else 'Needs improvement',
277
+ 'roe': lambda x: 'Strong' if x > 15 else 'Adequate' if x > 10 else 'Below target',
278
+ 'net_profit_margin': lambda x: 'Strong' if x > 10 else 'Adequate' if x > 5 else 'Needs improvement',
279
+ 'retained_earnings_ratio': lambda x: 'Strong' if x > 30 else 'Adequate' if x > 15 else 'Low retention',
280
+ 'sgr': lambda x: 'Strong' if x > 10 else 'Moderate' if x > 5 else 'Limited growth potential',
281
+ 'revenue_growth': lambda x: 'Strong' if x > 10 else 'Moderate' if x > 5 else 'Below industry average'
282
+ }
283
+ try:
284
+ return interpretations.get(metric_name, lambda x: 'No interpretation')(value)
285
+ except:
286
+ return 'Unable to interpret'
287
+
288
+ def generate_recommendations(self, metrics):
289
+ """Generate specific recommendations based on metrics"""
290
+ recommendations = []
291
+
292
+ if metrics.get('gross_profit_margin', 0) < 30:
293
+ recommendations.append("- Review pricing strategy and cost structure to improve gross margins")
294
+ if metrics.get('current_ratio', 0) < 1.5:
295
+ recommendations.append("- Strengthen working capital management to improve liquidity")
296
+ if metrics.get('debt_ratio', 0) > 60:
297
+ recommendations.append("- Consider debt reduction strategies to improve financial flexibility")
298
+ if metrics.get('ar_turnover', 0) < 4:
299
+ recommendations.append("- Improve accounts receivable collection practices")
300
+ if metrics.get('roe', 0) < 10:
301
+ recommendations.append("- Focus on improving operational efficiency to enhance returns")
302
+ if metrics.get('revenue_growth', 0) < 5:
303
+ recommendations.append("- Develop strategies to accelerate revenue growth")
304
+
305
+ def create_interface():
306
+ analyzer = FinancialAnalyzer()
307
+
308
+ iface = gr.Interface(
309
+ fn=analyzer.analyze_financials,
310
+ inputs=[
311
+ gr.File(label="Balance Sheet (Markdown)", type="filepath"),
312
+ gr.File(label="Income Statement (Markdown)", type="filepath")
313
+ ],
314
+ outputs=gr.Textbox(label="Analysis Results", lines=25),
315
+ title="AI Financial Statement Analyzer",
316
+ description="""Upload financial statements in Markdown format for AI-powered analysis.
317
+ Analysis is based on Corporate Finance Institute (CFI) standards.""",
318
+ examples=[
319
+ ["balance_sheet_example.md", "income_statement_example.md"]
320
+ ],
321
+ cache_examples=True
322
+ )
323
+
324
+ return iface
325
+
326
+ if __name__ == "__main__":
327
+ iface = create_interface()
328
+ iface.launch()
financial-metrics-calculator.py DELETED
@@ -1,165 +0,0 @@
1
- # Financial Metrics Calculator.ipynb
2
-
3
- import pandas as pd
4
- import json
5
- import numpy as np
6
-
7
- class FinancialMetricsCalculator:
8
- def __init__(self):
9
- print("Initializing Financial Metrics Calculator...")
10
-
11
- def clean_number(self, value):
12
- """Clean and convert numerical values"""
13
- try:
14
- if isinstance(value, str):
15
- value = value.replace('$', '').replace(',', '').strip()
16
- if '(' in value and ')' in value:
17
- value = '-' + value.replace('(', '').replace(')', '')
18
- return float(value or 0)
19
- except:
20
- return 0.0
21
-
22
- def is_valid_markdown(self, file_path):
23
- """Check if a file is a valid Markdown file"""
24
- try:
25
- with open(file_path, 'r') as f:
26
- content = f.read()
27
- return any(line.startswith('#') or '|' in line for line in content.split('\n'))
28
- except:
29
- return False
30
-
31
- def parse_financial_data(self, content):
32
- """Parse markdown content into structured data"""
33
- try:
34
- data = {}
35
- current_section = ""
36
- current_table = []
37
- headers = None
38
-
39
- for line in content.split('\n'):
40
- if line.startswith('#'):
41
- if current_table and headers:
42
- data[current_section] = self.process_table(headers, current_table)
43
- current_section = line.strip('# ')
44
- current_table = []
45
- headers = None
46
- elif '|' in line:
47
- if '-|-' not in line:
48
- row = [cell.strip() for cell in line.split('|')[1:-1]]
49
- if not headers:
50
- headers = row
51
- else:
52
- current_table.append(row)
53
-
54
- if current_table and headers:
55
- data[current_section] = self.process_table(headers, current_table)
56
-
57
- return data
58
- except Exception as e:
59
- print(f"Error parsing financial data: {str(e)}")
60
- return {}
61
-
62
- def process_table(self, headers, rows):
63
- """Process table data into structured format"""
64
- try:
65
- processed_data = {}
66
- for row in rows:
67
- if len(row) == len(headers):
68
- item_name = row[0].strip('*').strip()
69
- processed_data[item_name] = {}
70
- for i, value in enumerate(row[1:], 1):
71
- processed_data[item_name][headers[i]] = self.clean_number(value)
72
- return processed_data
73
- except Exception as e:
74
- print(f"Error processing table: {str(e)}")
75
- return {}
76
-
77
- def get_nested_value(self, data, section, key, year):
78
- """Safely get nested dictionary value"""
79
- try:
80
- return data.get(section, {}).get(key, {}).get(str(year), 0)
81
- except:
82
- return 0
83
-
84
- def extract_metrics(self, income_data, balance_data):
85
- """Extract and calculate key financial metrics"""
86
- try:
87
- metrics = {
88
- "Revenue": {
89
- "2025": self.get_nested_value(income_data, "Revenue", "Total Net Revenue", "2025"),
90
- "2024": self.get_nested_value(income_data, "Revenue", "Total Net Revenue", "2024"),
91
- "2021": self.get_nested_value(income_data, "Revenue", "Total Net Revenue", "2021")
92
- },
93
- "Profitability": {
94
- "Gross_Profit_2025": self.get_nested_value(income_data, "Cost and Gross Profit", "Gross Profit", "2025"),
95
- "EBIT_2025": self.get_nested_value(income_data, "Profit Summary", "EBIT", "2025"),
96
- "Net_Earnings_2025": self.get_nested_value(income_data, "Profit Summary", "Net Earnings", "2025"),
97
- "Operating_Expenses_2025": self.get_nested_value(income_data, "Operating Expenses", "Total Operating Expenses", "2025")
98
- },
99
- "Balance_Sheet": {
100
- "Total_Assets_2025": self.get_nested_value(balance_data, "Key Totals", "Total_Assets", "2025"),
101
- "Current_Assets_2025": self.get_nested_value(balance_data, "Key Totals", "Total_Current_Assets", "2025"),
102
- "Total_Liabilities_2025": self.get_nested_value(balance_data, "Key Totals", "Total_Liabilities", "2025"),
103
- "Current_Liabilities_2025": self.get_nested_value(balance_data, "Key Totals", "Total_Current_Liabilities", "2025"),
104
- "Equity_2025": self.get_nested_value(balance_data, "Key Totals", "Total_Shareholders_Equity", "2025"),
105
- "Inventory_2025": self.get_nested_value(balance_data, "Balance Sheet Data 2021-2025", "Inventory", "2025"),
106
- "Accounts_Receivable_2025": self.get_nested_value(balance_data, "Balance Sheet Data 2021-2025", "Accounts_Receivable", "2025"),
107
- "Long_Term_Debt_2025": self.get_nested_value(balance_data, "Balance Sheet Data 2021-2025", "Long_Term_Debt", "2025")
108
- },
109
- "Cash_Flow": {
110
- "Depreciation_2025": self.get_nested_value(income_data, "Operating Expenses", "Depreciation & Amortization", "2025"),
111
- "Interest_Expense_2025": self.get_nested_value(income_data, "Profit Summary", "Interest Expense", "2025")
112
- }
113
- }
114
-
115
- revenue_2025 = metrics["Revenue"]["2025"]
116
- if revenue_2025 != 0:
117
- metrics["Ratios"] = self.calculate_financial_ratios(metrics, revenue_2025)
118
-
119
- return metrics
120
- except Exception as e:
121
- print(f"Error extracting metrics: {str(e)}")
122
- return {}
123
-
124
- def calculate_financial_ratios(self, metrics, revenue):
125
- """Calculate financial ratios"""
126
- return {
127
- "Gross_Margin": (metrics["Profitability"]["Gross_Profit_2025"] / revenue) * 100,
128
- "Operating_Margin": (metrics["Profitability"]["EBIT_2025"] / revenue) * 100,
129
- "Net_Margin": (metrics["Profitability"]["Net_Earnings_2025"] / revenue) * 100,
130
- "Current_Ratio": metrics["Balance_Sheet"]["Current_Assets_2025"] / metrics["Balance_Sheet"]["Current_Liabilities_2025"] if metrics["Balance_Sheet"]["Current_Liabilities_2025"] != 0 else 0,
131
- "Quick_Ratio": (metrics["Balance_Sheet"]["Current_Assets_2025"] - metrics["Balance_Sheet"]["Inventory_2025"]) / metrics["Balance_Sheet"]["Current_Liabilities_2025"] if metrics["Balance_Sheet"]["Current_Liabilities_2025"] != 0 else 0,
132
- "Asset_Turnover": revenue / metrics["Balance_Sheet"]["Total_Assets_2025"] if metrics["Balance_Sheet"]["Total_Assets_2025"] != 0 else 0,
133
- "Receivables_Turnover": revenue / metrics["Balance_Sheet"]["Accounts_Receivable_2025"] if metrics["Balance_Sheet"]["Accounts_Receivable_2025"] != 0 else 0,
134
- "Debt_to_Equity": metrics["Balance_Sheet"]["Total_Liabilities_2025"] / metrics["Balance_Sheet"]["Equity_2025"] if metrics["Balance_Sheet"]["Equity_2025"] != 0 else 0,
135
- "Interest_Coverage": metrics["Profitability"]["EBIT_2025"] / metrics["Cash_Flow"]["Interest_Expense_2025"] if metrics["Cash_Flow"]["Interest_Expense_2025"] != 0 else 0,
136
- "Revenue_Growth": ((metrics["Revenue"]["2025"] / metrics["Revenue"]["2024"]) - 1) * 100 if metrics["Revenue"]["2024"] != 0 else 0,
137
- "5Year_Revenue_CAGR": ((metrics["Revenue"]["2025"] / metrics["Revenue"]["2021"]) ** (1/4) - 1) * 100 if metrics["Revenue"]["2021"] != 0 else 0
138
- }
139
-
140
- def process_financial_statements(self, balance_sheet_file, income_stmt_file):
141
- """Process financial statements and return metrics"""
142
- try:
143
- if not (self.is_valid_markdown(balance_sheet_file) and self.is_valid_markdown(income_stmt_file)):
144
- return "Error: Invalid file format"
145
-
146
- with open(balance_sheet_file, 'r') as f:
147
- balance_sheet = f.read()
148
- with open(income_stmt_file, 'r') as f:
149
- income_stmt = f.read()
150
-
151
- income_data = self.parse_financial_data(income_stmt)
152
- balance_data = self.parse_financial_data(balance_sheet)
153
- metrics = self.extract_metrics(income_data, balance_data)
154
-
155
- return metrics
156
- except Exception as e:
157
- print(f"Error processing statements: {str(e)}")
158
- return None
159
-
160
- # Example usage
161
- if __name__ == "__main__":
162
- calculator = FinancialMetricsCalculator()
163
- # Test with your markdown files
164
- metrics = calculator.process_financial_statements('balance_sheet.md', 'income_statement.md')
165
- print(json.dumps(metrics, indent=2))