Tonic commited on
Commit
cb59e63
·
unverified ·
1 Parent(s): 6730937

fix covariate and model training issues

Browse files
Files changed (1) hide show
  1. app.py +153 -36
app.py CHANGED
@@ -37,7 +37,7 @@ try:
37
  from sklearn.linear_model import LinearRegression, Ridge, Lasso
38
  from sklearn.svm import SVR
39
  from sklearn.neural_network import MLPRegressor
40
- from sklearn.model_selection import TimeSeriesSplit
41
  from sklearn.metrics import mean_squared_error, mean_absolute_error
42
  ENSEMBLE_AVAILABLE = True
43
  except ImportError:
@@ -1315,20 +1315,30 @@ def make_prediction_enhanced(symbol: str, timeframe: str = "1d", prediction_days
1315
  # Apply continuity correction
1316
  last_actual = df['Close'].iloc[-1]
1317
  first_pred = mean_pred[0]
1318
- if abs(first_pred - last_actual) > max(1e-6, 0.05 * abs(last_actual)):
1319
- print(f"Warning: Discontinuity detected between last actual ({last_actual}) and first prediction ({first_pred})")
1320
- mean_pred[0] = last_actual
1321
- # Adjust subsequent predictions to maintain trend with optional smoothing
 
 
 
1322
  if len(mean_pred) > 1:
1323
  # Calculate the trend from the original prediction
1324
  original_trend = mean_pred[1] - first_pred
1325
- # Apply the same trend but starting from the last actual value
1326
- for i in range(1, len(mean_pred)):
1327
- mean_pred[i] = last_actual + original_trend * i
 
 
 
 
1328
 
1329
  # Apply financial smoothing if enabled
1330
  if use_smoothing:
1331
  mean_pred = apply_financial_smoothing(mean_pred, smoothing_type, smoothing_window, smoothing_alpha, 3, use_smoothing)
 
 
 
1332
 
1333
  # If we had to limit the prediction length, extend the prediction recursively
1334
  if actual_prediction_length < trim_length:
@@ -1404,15 +1414,24 @@ def make_prediction_enhanced(symbol: str, timeframe: str = "1d", prediction_days
1404
  (next_quantiles[0, :, 2] - next_quantiles[0, :, 0]) / (2 * 1.645))
1405
 
1406
  # Check for discontinuity and apply continuity correction
1407
- if abs(next_mean_pred[0] - extended_mean_pred[-1]) > max(1e-6, 0.05 * abs(extended_mean_pred[-1])):
1408
- print(f"Warning: Discontinuity detected between last prediction ({extended_mean_pred[-1]}) and next prediction ({next_mean_pred[0]})")
1409
- # Apply continuity correction to first prediction
1410
- next_mean_pred[0] = extended_mean_pred[-1]
1411
- # Adjust subsequent predictions to maintain trend
1412
  if len(next_mean_pred) > 1:
 
1413
  original_trend = next_mean_pred[1] - next_mean_pred[0]
1414
- for i in range(1, len(next_mean_pred)):
1415
- next_mean_pred[i] = extended_mean_pred[-1] + original_trend * i
 
 
 
 
 
 
 
 
1416
 
1417
  # Apply financial smoothing if enabled
1418
  if use_smoothing and len(next_mean_pred) > 1:
@@ -3738,6 +3757,27 @@ def create_enhanced_ensemble_model(df: pd.DataFrame, covariate_data: Dict,
3738
  bb_position = bb_position.fillna(0.5) # Fill NaN with neutral position
3739
  features.append(bb_position.values)
3740
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3741
  # Add covariate data
3742
  if 'market_indices' in covariate_data:
3743
  for col in covariate_data['market_indices'].columns:
@@ -3885,22 +3925,60 @@ def create_enhanced_ensemble_model(df: pd.DataFrame, covariate_data: Dict,
3885
 
3886
  for name, model in models.items():
3887
  try:
3888
- # Use expanding window for time series
3889
- train_size = int(0.8 * len(X))
3890
- X_train, X_test = X[:train_size], X[train_size:]
3891
- y_train, y_test = y[:train_size], y[train_size:]
3892
 
3893
- # Train model
3894
- model.fit(X_train, y_train)
3895
 
3896
- # Make predictions
3897
- if len(X_test) > 0:
3898
- pred = model.predict(X_test)
3899
- mse = mean_squared_error(y_test, pred)
3900
- uncertainty = np.sqrt(mse) * np.ones(prediction_days)
 
 
 
3901
  else:
3902
- # Use last available data for prediction
3903
- pred = model.predict(X[-prediction_days:])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3904
  uncertainty = np.std(y) * np.ones(prediction_days)
3905
 
3906
  # Ensure prediction is the right length
@@ -3910,12 +3988,25 @@ def create_enhanced_ensemble_model(df: pd.DataFrame, covariate_data: Dict,
3910
  pred = pred[:prediction_days]
3911
  uncertainty = uncertainty[:prediction_days]
3912
  else:
3913
- # Extend with last value
3914
- last_val = pred[-1] if len(pred) > 0 else y[-1]
3915
- pred = np.pad(pred, (0, prediction_days - len(pred)),
3916
- mode='constant', constant_values=last_val)
3917
- uncertainty = np.pad(uncertainty, (0, prediction_days - len(uncertainty)),
3918
- mode='constant', constant_values=uncertainty[-1] if len(uncertainty) > 0 else np.std(y))
 
 
 
 
 
 
 
 
 
 
 
 
 
3919
 
3920
  # Validate prediction
3921
  if len(pred) == prediction_days and len(uncertainty) == prediction_days:
@@ -3939,10 +4030,36 @@ def create_enhanced_ensemble_model(df: pd.DataFrame, covariate_data: Dict,
3939
 
3940
  # Combine predictions using weighted average
3941
  weights = {}
 
 
3942
  for name in predictions.keys():
3943
  if name in uncertainties:
3944
- # Weight inversely proportional to uncertainty
3945
- weights[name] = 1.0 / np.mean(uncertainties[name])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3946
 
3947
  # Normalize weights
3948
  total_weight = sum(weights.values())
 
37
  from sklearn.linear_model import LinearRegression, Ridge, Lasso
38
  from sklearn.svm import SVR
39
  from sklearn.neural_network import MLPRegressor
40
+ from sklearn.model_selection import TimeSeriesSplit, cross_val_score
41
  from sklearn.metrics import mean_squared_error, mean_absolute_error
42
  ENSEMBLE_AVAILABLE = True
43
  except ImportError:
 
1315
  # Apply continuity correction
1316
  last_actual = df['Close'].iloc[-1]
1317
  first_pred = mean_pred[0]
1318
+ discontinuity_threshold = max(1e-6, 0.02 * abs(last_actual)) # 2% threshold
1319
+
1320
+ if abs(first_pred - last_actual) > discontinuity_threshold:
1321
+ print(f"Warning: Discontinuity detected between last actual ({last_actual:.4f}) and first prediction ({first_pred:.4f})")
1322
+ print(f"Discontinuity magnitude: {abs(first_pred - last_actual):.4f} ({abs(first_pred - last_actual)/last_actual*100:.2f}%)")
1323
+
1324
+ # Apply smooth continuity correction
1325
  if len(mean_pred) > 1:
1326
  # Calculate the trend from the original prediction
1327
  original_trend = mean_pred[1] - first_pred
1328
+
1329
+ # Apply a smooth transition over the first few predictions
1330
+ transition_length = min(3, len(mean_pred))
1331
+ for i in range(transition_length):
1332
+ # Gradually transition from last actual to predicted trend
1333
+ transition_factor = i / transition_length
1334
+ mean_pred[i] = last_actual + original_trend * i * transition_factor
1335
 
1336
  # Apply financial smoothing if enabled
1337
  if use_smoothing:
1338
  mean_pred = apply_financial_smoothing(mean_pred, smoothing_type, smoothing_window, smoothing_alpha, 3, use_smoothing)
1339
+ else:
1340
+ # Single prediction case
1341
+ mean_pred[0] = last_actual
1342
 
1343
  # If we had to limit the prediction length, extend the prediction recursively
1344
  if actual_prediction_length < trim_length:
 
1414
  (next_quantiles[0, :, 2] - next_quantiles[0, :, 0]) / (2 * 1.645))
1415
 
1416
  # Check for discontinuity and apply continuity correction
1417
+ if abs(next_mean_pred[0] - extended_mean_pred[-1]) > max(1e-6, 0.02 * abs(extended_mean_pred[-1])):
1418
+ print(f"Warning: Discontinuity detected between last prediction ({extended_mean_pred[-1]:.4f}) and next prediction ({next_mean_pred[0]:.4f})")
1419
+ print(f"Extension discontinuity magnitude: {abs(next_mean_pred[0] - extended_mean_pred[-1]):.4f}")
1420
+
1421
+ # Apply smooth continuity correction
1422
  if len(next_mean_pred) > 1:
1423
+ # Calculate the trend from the original prediction
1424
  original_trend = next_mean_pred[1] - next_mean_pred[0]
1425
+
1426
+ # Apply a smooth transition over the first few predictions
1427
+ transition_length = min(3, len(next_mean_pred))
1428
+ for i in range(transition_length):
1429
+ # Gradually transition from last prediction to predicted trend
1430
+ transition_factor = i / transition_length
1431
+ next_mean_pred[i] = extended_mean_pred[-1] + original_trend * i * transition_factor
1432
+ else:
1433
+ # Single prediction case
1434
+ next_mean_pred[0] = extended_mean_pred[-1]
1435
 
1436
  # Apply financial smoothing if enabled
1437
  if use_smoothing and len(next_mean_pred) > 1:
 
3757
  bb_position = bb_position.fillna(0.5) # Fill NaN with neutral position
3758
  features.append(bb_position.values)
3759
 
3760
+ # Add lagged price features to capture temporal patterns
3761
+ for lag in [1, 2, 3, 5, 10]:
3762
+ if len(target) > lag:
3763
+ lagged_prices = np.pad(target[:-lag], (lag, 0), mode='edge')
3764
+ features.append(lagged_prices)
3765
+
3766
+ # Add rolling statistics to capture trends
3767
+ for window in [5, 10, 20]:
3768
+ if len(target) >= window:
3769
+ rolling_mean = pd.Series(target).rolling(window=window, min_periods=1).mean().values
3770
+ rolling_std = pd.Series(target).rolling(window=window, min_periods=1).std().fillna(0).values
3771
+ features.append(rolling_mean)
3772
+ features.append(rolling_std)
3773
+
3774
+ # Add price momentum features
3775
+ if len(target) > 1:
3776
+ price_momentum_1d = np.pad(np.diff(target), (1, 0), mode='constant', constant_values=0)
3777
+ price_momentum_5d = np.pad(np.diff(target, n=5), (5, 0), mode='constant', constant_values=0)
3778
+ features.append(price_momentum_1d)
3779
+ features.append(price_momentum_5d)
3780
+
3781
  # Add covariate data
3782
  if 'market_indices' in covariate_data:
3783
  for col in covariate_data['market_indices'].columns:
 
3925
 
3926
  for name, model in models.items():
3927
  try:
3928
+ # Use ALL available data for training (no train/test split)
3929
+ # This maximizes the use of historical information
3930
+ print(f"Training {name} on all {len(X)} data points...")
 
3931
 
3932
+ # Train model on all available data
3933
+ model.fit(X, y)
3934
 
3935
+ # Generate predictions for the full prediction period
3936
+ # Use the most recent data points to generate future predictions
3937
+ if len(X) >= prediction_days:
3938
+ # Use the last prediction_days data points for prediction
3939
+ # This ensures we're using the most recent patterns and trends
3940
+ X_pred = X[-prediction_days:]
3941
+ pred = model.predict(X_pred)
3942
+ print(f" {name}: Generated {len(pred)} predictions using last {prediction_days} data points")
3943
  else:
3944
+ # If we don't have enough data, use all available data and extrapolate
3945
+ pred = model.predict(X)
3946
+ print(f" {name}: Generated {len(pred)} predictions using all {len(X)} data points")
3947
+
3948
+ if len(pred) < prediction_days:
3949
+ # Extend with trend-based predictions
3950
+ if len(pred) > 0:
3951
+ # Calculate trend from last few predictions
3952
+ trend_window = min(5, len(pred))
3953
+ if trend_window > 1:
3954
+ trend = np.mean(np.diff(pred[-trend_window:]))
3955
+ else:
3956
+ trend = 0
3957
+
3958
+ # Extend predictions using the trend
3959
+ last_pred = pred[-1] if len(pred) > 0 else y[-1]
3960
+ for i in range(len(pred), prediction_days):
3961
+ next_pred = last_pred + trend
3962
+ pred = np.append(pred, next_pred)
3963
+ last_pred = next_pred
3964
+
3965
+ print(f" {name}: Extended to {len(pred)} predictions using trend extrapolation")
3966
+ else:
3967
+ # No predictions available, use simple extrapolation
3968
+ last_price = y[-1]
3969
+ pred = np.array([last_price * (1 + 0.001 * i) for i in range(prediction_days)])
3970
+ print(f" {name}: Generated {len(pred)} predictions using simple extrapolation")
3971
+
3972
+ # Calculate uncertainty using cross-validation on all data
3973
+ # This gives us a better estimate of model performance
3974
+ try:
3975
+ cv_scores = cross_val_score(model, X, y, cv=min(5, len(X)//10), scoring='neg_mean_squared_error')
3976
+ mse = -np.mean(cv_scores)
3977
+ uncertainty = np.sqrt(mse) * np.ones(prediction_days)
3978
+ print(f" {name} CV MSE: {mse:.6f}")
3979
+ except Exception as cv_error:
3980
+ print(f" {name} CV failed, using fallback uncertainty: {str(cv_error)}")
3981
+ # Fallback uncertainty based on historical volatility
3982
  uncertainty = np.std(y) * np.ones(prediction_days)
3983
 
3984
  # Ensure prediction is the right length
 
3988
  pred = pred[:prediction_days]
3989
  uncertainty = uncertainty[:prediction_days]
3990
  else:
3991
+ # Extend with trend-based predictions
3992
+ if len(pred) > 0:
3993
+ # Calculate trend from last few predictions
3994
+ trend_window = min(5, len(pred))
3995
+ if trend_window > 1:
3996
+ trend = np.mean(np.diff(pred[-trend_window:]))
3997
+ else:
3998
+ trend = 0
3999
+
4000
+ # Extend predictions using the trend
4001
+ last_pred = pred[-1] if len(pred) > 0 else y[-1]
4002
+ for i in range(len(pred), prediction_days):
4003
+ next_pred = last_pred + trend
4004
+ pred = np.append(pred, next_pred)
4005
+ last_pred = next_pred
4006
+ else:
4007
+ # No predictions available, use simple extrapolation
4008
+ last_price = y[-1]
4009
+ pred = np.array([last_price * (1 + 0.001 * i) for i in range(prediction_days)])
4010
 
4011
  # Validate prediction
4012
  if len(pred) == prediction_days and len(uncertainty) == prediction_days:
 
4030
 
4031
  # Combine predictions using weighted average
4032
  weights = {}
4033
+ model_performances = {}
4034
+
4035
  for name in predictions.keys():
4036
  if name in uncertainties:
4037
+ # Calculate model performance on training data
4038
+ try:
4039
+ # Use cross-validation score as performance metric
4040
+ cv_scores = cross_val_score(models[name], X, y, cv=min(5, len(X)//10), scoring='r2')
4041
+ performance = np.mean(cv_scores)
4042
+ model_performances[name] = performance
4043
+
4044
+ # Weight based on both performance and uncertainty
4045
+ # Higher performance and lower uncertainty = higher weight
4046
+ uncertainty_factor = 1.0 / np.mean(uncertainties[name])
4047
+ performance_factor = max(0, performance) # Ensure non-negative
4048
+
4049
+ # Combine factors (you can adjust the balance)
4050
+ weights[name] = (0.7 * performance_factor + 0.3 * uncertainty_factor)
4051
+
4052
+ print(f" {name}: Performance={performance:.4f}, Uncertainty={np.mean(uncertainties[name]):.4f}, Weight={weights[name]:.4f}")
4053
+
4054
+ except Exception as e:
4055
+ print(f" {name}: Performance calculation failed, using uncertainty only: {str(e)}")
4056
+ # Fallback to uncertainty-based weighting
4057
+ weights[name] = 1.0 / np.mean(uncertainties[name])
4058
+ model_performances[name] = 0.0
4059
+ else:
4060
+ # Equal weight if no uncertainty available
4061
+ weights[name] = 1.0
4062
+ model_performances[name] = 0.0
4063
 
4064
  # Normalize weights
4065
  total_weight = sum(weights.values())