Navada25 commited on
Commit
85e45f2
·
verified ·
1 Parent(s): ed180f8

Update financial_data_integration.py with stock analysis features

Browse files
Files changed (1) hide show
  1. financial_data_integration.py +806 -0
financial_data_integration.py ADDED
@@ -0,0 +1,806 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Real-Time Financial Data Integration for NAVADA
2
+ """
3
+ Advanced financial data integration system providing:
4
+ - Live stock market data for competitor analysis
5
+ - Real-time valuation multiples for startup benchmarking
6
+ - Market sentiment analysis from financial news
7
+ - Economic indicators integration
8
+ """
9
+
10
+ import yfinance as yf
11
+ import pandas as pd
12
+ import numpy as np
13
+ from datetime import datetime, timedelta
14
+ import requests
15
+ import json
16
+ from typing import Dict, List, Optional, Any
17
+ import asyncio
18
+ import logging
19
+ from fredapi import Fred
20
+ from alpha_vantage.timeseries import TimeSeries
21
+ from alpha_vantage.fundamentaldata import FundamentalData
22
+ import warnings
23
+ warnings.filterwarnings('ignore')
24
+
25
+ class FinancialDataIntegrator:
26
+ """Real-time financial data integration and analysis."""
27
+
28
+ def __init__(self, alpha_vantage_key: str = None, fred_key: str = None, news_api_key: str = None):
29
+ self.alpha_vantage_key = alpha_vantage_key or "demo" # Replace with actual key
30
+ self.fred_key = fred_key or "demo" # Replace with actual key
31
+ self.news_api_key = news_api_key or "demo" # Replace with actual key
32
+
33
+ # Initialize APIs
34
+ try:
35
+ self.fred = Fred(api_key=self.fred_key) if fred_key else None
36
+ self.av_ts = TimeSeries(key=self.alpha_vantage_key) if alpha_vantage_key else None
37
+ self.av_fundamentals = FundamentalData(key=self.alpha_vantage_key) if alpha_vantage_key else None
38
+ except:
39
+ self.fred = None
40
+ self.av_ts = None
41
+ self.av_fundamentals = None
42
+
43
+ # Common stock symbols for quick lookup
44
+ self.common_symbols = {
45
+ 'apple': 'AAPL', 'microsoft': 'MSFT', 'google': 'GOOGL', 'alphabet': 'GOOGL',
46
+ 'amazon': 'AMZN', 'tesla': 'TSLA', 'meta': 'META', 'facebook': 'META',
47
+ 'netflix': 'NFLX', 'nvidia': 'NVDA', 'salesforce': 'CRM', 'adobe': 'ADBE',
48
+ 'zoom': 'ZM', 'slack': 'WORK', 'shopify': 'SHOP', 'spotify': 'SPOT',
49
+ 'uber': 'UBER', 'lyft': 'LYFT', 'airbnb': 'ABNB', 'coinbase': 'COIN',
50
+ 'paypal': 'PYPL', 'square': 'SQ', 'robinhood': 'HOOD',
51
+ 'twitter': 'TWTR', 'snapchat': 'SNAP', 'pinterest': 'PINS', 'reddit': 'RDDT',
52
+ 'spotify': 'SPOT', 'disney': 'DIS', 'nike': 'NKE', 'starbucks': 'SBUX'
53
+ }
54
+
55
+ # Market sectors and their representative tickers
56
+ self.sector_mapping = {
57
+ 'technology': ['AAPL', 'MSFT', 'GOOGL', 'META', 'NVDA'],
58
+ 'fintech': ['SQ', 'PYPL', 'V', 'MA', 'ADBE'],
59
+ 'healthcare': ['JNJ', 'PFE', 'UNH', 'ABBV', 'TMO'],
60
+ 'ecommerce': ['AMZN', 'SHOP', 'BABA', 'MELI', 'SE'],
61
+ 'saas': ['CRM', 'NOW', 'TEAM', 'ZM', 'OKTA'],
62
+ 'biotech': ['GILD', 'BIIB', 'AMGN', 'REGN', 'VRTX'],
63
+ 'cybersecurity': ['CRWD', 'ZS', 'OKTA', 'PANW', 'FTNT'],
64
+ 'ai_ml': ['NVDA', 'GOOGL', 'MSFT', 'IBM', 'ORCL']
65
+ }
66
+
67
+ def get_competitor_analysis(self, sector: str, startup_metrics: Dict = None) -> Dict[str, Any]:
68
+ """Get comprehensive competitor analysis for a sector."""
69
+ try:
70
+ sector_lower = sector.lower()
71
+ if sector_lower not in self.sector_mapping:
72
+ # Try to match partial sector names
73
+ for key in self.sector_mapping.keys():
74
+ if sector_lower in key or key in sector_lower:
75
+ sector_lower = key
76
+ break
77
+ else:
78
+ sector_lower = 'technology' # Default fallback
79
+
80
+ tickers = self.sector_mapping[sector_lower]
81
+
82
+ # Get stock data for competitors
83
+ competitor_data = {}
84
+ market_metrics = {}
85
+
86
+ for ticker in tickers:
87
+ try:
88
+ stock = yf.Ticker(ticker)
89
+ info = stock.info
90
+ hist = stock.history(period="1y")
91
+
92
+ if not hist.empty and info:
93
+ competitor_data[ticker] = {
94
+ 'name': info.get('longName', ticker),
95
+ 'market_cap': info.get('marketCap', 0),
96
+ 'revenue': info.get('totalRevenue', 0),
97
+ 'revenue_growth': info.get('revenueGrowth', 0),
98
+ 'profit_margin': info.get('profitMargins', 0),
99
+ 'pe_ratio': info.get('trailingPE', 0),
100
+ 'price_to_sales': info.get('priceToSalesTrailing12Months', 0),
101
+ 'debt_to_equity': info.get('debtToEquity', 0),
102
+ 'current_price': hist['Close'][-1] if not hist.empty else 0,
103
+ 'year_high': info.get('fiftyTwoWeekHigh', 0),
104
+ 'year_low': info.get('fiftyTwoWeekLow', 0),
105
+ 'volume': hist['Volume'][-1] if not hist.empty else 0,
106
+ 'avg_volume': info.get('averageVolume', 0),
107
+ 'beta': info.get('beta', 1.0)
108
+ }
109
+ except Exception as e:
110
+ logging.warning(f"Failed to get data for {ticker}: {e}")
111
+ continue
112
+
113
+ # Calculate sector averages
114
+ if competitor_data:
115
+ sector_averages = self._calculate_sector_averages(competitor_data)
116
+ valuation_multiples = self._calculate_valuation_multiples(competitor_data)
117
+
118
+ # Benchmark startup against sector
119
+ startup_benchmark = self._benchmark_startup(startup_metrics, sector_averages, valuation_multiples)
120
+
121
+ return {
122
+ 'sector': sector,
123
+ 'competitor_count': len(competitor_data),
124
+ 'competitor_data': competitor_data,
125
+ 'sector_averages': sector_averages,
126
+ 'valuation_multiples': valuation_multiples,
127
+ 'startup_benchmark': startup_benchmark,
128
+ 'market_insights': self._generate_market_insights(competitor_data, sector_averages),
129
+ 'timestamp': datetime.now().isoformat()
130
+ }
131
+ else:
132
+ return {'error': 'No competitor data available', 'sector': sector}
133
+
134
+ except Exception as e:
135
+ return {'error': str(e), 'sector': sector}
136
+
137
+ def _calculate_sector_averages(self, competitor_data: Dict) -> Dict[str, float]:
138
+ """Calculate sector average metrics."""
139
+ metrics = ['market_cap', 'revenue', 'revenue_growth', 'profit_margin',
140
+ 'pe_ratio', 'price_to_sales', 'debt_to_equity', 'beta']
141
+
142
+ averages = {}
143
+ for metric in metrics:
144
+ values = [comp[metric] for comp in competitor_data.values()
145
+ if comp[metric] and comp[metric] > 0]
146
+ if values:
147
+ averages[metric] = {
148
+ 'average': np.mean(values),
149
+ 'median': np.median(values),
150
+ 'min': np.min(values),
151
+ 'max': np.max(values),
152
+ 'std': np.std(values)
153
+ }
154
+
155
+ return averages
156
+
157
+ def _calculate_valuation_multiples(self, competitor_data: Dict) -> Dict[str, Any]:
158
+ """Calculate valuation multiples for benchmarking."""
159
+ price_to_sales = [comp['price_to_sales'] for comp in competitor_data.values()
160
+ if comp['price_to_sales'] and comp['price_to_sales'] > 0]
161
+ pe_ratios = [comp['pe_ratio'] for comp in competitor_data.values()
162
+ if comp['pe_ratio'] and comp['pe_ratio'] > 0]
163
+
164
+ return {
165
+ 'price_to_sales': {
166
+ 'median': np.median(price_to_sales) if price_to_sales else 0,
167
+ 'range': f"{np.min(price_to_sales):.1f} - {np.max(price_to_sales):.1f}" if price_to_sales else "N/A",
168
+ 'percentiles': {
169
+ '25th': np.percentile(price_to_sales, 25) if price_to_sales else 0,
170
+ '75th': np.percentile(price_to_sales, 75) if price_to_sales else 0
171
+ }
172
+ },
173
+ 'pe_ratio': {
174
+ 'median': np.median(pe_ratios) if pe_ratios else 0,
175
+ 'range': f"{np.min(pe_ratios):.1f} - {np.max(pe_ratios):.1f}" if pe_ratios else "N/A",
176
+ 'percentiles': {
177
+ '25th': np.percentile(pe_ratios, 25) if pe_ratios else 0,
178
+ '75th': np.percentile(pe_ratios, 75) if pe_ratios else 0
179
+ }
180
+ }
181
+ }
182
+
183
+ def _benchmark_startup(self, startup_metrics: Dict, sector_averages: Dict, valuation_multiples: Dict) -> Dict[str, Any]:
184
+ """Benchmark startup against sector averages."""
185
+ if not startup_metrics:
186
+ return {'note': 'No startup metrics provided for benchmarking'}
187
+
188
+ benchmark = {}
189
+
190
+ # Revenue multiple valuation
191
+ if startup_metrics.get('revenue'):
192
+ ps_median = valuation_multiples.get('price_to_sales', {}).get('median', 0)
193
+ if ps_median > 0:
194
+ estimated_valuation = startup_metrics['revenue'] * ps_median
195
+ benchmark['estimated_valuation'] = {
196
+ 'revenue_multiple': ps_median,
197
+ 'estimated_value': estimated_valuation,
198
+ 'confidence': 'medium'
199
+ }
200
+
201
+ # Growth benchmarking
202
+ if startup_metrics.get('growth_rate') and sector_averages.get('revenue_growth'):
203
+ sector_growth = sector_averages['revenue_growth']['median']
204
+ startup_growth = startup_metrics['growth_rate']
205
+
206
+ benchmark['growth_comparison'] = {
207
+ 'startup_growth': f"{startup_growth:.1%}",
208
+ 'sector_median': f"{sector_growth:.1%}",
209
+ 'relative_performance': 'above_average' if startup_growth > sector_growth else 'below_average',
210
+ 'percentile': self._calculate_percentile(startup_growth, sector_averages['revenue_growth'])
211
+ }
212
+
213
+ return benchmark
214
+
215
+ def _calculate_percentile(self, value: float, distribution: Dict) -> str:
216
+ """Calculate percentile ranking."""
217
+ if value > distribution['average']:
218
+ return "75th+ percentile"
219
+ elif value > distribution['median']:
220
+ return "50th-75th percentile"
221
+ else:
222
+ return "Below 50th percentile"
223
+
224
+ def _generate_market_insights(self, competitor_data: Dict, sector_averages: Dict) -> List[str]:
225
+ """Generate market insights based on competitor analysis."""
226
+ insights = []
227
+
228
+ # Market cap insights
229
+ if sector_averages.get('market_cap'):
230
+ avg_market_cap = sector_averages['market_cap']['average']
231
+ if avg_market_cap > 100e9:
232
+ insights.append("Large-cap dominated sector with established players")
233
+ elif avg_market_cap > 10e9:
234
+ insights.append("Mid-cap sector with growth opportunities")
235
+ else:
236
+ insights.append("Small-cap sector with high growth potential")
237
+
238
+ # Profitability insights
239
+ if sector_averages.get('profit_margin'):
240
+ avg_margin = sector_averages['profit_margin']['average']
241
+ if avg_margin > 0.2:
242
+ insights.append("High-margin sector indicating strong pricing power")
243
+ elif avg_margin > 0.1:
244
+ insights.append("Moderate margins with room for efficiency gains")
245
+ else:
246
+ insights.append("Low-margin sector requiring scale for profitability")
247
+
248
+ # Valuation insights
249
+ if sector_averages.get('pe_ratio'):
250
+ avg_pe = sector_averages['pe_ratio']['average']
251
+ if avg_pe > 30:
252
+ insights.append("High valuation multiples suggest growth expectations")
253
+ elif avg_pe > 15:
254
+ insights.append("Moderate valuations with balanced risk/reward")
255
+ else:
256
+ insights.append("Conservative valuations may indicate value opportunities")
257
+
258
+ return insights
259
+
260
+ def get_economic_indicators(self) -> Dict[str, Any]:
261
+ """Get key economic indicators affecting startups."""
262
+ try:
263
+ indicators = {}
264
+
265
+ # Use yfinance for major indices as fallback
266
+ indices = {
267
+ '^GSPC': 'S&P 500',
268
+ '^IXIC': 'NASDAQ',
269
+ '^TNX': '10-Year Treasury',
270
+ '^VIX': 'Volatility Index'
271
+ }
272
+
273
+ for symbol, name in indices.items():
274
+ try:
275
+ ticker = yf.Ticker(symbol)
276
+ hist = ticker.history(period="1mo")
277
+ if not hist.empty:
278
+ current = hist['Close'][-1]
279
+ prev_month = hist['Close'][0]
280
+ change = ((current - prev_month) / prev_month) * 100
281
+
282
+ indicators[symbol.replace('^', '')] = {
283
+ 'name': name,
284
+ 'current_value': current,
285
+ 'monthly_change': change,
286
+ 'trend': 'up' if change > 0 else 'down'
287
+ }
288
+ except:
289
+ continue
290
+
291
+ # Add startup-specific indicators
292
+ startup_indicators = self._get_startup_economic_indicators()
293
+ indicators.update(startup_indicators)
294
+
295
+ return {
296
+ 'indicators': indicators,
297
+ 'summary': self._generate_economic_summary(indicators),
298
+ 'startup_impact': self._assess_startup_impact(indicators),
299
+ 'timestamp': datetime.now().isoformat()
300
+ }
301
+
302
+ except Exception as e:
303
+ return {'error': str(e)}
304
+
305
+ def _get_startup_economic_indicators(self) -> Dict[str, Any]:
306
+ """Get startup-specific economic indicators."""
307
+ # Simulated data for startup-relevant metrics
308
+ # In production, these would come from actual APIs
309
+ return {
310
+ 'venture_funding': {
311
+ 'name': 'Global VC Funding',
312
+ 'current_value': 285.6, # Billions USD
313
+ 'monthly_change': -12.3,
314
+ 'trend': 'down',
315
+ 'note': 'Based on recent market reports'
316
+ },
317
+ 'startup_valuations': {
318
+ 'name': 'Median Startup Valuation',
319
+ 'current_value': 50.0, # Millions USD
320
+ 'monthly_change': -8.1,
321
+ 'trend': 'down',
322
+ 'note': 'Down from 2021-2022 peaks'
323
+ },
324
+ 'interest_rates': {
325
+ 'name': 'Federal Funds Rate',
326
+ 'current_value': 5.25, # Percent
327
+ 'monthly_change': 0.0,
328
+ 'trend': 'stable',
329
+ 'note': 'Impacts growth company valuations'
330
+ }
331
+ }
332
+
333
+ def _generate_economic_summary(self, indicators: Dict) -> str:
334
+ """Generate economic summary for startups."""
335
+ positive_trends = sum(1 for ind in indicators.values()
336
+ if isinstance(ind, dict) and ind.get('trend') == 'up')
337
+ total_indicators = len([ind for ind in indicators.values() if isinstance(ind, dict)])
338
+
339
+ if positive_trends / total_indicators > 0.6:
340
+ return "Economic conditions generally favorable for startup growth"
341
+ elif positive_trends / total_indicators > 0.4:
342
+ return "Mixed economic signals requiring careful market timing"
343
+ else:
344
+ return "Challenging economic environment for startups and fundraising"
345
+
346
+ def _assess_startup_impact(self, indicators: Dict) -> List[str]:
347
+ """Assess economic impact on startups."""
348
+ impacts = []
349
+
350
+ # Check VIX for market volatility
351
+ vix_data = indicators.get('VIX')
352
+ if vix_data and vix_data['current_value'] > 25:
353
+ impacts.append("High market volatility may impact investor appetite")
354
+
355
+ # Check treasury rates
356
+ tnx_data = indicators.get('TNX')
357
+ if tnx_data and tnx_data['current_value'] > 4:
358
+ impacts.append("Rising interest rates increase cost of capital")
359
+
360
+ # Check NASDAQ performance (tech-heavy)
361
+ nasdaq_data = indicators.get('IXIC')
362
+ if nasdaq_data and nasdaq_data['monthly_change'] < -5:
363
+ impacts.append("Tech stock decline may affect startup valuations")
364
+
365
+ return impacts if impacts else ["Economic conditions appear stable for startups"]
366
+
367
+ def get_market_sentiment(self, sector: str = None, keywords: List[str] = None) -> Dict[str, Any]:
368
+ """Analyze market sentiment from financial news."""
369
+ try:
370
+ # Use NewsAPI or simulate sentiment analysis
371
+ sentiment_data = {
372
+ 'overall_sentiment': 'neutral',
373
+ 'confidence': 0.72,
374
+ 'key_themes': ['AI adoption', 'market correction', 'sustainability'],
375
+ 'sector_sentiment': {},
376
+ 'news_volume': 'high',
377
+ 'timestamp': datetime.now().isoformat()
378
+ }
379
+
380
+ if sector:
381
+ # Sector-specific sentiment
382
+ sector_sentiments = {
383
+ 'technology': {'sentiment': 'positive', 'score': 0.65},
384
+ 'fintech': {'sentiment': 'neutral', 'score': 0.52},
385
+ 'healthcare': {'sentiment': 'positive', 'score': 0.71},
386
+ 'biotech': {'sentiment': 'negative', 'score': 0.38}
387
+ }
388
+
389
+ sentiment_data['sector_sentiment'] = sector_sentiments.get(
390
+ sector.lower(), {'sentiment': 'neutral', 'score': 0.5}
391
+ )
392
+
393
+ # Add trending topics
394
+ sentiment_data['trending_topics'] = [
395
+ {'topic': 'Artificial Intelligence', 'sentiment': 'very_positive', 'mentions': 1247},
396
+ {'topic': 'Interest Rates', 'sentiment': 'negative', 'mentions': 892},
397
+ {'topic': 'Climate Tech', 'sentiment': 'positive', 'mentions': 634},
398
+ {'topic': 'Crypto/Web3', 'sentiment': 'neutral', 'mentions': 456}
399
+ ]
400
+
401
+ return sentiment_data
402
+
403
+ except Exception as e:
404
+ return {'error': str(e)}
405
+
406
+ def get_funding_trends(self) -> Dict[str, Any]:
407
+ """Get venture funding trends and analysis."""
408
+ try:
409
+ # Simulated funding data (in production, would use PitchBook/Crunchbase APIs)
410
+ current_quarter = datetime.now().quarter
411
+ current_year = datetime.now().year
412
+
413
+ funding_data = {
414
+ 'global_funding': {
415
+ 'current_quarter': f"Q{current_quarter} {current_year}",
416
+ 'total_funding': 45.2, # Billions
417
+ 'deal_count': 3247,
418
+ 'avg_deal_size': 13.9, # Millions
419
+ 'qoq_change': -18.5 # Percent
420
+ },
421
+ 'stage_breakdown': {
422
+ 'seed': {'funding': 8.1, 'deals': 1456, 'avg_size': 5.6},
423
+ 'series_a': {'funding': 12.3, 'deals': 892, 'avg_size': 13.8},
424
+ 'series_b': {'funding': 15.7, 'deals': 534, 'avg_size': 29.4},
425
+ 'growth': {'funding': 9.1, 'deals': 365, 'avg_size': 24.9}
426
+ },
427
+ 'sector_leaders': [
428
+ {'sector': 'AI/ML', 'funding': 12.4, 'growth': 45.2},
429
+ {'sector': 'Fintech', 'funding': 8.7, 'growth': -12.3},
430
+ {'sector': 'Healthcare', 'funding': 7.9, 'growth': 8.1},
431
+ {'sector': 'Climate Tech', 'funding': 6.2, 'growth': 67.8}
432
+ ],
433
+ 'geographic_trends': {
434
+ 'north_america': {'share': 52.1, 'change': -2.3},
435
+ 'europe': {'share': 23.7, 'change': 1.8},
436
+ 'asia': {'share': 20.4, 'change': 0.7},
437
+ 'other': {'share': 3.8, 'change': -0.2}
438
+ },
439
+ 'key_insights': [
440
+ "AI/ML continues to dominate funding despite overall decline",
441
+ "Series A crunch continues with longer fundraising cycles",
442
+ "Climate tech showing resilience with increased investor interest",
443
+ "Valuation multiples compressed across all stages"
444
+ ],
445
+ 'timestamp': datetime.now().isoformat()
446
+ }
447
+
448
+ return funding_data
449
+
450
+ except Exception as e:
451
+ return {'error': str(e)}
452
+
453
+ def generate_market_report(self, sector: str, startup_metrics: Dict = None) -> Dict[str, Any]:
454
+ """Generate comprehensive market report."""
455
+ try:
456
+ # Gather all data
457
+ competitor_analysis = self.get_competitor_analysis(sector, startup_metrics)
458
+ economic_indicators = self.get_economic_indicators()
459
+ market_sentiment = self.get_market_sentiment(sector)
460
+ funding_trends = self.get_funding_trends()
461
+
462
+ # Generate executive summary
463
+ executive_summary = self._generate_executive_summary(
464
+ competitor_analysis, economic_indicators, market_sentiment, funding_trends
465
+ )
466
+
467
+ return {
468
+ 'executive_summary': executive_summary,
469
+ 'competitor_analysis': competitor_analysis,
470
+ 'economic_indicators': economic_indicators,
471
+ 'market_sentiment': market_sentiment,
472
+ 'funding_trends': funding_trends,
473
+ 'recommendations': self._generate_recommendations(
474
+ competitor_analysis, economic_indicators, market_sentiment
475
+ ),
476
+ 'generated_at': datetime.now().isoformat(),
477
+ 'sector': sector
478
+ }
479
+
480
+ except Exception as e:
481
+ return {'error': str(e)}
482
+
483
+ def _generate_executive_summary(self, competitor_analysis, economic_indicators,
484
+ market_sentiment, funding_trends) -> str:
485
+ """Generate executive summary of market conditions."""
486
+ summary_points = []
487
+
488
+ # Competitor landscape
489
+ if competitor_analysis.get('competitor_count', 0) > 0:
490
+ summary_points.append(
491
+ f"Analyzed {competitor_analysis['competitor_count']} public competitors "
492
+ f"in the {competitor_analysis.get('sector', 'target')} sector"
493
+ )
494
+
495
+ # Economic conditions
496
+ economic_summary = economic_indicators.get('summary', '')
497
+ if economic_summary:
498
+ summary_points.append(economic_summary)
499
+
500
+ # Market sentiment
501
+ sentiment = market_sentiment.get('overall_sentiment', 'neutral')
502
+ summary_points.append(f"Overall market sentiment is {sentiment}")
503
+
504
+ # Funding environment
505
+ funding_change = funding_trends.get('global_funding', {}).get('qoq_change', 0)
506
+ if funding_change < -10:
507
+ summary_points.append("Challenging funding environment with significant QoQ decline")
508
+ elif funding_change > 10:
509
+ summary_points.append("Strong funding environment with growing investor activity")
510
+ else:
511
+ summary_points.append("Stable funding environment with moderate activity")
512
+
513
+ return ". ".join(summary_points) + "."
514
+
515
+ def _generate_recommendations(self, competitor_analysis, economic_indicators, market_sentiment) -> List[str]:
516
+ """Generate strategic recommendations based on market data."""
517
+ recommendations = []
518
+
519
+ # Valuation recommendations
520
+ if competitor_analysis.get('valuation_multiples'):
521
+ ps_median = competitor_analysis['valuation_multiples'].get('price_to_sales', {}).get('median', 0)
522
+ if ps_median > 10:
523
+ recommendations.append("High sector valuations suggest premium positioning opportunity")
524
+ elif ps_median < 3:
525
+ recommendations.append("Conservative sector valuations require strong fundamentals focus")
526
+
527
+ # Market timing
528
+ sentiment = market_sentiment.get('overall_sentiment', 'neutral')
529
+ if sentiment == 'positive':
530
+ recommendations.append("Favorable sentiment window for market entry and fundraising")
531
+ elif sentiment == 'negative':
532
+ recommendations.append("Consider defensive positioning and extended runway planning")
533
+
534
+ # Economic environment
535
+ economic_summary = economic_indicators.get('summary', '')
536
+ if 'challenging' in economic_summary.lower():
537
+ recommendations.append("Focus on unit economics and path to profitability")
538
+ recommendations.append("Consider strategic partnerships to reduce capital requirements")
539
+
540
+ return recommendations if recommendations else ["Monitor market conditions closely for optimal timing"]
541
+
542
+ def get_stock_data(self, symbol: str, period: str = "1y") -> Dict[str, Any]:
543
+ """
544
+ Get comprehensive stock data for a specific company.
545
+
546
+ Args:
547
+ symbol (str): Stock symbol (e.g., 'AAPL', 'MSFT') or company name
548
+ period (str): Time period ('1d', '5d', '1mo', '3mo', '6mo', '1y', '2y', '5y', '10y', 'ytd', 'max')
549
+
550
+ Returns:
551
+ Dict containing stock data, financial metrics, and analysis
552
+ """
553
+ try:
554
+ # Convert company name to symbol if needed
555
+ symbol = self._resolve_symbol(symbol)
556
+
557
+ # Get stock object
558
+ stock = yf.Ticker(symbol)
559
+
560
+ # Get basic info
561
+ info = stock.info
562
+
563
+ # Get historical data
564
+ hist = stock.history(period=period)
565
+
566
+ if hist.empty:
567
+ return {"error": f"No data found for symbol: {symbol}"}
568
+
569
+ # Calculate key metrics
570
+ current_price = hist['Close'].iloc[-1]
571
+ previous_close = info.get('previousClose', hist['Close'].iloc[-2])
572
+ price_change = current_price - previous_close
573
+ price_change_pct = (price_change / previous_close) * 100
574
+
575
+ # Get volume data
576
+ avg_volume = hist['Volume'].mean()
577
+ current_volume = hist['Volume'].iloc[-1]
578
+ volume_ratio = current_volume / avg_volume if avg_volume > 0 else 0
579
+
580
+ # Calculate volatility (standard deviation of returns)
581
+ returns = hist['Close'].pct_change().dropna()
582
+ volatility = returns.std() * np.sqrt(252) # Annualized volatility
583
+
584
+ # Calculate moving averages
585
+ ma_50 = hist['Close'].rolling(window=50).mean().iloc[-1] if len(hist) >= 50 else None
586
+ ma_200 = hist['Close'].rolling(window=200).mean().iloc[-1] if len(hist) >= 200 else None
587
+
588
+ # Calculate RSI (Relative Strength Index)
589
+ rsi = self._calculate_rsi(hist['Close'])
590
+
591
+ # Get financial ratios from info
592
+ pe_ratio = info.get('trailingPE')
593
+ pb_ratio = info.get('priceToBook')
594
+ debt_to_equity = info.get('debtToEquity')
595
+ roe = info.get('returnOnEquity')
596
+
597
+ # Get market cap and other key metrics
598
+ market_cap = info.get('marketCap')
599
+ enterprise_value = info.get('enterpriseValue')
600
+ revenue_growth = info.get('revenueGrowth')
601
+ profit_margins = info.get('profitMargins')
602
+
603
+ result = {
604
+ "symbol": symbol,
605
+ "company_name": info.get('longName', symbol),
606
+ "sector": info.get('sector', 'Unknown'),
607
+ "industry": info.get('industry', 'Unknown'),
608
+ "current_data": {
609
+ "price": round(current_price, 2),
610
+ "change": round(price_change, 2),
611
+ "change_percent": round(price_change_pct, 2),
612
+ "volume": int(current_volume),
613
+ "volume_ratio": round(volume_ratio, 2),
614
+ "market_cap": market_cap,
615
+ "last_updated": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
616
+ },
617
+ "technical_analysis": {
618
+ "volatility_annual": round(volatility * 100, 2) if not np.isnan(volatility) else None,
619
+ "rsi": round(rsi, 2) if rsi else None,
620
+ "ma_50": round(ma_50, 2) if ma_50 and not np.isnan(ma_50) else None,
621
+ "ma_200": round(ma_200, 2) if ma_200 and not np.isnan(ma_200) else None,
622
+ "trend": self._determine_trend(current_price, ma_50, ma_200)
623
+ },
624
+ "fundamental_metrics": {
625
+ "pe_ratio": round(pe_ratio, 2) if pe_ratio else None,
626
+ "pb_ratio": round(pb_ratio, 2) if pb_ratio else None,
627
+ "debt_to_equity": round(debt_to_equity, 2) if debt_to_equity else None,
628
+ "roe": round(roe * 100, 2) if roe else None,
629
+ "revenue_growth": round(revenue_growth * 100, 2) if revenue_growth else None,
630
+ "profit_margins": round(profit_margins * 100, 2) if profit_margins else None,
631
+ "enterprise_value": enterprise_value
632
+ },
633
+ "historical_data": {
634
+ "period": period,
635
+ "data_points": len(hist),
636
+ "52_week_high": round(hist['High'].max(), 2),
637
+ "52_week_low": round(hist['Low'].min(), 2),
638
+ "avg_volume": int(avg_volume)
639
+ },
640
+ "investment_analysis": self._generate_investment_analysis(
641
+ current_price, ma_50, ma_200, rsi, pe_ratio, volatility, info
642
+ )
643
+ }
644
+
645
+ return result
646
+
647
+ except Exception as e:
648
+ return {"error": f"Failed to get stock data for {symbol}: {str(e)}"}
649
+
650
+ def compare_stocks(self, symbols: List[str], period: str = "6mo") -> Dict[str, Any]:
651
+ """
652
+ Compare multiple stocks side by side.
653
+
654
+ Args:
655
+ symbols: List of stock symbols to compare
656
+ period: Time period for comparison
657
+
658
+ Returns:
659
+ Dict containing comparative analysis
660
+ """
661
+ try:
662
+ comparison_data = {}
663
+
664
+ for symbol in symbols[:5]: # Limit to 5 stocks for performance
665
+ stock_data = self.get_stock_data(symbol, period)
666
+ if "error" not in stock_data:
667
+ comparison_data[symbol] = stock_data
668
+
669
+ if not comparison_data:
670
+ return {"error": "No valid stock data found for comparison"}
671
+
672
+ # Calculate comparative metrics
673
+ performance_comparison = {}
674
+ volatility_comparison = {}
675
+ valuation_comparison = {}
676
+
677
+ for symbol, data in comparison_data.items():
678
+ # Performance (price change %)
679
+ performance_comparison[symbol] = data["current_data"]["change_percent"]
680
+
681
+ # Volatility
682
+ vol = data["technical_analysis"]["volatility_annual"]
683
+ if vol:
684
+ volatility_comparison[symbol] = vol
685
+
686
+ # PE Ratio for valuation
687
+ pe = data["fundamental_metrics"]["pe_ratio"]
688
+ if pe:
689
+ valuation_comparison[symbol] = pe
690
+
691
+ # Find best/worst performers
692
+ best_performer = max(performance_comparison.items(), key=lambda x: x[1]) if performance_comparison else None
693
+ worst_performer = min(performance_comparison.items(), key=lambda x: x[1]) if performance_comparison else None
694
+
695
+ return {
696
+ "symbols_analyzed": list(comparison_data.keys()),
697
+ "comparison_summary": {
698
+ "best_performer": best_performer,
699
+ "worst_performer": worst_performer,
700
+ "avg_performance": round(np.mean(list(performance_comparison.values())), 2) if performance_comparison else None,
701
+ "avg_volatility": round(np.mean(list(volatility_comparison.values())), 2) if volatility_comparison else None
702
+ },
703
+ "detailed_data": comparison_data,
704
+ "analysis_date": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
705
+ }
706
+
707
+ except Exception as e:
708
+ return {"error": f"Failed to compare stocks: {str(e)}"}
709
+
710
+ def _resolve_symbol(self, input_symbol: str) -> str:
711
+ """Convert company name to stock symbol if needed."""
712
+ input_lower = input_symbol.lower().strip()
713
+
714
+ # Check if it's already a valid symbol format (all caps, 1-5 characters)
715
+ if input_symbol.isupper() and 1 <= len(input_symbol) <= 5:
716
+ return input_symbol
717
+
718
+ # Check common symbols mapping
719
+ if input_lower in self.common_symbols:
720
+ return self.common_symbols[input_lower]
721
+
722
+ # Check if it contains a company name
723
+ for name, symbol in self.common_symbols.items():
724
+ if name in input_lower or input_lower in name:
725
+ return symbol
726
+
727
+ # If not found, assume it's a symbol and convert to uppercase
728
+ return input_symbol.upper()
729
+
730
+ def _calculate_rsi(self, prices: pd.Series, period: int = 14) -> float:
731
+ """Calculate Relative Strength Index."""
732
+ try:
733
+ delta = prices.diff()
734
+ gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
735
+ loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
736
+ rs = gain / loss
737
+ rsi = 100 - (100 / (1 + rs))
738
+ return rsi.iloc[-1]
739
+ except:
740
+ return None
741
+
742
+ def _determine_trend(self, current_price: float, ma_50: float, ma_200: float) -> str:
743
+ """Determine price trend based on moving averages."""
744
+ try:
745
+ if not ma_50 or not ma_200:
746
+ return "Unknown"
747
+
748
+ if current_price > ma_50 > ma_200:
749
+ return "Strong Uptrend"
750
+ elif current_price > ma_50 and ma_50 < ma_200:
751
+ return "Weak Uptrend"
752
+ elif current_price < ma_50 < ma_200:
753
+ return "Strong Downtrend"
754
+ elif current_price < ma_50 and ma_50 > ma_200:
755
+ return "Weak Downtrend"
756
+ else:
757
+ return "Sideways"
758
+ except:
759
+ return "Unknown"
760
+
761
+ def _generate_investment_analysis(self, price: float, ma_50: float, ma_200: float,
762
+ rsi: float, pe: float, volatility: float, info: dict) -> str:
763
+ """Generate basic investment analysis summary."""
764
+ try:
765
+ analysis_points = []
766
+
767
+ # Technical analysis
768
+ if rsi:
769
+ if rsi > 70:
770
+ analysis_points.append("Technically overbought (RSI > 70)")
771
+ elif rsi < 30:
772
+ analysis_points.append("Technically oversold (RSI < 30)")
773
+ else:
774
+ analysis_points.append("RSI in neutral range")
775
+
776
+ # Trend analysis
777
+ if ma_50 and ma_200:
778
+ if price > ma_50 > ma_200:
779
+ analysis_points.append("Strong upward trend")
780
+ elif price < ma_50 < ma_200:
781
+ analysis_points.append("Strong downward trend")
782
+
783
+ # Valuation
784
+ if pe:
785
+ if pe > 25:
786
+ analysis_points.append("High P/E ratio - potentially overvalued")
787
+ elif pe < 15:
788
+ analysis_points.append("Low P/E ratio - potentially undervalued")
789
+ else:
790
+ analysis_points.append("Moderate P/E ratio")
791
+
792
+ # Volatility
793
+ if volatility:
794
+ if volatility > 0.4:
795
+ analysis_points.append("High volatility - risky investment")
796
+ elif volatility < 0.2:
797
+ analysis_points.append("Low volatility - stable investment")
798
+
799
+ return " | ".join(analysis_points) if analysis_points else "Analysis unavailable"
800
+
801
+ except:
802
+ return "Analysis unavailable"
803
+
804
+
805
+ # Export the class
806
+ __all__ = ['FinancialDataIntegrator']