Add dividend trend service, documentation, and requirements backup
This commit is contained in:
parent
156b218c94
commit
7db493893e
260
ETF_Portal/services/dividend_trend_service.py
Normal file
260
ETF_Portal/services/dividend_trend_service.py
Normal file
@ -0,0 +1,260 @@
|
||||
from typing import Dict, Tuple, List, Optional
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from scipy import stats
|
||||
from sklearn.ensemble import RandomForestRegressor
|
||||
from statsmodels.tsa.arima.model import ARIMA
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class DividendTrendService:
|
||||
"""Service for calculating comprehensive dividend trend analysis"""
|
||||
|
||||
def __init__(self):
|
||||
self.min_history_years = 5
|
||||
self.required_data_points = 60 # 5 years of monthly data
|
||||
|
||||
def calculate_dividend_trend(self, etf_data: Dict) -> Dict:
|
||||
"""
|
||||
Calculate comprehensive dividend trend analysis
|
||||
|
||||
Args:
|
||||
etf_data: Dictionary containing ETF data including:
|
||||
- dividends: List of historical dividend payments
|
||||
- prices: List of historical prices
|
||||
- metadata: ETF metadata (age, type, etc.)
|
||||
|
||||
Returns:
|
||||
Dictionary containing comprehensive dividend trend analysis
|
||||
"""
|
||||
try:
|
||||
# 1. Basic Data Validation
|
||||
if not self._validate_data(etf_data):
|
||||
return self._create_error_response("Insufficient historical data")
|
||||
|
||||
# 2. Calculate Theoretical Models
|
||||
gordon_growth = self._calculate_gordon_growth(etf_data)
|
||||
ddm_value = self._calculate_ddm(etf_data)
|
||||
|
||||
# 3. Calculate Empirical Metrics
|
||||
empirical_metrics = self._calculate_empirical_metrics(etf_data)
|
||||
|
||||
# 4. Calculate Risk Metrics
|
||||
risk_metrics = self._calculate_risk_metrics(etf_data)
|
||||
|
||||
# 5. Generate ML Predictions
|
||||
ml_predictions = self._generate_ml_predictions(etf_data)
|
||||
|
||||
return {
|
||||
'gordon_growth': gordon_growth,
|
||||
'ddm_value': ddm_value,
|
||||
'empirical_metrics': empirical_metrics,
|
||||
'risk_metrics': risk_metrics,
|
||||
'ml_predictions': ml_predictions
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error calculating dividend trend: {str(e)}")
|
||||
return self._create_error_response(str(e))
|
||||
|
||||
def _validate_data(self, etf_data: Dict) -> bool:
|
||||
"""Validate that we have sufficient historical data"""
|
||||
if not etf_data.get('dividends') or not etf_data.get('prices'):
|
||||
return False
|
||||
|
||||
dividends = pd.Series(etf_data['dividends'])
|
||||
if len(dividends) < self.required_data_points:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _calculate_gordon_growth(self, etf_data: Dict) -> float:
|
||||
"""Calculate growth rate using Gordon Growth Model"""
|
||||
try:
|
||||
dividends = pd.Series(etf_data['dividends'])
|
||||
prices = pd.Series(etf_data['prices'])
|
||||
|
||||
# Calculate required rate of return (r)
|
||||
returns = prices.pct_change().dropna()
|
||||
r = returns.mean() * 12 # Annualized
|
||||
|
||||
# Calculate current dividend (D)
|
||||
D = dividends.iloc[-1]
|
||||
|
||||
# Calculate current price (P)
|
||||
P = prices.iloc[-1]
|
||||
|
||||
# Solve for g: P = D/(r-g)
|
||||
g = r - (D/P)
|
||||
|
||||
return g
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in Gordon Growth calculation: {str(e)}")
|
||||
return 0.0
|
||||
|
||||
def _calculate_ddm(self, etf_data: Dict) -> float:
|
||||
"""Calculate value using Dividend Discount Model"""
|
||||
try:
|
||||
dividends = pd.Series(etf_data['dividends'])
|
||||
|
||||
# Calculate growth rate
|
||||
growth_rate = self._calculate_growth_rate(dividends)
|
||||
|
||||
# Calculate discount rate (using CAPM)
|
||||
discount_rate = self._calculate_discount_rate(etf_data)
|
||||
|
||||
# Calculate terminal value
|
||||
terminal_value = self._calculate_terminal_value(dividends, growth_rate, discount_rate)
|
||||
|
||||
# Calculate present value of dividends
|
||||
present_value = self._calculate_present_value(dividends, discount_rate)
|
||||
|
||||
return present_value + terminal_value
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in DDM calculation: {str(e)}")
|
||||
return 0.0
|
||||
|
||||
def _calculate_empirical_metrics(self, etf_data: Dict) -> Dict:
|
||||
"""Calculate empirical metrics from historical data"""
|
||||
try:
|
||||
dividends = pd.Series(etf_data['dividends'])
|
||||
|
||||
# Calculate rolling growth
|
||||
rolling_growth = self._calculate_rolling_growth(dividends)
|
||||
|
||||
# Calculate volatility
|
||||
volatility = self._calculate_volatility(dividends)
|
||||
|
||||
# Calculate autocorrelation
|
||||
autocorrelation = self._calculate_autocorrelation(dividends)
|
||||
|
||||
return {
|
||||
'rolling_growth': rolling_growth,
|
||||
'volatility': volatility,
|
||||
'autocorrelation': autocorrelation
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error calculating empirical metrics: {str(e)}")
|
||||
return {}
|
||||
|
||||
def _calculate_risk_metrics(self, etf_data: Dict) -> Dict:
|
||||
"""Calculate risk metrics"""
|
||||
try:
|
||||
dividends = pd.Series(etf_data['dividends'])
|
||||
prices = pd.Series(etf_data['prices'])
|
||||
|
||||
# Calculate coverage ratio
|
||||
coverage_ratio = self._calculate_coverage_ratio(etf_data)
|
||||
|
||||
# Calculate payout ratio
|
||||
payout_ratio = self._calculate_payout_ratio(etf_data)
|
||||
|
||||
# Calculate market correlation
|
||||
market_correlation = self._calculate_market_correlation(etf_data)
|
||||
|
||||
return {
|
||||
'coverage_ratio': coverage_ratio,
|
||||
'payout_ratio': payout_ratio,
|
||||
'market_correlation': market_correlation
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error calculating risk metrics: {str(e)}")
|
||||
return {}
|
||||
|
||||
def _generate_ml_predictions(self, etf_data: Dict) -> Dict:
|
||||
"""Generate machine learning predictions"""
|
||||
try:
|
||||
dividends = pd.Series(etf_data['dividends'])
|
||||
|
||||
# Generate time series forecast
|
||||
forecast = self._generate_time_series_forecast(dividends)
|
||||
|
||||
# Calculate confidence interval
|
||||
confidence_interval = self._calculate_confidence_interval(forecast)
|
||||
|
||||
return {
|
||||
'next_year_growth': forecast,
|
||||
'confidence_interval': confidence_interval
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error generating ML predictions: {str(e)}")
|
||||
return {}
|
||||
|
||||
def _create_error_response(self, error_message: str) -> Dict:
|
||||
"""Create a standardized error response"""
|
||||
return {
|
||||
'error': error_message,
|
||||
'gordon_growth': 0.0,
|
||||
'ddm_value': 0.0,
|
||||
'empirical_metrics': {},
|
||||
'risk_metrics': {},
|
||||
'ml_predictions': {}
|
||||
}
|
||||
|
||||
# Helper methods for specific calculations
|
||||
def _calculate_growth_rate(self, dividends: pd.Series) -> float:
|
||||
"""Calculate dividend growth rate"""
|
||||
return dividends.pct_change().mean() * 12 # Annualized
|
||||
|
||||
def _calculate_discount_rate(self, etf_data: Dict) -> float:
|
||||
"""Calculate discount rate using CAPM"""
|
||||
# Implementation needed
|
||||
return 0.1 # Placeholder
|
||||
|
||||
def _calculate_terminal_value(self, dividends: pd.Series, growth_rate: float, discount_rate: float) -> float:
|
||||
"""Calculate terminal value for DDM"""
|
||||
# Implementation needed
|
||||
return 0.0 # Placeholder
|
||||
|
||||
def _calculate_present_value(self, dividends: pd.Series, discount_rate: float) -> float:
|
||||
"""Calculate present value of dividends"""
|
||||
# Implementation needed
|
||||
return 0.0 # Placeholder
|
||||
|
||||
def _calculate_rolling_growth(self, dividends: pd.Series) -> float:
|
||||
"""Calculate rolling 12-month growth rate"""
|
||||
return dividends.pct_change(12).mean()
|
||||
|
||||
def _calculate_volatility(self, dividends: pd.Series) -> float:
|
||||
"""Calculate dividend volatility"""
|
||||
return dividends.pct_change().std() * np.sqrt(12) # Annualized
|
||||
|
||||
def _calculate_autocorrelation(self, dividends: pd.Series) -> float:
|
||||
"""Calculate autocorrelation of dividend payments"""
|
||||
return dividends.autocorr()
|
||||
|
||||
def _calculate_coverage_ratio(self, etf_data: Dict) -> float:
|
||||
"""Calculate dividend coverage ratio"""
|
||||
# Implementation needed
|
||||
return 0.0 # Placeholder
|
||||
|
||||
def _calculate_payout_ratio(self, etf_data: Dict) -> float:
|
||||
"""Calculate payout ratio"""
|
||||
# Implementation needed
|
||||
return 0.0 # Placeholder
|
||||
|
||||
def _calculate_market_correlation(self, etf_data: Dict) -> float:
|
||||
"""Calculate correlation with market returns"""
|
||||
# Implementation needed
|
||||
return 0.0 # Placeholder
|
||||
|
||||
def _generate_time_series_forecast(self, dividends: pd.Series) -> float:
|
||||
"""Generate time series forecast using ARIMA"""
|
||||
try:
|
||||
model = ARIMA(dividends, order=(1,1,1))
|
||||
model_fit = model.fit()
|
||||
forecast = model_fit.forecast(steps=12)
|
||||
return forecast.mean()
|
||||
except:
|
||||
return 0.0
|
||||
|
||||
def _calculate_confidence_interval(self, forecast: float) -> Tuple[float, float]:
|
||||
"""Calculate confidence interval for forecast"""
|
||||
# Implementation needed
|
||||
return (0.0, 0.0) # Placeholder
|
||||
162
docs/dividend_trend_analysis.md
Normal file
162
docs/dividend_trend_analysis.md
Normal file
@ -0,0 +1,162 @@
|
||||
# Dividend Trend Analysis Framework
|
||||
|
||||
## 1. Theoretical Foundations
|
||||
|
||||
### 1.1 Gordon Growth Model
|
||||
- Basic formula: P = D/(r-g)
|
||||
- Where:
|
||||
- P = Price
|
||||
- D = Dividend
|
||||
- r = Required rate of return
|
||||
- g = Growth rate
|
||||
- Application: Use to validate dividend sustainability
|
||||
|
||||
### 1.2 Dividend Discount Model (DDM)
|
||||
- Multi-stage DDM for ETFs with varying growth phases
|
||||
- Terminal value calculation using industry averages
|
||||
- Sensitivity analysis for different growth scenarios
|
||||
|
||||
### 1.3 Modern Portfolio Theory (MPT)
|
||||
- Dividend yield as a risk factor
|
||||
- Correlation with market returns
|
||||
- Beta calculation specific to dividend-paying securities
|
||||
|
||||
## 2. Empirical Analysis Framework
|
||||
|
||||
### 2.1 Historical Data Analysis
|
||||
- Rolling 12-month dividend growth rates
|
||||
- Year-over-year comparisons
|
||||
- Seasonality analysis
|
||||
- Maximum drawdown during dividend cuts
|
||||
|
||||
### 2.2 Statistical Measures
|
||||
- Mean reversion analysis
|
||||
- Volatility clustering
|
||||
- Autocorrelation of dividend payments
|
||||
- Skewness and kurtosis of dividend distributions
|
||||
|
||||
### 2.3 Machine Learning Components
|
||||
- Time series forecasting (ARIMA/SARIMA)
|
||||
- Random Forest for feature importance
|
||||
- Gradient Boosting for non-linear relationships
|
||||
- Clustering for similar ETF behavior
|
||||
|
||||
## 3. Risk Assessment Framework
|
||||
|
||||
### 3.1 Quantitative Risk Metrics
|
||||
- Dividend Coverage Ratio
|
||||
- Payout Ratio
|
||||
- Free Cash Flow to Dividend Ratio
|
||||
- Interest Coverage Ratio
|
||||
|
||||
### 3.2 Market Risk Factors
|
||||
- Interest Rate Sensitivity
|
||||
- Credit Spread Impact
|
||||
- Market Volatility Correlation
|
||||
- Sector-Specific Risks
|
||||
|
||||
### 3.3 Structural Risk Analysis
|
||||
- ETF Structure (Physical vs Synthetic)
|
||||
- Tracking Error
|
||||
- Liquidity Risk
|
||||
- Counterparty Risk
|
||||
|
||||
## 4. Implementation Guidelines
|
||||
|
||||
### 4.1 Data Requirements
|
||||
- Minimum 5 years of historical data
|
||||
- Monthly dividend payments
|
||||
- NAV/Price history
|
||||
- Trading volume
|
||||
- AUM (Assets Under Management)
|
||||
|
||||
### 4.2 Calculation Methodology
|
||||
```python
|
||||
def calculate_dividend_trend(etf_data: Dict) -> Dict:
|
||||
"""
|
||||
Calculate comprehensive dividend trend analysis
|
||||
|
||||
Returns:
|
||||
{
|
||||
'gordon_growth': float, # Growth rate from Gordon model
|
||||
'ddm_value': float, # Value from DDM
|
||||
'empirical_metrics': {
|
||||
'rolling_growth': float,
|
||||
'volatility': float,
|
||||
'autocorrelation': float
|
||||
},
|
||||
'risk_metrics': {
|
||||
'coverage_ratio': float,
|
||||
'payout_ratio': float,
|
||||
'market_correlation': float
|
||||
},
|
||||
'ml_predictions': {
|
||||
'next_year_growth': float,
|
||||
'confidence_interval': Tuple[float, float]
|
||||
}
|
||||
}
|
||||
"""
|
||||
pass
|
||||
```
|
||||
|
||||
### 4.3 Validation Framework
|
||||
- Backtesting against historical data
|
||||
- Cross-validation with similar ETFs
|
||||
- Stress testing under market conditions
|
||||
- Sensitivity analysis of parameters
|
||||
|
||||
## 5. Practical Considerations
|
||||
|
||||
### 5.1 ETF-Specific Adjustments
|
||||
- New ETFs (< 2 years): Use peer comparison
|
||||
- Established ETFs: Focus on historical patterns
|
||||
- Sector ETFs: Consider industry cycles
|
||||
- Global ETFs: Account for currency effects
|
||||
|
||||
### 5.2 Market Conditions
|
||||
- Interest rate environment
|
||||
- Economic cycle position
|
||||
- Sector rotation impact
|
||||
- Market sentiment indicators
|
||||
|
||||
### 5.3 Reporting Standards
|
||||
- Clear confidence intervals
|
||||
- Multiple scenario analysis
|
||||
- Risk factor decomposition
|
||||
- Historical comparison benchmarks
|
||||
|
||||
## 6. Continuous Improvement
|
||||
|
||||
### 6.1 Performance Monitoring
|
||||
- Track prediction accuracy
|
||||
- Monitor model drift
|
||||
- Update parameters quarterly
|
||||
- Validate against new data
|
||||
|
||||
### 6.2 Model Updates
|
||||
- Incorporate new market data
|
||||
- Adjust for structural changes
|
||||
- Update peer comparisons
|
||||
- Refine risk parameters
|
||||
|
||||
## 7. Implementation Roadmap
|
||||
|
||||
1. Phase 1: Basic Implementation
|
||||
- Gordon Growth Model
|
||||
- Historical trend analysis
|
||||
- Basic risk metrics
|
||||
|
||||
2. Phase 2: Advanced Features
|
||||
- Machine Learning components
|
||||
- Market risk factors
|
||||
- Structural analysis
|
||||
|
||||
3. Phase 3: Optimization
|
||||
- Parameter tuning
|
||||
- Performance validation
|
||||
- Reporting improvements
|
||||
|
||||
4. Phase 4: Maintenance
|
||||
- Regular updates
|
||||
- Performance monitoring
|
||||
- Model refinement
|
||||
20
requirements.txt.backup
Normal file
20
requirements.txt.backup
Normal file
@ -0,0 +1,20 @@
|
||||
openai
|
||||
langchain
|
||||
langchain_openai
|
||||
chromadb
|
||||
docx2txt
|
||||
pypdf
|
||||
streamlit>=1.28.0
|
||||
tiktoken
|
||||
pdfkit
|
||||
pandas>=1.5.3
|
||||
numpy>=1.24.3
|
||||
matplotlib>=3.7.1
|
||||
seaborn>=0.12.2
|
||||
fmp-python>=0.1.5
|
||||
plotly>=5.14.1
|
||||
requests>=2.31.0
|
||||
reportlab>=3.6.13
|
||||
psutil>=5.9.0
|
||||
click>=8.1.0
|
||||
yfinance>=0.2.36
|
||||
Loading…
Reference in New Issue
Block a user