Spaces:
Paused
Paused
Tonic commited on
fix covariate and model training issues
Browse files
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 |
-
|
| 1319 |
-
|
| 1320 |
-
|
| 1321 |
-
|
|
|
|
|
|
|
|
|
|
| 1322 |
if len(mean_pred) > 1:
|
| 1323 |
# Calculate the trend from the original prediction
|
| 1324 |
original_trend = mean_pred[1] - first_pred
|
| 1325 |
-
|
| 1326 |
-
|
| 1327 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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.
|
| 1408 |
-
print(f"Warning: Discontinuity detected between last prediction ({extended_mean_pred[-1]}) and next prediction ({next_mean_pred[0]})")
|
| 1409 |
-
|
| 1410 |
-
|
| 1411 |
-
#
|
| 1412 |
if len(next_mean_pred) > 1:
|
|
|
|
| 1413 |
original_trend = next_mean_pred[1] - next_mean_pred[0]
|
| 1414 |
-
|
| 1415 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 3889 |
-
|
| 3890 |
-
|
| 3891 |
-
y_train, y_test = y[:train_size], y[train_size:]
|
| 3892 |
|
| 3893 |
-
# Train model
|
| 3894 |
-
model.fit(
|
| 3895 |
|
| 3896 |
-
#
|
| 3897 |
-
|
| 3898 |
-
|
| 3899 |
-
|
| 3900 |
-
|
|
|
|
|
|
|
|
|
|
| 3901 |
else:
|
| 3902 |
-
#
|
| 3903 |
-
pred = model.predict(X
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 3914 |
-
|
| 3915 |
-
|
| 3916 |
-
|
| 3917 |
-
|
| 3918 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
#
|
| 3945 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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())
|