Spaces:
Runtime error
Runtime error
Tonic commited on
Commit ·
8481b42
1
Parent(s): e115d61
attempt more advanced predictions using model ensemble
Browse files- README.md +116 -132
- app.py +732 -43
- requirements.txt +5 -1
README.md
CHANGED
|
@@ -13,158 +13,142 @@ tags:
|
|
| 13 |
- mcp-server-track
|
| 14 |
---
|
| 15 |
|
| 16 |
-
# Stock
|
| 17 |
|
| 18 |
-
A comprehensive stock
|
| 19 |
|
| 20 |
## Features
|
| 21 |
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
- **
|
| 27 |
-
- RSI (Relative Strength Index)
|
| 28 |
-
- MACD (Moving Average Convergence Divergence)
|
| 29 |
-
- Bollinger Bands
|
| 30 |
-
- Simple Moving Averages (20, 50, and 200-day)
|
| 31 |
-
|
| 32 |
-
- **Trading Signals**:
|
| 33 |
-
- Buy/Sell recommendations based on multiple indicators
|
| 34 |
-
- Overall trading signal combining all indicators
|
| 35 |
-
- Confidence intervals for predictions
|
| 36 |
-
|
| 37 |
-
- **Interactive Visualizations**:
|
| 38 |
-
- Price prediction with confidence intervals
|
| 39 |
-
- Technical indicators overlay
|
| 40 |
-
- Volume analysis
|
| 41 |
-
- Historical price trends
|
| 42 |
-
|
| 43 |
-
- **Structured Product Analysis**:
|
| 44 |
-
- Extended prediction horizons (up to 1 year)
|
| 45 |
-
- Historical analysis up to 10 years
|
| 46 |
-
- Comprehensive risk metrics
|
| 47 |
-
- Sector and industry analysis
|
| 48 |
-
- Liquidity assessment
|
| 49 |
-
|
| 50 |
-
## Structured Product Features
|
| 51 |
-
|
| 52 |
-
### Extended Time Horizons
|
| 53 |
-
- Prediction window up to 365 days
|
| 54 |
-
- Historical data analysis up to 10 years
|
| 55 |
-
- Long-term trend analysis
|
| 56 |
-
- Extended technical indicators
|
| 57 |
-
|
| 58 |
-
### Risk Analysis
|
| 59 |
-
- Annualized volatility
|
| 60 |
-
- Maximum drawdown analysis
|
| 61 |
-
- Current drawdown tracking
|
| 62 |
-
- Sharpe and Sortino ratios
|
| 63 |
-
- Risk-adjusted return metrics
|
| 64 |
-
|
| 65 |
-
### Product Metrics
|
| 66 |
-
- Market capitalization
|
| 67 |
-
- Sector and industry classification
|
| 68 |
-
- Dividend yield analysis
|
| 69 |
-
- Volume metrics
|
| 70 |
-
- Liquidity scoring
|
| 71 |
-
|
| 72 |
-
### Sector Analysis
|
| 73 |
-
- Market cap ranking (Large/Mid/Small)
|
| 74 |
-
- Sector exposure
|
| 75 |
-
- Industry classification
|
| 76 |
-
- Liquidity assessment
|
| 77 |
|
| 78 |
-
##
|
| 79 |
|
| 80 |
-
1.
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
```
|
| 85 |
|
| 86 |
-
2.
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
|
| 92 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
```bash
|
| 94 |
pip install -r requirements.txt
|
| 95 |
```
|
| 96 |
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
1. Start the Gradio demo:
|
| 100 |
```bash
|
| 101 |
python app.py
|
| 102 |
```
|
| 103 |
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
3. Enter a stock symbol (e.g., AAPL, GOOGL, MSFT) and select your desired parameters:
|
| 107 |
-
- Timeframe (1d, 1h, 15m)
|
| 108 |
-
- Number of days to predict (up to 365 days)
|
| 109 |
-
- Historical lookback period (up to 10 years)
|
| 110 |
-
- Prediction strategy (Chronos or Technical)
|
| 111 |
-
|
| 112 |
-
4. Click "Analyze Stock" to get:
|
| 113 |
-
- Price predictions and trading signals
|
| 114 |
-
- Structured product metrics
|
| 115 |
-
- Risk analysis
|
| 116 |
-
- Sector analysis
|
| 117 |
-
|
| 118 |
-
## Using for Structured Products
|
| 119 |
-
|
| 120 |
-
### Initial Screening
|
| 121 |
-
1. Use extended lookback period (up to 10 years) for long-term performance analysis
|
| 122 |
-
2. Look for stocks with stable volatility and good risk-adjusted returns
|
| 123 |
-
3. Check liquidity scores for trading feasibility
|
| 124 |
-
|
| 125 |
-
### Risk Assessment
|
| 126 |
-
1. Review risk metrics to match client risk profile
|
| 127 |
-
2. Analyze maximum drawdowns for worst-case scenarios
|
| 128 |
-
3. Compare risk-adjusted returns using Sharpe and Sortino ratios
|
| 129 |
-
|
| 130 |
-
### Product Structuring
|
| 131 |
-
1. Use prediction horizon (up to 1 year) for product maturity design
|
| 132 |
-
2. Consider dividend yields for income-generating products
|
| 133 |
-
3. Use sector analysis for proper diversification
|
| 134 |
-
|
| 135 |
-
### Portfolio Construction
|
| 136 |
-
1. Analyze multiple stocks for diversified bundles
|
| 137 |
-
2. Use sector metrics to avoid overexposure
|
| 138 |
-
3. Consider market cap rankings for appropriate sizing
|
| 139 |
-
|
| 140 |
-
## Prediction Strategies
|
| 141 |
-
|
| 142 |
-
### Chronos Strategy
|
| 143 |
-
Uses Amazon's Chronos model for ML-based price prediction. This strategy:
|
| 144 |
-
- Analyzes historical price patterns
|
| 145 |
-
- Generates probabilistic forecasts
|
| 146 |
-
- Provides confidence intervals
|
| 147 |
-
|
| 148 |
-
### Technical Strategy
|
| 149 |
-
Uses traditional technical analysis indicators to generate predictions:
|
| 150 |
-
- RSI for overbought/oversold conditions
|
| 151 |
-
- MACD for trend direction
|
| 152 |
-
- Bollinger Bands for volatility
|
| 153 |
-
- Moving Averages for trend confirmation
|
| 154 |
-
|
| 155 |
-
## Trading Signals
|
| 156 |
-
|
| 157 |
-
The demo provides trading signals based on multiple technical indicators:
|
| 158 |
-
- RSI: Oversold (<30), Overbought (>70), Neutral
|
| 159 |
-
- MACD: Buy (MACD > Signal), Sell (MACD < Signal)
|
| 160 |
-
- Bollinger Bands: Buy (price < lower band), Sell (price > upper band)
|
| 161 |
-
- SMA: Buy (20-day > 50-day), Sell (20-day < 50-day)
|
| 162 |
|
| 163 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
|
| 165 |
## Contributing
|
| 166 |
|
| 167 |
-
Contributions are welcome! Please feel free to submit
|
| 168 |
|
| 169 |
## License
|
| 170 |
|
|
|
|
| 13 |
- mcp-server-track
|
| 14 |
---
|
| 15 |
|
| 16 |
+
# Advanced Stock Prediction Analysis
|
| 17 |
|
| 18 |
+
A comprehensive stock prediction and analysis tool that combines Chronos forecasting with advanced features including regime detection, ensemble methods, and stress testing.
|
| 19 |
|
| 20 |
## Features
|
| 21 |
|
| 22 |
+
### Core Prediction Engine
|
| 23 |
+
- **Chronos Forecasting**: State-of-the-art time series forecasting using Amazon's Chronos model
|
| 24 |
+
- **Technical Analysis**: Traditional technical indicators (RSI, MACD, Bollinger Bands, SMA)
|
| 25 |
+
- **Multi-timeframe Support**: Daily, hourly, and 15-minute analysis
|
| 26 |
+
- **Real-time Data**: Live market data via yfinance
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
|
| 28 |
+
### Advanced Features
|
| 29 |
|
| 30 |
+
#### 1. Market Regime Detection
|
| 31 |
+
- **Hidden Markov Models (HMM)**: Automatic detection of market regimes (bull, bear, sideways)
|
| 32 |
+
- **Volatility-based Fallback**: Simplified regime detection when HMM is unavailable
|
| 33 |
+
- **Regime-adjusted Signals**: Trading signals that adapt to current market conditions
|
|
|
|
| 34 |
|
| 35 |
+
#### 2. Ensemble Methods
|
| 36 |
+
- **Multi-model Combination**: Combines Chronos, technical, and statistical predictions
|
| 37 |
+
- **Adaptive Weighting**: User-configurable weights for different models
|
| 38 |
+
- **Uncertainty Quantification**: Advanced uncertainty estimation with skewness adjustment
|
| 39 |
+
|
| 40 |
+
#### 3. Advanced Risk Metrics
|
| 41 |
+
- **Tail Risk Analysis**: VaR and CVaR calculations
|
| 42 |
+
- **Market Correlation**: Beta, alpha, and correlation with market indices
|
| 43 |
+
- **Risk-adjusted Returns**: Sharpe, Sortino, and Calmar ratios
|
| 44 |
+
- **Drawdown Analysis**: Maximum drawdown and recovery metrics
|
| 45 |
+
|
| 46 |
+
#### 4. Stress Testing
|
| 47 |
+
- **Scenario Analysis**: Market crash, high volatility, bull market scenarios
|
| 48 |
+
- **Interest Rate Shocks**: Impact of rate changes on predictions
|
| 49 |
+
- **Custom Scenarios**: User-defined stress test parameters
|
| 50 |
|
| 51 |
+
#### 5. Enhanced Uncertainty Quantification
|
| 52 |
+
- **Skewness-aware**: Accounts for non-normal return distributions
|
| 53 |
+
- **Adaptive Smoothing**: Reduces prediction drift based on uncertainty
|
| 54 |
+
- **Confidence Intervals**: Dynamic confidence levels based on market conditions
|
| 55 |
+
|
| 56 |
+
## Installation
|
| 57 |
+
|
| 58 |
+
1. Install dependencies:
|
| 59 |
```bash
|
| 60 |
pip install -r requirements.txt
|
| 61 |
```
|
| 62 |
|
| 63 |
+
2. Run the application:
|
|
|
|
|
|
|
| 64 |
```bash
|
| 65 |
python app.py
|
| 66 |
```
|
| 67 |
|
| 68 |
+
## Usage
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
|
| 70 |
+
### Basic Analysis
|
| 71 |
+
1. Enter a stock symbol (e.g., AAPL, MSFT, GOOGL)
|
| 72 |
+
2. Select timeframe (Daily, Hourly, or 15-minute)
|
| 73 |
+
3. Choose prediction strategy (Chronos or Technical)
|
| 74 |
+
4. Set prediction days and lookback period
|
| 75 |
+
5. Click "Analyze Stock"
|
| 76 |
+
|
| 77 |
+
### Advanced Settings
|
| 78 |
+
- **Ensemble Methods**: Enable/disable multi-model combination
|
| 79 |
+
- **Regime Detection**: Enable/disable market regime analysis
|
| 80 |
+
- **Stress Testing**: Enable/disable scenario analysis
|
| 81 |
+
- **Risk-free Rate**: Set annual risk-free rate for calculations
|
| 82 |
+
- **Market Index**: Choose correlation index (S&P 500, Dow Jones, NASDAQ, Russell 2000)
|
| 83 |
+
- **Ensemble Weights**: Adjust weights for Chronos, Technical, and Statistical models
|
| 84 |
+
|
| 85 |
+
### Output Sections
|
| 86 |
+
|
| 87 |
+
#### Daily Analysis
|
| 88 |
+
- **Structured Product Metrics**: Market cap, sector, financial ratios
|
| 89 |
+
- **Advanced Risk Analysis**: Comprehensive risk metrics with market correlation
|
| 90 |
+
- **Market Regime Analysis**: Current regime and transition probabilities
|
| 91 |
+
- **Trading Signals**: Advanced signals with confidence levels
|
| 92 |
+
- **Stress Test Results**: Scenario analysis outcomes
|
| 93 |
+
- **Ensemble Analysis**: Multi-model combination details
|
| 94 |
+
|
| 95 |
+
#### Hourly/15-minute Analysis
|
| 96 |
+
- **Intraday Metrics**: High-frequency volatility and momentum indicators
|
| 97 |
+
- **Volume Analysis**: Volume-price trends and momentum
|
| 98 |
+
- **Real-time Indicators**: Pre/post market data analysis
|
| 99 |
+
|
| 100 |
+
## Technical Details
|
| 101 |
+
|
| 102 |
+
### Regime Detection
|
| 103 |
+
- Uses Hidden Markov Models with 3 states (low volatility, normal, high volatility)
|
| 104 |
+
- Falls back to volatility-based detection if HMM unavailable
|
| 105 |
+
- Regime probabilities influence trading signal thresholds
|
| 106 |
+
|
| 107 |
+
### Ensemble Methods
|
| 108 |
+
- **Chronos**: Primary deep learning model (60% default weight)
|
| 109 |
+
- **Technical**: Traditional indicators with mean reversion (20% default weight)
|
| 110 |
+
- **Statistical**: ARIMA-like models with momentum (20% default weight)
|
| 111 |
+
|
| 112 |
+
### Stress Testing Scenarios
|
| 113 |
+
- **Market Crash**: 3x volatility, -15% return shock
|
| 114 |
+
- **High Volatility**: 2x volatility, -5% return shock
|
| 115 |
+
- **Low Volatility**: 0.5x volatility, +2% return shock
|
| 116 |
+
- **Bull Market**: 1.2x volatility, +10% return shock
|
| 117 |
+
- **Interest Rate Shock**: 1.5x volatility, -8% return shock
|
| 118 |
+
|
| 119 |
+
### Uncertainty Quantification
|
| 120 |
+
- Skewness-adjusted confidence intervals
|
| 121 |
+
- Adaptive smoothing based on prediction uncertainty
|
| 122 |
+
- Time-varying volatility modeling
|
| 123 |
+
|
| 124 |
+
## Dependencies
|
| 125 |
+
|
| 126 |
+
### Core
|
| 127 |
+
- `torch>=2.1.2`: PyTorch for deep learning
|
| 128 |
+
- `chronos-forecasting>=1.0.0`: Amazon's Chronos model
|
| 129 |
+
- `yfinance>=0.2.0`: Yahoo Finance data
|
| 130 |
+
- `gradio>=4.0.0`: Web interface
|
| 131 |
+
|
| 132 |
+
### Advanced Features
|
| 133 |
+
- `hmmlearn>=0.3.0`: Hidden Markov Models for regime detection
|
| 134 |
+
- `scipy>=1.10.0`: Scientific computing and statistics
|
| 135 |
+
- `scikit-learn>=1.0.0`: Machine learning utilities
|
| 136 |
+
- `plotly>=5.0.0`: Interactive visualizations
|
| 137 |
+
|
| 138 |
+
## Limitations
|
| 139 |
+
|
| 140 |
+
1. **Market Hours**: Intraday data (hourly/15-minute) only available during market hours
|
| 141 |
+
2. **Data Quality**: Dependent on yfinance data availability and quality
|
| 142 |
+
3. **Model Complexity**: Advanced features may increase computation time
|
| 143 |
+
4. **GPU Requirements**: Chronos model requires CUDA-capable GPU for optimal performance
|
| 144 |
+
|
| 145 |
+
## Disclaimer
|
| 146 |
+
|
| 147 |
+
This tool is for educational and research purposes only. Stock predictions are inherently uncertain and should not be used as the sole basis for investment decisions. Always conduct thorough research and consider consulting with financial professionals before making investment decisions.
|
| 148 |
|
| 149 |
## Contributing
|
| 150 |
|
| 151 |
+
Contributions are welcome! Please feel free to submit pull requests or open issues for bugs and feature requests.
|
| 152 |
|
| 153 |
## License
|
| 154 |
|
app.py
CHANGED
|
@@ -16,12 +16,37 @@ import gc
|
|
| 16 |
import pytz
|
| 17 |
import time
|
| 18 |
import random
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
# Initialize global variables
|
| 21 |
pipeline = None
|
| 22 |
scaler = MinMaxScaler(feature_range=(-1, 1))
|
| 23 |
scaler.fit_transform([[-1, 1]])
|
| 24 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
def retry_yfinance_request(func, max_retries=3, initial_delay=1):
|
| 26 |
"""
|
| 27 |
Retry mechanism for yfinance requests with exponential backoff.
|
|
@@ -342,15 +367,24 @@ def calculate_bollinger_bands(prices: pd.Series, period: int = 20, std_dev: int
|
|
| 342 |
return upper_band, middle_band, lower_band
|
| 343 |
|
| 344 |
@spaces.GPU(duration=180)
|
| 345 |
-
def make_prediction(symbol: str, timeframe: str = "1d", prediction_days: int = 5, strategy: str = "chronos"
|
|
|
|
|
|
|
|
|
|
| 346 |
"""
|
| 347 |
-
Make prediction using selected strategy with
|
| 348 |
|
| 349 |
Args:
|
| 350 |
symbol (str): Stock symbol
|
| 351 |
timeframe (str): Data timeframe ('1d', '1h', '15m')
|
| 352 |
prediction_days (int): Number of days to predict
|
| 353 |
strategy (str): Prediction strategy to use
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 354 |
|
| 355 |
Returns:
|
| 356 |
Tuple[Dict, go.Figure]: Trading signals and visualization plot
|
|
@@ -976,11 +1010,590 @@ def calculate_trading_signals(df: pd.DataFrame) -> Dict:
|
|
| 976 |
|
| 977 |
return signals
|
| 978 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 979 |
def create_interface():
|
| 980 |
"""Create the Gradio interface with separate tabs for different timeframes"""
|
| 981 |
-
with gr.Blocks(title="
|
| 982 |
-
gr.Markdown("#
|
| 983 |
-
gr.Markdown("Analyze stocks
|
| 984 |
|
| 985 |
# Add market status message
|
| 986 |
market_status = "Market is currently closed" if not is_market_open() else "Market is currently open"
|
|
@@ -990,6 +1603,50 @@ def create_interface():
|
|
| 990 |
Next trading day: {next_trading_day.strftime('%Y-%m-%d')}
|
| 991 |
""")
|
| 992 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 993 |
with gr.Tabs() as tabs:
|
| 994 |
# Daily Analysis Tab
|
| 995 |
with gr.TabItem("Daily Analysis"):
|
|
@@ -1022,18 +1679,24 @@ def create_interface():
|
|
| 1022 |
|
| 1023 |
with gr.Row():
|
| 1024 |
with gr.Column():
|
| 1025 |
-
|
| 1026 |
gr.Markdown("### Structured Product Metrics")
|
| 1027 |
daily_metrics = gr.JSON(label="Product Metrics")
|
| 1028 |
|
| 1029 |
-
gr.Markdown("### Risk Analysis")
|
| 1030 |
daily_risk_metrics = gr.JSON(label="Risk Metrics")
|
| 1031 |
|
| 1032 |
-
gr.Markdown("###
|
| 1033 |
-
|
| 1034 |
-
|
| 1035 |
gr.Markdown("### Trading Signals")
|
| 1036 |
daily_signals = gr.JSON(label="Trading Signals")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1037 |
|
| 1038 |
# Hourly Analysis Tab
|
| 1039 |
with gr.TabItem("Hourly Analysis"):
|
|
@@ -1138,9 +1801,34 @@ def create_interface():
|
|
| 1138 |
gr.Markdown("### Sector & Financial Analysis")
|
| 1139 |
min15_sector_metrics = gr.JSON(label="Sector Metrics")
|
| 1140 |
|
| 1141 |
-
def analyze_stock(symbol, timeframe, prediction_days, lookback_days, strategy
|
|
|
|
|
|
|
| 1142 |
try:
|
| 1143 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1144 |
|
| 1145 |
# Get historical data for additional metrics
|
| 1146 |
df = get_historical_data(symbol, timeframe, lookback_days)
|
|
@@ -1161,19 +1849,8 @@ def create_interface():
|
|
| 1161 |
"Price_to_Sales": df['Price_to_Sales'].iloc[-1]
|
| 1162 |
}
|
| 1163 |
|
| 1164 |
-
# Calculate risk metrics
|
| 1165 |
-
risk_metrics =
|
| 1166 |
-
"Annualized_Volatility": df['Annualized_Vol'].iloc[-1],
|
| 1167 |
-
"Max_Drawdown": df['Max_Drawdown'].iloc[-1],
|
| 1168 |
-
"Current_Drawdown": df['Drawdown'].iloc[-1],
|
| 1169 |
-
"Sharpe_Ratio": (df['Returns'].mean() * 252) / (df['Returns'].std() * np.sqrt(252)),
|
| 1170 |
-
"Sortino_Ratio": (df['Returns'].mean() * 252) / (df['Returns'][df['Returns'] < 0].std() * np.sqrt(252)),
|
| 1171 |
-
"Return_on_Equity": df['Return_on_Equity'].iloc[-1],
|
| 1172 |
-
"Return_on_Assets": df['Return_on_Assets'].iloc[-1],
|
| 1173 |
-
"Debt_to_Equity": df['Debt_to_Equity'].iloc[-1],
|
| 1174 |
-
"Current_Ratio": df['Current_Ratio'].iloc[-1],
|
| 1175 |
-
"Quick_Ratio": df['Quick_Ratio'].iloc[-1]
|
| 1176 |
-
}
|
| 1177 |
|
| 1178 |
# Calculate sector metrics
|
| 1179 |
sector_metrics = {
|
|
@@ -1197,7 +1874,15 @@ def create_interface():
|
|
| 1197 |
}
|
| 1198 |
product_metrics.update(intraday_metrics)
|
| 1199 |
|
| 1200 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1201 |
except Exception as e:
|
| 1202 |
error_message = str(e)
|
| 1203 |
if "Market is currently closed" in error_message:
|
|
@@ -1209,40 +1894,44 @@ def create_interface():
|
|
| 1209 |
raise gr.Error(error_message)
|
| 1210 |
|
| 1211 |
# Daily analysis button click
|
| 1212 |
-
def daily_analysis(s: str, pd: int, ld: int, st: str
|
|
|
|
| 1213 |
"""
|
| 1214 |
-
Process daily timeframe stock analysis
|
| 1215 |
|
| 1216 |
Args:
|
| 1217 |
s (str): Stock symbol (e.g., "AAPL", "MSFT", "GOOGL")
|
| 1218 |
pd (int): Number of days to predict (1-365)
|
| 1219 |
ld (int): Historical lookback period in days (1-3650)
|
| 1220 |
st (str): Prediction strategy to use ("chronos" or "technical")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1221 |
|
| 1222 |
Returns:
|
| 1223 |
-
Tuple
|
| 1224 |
-
- Trading signals dictionary
|
| 1225 |
-
- Plotly figure with price and technical analysis
|
| 1226 |
-
- Product metrics dictionary
|
| 1227 |
-
- Risk metrics dictionary
|
| 1228 |
-
- Sector metrics dictionary
|
| 1229 |
-
|
| 1230 |
-
Example:
|
| 1231 |
-
>>> daily_analysis("AAPL", 30, 365, "chronos")
|
| 1232 |
-
({'RSI': 'Neutral', 'MACD': 'Buy', ...}, <Figure>, {...}, {...}, {...})
|
| 1233 |
"""
|
| 1234 |
-
return analyze_stock(s, "1d", pd, ld, st)
|
| 1235 |
|
| 1236 |
daily_predict_btn.click(
|
| 1237 |
fn=daily_analysis,
|
| 1238 |
-
inputs=[daily_symbol, daily_prediction_days, daily_lookback_days, daily_strategy
|
| 1239 |
-
|
|
|
|
|
|
|
|
|
|
| 1240 |
)
|
| 1241 |
|
| 1242 |
# Hourly analysis button click
|
| 1243 |
-
def hourly_analysis(s: str, pd: int, ld: int, st: str
|
|
|
|
| 1244 |
"""
|
| 1245 |
-
Process hourly timeframe stock analysis
|
| 1246 |
|
| 1247 |
Args:
|
| 1248 |
s (str): Stock symbol (e.g., "AAPL", "MSFT", "GOOGL")
|
|
|
|
| 16 |
import pytz
|
| 17 |
import time
|
| 18 |
import random
|
| 19 |
+
from scipy import stats
|
| 20 |
+
from scipy.optimize import minimize
|
| 21 |
+
import warnings
|
| 22 |
+
warnings.filterwarnings('ignore')
|
| 23 |
+
|
| 24 |
+
# Additional imports for advanced features
|
| 25 |
+
try:
|
| 26 |
+
from hmmlearn import hmm
|
| 27 |
+
HMM_AVAILABLE = True
|
| 28 |
+
except ImportError:
|
| 29 |
+
HMM_AVAILABLE = False
|
| 30 |
+
print("Warning: hmmlearn not available. Regime detection will use simplified methods.")
|
| 31 |
+
|
| 32 |
+
try:
|
| 33 |
+
from sklearn.ensemble import RandomForestRegressor
|
| 34 |
+
from sklearn.linear_model import LinearRegression
|
| 35 |
+
ENSEMBLE_AVAILABLE = True
|
| 36 |
+
except ImportError:
|
| 37 |
+
ENSEMBLE_AVAILABLE = False
|
| 38 |
+
print("Warning: scikit-learn not available. Ensemble methods will be simplified.")
|
| 39 |
|
| 40 |
# Initialize global variables
|
| 41 |
pipeline = None
|
| 42 |
scaler = MinMaxScaler(feature_range=(-1, 1))
|
| 43 |
scaler.fit_transform([[-1, 1]])
|
| 44 |
|
| 45 |
+
# Global market data cache
|
| 46 |
+
market_data_cache = {}
|
| 47 |
+
cache_expiry = {}
|
| 48 |
+
CACHE_DURATION = 3600 # 1 hour cache
|
| 49 |
+
|
| 50 |
def retry_yfinance_request(func, max_retries=3, initial_delay=1):
|
| 51 |
"""
|
| 52 |
Retry mechanism for yfinance requests with exponential backoff.
|
|
|
|
| 367 |
return upper_band, middle_band, lower_band
|
| 368 |
|
| 369 |
@spaces.GPU(duration=180)
|
| 370 |
+
def make_prediction(symbol: str, timeframe: str = "1d", prediction_days: int = 5, strategy: str = "chronos",
|
| 371 |
+
use_ensemble: bool = True, use_regime_detection: bool = True, use_stress_testing: bool = True,
|
| 372 |
+
risk_free_rate: float = 0.02, ensemble_weights: Dict = None,
|
| 373 |
+
market_index: str = "^GSPC") -> Tuple[Dict, go.Figure]:
|
| 374 |
"""
|
| 375 |
+
Make prediction using selected strategy with advanced features.
|
| 376 |
|
| 377 |
Args:
|
| 378 |
symbol (str): Stock symbol
|
| 379 |
timeframe (str): Data timeframe ('1d', '1h', '15m')
|
| 380 |
prediction_days (int): Number of days to predict
|
| 381 |
strategy (str): Prediction strategy to use
|
| 382 |
+
use_ensemble (bool): Whether to use ensemble methods
|
| 383 |
+
use_regime_detection (bool): Whether to use regime detection
|
| 384 |
+
use_stress_testing (bool): Whether to perform stress testing
|
| 385 |
+
risk_free_rate (float): Risk-free rate for calculations
|
| 386 |
+
ensemble_weights (Dict): Weights for ensemble models
|
| 387 |
+
market_index (str): Market index for correlation analysis
|
| 388 |
|
| 389 |
Returns:
|
| 390 |
Tuple[Dict, go.Figure]: Trading signals and visualization plot
|
|
|
|
| 1010 |
|
| 1011 |
return signals
|
| 1012 |
|
| 1013 |
+
def get_market_data(symbol: str = "^GSPC", lookback_days: int = 365) -> pd.DataFrame:
|
| 1014 |
+
"""
|
| 1015 |
+
Fetch market data (S&P 500 by default) for correlation analysis and regime detection.
|
| 1016 |
+
|
| 1017 |
+
Args:
|
| 1018 |
+
symbol (str): Market index symbol (default: ^GSPC for S&P 500)
|
| 1019 |
+
lookback_days (int): Number of days to look back
|
| 1020 |
+
|
| 1021 |
+
Returns:
|
| 1022 |
+
pd.DataFrame: Market data with returns
|
| 1023 |
+
"""
|
| 1024 |
+
cache_key = f"{symbol}_{lookback_days}"
|
| 1025 |
+
current_time = time.time()
|
| 1026 |
+
|
| 1027 |
+
# Check cache
|
| 1028 |
+
if cache_key in market_data_cache and current_time < cache_expiry.get(cache_key, 0):
|
| 1029 |
+
return market_data_cache[cache_key]
|
| 1030 |
+
|
| 1031 |
+
try:
|
| 1032 |
+
ticker = yf.Ticker(symbol)
|
| 1033 |
+
end_date = datetime.now()
|
| 1034 |
+
start_date = end_date - timedelta(days=lookback_days)
|
| 1035 |
+
|
| 1036 |
+
def fetch_market_history():
|
| 1037 |
+
return ticker.history(
|
| 1038 |
+
start=start_date,
|
| 1039 |
+
end=end_date,
|
| 1040 |
+
interval="1d",
|
| 1041 |
+
prepost=False,
|
| 1042 |
+
actions=False,
|
| 1043 |
+
auto_adjust=True
|
| 1044 |
+
)
|
| 1045 |
+
|
| 1046 |
+
df = retry_yfinance_request(fetch_market_history)
|
| 1047 |
+
|
| 1048 |
+
if not df.empty:
|
| 1049 |
+
df['Returns'] = df['Close'].pct_change()
|
| 1050 |
+
df['Volatility'] = df['Returns'].rolling(window=20).std()
|
| 1051 |
+
|
| 1052 |
+
# Cache the data
|
| 1053 |
+
market_data_cache[cache_key] = df
|
| 1054 |
+
cache_expiry[cache_key] = current_time + CACHE_DURATION
|
| 1055 |
+
|
| 1056 |
+
return df
|
| 1057 |
+
except Exception as e:
|
| 1058 |
+
print(f"Warning: Could not fetch market data for {symbol}: {str(e)}")
|
| 1059 |
+
return pd.DataFrame()
|
| 1060 |
+
|
| 1061 |
+
def detect_market_regime(returns: pd.Series, n_regimes: int = 3) -> Dict:
|
| 1062 |
+
"""
|
| 1063 |
+
Detect market regime using Hidden Markov Model or simplified methods.
|
| 1064 |
+
|
| 1065 |
+
Args:
|
| 1066 |
+
returns (pd.Series): Price returns
|
| 1067 |
+
n_regimes (int): Number of regimes to detect
|
| 1068 |
+
|
| 1069 |
+
Returns:
|
| 1070 |
+
Dict: Regime information including probabilities and characteristics
|
| 1071 |
+
"""
|
| 1072 |
+
if len(returns) < 50:
|
| 1073 |
+
return {"regime": 1, "probabilities": [1.0], "volatility": returns.std()}
|
| 1074 |
+
|
| 1075 |
+
try:
|
| 1076 |
+
if HMM_AVAILABLE:
|
| 1077 |
+
# Use HMM for regime detection
|
| 1078 |
+
model = hmm.GaussianHMM(n_components=n_regimes, random_state=42, covariance_type="full")
|
| 1079 |
+
model.fit(returns.dropna().reshape(-1, 1))
|
| 1080 |
+
|
| 1081 |
+
# Get regime probabilities for the last observation
|
| 1082 |
+
regime_probs = model.predict_proba(returns.dropna().reshape(-1, 1))
|
| 1083 |
+
current_regime = model.predict(returns.dropna().reshape(-1, 1))[-1]
|
| 1084 |
+
|
| 1085 |
+
# Calculate regime characteristics
|
| 1086 |
+
regime_means = model.means_.flatten()
|
| 1087 |
+
regime_vols = np.sqrt(model.covars_.diagonal(axis1=1, axis2=2))
|
| 1088 |
+
|
| 1089 |
+
return {
|
| 1090 |
+
"regime": int(current_regime),
|
| 1091 |
+
"probabilities": regime_probs[-1].tolist(),
|
| 1092 |
+
"means": regime_means.tolist(),
|
| 1093 |
+
"volatilities": regime_vols.tolist(),
|
| 1094 |
+
"method": "HMM"
|
| 1095 |
+
}
|
| 1096 |
+
else:
|
| 1097 |
+
# Simplified regime detection using volatility clustering
|
| 1098 |
+
volatility = returns.rolling(window=20).std().dropna()
|
| 1099 |
+
vol_percentile = volatility.iloc[-1] / volatility.quantile(0.8)
|
| 1100 |
+
|
| 1101 |
+
if vol_percentile > 1.2:
|
| 1102 |
+
regime = 2 # High volatility regime
|
| 1103 |
+
elif vol_percentile < 0.8:
|
| 1104 |
+
regime = 0 # Low volatility regime
|
| 1105 |
+
else:
|
| 1106 |
+
regime = 1 # Normal regime
|
| 1107 |
+
|
| 1108 |
+
return {
|
| 1109 |
+
"regime": regime,
|
| 1110 |
+
"probabilities": [0.1, 0.8, 0.1] if regime == 1 else [0.8, 0.1, 0.1] if regime == 0 else [0.1, 0.1, 0.8],
|
| 1111 |
+
"volatility": volatility.iloc[-1],
|
| 1112 |
+
"method": "Volatility-based"
|
| 1113 |
+
}
|
| 1114 |
+
except Exception as e:
|
| 1115 |
+
print(f"Warning: Regime detection failed: {str(e)}")
|
| 1116 |
+
return {"regime": 1, "probabilities": [1.0], "volatility": returns.std(), "method": "Fallback"}
|
| 1117 |
+
|
| 1118 |
+
def calculate_advanced_risk_metrics(df: pd.DataFrame, market_returns: pd.Series = None,
|
| 1119 |
+
risk_free_rate: float = 0.02) -> Dict:
|
| 1120 |
+
"""
|
| 1121 |
+
Calculate advanced risk metrics including tail risk and market correlation.
|
| 1122 |
+
|
| 1123 |
+
Args:
|
| 1124 |
+
df (pd.DataFrame): Stock data
|
| 1125 |
+
market_returns (pd.Series): Market returns for correlation analysis
|
| 1126 |
+
risk_free_rate (float): Annual risk-free rate
|
| 1127 |
+
|
| 1128 |
+
Returns:
|
| 1129 |
+
Dict: Advanced risk metrics
|
| 1130 |
+
"""
|
| 1131 |
+
returns = df['Returns'].dropna()
|
| 1132 |
+
|
| 1133 |
+
if len(returns) < 30:
|
| 1134 |
+
return {"error": "Insufficient data for risk calculation"}
|
| 1135 |
+
|
| 1136 |
+
# Basic metrics
|
| 1137 |
+
annual_return = returns.mean() * 252
|
| 1138 |
+
annual_vol = returns.std() * np.sqrt(252)
|
| 1139 |
+
|
| 1140 |
+
# Market-adjusted metrics
|
| 1141 |
+
if market_returns is not None and len(market_returns) > 0:
|
| 1142 |
+
# Align dates
|
| 1143 |
+
aligned_returns = returns.reindex(market_returns.index).dropna()
|
| 1144 |
+
aligned_market = market_returns.reindex(aligned_returns.index).dropna()
|
| 1145 |
+
|
| 1146 |
+
if len(aligned_returns) > 10:
|
| 1147 |
+
beta = np.cov(aligned_returns, aligned_market)[0,1] / np.var(aligned_market)
|
| 1148 |
+
alpha = aligned_returns.mean() - beta * aligned_market.mean()
|
| 1149 |
+
correlation = np.corrcoef(aligned_returns, aligned_market)[0,1]
|
| 1150 |
+
else:
|
| 1151 |
+
beta = 1.0
|
| 1152 |
+
alpha = 0.0
|
| 1153 |
+
correlation = 0.0
|
| 1154 |
+
else:
|
| 1155 |
+
beta = 1.0
|
| 1156 |
+
alpha = 0.0
|
| 1157 |
+
correlation = 0.0
|
| 1158 |
+
|
| 1159 |
+
# Tail risk metrics
|
| 1160 |
+
var_95 = np.percentile(returns, 5)
|
| 1161 |
+
var_99 = np.percentile(returns, 1)
|
| 1162 |
+
cvar_95 = returns[returns <= var_95].mean()
|
| 1163 |
+
cvar_99 = returns[returns <= var_99].mean()
|
| 1164 |
+
|
| 1165 |
+
# Maximum drawdown
|
| 1166 |
+
cumulative_returns = (1 + returns).cumprod()
|
| 1167 |
+
rolling_max = cumulative_returns.expanding().max()
|
| 1168 |
+
drawdown = (cumulative_returns - rolling_max) / rolling_max
|
| 1169 |
+
max_drawdown = drawdown.min()
|
| 1170 |
+
|
| 1171 |
+
# Skewness and kurtosis
|
| 1172 |
+
skewness = stats.skew(returns)
|
| 1173 |
+
kurtosis = stats.kurtosis(returns)
|
| 1174 |
+
|
| 1175 |
+
# Risk-adjusted returns
|
| 1176 |
+
sharpe_ratio = (annual_return - risk_free_rate) / annual_vol if annual_vol > 0 else 0
|
| 1177 |
+
sortino_ratio = (annual_return - risk_free_rate) / (returns[returns < 0].std() * np.sqrt(252)) if returns[returns < 0].std() > 0 else 0
|
| 1178 |
+
calmar_ratio = annual_return / abs(max_drawdown) if max_drawdown != 0 else 0
|
| 1179 |
+
|
| 1180 |
+
# Information ratio (if market data available)
|
| 1181 |
+
if market_returns is not None and len(market_returns) > 0:
|
| 1182 |
+
excess_returns = aligned_returns - aligned_market
|
| 1183 |
+
information_ratio = excess_returns.mean() / excess_returns.std() if excess_returns.std() > 0 else 0
|
| 1184 |
+
else:
|
| 1185 |
+
information_ratio = 0
|
| 1186 |
+
|
| 1187 |
+
return {
|
| 1188 |
+
"Annual_Return": annual_return,
|
| 1189 |
+
"Annual_Volatility": annual_vol,
|
| 1190 |
+
"Sharpe_Ratio": sharpe_ratio,
|
| 1191 |
+
"Sortino_Ratio": sortino_ratio,
|
| 1192 |
+
"Calmar_Ratio": calmar_ratio,
|
| 1193 |
+
"Information_Ratio": information_ratio,
|
| 1194 |
+
"Beta": beta,
|
| 1195 |
+
"Alpha": alpha * 252,
|
| 1196 |
+
"Correlation_with_Market": correlation,
|
| 1197 |
+
"VaR_95": var_95,
|
| 1198 |
+
"VaR_99": var_99,
|
| 1199 |
+
"CVaR_95": cvar_95,
|
| 1200 |
+
"CVaR_99": cvar_99,
|
| 1201 |
+
"Max_Drawdown": max_drawdown,
|
| 1202 |
+
"Skewness": skewness,
|
| 1203 |
+
"Kurtosis": kurtosis,
|
| 1204 |
+
"Risk_Free_Rate": risk_free_rate
|
| 1205 |
+
}
|
| 1206 |
+
|
| 1207 |
+
def create_ensemble_prediction(df: pd.DataFrame, prediction_days: int,
|
| 1208 |
+
ensemble_weights: Dict = None) -> Tuple[np.ndarray, np.ndarray]:
|
| 1209 |
+
"""
|
| 1210 |
+
Create ensemble prediction combining multiple models.
|
| 1211 |
+
|
| 1212 |
+
Args:
|
| 1213 |
+
df (pd.DataFrame): Historical data
|
| 1214 |
+
prediction_days (int): Number of days to predict
|
| 1215 |
+
ensemble_weights (Dict): Weights for different models
|
| 1216 |
+
|
| 1217 |
+
Returns:
|
| 1218 |
+
Tuple[np.ndarray, np.ndarray]: Mean and uncertainty predictions
|
| 1219 |
+
"""
|
| 1220 |
+
if ensemble_weights is None:
|
| 1221 |
+
ensemble_weights = {"chronos": 0.6, "technical": 0.2, "statistical": 0.2}
|
| 1222 |
+
|
| 1223 |
+
predictions = {}
|
| 1224 |
+
uncertainties = {}
|
| 1225 |
+
|
| 1226 |
+
# Chronos prediction (placeholder - will be filled by main prediction function)
|
| 1227 |
+
predictions["chronos"] = np.array([])
|
| 1228 |
+
uncertainties["chronos"] = np.array([])
|
| 1229 |
+
|
| 1230 |
+
# Technical prediction
|
| 1231 |
+
if ensemble_weights.get("technical", 0) > 0:
|
| 1232 |
+
try:
|
| 1233 |
+
last_price = df['Close'].iloc[-1]
|
| 1234 |
+
rsi = df['RSI'].iloc[-1]
|
| 1235 |
+
macd = df['MACD'].iloc[-1]
|
| 1236 |
+
macd_signal = df['MACD_Signal'].iloc[-1]
|
| 1237 |
+
volatility = df['Volatility'].iloc[-1]
|
| 1238 |
+
|
| 1239 |
+
# Enhanced technical prediction
|
| 1240 |
+
trend = 1 if (rsi > 50 and macd > macd_signal) else -1
|
| 1241 |
+
mean_reversion = (df['SMA_200'].iloc[-1] - last_price) / last_price if 'SMA_200' in df.columns else 0
|
| 1242 |
+
|
| 1243 |
+
tech_pred = []
|
| 1244 |
+
for i in range(1, prediction_days + 1):
|
| 1245 |
+
# Combine trend and mean reversion
|
| 1246 |
+
prediction = last_price * (1 + trend * volatility * 0.3 + mean_reversion * 0.1 * i)
|
| 1247 |
+
tech_pred.append(prediction)
|
| 1248 |
+
|
| 1249 |
+
predictions["technical"] = np.array(tech_pred)
|
| 1250 |
+
uncertainties["technical"] = np.array([volatility * last_price * i for i in range(1, prediction_days + 1)])
|
| 1251 |
+
except Exception as e:
|
| 1252 |
+
print(f"Technical prediction error: {str(e)}")
|
| 1253 |
+
predictions["technical"] = np.array([])
|
| 1254 |
+
uncertainties["technical"] = np.array([])
|
| 1255 |
+
|
| 1256 |
+
# Statistical prediction (ARIMA-like)
|
| 1257 |
+
if ensemble_weights.get("statistical", 0) > 0:
|
| 1258 |
+
try:
|
| 1259 |
+
returns = df['Returns'].dropna()
|
| 1260 |
+
if len(returns) > 10:
|
| 1261 |
+
# Simple moving average with momentum
|
| 1262 |
+
ma_short = df['Close'].rolling(window=10).mean().iloc[-1]
|
| 1263 |
+
ma_long = df['Close'].rolling(window=30).mean().iloc[-1]
|
| 1264 |
+
momentum = (ma_short - ma_long) / ma_long
|
| 1265 |
+
|
| 1266 |
+
last_price = df['Close'].iloc[-1]
|
| 1267 |
+
stat_pred = []
|
| 1268 |
+
for i in range(1, prediction_days + 1):
|
| 1269 |
+
# Mean reversion with momentum
|
| 1270 |
+
prediction = last_price * (1 + momentum * 0.5 - 0.001 * i) # Decay factor
|
| 1271 |
+
stat_pred.append(prediction)
|
| 1272 |
+
|
| 1273 |
+
predictions["statistical"] = np.array(stat_pred)
|
| 1274 |
+
uncertainties["statistical"] = np.array([returns.std() * last_price * np.sqrt(i) for i in range(1, prediction_days + 1)])
|
| 1275 |
+
else:
|
| 1276 |
+
predictions["statistical"] = np.array([])
|
| 1277 |
+
uncertainties["statistical"] = np.array([])
|
| 1278 |
+
except Exception as e:
|
| 1279 |
+
print(f"Statistical prediction error: {str(e)}")
|
| 1280 |
+
predictions["statistical"] = np.array([])
|
| 1281 |
+
uncertainties["statistical"] = np.array([])
|
| 1282 |
+
|
| 1283 |
+
# Combine predictions
|
| 1284 |
+
valid_predictions = {k: v for k, v in predictions.items() if len(v) > 0}
|
| 1285 |
+
valid_uncertainties = {k: v for k, v in uncertainties.items() if len(v) > 0}
|
| 1286 |
+
|
| 1287 |
+
if not valid_predictions:
|
| 1288 |
+
return np.array([]), np.array([])
|
| 1289 |
+
|
| 1290 |
+
# Weighted ensemble
|
| 1291 |
+
total_weight = sum(ensemble_weights.get(k, 0) for k in valid_predictions.keys())
|
| 1292 |
+
if total_weight == 0:
|
| 1293 |
+
return np.array([]), np.array([])
|
| 1294 |
+
|
| 1295 |
+
# Normalize weights
|
| 1296 |
+
normalized_weights = {k: ensemble_weights.get(k, 0) / total_weight for k in valid_predictions.keys()}
|
| 1297 |
+
|
| 1298 |
+
# Calculate weighted mean and uncertainty
|
| 1299 |
+
max_length = max(len(v) for v in valid_predictions.values())
|
| 1300 |
+
ensemble_mean = np.zeros(max_length)
|
| 1301 |
+
ensemble_uncertainty = np.zeros(max_length)
|
| 1302 |
+
|
| 1303 |
+
for model, pred in valid_predictions.items():
|
| 1304 |
+
weight = normalized_weights[model]
|
| 1305 |
+
if len(pred) < max_length:
|
| 1306 |
+
# Extend prediction using last value
|
| 1307 |
+
extended_pred = np.concatenate([pred, np.full(max_length - len(pred), pred[-1])])
|
| 1308 |
+
extended_unc = np.concatenate([valid_uncertainties[model], np.full(max_length - len(pred), valid_uncertainties[model][-1])])
|
| 1309 |
+
else:
|
| 1310 |
+
extended_pred = pred[:max_length]
|
| 1311 |
+
extended_unc = valid_uncertainties[model][:max_length]
|
| 1312 |
+
|
| 1313 |
+
ensemble_mean += weight * extended_pred
|
| 1314 |
+
ensemble_uncertainty += weight * extended_unc
|
| 1315 |
+
|
| 1316 |
+
return ensemble_mean, ensemble_uncertainty
|
| 1317 |
+
|
| 1318 |
+
def stress_test_scenarios(df: pd.DataFrame, prediction: np.ndarray,
|
| 1319 |
+
scenarios: Dict = None) -> Dict:
|
| 1320 |
+
"""
|
| 1321 |
+
Perform stress testing under various market scenarios.
|
| 1322 |
+
|
| 1323 |
+
Args:
|
| 1324 |
+
df (pd.DataFrame): Historical data
|
| 1325 |
+
prediction (np.ndarray): Base prediction
|
| 1326 |
+
scenarios (Dict): Stress test scenarios
|
| 1327 |
+
|
| 1328 |
+
Returns:
|
| 1329 |
+
Dict: Stress test results
|
| 1330 |
+
"""
|
| 1331 |
+
if scenarios is None:
|
| 1332 |
+
scenarios = {
|
| 1333 |
+
"market_crash": {"volatility_multiplier": 3.0, "return_shock": -0.15},
|
| 1334 |
+
"high_volatility": {"volatility_multiplier": 2.0, "return_shock": -0.05},
|
| 1335 |
+
"low_volatility": {"volatility_multiplier": 0.5, "return_shock": 0.02},
|
| 1336 |
+
"bull_market": {"volatility_multiplier": 1.2, "return_shock": 0.10},
|
| 1337 |
+
"interest_rate_shock": {"volatility_multiplier": 1.5, "return_shock": -0.08}
|
| 1338 |
+
}
|
| 1339 |
+
|
| 1340 |
+
base_volatility = df['Volatility'].iloc[-1]
|
| 1341 |
+
base_return = df['Returns'].mean()
|
| 1342 |
+
last_price = df['Close'].iloc[-1]
|
| 1343 |
+
|
| 1344 |
+
stress_results = {}
|
| 1345 |
+
|
| 1346 |
+
for scenario_name, params in scenarios.items():
|
| 1347 |
+
try:
|
| 1348 |
+
# Calculate stressed parameters
|
| 1349 |
+
stressed_vol = base_volatility * params["volatility_multiplier"]
|
| 1350 |
+
stressed_return = base_return + params["return_shock"]
|
| 1351 |
+
|
| 1352 |
+
# Generate stressed prediction
|
| 1353 |
+
stressed_pred = []
|
| 1354 |
+
for i, pred in enumerate(prediction):
|
| 1355 |
+
# Apply stress factors
|
| 1356 |
+
stress_factor = 1 + stressed_return * (i + 1) / 252
|
| 1357 |
+
volatility_impact = np.random.normal(0, stressed_vol * np.sqrt((i + 1) / 252))
|
| 1358 |
+
stressed_price = pred * stress_factor * (1 + volatility_impact)
|
| 1359 |
+
stressed_pred.append(stressed_price)
|
| 1360 |
+
|
| 1361 |
+
# Calculate stress metrics
|
| 1362 |
+
stress_results[scenario_name] = {
|
| 1363 |
+
"prediction": np.array(stressed_pred),
|
| 1364 |
+
"max_loss": min(stressed_pred) / last_price - 1,
|
| 1365 |
+
"volatility": stressed_vol,
|
| 1366 |
+
"expected_return": stressed_return,
|
| 1367 |
+
"var_95": np.percentile([p / last_price - 1 for p in stressed_pred], 5)
|
| 1368 |
+
}
|
| 1369 |
+
except Exception as e:
|
| 1370 |
+
print(f"Stress test error for {scenario_name}: {str(e)}")
|
| 1371 |
+
stress_results[scenario_name] = {"error": str(e)}
|
| 1372 |
+
|
| 1373 |
+
return stress_results
|
| 1374 |
+
|
| 1375 |
+
def calculate_skewed_uncertainty(quantiles: np.ndarray, confidence_level: float = 0.9) -> np.ndarray:
|
| 1376 |
+
"""
|
| 1377 |
+
Calculate uncertainty accounting for skewness in return distributions.
|
| 1378 |
+
|
| 1379 |
+
Args:
|
| 1380 |
+
quantiles (np.ndarray): Quantile predictions from Chronos
|
| 1381 |
+
confidence_level (float): Confidence level for uncertainty calculation
|
| 1382 |
+
|
| 1383 |
+
Returns:
|
| 1384 |
+
np.ndarray: Uncertainty estimates
|
| 1385 |
+
"""
|
| 1386 |
+
try:
|
| 1387 |
+
lower = quantiles[0, :, 0]
|
| 1388 |
+
median = quantiles[0, :, 1]
|
| 1389 |
+
upper = quantiles[0, :, 2]
|
| 1390 |
+
|
| 1391 |
+
# Calculate skewness for each prediction point
|
| 1392 |
+
uncertainties = []
|
| 1393 |
+
for i in range(len(lower)):
|
| 1394 |
+
# Calculate skewness
|
| 1395 |
+
if upper[i] != median[i] and median[i] != lower[i]:
|
| 1396 |
+
skewness = (median[i] - lower[i]) / (upper[i] - median[i])
|
| 1397 |
+
else:
|
| 1398 |
+
skewness = 1.0
|
| 1399 |
+
|
| 1400 |
+
# Adjust z-score based on skewness
|
| 1401 |
+
if skewness > 1.2: # Right-skewed
|
| 1402 |
+
z_score = stats.norm.ppf(confidence_level) * (1 + 0.1 * skewness)
|
| 1403 |
+
elif skewness < 0.8: # Left-skewed
|
| 1404 |
+
z_score = stats.norm.ppf(confidence_level) * (1 - 0.1 * abs(skewness))
|
| 1405 |
+
else:
|
| 1406 |
+
z_score = stats.norm.ppf(confidence_level)
|
| 1407 |
+
|
| 1408 |
+
# Calculate uncertainty
|
| 1409 |
+
uncertainty = (upper[i] - lower[i]) / (2 * z_score)
|
| 1410 |
+
uncertainties.append(uncertainty)
|
| 1411 |
+
|
| 1412 |
+
return np.array(uncertainties)
|
| 1413 |
+
except Exception as e:
|
| 1414 |
+
print(f"Skewed uncertainty calculation error: {str(e)}")
|
| 1415 |
+
# Fallback to simple calculation
|
| 1416 |
+
return (quantiles[0, :, 2] - quantiles[0, :, 0]) / (2 * 1.645)
|
| 1417 |
+
|
| 1418 |
+
def adaptive_smoothing(new_pred: np.ndarray, historical_pred: np.ndarray,
|
| 1419 |
+
prediction_uncertainty: np.ndarray) -> np.ndarray:
|
| 1420 |
+
"""
|
| 1421 |
+
Apply adaptive smoothing based on prediction uncertainty.
|
| 1422 |
+
|
| 1423 |
+
Args:
|
| 1424 |
+
new_pred (np.ndarray): New predictions
|
| 1425 |
+
historical_pred (np.ndarray): Historical predictions
|
| 1426 |
+
prediction_uncertainty (np.ndarray): Prediction uncertainty
|
| 1427 |
+
|
| 1428 |
+
Returns:
|
| 1429 |
+
np.ndarray: Smoothed predictions
|
| 1430 |
+
"""
|
| 1431 |
+
try:
|
| 1432 |
+
if len(historical_pred) == 0:
|
| 1433 |
+
return new_pred
|
| 1434 |
+
|
| 1435 |
+
# Calculate adaptive alpha based on uncertainty
|
| 1436 |
+
uncertainty_ratio = prediction_uncertainty / np.mean(np.abs(historical_pred))
|
| 1437 |
+
|
| 1438 |
+
if uncertainty_ratio > 0.1: # High uncertainty
|
| 1439 |
+
alpha = 0.1 # More smoothing
|
| 1440 |
+
elif uncertainty_ratio < 0.05: # Low uncertainty
|
| 1441 |
+
alpha = 0.5 # Less smoothing
|
| 1442 |
+
else:
|
| 1443 |
+
alpha = 0.3 # Default
|
| 1444 |
+
|
| 1445 |
+
# Apply weighted smoothing
|
| 1446 |
+
smoothed = alpha * new_pred + (1 - alpha) * historical_pred[-len(new_pred):]
|
| 1447 |
+
return smoothed
|
| 1448 |
+
except Exception as e:
|
| 1449 |
+
print(f"Adaptive smoothing error: {str(e)}")
|
| 1450 |
+
return new_pred
|
| 1451 |
+
|
| 1452 |
+
def advanced_trading_signals(df: pd.DataFrame, regime_info: Dict = None) -> Dict:
|
| 1453 |
+
"""
|
| 1454 |
+
Generate advanced trading signals with confidence levels and regime awareness.
|
| 1455 |
+
|
| 1456 |
+
Args:
|
| 1457 |
+
df (pd.DataFrame): Stock data
|
| 1458 |
+
regime_info (Dict): Market regime information
|
| 1459 |
+
|
| 1460 |
+
Returns:
|
| 1461 |
+
Dict: Advanced trading signals
|
| 1462 |
+
"""
|
| 1463 |
+
try:
|
| 1464 |
+
# Calculate signal strength and confidence
|
| 1465 |
+
rsi = df['RSI'].iloc[-1]
|
| 1466 |
+
macd = df['MACD'].iloc[-1]
|
| 1467 |
+
macd_signal = df['MACD_Signal'].iloc[-1]
|
| 1468 |
+
|
| 1469 |
+
rsi_strength = abs(rsi - 50) / 50 # 0-1 scale
|
| 1470 |
+
macd_strength = abs(macd - macd_signal) / df['Close'].iloc[-1]
|
| 1471 |
+
|
| 1472 |
+
# Regime-adjusted thresholds
|
| 1473 |
+
if regime_info and "volatilities" in regime_info:
|
| 1474 |
+
volatility_regime = df['Volatility'].iloc[-1] / np.mean(regime_info["volatilities"])
|
| 1475 |
+
else:
|
| 1476 |
+
volatility_regime = 1.0
|
| 1477 |
+
|
| 1478 |
+
# Adjust RSI thresholds based on volatility
|
| 1479 |
+
rsi_oversold = 30 + (volatility_regime - 1) * 10
|
| 1480 |
+
rsi_overbought = 70 - (volatility_regime - 1) * 10
|
| 1481 |
+
|
| 1482 |
+
# Calculate signals with confidence
|
| 1483 |
+
signals = {}
|
| 1484 |
+
|
| 1485 |
+
# RSI signal
|
| 1486 |
+
if rsi < rsi_oversold:
|
| 1487 |
+
rsi_signal = "Oversold"
|
| 1488 |
+
rsi_confidence = min(0.9, 0.5 + rsi_strength * 0.4)
|
| 1489 |
+
elif rsi > rsi_overbought:
|
| 1490 |
+
rsi_signal = "Overbought"
|
| 1491 |
+
rsi_confidence = min(0.9, 0.5 + rsi_strength * 0.4)
|
| 1492 |
+
else:
|
| 1493 |
+
rsi_signal = "Neutral"
|
| 1494 |
+
rsi_confidence = 0.3
|
| 1495 |
+
|
| 1496 |
+
signals["RSI"] = {
|
| 1497 |
+
"signal": rsi_signal,
|
| 1498 |
+
"strength": rsi_strength,
|
| 1499 |
+
"confidence": rsi_confidence,
|
| 1500 |
+
"value": rsi
|
| 1501 |
+
}
|
| 1502 |
+
|
| 1503 |
+
# MACD signal
|
| 1504 |
+
if macd > macd_signal:
|
| 1505 |
+
macd_signal = "Buy"
|
| 1506 |
+
macd_confidence = min(0.8, 0.4 + macd_strength * 40)
|
| 1507 |
+
else:
|
| 1508 |
+
macd_signal = "Sell"
|
| 1509 |
+
macd_confidence = min(0.8, 0.4 + macd_strength * 40)
|
| 1510 |
+
|
| 1511 |
+
signals["MACD"] = {
|
| 1512 |
+
"signal": macd_signal,
|
| 1513 |
+
"strength": macd_strength,
|
| 1514 |
+
"confidence": macd_confidence,
|
| 1515 |
+
"value": macd
|
| 1516 |
+
}
|
| 1517 |
+
|
| 1518 |
+
# Bollinger Bands signal
|
| 1519 |
+
if 'BB_Upper' in df.columns and 'BB_Lower' in df.columns:
|
| 1520 |
+
current_price = df['Close'].iloc[-1]
|
| 1521 |
+
bb_upper = df['BB_Upper'].iloc[-1]
|
| 1522 |
+
bb_lower = df['BB_Lower'].iloc[-1]
|
| 1523 |
+
|
| 1524 |
+
if current_price < bb_lower:
|
| 1525 |
+
bb_signal = "Buy"
|
| 1526 |
+
bb_confidence = 0.7
|
| 1527 |
+
elif current_price > bb_upper:
|
| 1528 |
+
bb_signal = "Sell"
|
| 1529 |
+
bb_confidence = 0.7
|
| 1530 |
+
else:
|
| 1531 |
+
bb_signal = "Hold"
|
| 1532 |
+
bb_confidence = 0.5
|
| 1533 |
+
|
| 1534 |
+
signals["Bollinger"] = {
|
| 1535 |
+
"signal": bb_signal,
|
| 1536 |
+
"confidence": bb_confidence,
|
| 1537 |
+
"position": (current_price - bb_lower) / (bb_upper - bb_lower) if bb_upper != bb_lower else 0.5
|
| 1538 |
+
}
|
| 1539 |
+
|
| 1540 |
+
# SMA signal
|
| 1541 |
+
if 'SMA_20' in df.columns and 'SMA_50' in df.columns:
|
| 1542 |
+
sma_20 = df['SMA_20'].iloc[-1]
|
| 1543 |
+
sma_50 = df['SMA_50'].iloc[-1]
|
| 1544 |
+
|
| 1545 |
+
if sma_20 > sma_50:
|
| 1546 |
+
sma_signal = "Buy"
|
| 1547 |
+
sma_confidence = 0.6
|
| 1548 |
+
else:
|
| 1549 |
+
sma_signal = "Sell"
|
| 1550 |
+
sma_confidence = 0.6
|
| 1551 |
+
|
| 1552 |
+
signals["SMA"] = {
|
| 1553 |
+
"signal": sma_signal,
|
| 1554 |
+
"confidence": sma_confidence,
|
| 1555 |
+
"ratio": sma_20 / sma_50 if sma_50 != 0 else 1.0
|
| 1556 |
+
}
|
| 1557 |
+
|
| 1558 |
+
# Calculate weighted overall signal
|
| 1559 |
+
buy_signals = []
|
| 1560 |
+
sell_signals = []
|
| 1561 |
+
|
| 1562 |
+
for signal_name, signal_data in signals.items():
|
| 1563 |
+
if signal_data["signal"] == "Buy":
|
| 1564 |
+
buy_signals.append(signal_data["strength"] * signal_data["confidence"])
|
| 1565 |
+
elif signal_data["signal"] == "Sell":
|
| 1566 |
+
sell_signals.append(signal_data["strength"] * signal_data["confidence"])
|
| 1567 |
+
|
| 1568 |
+
weighted_buy = sum(buy_signals) if buy_signals else 0
|
| 1569 |
+
weighted_sell = sum(sell_signals) if sell_signals else 0
|
| 1570 |
+
|
| 1571 |
+
if weighted_buy > weighted_sell:
|
| 1572 |
+
overall_signal = "Buy"
|
| 1573 |
+
overall_confidence = weighted_buy / (weighted_buy + weighted_sell) if (weighted_buy + weighted_sell) > 0 else 0
|
| 1574 |
+
elif weighted_sell > weighted_buy:
|
| 1575 |
+
overall_signal = "Sell"
|
| 1576 |
+
overall_confidence = weighted_sell / (weighted_buy + weighted_sell) if (weighted_buy + weighted_sell) > 0 else 0
|
| 1577 |
+
else:
|
| 1578 |
+
overall_signal = "Hold"
|
| 1579 |
+
overall_confidence = 0.5
|
| 1580 |
+
|
| 1581 |
+
return {
|
| 1582 |
+
"signals": signals,
|
| 1583 |
+
"overall_signal": overall_signal,
|
| 1584 |
+
"confidence": overall_confidence,
|
| 1585 |
+
"regime_adjusted": regime_info is not None
|
| 1586 |
+
}
|
| 1587 |
+
|
| 1588 |
+
except Exception as e:
|
| 1589 |
+
print(f"Advanced trading signals error: {str(e)}")
|
| 1590 |
+
return {"error": str(e)}
|
| 1591 |
+
|
| 1592 |
def create_interface():
|
| 1593 |
"""Create the Gradio interface with separate tabs for different timeframes"""
|
| 1594 |
+
with gr.Blocks(title="Advanced Stock Prediction Analysis") as demo:
|
| 1595 |
+
gr.Markdown("# Advanced Stock Prediction Analysis")
|
| 1596 |
+
gr.Markdown("Analyze stocks with advanced features including regime detection, ensemble methods, and stress testing.")
|
| 1597 |
|
| 1598 |
# Add market status message
|
| 1599 |
market_status = "Market is currently closed" if not is_market_open() else "Market is currently open"
|
|
|
|
| 1603 |
Next trading day: {next_trading_day.strftime('%Y-%m-%d')}
|
| 1604 |
""")
|
| 1605 |
|
| 1606 |
+
# Advanced Settings Accordion
|
| 1607 |
+
with gr.Accordion("Advanced Settings", open=False):
|
| 1608 |
+
with gr.Row():
|
| 1609 |
+
with gr.Column():
|
| 1610 |
+
use_ensemble = gr.Checkbox(label="Use Ensemble Methods", value=True)
|
| 1611 |
+
use_regime_detection = gr.Checkbox(label="Use Regime Detection", value=True)
|
| 1612 |
+
use_stress_testing = gr.Checkbox(label="Use Stress Testing", value=True)
|
| 1613 |
+
risk_free_rate = gr.Slider(
|
| 1614 |
+
minimum=0.0,
|
| 1615 |
+
maximum=0.1,
|
| 1616 |
+
value=0.02,
|
| 1617 |
+
step=0.001,
|
| 1618 |
+
label="Risk-Free Rate (Annual)"
|
| 1619 |
+
)
|
| 1620 |
+
market_index = gr.Dropdown(
|
| 1621 |
+
choices=["^GSPC", "^DJI", "^IXIC", "^RUT"],
|
| 1622 |
+
label="Market Index for Correlation",
|
| 1623 |
+
value="^GSPC"
|
| 1624 |
+
)
|
| 1625 |
+
|
| 1626 |
+
with gr.Column():
|
| 1627 |
+
gr.Markdown("### Ensemble Weights")
|
| 1628 |
+
chronos_weight = gr.Slider(
|
| 1629 |
+
minimum=0.0,
|
| 1630 |
+
maximum=1.0,
|
| 1631 |
+
value=0.6,
|
| 1632 |
+
step=0.1,
|
| 1633 |
+
label="Chronos Weight"
|
| 1634 |
+
)
|
| 1635 |
+
technical_weight = gr.Slider(
|
| 1636 |
+
minimum=0.0,
|
| 1637 |
+
maximum=1.0,
|
| 1638 |
+
value=0.2,
|
| 1639 |
+
step=0.1,
|
| 1640 |
+
label="Technical Weight"
|
| 1641 |
+
)
|
| 1642 |
+
statistical_weight = gr.Slider(
|
| 1643 |
+
minimum=0.0,
|
| 1644 |
+
maximum=1.0,
|
| 1645 |
+
value=0.2,
|
| 1646 |
+
step=0.1,
|
| 1647 |
+
label="Statistical Weight"
|
| 1648 |
+
)
|
| 1649 |
+
|
| 1650 |
with gr.Tabs() as tabs:
|
| 1651 |
# Daily Analysis Tab
|
| 1652 |
with gr.TabItem("Daily Analysis"):
|
|
|
|
| 1679 |
|
| 1680 |
with gr.Row():
|
| 1681 |
with gr.Column():
|
|
|
|
| 1682 |
gr.Markdown("### Structured Product Metrics")
|
| 1683 |
daily_metrics = gr.JSON(label="Product Metrics")
|
| 1684 |
|
| 1685 |
+
gr.Markdown("### Advanced Risk Analysis")
|
| 1686 |
daily_risk_metrics = gr.JSON(label="Risk Metrics")
|
| 1687 |
|
| 1688 |
+
gr.Markdown("### Market Regime Analysis")
|
| 1689 |
+
daily_regime_metrics = gr.JSON(label="Regime Metrics")
|
| 1690 |
+
|
| 1691 |
gr.Markdown("### Trading Signals")
|
| 1692 |
daily_signals = gr.JSON(label="Trading Signals")
|
| 1693 |
+
|
| 1694 |
+
with gr.Column():
|
| 1695 |
+
gr.Markdown("### Stress Test Results")
|
| 1696 |
+
daily_stress_results = gr.JSON(label="Stress Test Results")
|
| 1697 |
+
|
| 1698 |
+
gr.Markdown("### Ensemble Analysis")
|
| 1699 |
+
daily_ensemble_metrics = gr.JSON(label="Ensemble Metrics")
|
| 1700 |
|
| 1701 |
# Hourly Analysis Tab
|
| 1702 |
with gr.TabItem("Hourly Analysis"):
|
|
|
|
| 1801 |
gr.Markdown("### Sector & Financial Analysis")
|
| 1802 |
min15_sector_metrics = gr.JSON(label="Sector Metrics")
|
| 1803 |
|
| 1804 |
+
def analyze_stock(symbol, timeframe, prediction_days, lookback_days, strategy,
|
| 1805 |
+
use_ensemble, use_regime_detection, use_stress_testing,
|
| 1806 |
+
risk_free_rate, market_index, chronos_weight, technical_weight, statistical_weight):
|
| 1807 |
try:
|
| 1808 |
+
# Create ensemble weights
|
| 1809 |
+
ensemble_weights = {
|
| 1810 |
+
"chronos": chronos_weight,
|
| 1811 |
+
"technical": technical_weight,
|
| 1812 |
+
"statistical": statistical_weight
|
| 1813 |
+
}
|
| 1814 |
+
|
| 1815 |
+
# Get market data for correlation analysis
|
| 1816 |
+
market_df = get_market_data(market_index, lookback_days)
|
| 1817 |
+
market_returns = market_df['Returns'] if not market_df.empty else None
|
| 1818 |
+
|
| 1819 |
+
# Make prediction with advanced features
|
| 1820 |
+
signals, fig = make_prediction(
|
| 1821 |
+
symbol=symbol,
|
| 1822 |
+
timeframe=timeframe,
|
| 1823 |
+
prediction_days=prediction_days,
|
| 1824 |
+
strategy=strategy,
|
| 1825 |
+
use_ensemble=use_ensemble,
|
| 1826 |
+
use_regime_detection=use_regime_detection,
|
| 1827 |
+
use_stress_testing=use_stress_testing,
|
| 1828 |
+
risk_free_rate=risk_free_rate,
|
| 1829 |
+
ensemble_weights=ensemble_weights,
|
| 1830 |
+
market_index=market_index
|
| 1831 |
+
)
|
| 1832 |
|
| 1833 |
# Get historical data for additional metrics
|
| 1834 |
df = get_historical_data(symbol, timeframe, lookback_days)
|
|
|
|
| 1849 |
"Price_to_Sales": df['Price_to_Sales'].iloc[-1]
|
| 1850 |
}
|
| 1851 |
|
| 1852 |
+
# Calculate advanced risk metrics
|
| 1853 |
+
risk_metrics = calculate_advanced_risk_metrics(df, market_returns, risk_free_rate)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1854 |
|
| 1855 |
# Calculate sector metrics
|
| 1856 |
sector_metrics = {
|
|
|
|
| 1874 |
}
|
| 1875 |
product_metrics.update(intraday_metrics)
|
| 1876 |
|
| 1877 |
+
# Extract regime and stress test information
|
| 1878 |
+
regime_metrics = signals.get("regime_info", {})
|
| 1879 |
+
stress_results = signals.get("stress_test_results", {})
|
| 1880 |
+
ensemble_metrics = {
|
| 1881 |
+
"ensemble_used": signals.get("ensemble_used", False),
|
| 1882 |
+
"ensemble_weights": ensemble_weights
|
| 1883 |
+
}
|
| 1884 |
+
|
| 1885 |
+
return signals, fig, product_metrics, risk_metrics, sector_metrics, regime_metrics, stress_results, ensemble_metrics
|
| 1886 |
except Exception as e:
|
| 1887 |
error_message = str(e)
|
| 1888 |
if "Market is currently closed" in error_message:
|
|
|
|
| 1894 |
raise gr.Error(error_message)
|
| 1895 |
|
| 1896 |
# Daily analysis button click
|
| 1897 |
+
def daily_analysis(s: str, pd: int, ld: int, st: str, ue: bool, urd: bool, ust: bool,
|
| 1898 |
+
rfr: float, mi: str, cw: float, tw: float, sw: float) -> Tuple[Dict, go.Figure, Dict, Dict, Dict, Dict, Dict, Dict]:
|
| 1899 |
"""
|
| 1900 |
+
Process daily timeframe stock analysis with advanced features.
|
| 1901 |
|
| 1902 |
Args:
|
| 1903 |
s (str): Stock symbol (e.g., "AAPL", "MSFT", "GOOGL")
|
| 1904 |
pd (int): Number of days to predict (1-365)
|
| 1905 |
ld (int): Historical lookback period in days (1-3650)
|
| 1906 |
st (str): Prediction strategy to use ("chronos" or "technical")
|
| 1907 |
+
ue (bool): Use ensemble methods
|
| 1908 |
+
urd (bool): Use regime detection
|
| 1909 |
+
ust (bool): Use stress testing
|
| 1910 |
+
rfr (float): Risk-free rate
|
| 1911 |
+
mi (str): Market index
|
| 1912 |
+
cw (float): Chronos weight
|
| 1913 |
+
tw (float): Technical weight
|
| 1914 |
+
sw (float): Statistical weight
|
| 1915 |
|
| 1916 |
Returns:
|
| 1917 |
+
Tuple containing all analysis results
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1918 |
"""
|
| 1919 |
+
return analyze_stock(s, "1d", pd, ld, st, ue, urd, ust, rfr, mi, cw, tw, sw)
|
| 1920 |
|
| 1921 |
daily_predict_btn.click(
|
| 1922 |
fn=daily_analysis,
|
| 1923 |
+
inputs=[daily_symbol, daily_prediction_days, daily_lookback_days, daily_strategy,
|
| 1924 |
+
use_ensemble, use_regime_detection, use_stress_testing, risk_free_rate, market_index,
|
| 1925 |
+
chronos_weight, technical_weight, statistical_weight],
|
| 1926 |
+
outputs=[daily_signals, daily_plot, daily_metrics, daily_risk_metrics, daily_sector_metrics,
|
| 1927 |
+
daily_regime_metrics, daily_stress_results, daily_ensemble_metrics]
|
| 1928 |
)
|
| 1929 |
|
| 1930 |
# Hourly analysis button click
|
| 1931 |
+
def hourly_analysis(s: str, pd: int, ld: int, st: str, ue: bool, urd: bool, ust: bool,
|
| 1932 |
+
rfr: float, mi: str, cw: float, tw: float, sw: float) -> Tuple[Dict, go.Figure, Dict, Dict, Dict]:
|
| 1933 |
"""
|
| 1934 |
+
Process hourly timeframe stock analysis with advanced features.
|
| 1935 |
|
| 1936 |
Args:
|
| 1937 |
s (str): Stock symbol (e.g., "AAPL", "MSFT", "GOOGL")
|
requirements.txt
CHANGED
|
@@ -92,4 +92,8 @@ typer
|
|
| 92 |
diskcache
|
| 93 |
anthropic
|
| 94 |
gradio>=4.0.0
|
| 95 |
-
chronos-forecasting>=1.0.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
diskcache
|
| 93 |
anthropic
|
| 94 |
gradio>=4.0.0
|
| 95 |
+
chronos-forecasting>=1.0.0
|
| 96 |
+
|
| 97 |
+
# Advanced features dependencies
|
| 98 |
+
hmmlearn>=0.3.0
|
| 99 |
+
scipy>=1.10.0
|