Closes #ISSUE-027-REGIME-UI - Implement regime indicator badge and dynamic meta-learner threshold

This commit is contained in:
Antigravity Agent
2026-06-17 19:59:37 +02:00
parent dcb59c17f0
commit 1dc637aaf6
11 changed files with 103 additions and 26 deletions

View File

@@ -484,7 +484,7 @@ def train_and_forecast():
"""
if not ML_LIBRARIES_AVAILABLE:
print("Scikit-learn not available. Skipping model fitting.")
return get_mock_predictions()
return get_mock_predictions(), 0
# Load data
csv_path = os.path.join('backend', 'data', 'BTC-USD.csv')
@@ -572,11 +572,28 @@ def train_and_forecast():
X_train_selected = X_train_scaled
X_test_selected = X_test_scaled
try:
high_alpha_cols = {
'v_supply', 'asopr', 'sth_sopr', 'lth_sopr', 'theta', 'squeeze_risk',
'd_liq', 'f_comp', 'z_f', 'z_f_squeeze_trigger', 'cvd_inst', 'cvd_ret',
'div_cvd', 'lambda_kyle'
}
# Identify indices of high-alpha features that are present in X_train
high_alpha_indices = [
i for i, col in enumerate(X_train.columns)
if col in high_alpha_cols
]
# Fit selector classifier (Random Forest)
selector_clf = RandomForestClassifier(n_estimators=30, max_depth=4, random_state=42)
# Boruta shadow model sweep
boruta_idx = boruta_shadow_pruning(X_train_scaled, y_train)
# Ensure high-alpha indices are retained in Boruta output
for idx in high_alpha_indices:
if idx not in boruta_idx:
boruta_idx.append(idx)
X_train_boruta = X_train_scaled[:, boruta_idx]
# PIMP permutation feature filter
@@ -584,6 +601,12 @@ def train_and_forecast():
# Map back to original indices
selected_feature_indices = [boruta_idx[i] for i in pimp_idx]
# Ensure high-alpha indices are retained in the final selection
for idx in high_alpha_indices:
if idx not in selected_feature_indices:
selected_feature_indices.append(idx)
X_train_selected = X_train_scaled[:, selected_feature_indices]
X_test_selected = X_test_scaled[:, selected_feature_indices]
print(f"Boruta & PIMP Selection ({h_label}): Reduced features from {X_train_scaled.shape[1]} to {X_train_selected.shape[1]}")
@@ -618,10 +641,16 @@ def train_and_forecast():
meta_clf.fit(X_train_micro, y_reliability)
# Compute confidence score r_hat on test sample
r_pred = float(meta_clf.predict_proba(X_test_micro)[0][1])
if len(meta_clf.classes_) >= 2:
r_pred = float(meta_clf.predict_proba(X_test_micro)[0][1])
train_r_probs = meta_clf.predict_proba(X_train_micro)[:, 1]
else:
r_pred = float(meta_clf.classes_[0])
train_r_probs = np.full(len(X_train_micro), float(meta_clf.classes_[0]))
# 3. Apply Ironclad Execution Rule: Execute ONLY if confidence exceeds threshold theta_conf = 0.55
theta_conf = 0.55
theta_conf = float(np.mean(train_r_probs))
# 3. Apply Ironclad Execution Rule: Execute ONLY if confidence exceeds threshold theta_conf
if r_pred >= theta_conf:
# Retrieve expected direction probability
classes = list(clf.classes_)
@@ -644,7 +673,7 @@ def train_and_forecast():
print(f"Model {name} failed on horizon {h_label}: {e}")
predictions[name][h_label] = 0.5
return predictions
return predictions, active_regime
def get_mock_predictions():
@@ -769,7 +798,7 @@ def main():
# Ingest live data first
fetch_real_data()
preds = train_and_forecast()
preds, active_regime = train_and_forecast()
output_dir = os.path.join('public', 'data')
os.makedirs(output_dir, exist_ok=True)
@@ -778,6 +807,7 @@ def main():
payload = {
"isShieldActive": not (ML_LIBRARIES_AVAILABLE and os.path.exists(os.path.join('backend', 'data', 'BTC-USD.csv'))),
"activeRegime": int(active_regime) + 1,
"predictions": {
"BTC": preds,
"ETH": {