Spaces:
Sleeping
Sleeping
Upload 2 files
Browse files- financial-ai-analyzer.txt +126 -0
- financial-metrics-calculator.py +165 -0
financial-ai-analyzer.txt
ADDED
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Financial AI Analyzer.ipynb
|
2 |
+
|
3 |
+
import torch
|
4 |
+
from transformers import (
|
5 |
+
AutoTokenizer,
|
6 |
+
AutoModelForCausalLM,
|
7 |
+
AutoModelForSequenceClassification
|
8 |
+
)
|
9 |
+
import gradio as gr
|
10 |
+
from financial_metrics_calculator import FinancialMetricsCalculator # Import from first notebook
|
11 |
+
|
12 |
+
class FinancialAIAnalyzer:
|
13 |
+
def __init__(self):
|
14 |
+
print("Initializing AI Models...")
|
15 |
+
self.metrics_calculator = FinancialMetricsCalculator()
|
16 |
+
self.initialize_models()
|
17 |
+
self.last_metrics = {}
|
18 |
+
print("Initialization complete!")
|
19 |
+
|
20 |
+
def initialize_models(self):
|
21 |
+
"""Initialize both TinyLlama and FinBERT models"""
|
22 |
+
try:
|
23 |
+
# Initialize TinyLlama
|
24 |
+
self.llama_tokenizer = AutoTokenizer.from_pretrained("TinyLlama/TinyLlama-1.1B-Chat-v1.0")
|
25 |
+
self.llama_model = AutoModelForCausalLM.from_pretrained("TinyLlama/TinyLlama-1.1B-Chat-v1.0")
|
26 |
+
self.llama_model.eval()
|
27 |
+
|
28 |
+
# Initialize FinBERT
|
29 |
+
self.finbert_tokenizer = AutoTokenizer.from_pretrained("ProsusAI/finbert")
|
30 |
+
self.finbert_model = AutoModelForSequenceClassification.from_pretrained("ProsusAI/finbert")
|
31 |
+
self.finbert_model.eval()
|
32 |
+
|
33 |
+
print("Models loaded successfully!")
|
34 |
+
except Exception as e:
|
35 |
+
print(f"Error initializing models: {str(e)}")
|
36 |
+
raise
|
37 |
+
|
38 |
+
def get_sentiment_analysis(self, metrics):
|
39 |
+
"""Get financial sentiment analysis using FinBERT"""
|
40 |
+
try:
|
41 |
+
financial_text = f"""
|
42 |
+
Revenue growth: {metrics['Ratios'].get('Revenue_Growth', 0):.2f}%
|
43 |
+
Profit margin: {metrics['Ratios'].get('Net_Margin', 0):.2f}%
|
44 |
+
Debt to equity: {metrics['Ratios'].get('Debt_to_Equity', 0):.2f}
|
45 |
+
Interest coverage: {metrics['Ratios'].get('Interest_Coverage', 0):.2f}%
|
46 |
+
Current ratio: {metrics['Ratios'].get('Current_Ratio', 0):.2f}
|
47 |
+
"""
|
48 |
+
|
49 |
+
inputs = self.finbert_tokenizer(financial_text, return_tensors="pt", padding=True, truncation=True)
|
50 |
+
outputs = self.finbert_model(**inputs)
|
51 |
+
probabilities = torch.nn.functional.softmax(outputs.logits, dim=-1)
|
52 |
+
sentiment_scores = probabilities.detach().numpy()[0]
|
53 |
+
|
54 |
+
sentiments = ['negative', 'neutral', 'positive']
|
55 |
+
sentiment_dict = dict(zip(sentiments, [float(score) for score in sentiment_scores]))
|
56 |
+
|
57 |
+
return sentiment_dict
|
58 |
+
except Exception as e:
|
59 |
+
print(f"Error in sentiment analysis: {str(e)}")
|
60 |
+
return {}
|
61 |
+
|
62 |
+
def generate_prompt(self, metrics, sentiment_dict):
|
63 |
+
"""Create analysis prompt"""
|
64 |
+
try:
|
65 |
+
# [Previous generate_prompt code remains the same]
|
66 |
+
pass
|
67 |
+
except Exception as e:
|
68 |
+
print(f"Error generating prompt: {str(e)}")
|
69 |
+
return ""
|
70 |
+
|
71 |
+
def generate_analysis(self, prompt):
|
72 |
+
"""Generate analysis using TinyLlama"""
|
73 |
+
try:
|
74 |
+
# [Previous generate_analysis code remains the same]
|
75 |
+
pass
|
76 |
+
except Exception as e:
|
77 |
+
print(f"Error generating analysis: {str(e)}")
|
78 |
+
return self.generate_fallback_analysis(self.last_metrics)
|
79 |
+
|
80 |
+
def generate_fallback_analysis(self, metrics):
|
81 |
+
"""Generate fallback analysis"""
|
82 |
+
try:
|
83 |
+
# [Previous generate_fallback_analysis code remains the same]
|
84 |
+
pass
|
85 |
+
except Exception as e:
|
86 |
+
return f"Error generating fallback analysis: {str(e)}"
|
87 |
+
|
88 |
+
def analyze_financials(self, balance_sheet_file, income_stmt_file):
|
89 |
+
"""Main analysis function"""
|
90 |
+
try:
|
91 |
+
# Get metrics from calculator
|
92 |
+
metrics = self.metrics_calculator.process_financial_statements(balance_sheet_file, income_stmt_file)
|
93 |
+
if isinstance(metrics, str): # Error message
|
94 |
+
return metrics
|
95 |
+
|
96 |
+
self.last_metrics = metrics
|
97 |
+
sentiment_dict = self.get_sentiment_analysis(metrics)
|
98 |
+
prompt = self.generate_prompt(metrics, sentiment_dict)
|
99 |
+
analysis = self.generate_analysis(prompt)
|
100 |
+
|
101 |
+
results = {
|
102 |
+
"Financial Analysis": {
|
103 |
+
"Key Metrics": metrics,
|
104 |
+
"Market Sentiment": sentiment_dict,
|
105 |
+
"AI Insights": analysis,
|
106 |
+
"Analysis Period": "2021-2025",
|
107 |
+
"Note": "All monetary values in millions ($M)"
|
108 |
+
}
|
109 |
+
}
|
110 |
+
|
111 |
+
return json.dumps(results, indent=2)
|
112 |
+
except Exception as e:
|
113 |
+
return f"Error in analysis: {str(e)}"
|
114 |
+
|
115 |
+
def create_interface():
|
116 |
+
analyzer = FinancialAIAnalyzer()
|
117 |
+
|
118 |
+
iface = gr.Interface(
|
119 |
+
fn=analyzer.analyze_financials,
|
120 |
+
inputs=[
|
121 |
+
gr.File(label="Balance Sheet (Markdown)", type="filepath"),
|
122 |
+
gr.File(label="Income Statement (Markdown)", type="filepath")
|
123 |
+
],
|
124 |
+
outputs=gr.Textbox(label="Analysis Results", lines=25),
|
125 |
+
title="AI Financial Statement Analyzer",
|
126 |
+
description="Upload financial statements in Markdown format for AI-
|
financial-metrics-calculator.py
ADDED
@@ -0,0 +1,165 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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))
|