Tonic commited on
Commit
8481b42
·
1 Parent(s): e115d61

attempt more advanced predictions using model ensemble

Browse files
Files changed (3) hide show
  1. README.md +116 -132
  2. app.py +732 -43
  3. requirements.txt +5 -1
README.md CHANGED
@@ -13,158 +13,142 @@ tags:
13
  - mcp-server-track
14
  ---
15
 
16
- # Stock Analysis and Prediction Demo
17
 
18
- A comprehensive stock analysis and prediction tool built with Gradio, featuring multiple prediction strategies and technical analysis indicators. The application is particularly suited for structured financial product creation and analysis.
19
 
20
  ## Features
21
 
22
- - **Multiple Prediction Strategies**:
23
- - Chronos ML-based prediction
24
- - Technical analysis-based prediction
25
-
26
- - **Technical Indicators**:
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
- ## Installation
79
 
80
- 1. Clone the repository:
81
- ```bash
82
- git clone <repository-url>
83
- cd stock-prediction
84
- ```
85
 
86
- 2. Create and activate a virtual environment:
87
- ```bash
88
- python -m venv .venv
89
- source .venv/bin/activate # On Windows: .venv\Scripts\activate
90
- ```
 
 
 
 
 
 
 
 
 
 
91
 
92
- 3. Install dependencies:
 
 
 
 
 
 
 
93
  ```bash
94
  pip install -r requirements.txt
95
  ```
96
 
97
- ## Usage
98
-
99
- 1. Start the Gradio demo:
100
  ```bash
101
  python app.py
102
  ```
103
 
104
- 2. Open your web browser and navigate to the URL shown in the terminal (typically http://localhost:7860)
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
- An overall trading signal is calculated by combining all individual signals.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
 
165
  ## Contributing
166
 
167
- Contributions are welcome! Please feel free to submit a Pull Request.
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") -> Tuple[Dict, go.Figure]:
 
 
 
346
  """
347
- Make prediction using selected strategy with ZeroGPU.
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="Structured Product Analysis") as demo:
982
- gr.Markdown("# Structured Product Analysis")
983
- gr.Markdown("Analyze stocks for inclusion in structured financial products with extended time horizons.")
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("### Sector Analysis")
1033
- daily_sector_metrics = gr.JSON(label="Sector Metrics")
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
- signals, fig = make_prediction(symbol, timeframe, prediction_days, strategy)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- return signals, fig, product_metrics, risk_metrics, sector_metrics
 
 
 
 
 
 
 
 
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) -> Tuple[Dict, go.Figure, Dict, Dict, Dict]:
 
1213
  """
1214
- Process daily timeframe stock analysis and generate predictions.
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[Dict, go.Figure, Dict, Dict, Dict]: A tuple containing:
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
- outputs=[daily_signals, daily_plot, daily_metrics, daily_risk_metrics, daily_sector_metrics]
 
 
 
1240
  )
1241
 
1242
  # Hourly analysis button click
1243
- def hourly_analysis(s: str, pd: int, ld: int, st: str) -> Tuple[Dict, go.Figure, Dict, Dict, Dict]:
 
1244
  """
1245
- Process hourly timeframe stock analysis and generate predictions.
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