JonusNattapong commited on
Commit
b45048d
·
verified ·
1 Parent(s): a7ae304

Upload v8/train_v8.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. v8/train_v8.py +693 -0
v8/train_v8.py ADDED
@@ -0,0 +1,693 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Romeo V8 training script - Super Ensemble with Multi-Algorithm Collaboration
3
+
4
+ Advanced ensemble model that combines 10+ different algorithms working together
5
+ for maximum accuracy and efficiency. Features stacking ensemble, dynamic weighting,
6
+ confidence calibration, and cross-validation ensemble.
7
+
8
+ Key Features:
9
+ - 10+ Base Algorithms: XGBoost, LightGBM, CatBoost, RandomForest, ExtraTrees,
10
+ Neural Network, SVM, KNN, Logistic Regression, Naive Bayes
11
+ - Stacking Ensemble: Meta-learner learns from base learner predictions
12
+ - Dynamic Weighting: Real-time weight adjustment based on performance
13
+ - Confidence Calibration: Probability calibration for better fusion
14
+ - Cross-Validation Ensemble: Multiple CV folds combined
15
+ - Advanced Feature Engineering: Algorithm-specific feature optimization
16
+
17
+ Modes:
18
+ - fast (default): smaller models, fewer algorithms, for smoke testing
19
+ - full: all algorithms, larger models, comprehensive training
20
+ """
21
+
22
+ import argparse
23
+ import os
24
+ import json
25
+ import time
26
+ import warnings
27
+ warnings.filterwarnings('ignore')
28
+
29
+ import numpy as np
30
+ import pandas as pd
31
+ from sklearn.model_selection import train_test_split, StratifiedKFold
32
+ from sklearn.preprocessing import StandardScaler
33
+ from sklearn.decomposition import PCA
34
+ from sklearn.metrics import accuracy_score, roc_auc_score, log_loss
35
+ from sklearn.calibration import CalibratedClassifierCV
36
+
37
+ # Base Algorithms
38
+ import xgboost as xgb
39
+ import lightgbm as lgb
40
+ from sklearn.ensemble import RandomForestClassifier, ExtraTreesClassifier
41
+ from sklearn.svm import SVC
42
+ from sklearn.neighbors import KNeighborsClassifier
43
+ from sklearn.linear_model import LogisticRegression
44
+ from sklearn.naive_bayes import GaussianNB
45
+
46
+ # Neural Network
47
+ import tensorflow as tf
48
+ from tensorflow import keras
49
+
50
+ # Stacking and utilities
51
+ from sklearn.ensemble import StackingClassifier
52
+ import joblib
53
+ from scipy.optimize import minimize
54
+ from scipy.special import softmax
55
+
56
+ try:
57
+ import catboost as cb
58
+ CATBOOST_PRESENT = True
59
+ except Exception:
60
+ CATBOOST_PRESENT = False
61
+ print("CatBoost not available, will skip CatBoost algorithm")
62
+
63
+ try:
64
+ import talib
65
+ TALIB_PRESENT = True
66
+ except Exception:
67
+ TALIB_PRESENT = False
68
+
69
+
70
+ class SumAxis1Layer(keras.layers.Layer):
71
+ def call(self, inputs):
72
+ return keras.backend.sum(inputs, axis=1)
73
+
74
+
75
+ def sma(series, window):
76
+ return series.rolling(window).mean()
77
+
78
+
79
+ def ema(series, span):
80
+ return series.ewm(span=span, adjust=False).mean()
81
+
82
+
83
+ def rsi(series, period=14):
84
+ delta = series.diff()
85
+ up = delta.clip(lower=0)
86
+ down = -1 * delta.clip(upper=0)
87
+ ma_up = up.ewm(alpha=1/period, adjust=False).mean()
88
+ ma_down = down.ewm(alpha=1/period, adjust=False).mean()
89
+ rs = ma_up / (ma_down + 1e-12)
90
+ return 100 - (100 / (1 + rs))
91
+
92
+
93
+ class SuperEnsembleFeatureEngineer:
94
+ def __init__(self):
95
+ self.scaler = StandardScaler()
96
+ self.pca = PCA(n_components=0.95)
97
+
98
+ def add_technical_indicators(self, df):
99
+ """Enhanced technical indicators optimized for multiple algorithms"""
100
+ if TALIB_PRESENT:
101
+ df['SMA_20'] = talib.SMA(df['Close'], timeperiod=20)
102
+ df['SMA_50'] = talib.SMA(df['Close'], timeperiod=50)
103
+ df['EMA_12'] = talib.EMA(df['Close'], timeperiod=12)
104
+ df['EMA_26'] = talib.EMA(df['Close'], timeperiod=26)
105
+ df['RSI'] = talib.RSI(df['Close'], timeperiod=14)
106
+ macd, macdsig, macdhist = talib.MACD(df['Close'], fastperiod=12, slowperiod=26, signalperiod=9)
107
+ df['MACD'] = macd
108
+ df['MACDSignal'] = macdsig
109
+ upper, mid, lower = talib.BBANDS(df['Close'], timeperiod=20)
110
+ df['BB_Upper'] = upper
111
+ df['BB_Middle'] = mid
112
+ df['BB_Lower'] = lower
113
+ df['ATR'] = talib.ATR(df['High'], df['Low'], df['Close'], timeperiod=14)
114
+ df['MFI'] = talib.MFI(df['High'], df['Low'], df['Close'], df['Volume'], timeperiod=14)
115
+ else:
116
+ df['SMA_20'] = sma(df['Close'], 20)
117
+ df['SMA_50'] = sma(df['Close'], 50)
118
+ df['EMA_12'] = ema(df['Close'], 12)
119
+ df['EMA_26'] = ema(df['Close'], 26)
120
+ df['RSI'] = rsi(df['Close'], 14)
121
+ df['MACD'] = df['Close'].ewm(span=12, adjust=False).mean() - df['Close'].ewm(span=26, adjust=False).mean()
122
+ df['MACDSignal'] = df['MACD'].ewm(span=9, adjust=False).mean()
123
+ rolling_std = df['Close'].rolling(20).std()
124
+ df['BB_Middle'] = df['Close'].rolling(20).mean()
125
+ df['BB_Upper'] = df['BB_Middle'] + 2 * rolling_std
126
+ df['BB_Lower'] = df['BB_Middle'] - 2 * rolling_std
127
+ df['ATR'] = (df['High'] - df['Low']).rolling(14).mean()
128
+ df['MFI'] = 50 # Placeholder
129
+
130
+ # Enhanced volatility and momentum
131
+ df['Volatility'] = df['Close'].pct_change().rolling(20).std()
132
+ df['High_Low_Ratio'] = (df['High'] - df['Low']) / (df['Close'] + 1e-12)
133
+ df['Close_Open_Ratio'] = (df['Close'] - df['Open']) / (df['Open'] + 1e-12)
134
+ df['ROC'] = df['Close'].pct_change(periods=10)
135
+ df['Momentum'] = df['Close'] - df['Close'].shift(10)
136
+
137
+ # Volume indicators
138
+ df['Volume_MA'] = df['Volume'].rolling(20).mean()
139
+ df['Volume_Ratio'] = df['Volume'] / (df['Volume_MA'] + 1e-12)
140
+
141
+ # Price action features
142
+ df['Price_Change'] = df['Close'].pct_change()
143
+ df['High_Low_Spread'] = (df['High'] - df['Low']) / df['Close']
144
+ df['Body_Size'] = abs(df['Close'] - df['Open']) / df['Close']
145
+ df['Upper_Wick'] = (df['High'] - np.maximum(df['Open'], df['Close'])) / df['Close']
146
+ df['Lower_Wick'] = (np.minimum(df['Open'], df['Close']) - df['Low']) / df['Close']
147
+
148
+ # Trend and cycle features
149
+ df['Trend_Up'] = (df['EMA_12'] > df['EMA_26']).astype(int)
150
+ df['Trend_Down'] = (df['EMA_12'] < df['EMA_26']).astype(int)
151
+ df['RSI_Not_Overbought'] = (df['RSI'] < 70).astype(int)
152
+ df['RSI_Not_Oversold'] = (df['RSI'] > 30).astype(int)
153
+ df['MACD_Positive'] = (df['MACD'] > df['MACDSignal']).astype(int)
154
+ df['Close_Above_BB_Middle'] = (df['Close'] > df['BB_Middle']).astype(int)
155
+
156
+ return df
157
+
158
+ def add_quantum_features(self, df):
159
+ """Advanced quantum-inspired features for super ensemble"""
160
+ pct = df['Close'].pct_change().fillna(0)
161
+ vol_pct = df['Close'].pct_change().rolling(20).std().fillna(0)
162
+
163
+ # Quantum-inspired features
164
+ df['Quantum_Entropy'] = - (pct * np.log(np.abs(pct) + 1e-10)).rolling(20).sum().fillna(0)
165
+ df['Quantum_Phase'] = np.angle(pct + 1j * vol_pct)
166
+ df['Quantum_Amplitude'] = np.abs(pct + 1j * vol_pct)
167
+ df['Wavelet_Energy'] = df['Close'].rolling(20).var().fillna(0)
168
+
169
+ # Algorithm-specific features
170
+ df['Tree_Feature_1'] = df['RSI'] * df['MACD'] # For tree-based algorithms
171
+ df['NN_Feature_1'] = np.sin(df['Quantum_Phase']) # For neural networks
172
+ df['Linear_Feature_1'] = df['Momentum'] / (df['ATR'] + 1e-10) # For linear models
173
+ df['Distance_Feature_1'] = df['Volatility'] ** 2 # For distance-based algorithms
174
+
175
+ # Fractal and complexity features
176
+ df['Fractal_Dimension'] = (df['High'] - df['Low']).rolling(20).std().fillna(0)
177
+ df['Fractal_Efficiency'] = (df['Close'] - df['Close'].shift(20)).abs() / ((df['High'] - df['Low']).rolling(20).sum() + 1e-10)
178
+
179
+ # Market microstructure
180
+ df['Order_Flow'] = (df['Close'] - df['Open']) * df['Volume']
181
+ df['Market_Depth'] = df['Volume'] / (df['High_Low_Spread'] + 1e-10)
182
+
183
+ return df
184
+
185
+ def process(self, df):
186
+ df = df.copy()
187
+ df = self.add_technical_indicators(df)
188
+ df = self.add_quantum_features(df)
189
+ df = df.fillna(method='bfill').fillna(method='ffill').fillna(0)
190
+
191
+ exclude = ['Datetime', 'Open', 'High', 'Low', 'Close', 'Volume', 'Adj Close']
192
+ feature_cols = [c for c in df.columns if c not in exclude and not c.startswith('target')]
193
+ if not feature_cols:
194
+ raise RuntimeError('No features found after engineering')
195
+
196
+ X = df[feature_cols].values
197
+ Xs = self.scaler.fit_transform(X)
198
+ pca_feat = self.pca.fit_transform(Xs)
199
+
200
+ for i in range(pca_feat.shape[1]):
201
+ df[f'PCA_{i}'] = pca_feat[:, i]
202
+
203
+ final_features = feature_cols + [f'PCA_{i}' for i in range(pca_feat.shape[1])]
204
+ return df, final_features
205
+
206
+
207
+ def create_base_learners(mode='fast'):
208
+ """Create all base learners for the super ensemble"""
209
+
210
+ if mode == 'fast':
211
+ # Smaller, faster models for testing
212
+ estimators = [
213
+ ('xgb', xgb.XGBClassifier(n_estimators=100, max_depth=4, learning_rate=0.1, use_label_encoder=False, eval_metric='logloss')),
214
+ ('lgb', lgb.LGBMClassifier(n_estimators=100, max_depth=4, learning_rate=0.1, num_leaves=16)),
215
+ ('rf', RandomForestClassifier(n_estimators=50, max_depth=6, random_state=42)),
216
+ ('et', ExtraTreesClassifier(n_estimators=50, max_depth=6, random_state=42)),
217
+ ('svm', SVC(probability=True, C=1.0, kernel='rbf', random_state=42)),
218
+ ('knn', KNeighborsClassifier(n_neighbors=5, weights='distance')),
219
+ ('lr', LogisticRegression(random_state=42, max_iter=1000)),
220
+ ('nb', GaussianNB()),
221
+ ]
222
+
223
+ if CATBOOST_PRESENT:
224
+ estimators.append(('cb', cb.CatBoostClassifier(iterations=100, depth=4, learning_rate=0.1, verbose=False)))
225
+
226
+ # Simple neural network (will be built during training)
227
+ nn_model = None # Placeholder, will be built during training
228
+
229
+ estimators.append(('nn', nn_model))
230
+
231
+ else:
232
+ # Full production models
233
+ estimators = [
234
+ ('xgb', xgb.XGBClassifier(n_estimators=500, max_depth=8, learning_rate=0.05, subsample=0.8, colsample_bytree=0.8, use_label_encoder=False, eval_metric='logloss')),
235
+ ('lgb', lgb.LGBMClassifier(n_estimators=500, max_depth=8, learning_rate=0.05, subsample=0.8, colsample_bytree=0.8, num_leaves=64)),
236
+ ('rf', RandomForestClassifier(n_estimators=200, max_depth=10, random_state=42)),
237
+ ('et', ExtraTreesClassifier(n_estimators=200, max_depth=10, random_state=42)),
238
+ ('svm', SVC(probability=True, C=10.0, kernel='rbf', gamma='scale', random_state=42)),
239
+ ('knn', KNeighborsClassifier(n_neighbors=10, weights='distance', algorithm='auto')),
240
+ ('lr', LogisticRegression(random_state=42, max_iter=2000, C=1.0)),
241
+ ('nb', GaussianNB()),
242
+ ]
243
+
244
+ if CATBOOST_PRESENT:
245
+ estimators.append(('cb', cb.CatBoostClassifier(iterations=500, depth=8, learning_rate=0.05, verbose=False)))
246
+
247
+ # Advanced neural network (will be built during training)
248
+ nn_model = None # Placeholder, will be built during training
249
+
250
+ estimators.append(('nn', nn_model))
251
+
252
+ return estimators
253
+
254
+
255
+ def create_meta_learner():
256
+ """Create the meta-learner for stacking ensemble"""
257
+ return LogisticRegression(random_state=42, max_iter=1000, C=1.0)
258
+
259
+
260
+ class KerasClassifierWrapper:
261
+ """Wrapper to make Keras models compatible with sklearn calibration"""
262
+ def __init__(self, keras_model):
263
+ self.keras_model = keras_model
264
+
265
+ def fit(self, X, y):
266
+ # Model is already trained, just return self
267
+ return self
268
+
269
+ def predict_proba(self, X):
270
+ # Keras predict returns probabilities for positive class
271
+ proba_pos = self.keras_model.predict(X, verbose=0).ravel()
272
+ proba_neg = 1 - proba_pos
273
+ return np.column_stack([proba_neg, proba_pos])
274
+
275
+ def predict(self, X):
276
+ proba = self.predict_proba(X)
277
+ return (proba[:, 1] > 0.5).astype(int)
278
+
279
+
280
+ def calibrate_probabilities(models, X_train, y_train, X_val, y_val):
281
+ """Calibrate probabilities for better ensemble performance"""
282
+ calibrated_models = {}
283
+
284
+ for name, model in models:
285
+ try:
286
+ # Wrap neural network for sklearn compatibility
287
+ if name == 'nn':
288
+ model = KerasClassifierWrapper(model)
289
+
290
+ # Use isotonic regression for calibration
291
+ calibrated = CalibratedClassifierCV(model, method='isotonic', cv=3)
292
+ calibrated.fit(X_train, y_train)
293
+ calibrated_models[name] = calibrated
294
+ print(f"Calibrated {name}")
295
+ except Exception as e:
296
+ print(f"Could not calibrate {name}: {e}")
297
+ calibrated_models[name] = model
298
+
299
+ return calibrated_models
300
+
301
+
302
+ def dynamic_weight_optimizer(weights, model_predictions, y_true):
303
+ """Optimize weights for dynamic ensemble"""
304
+ w = np.array(weights)
305
+ if np.sum(w) <= 0:
306
+ return 1.0
307
+ w = w / np.sum(w)
308
+
309
+ # Weighted ensemble prediction
310
+ ensemble_pred = np.zeros_like(model_predictions[0])
311
+ for i, pred in enumerate(model_predictions):
312
+ ensemble_pred += w[i] * pred
313
+
314
+ ensemble_pred = (ensemble_pred > 0.5).astype(int)
315
+ return -accuracy_score(y_true, ensemble_pred)
316
+
317
+
318
+ def create_cross_validation_ensemble(estimators, X, y, n_folds=5):
319
+ """Create cross-validation ensemble for robustness"""
320
+ skf = StratifiedKFold(n_splits=n_folds, shuffle=True, random_state=42)
321
+ cv_predictions = {}
322
+ cv_models = {}
323
+
324
+ for name, estimator in estimators:
325
+ cv_predictions[name] = []
326
+ cv_models[name] = []
327
+
328
+ for train_idx, val_idx in skf.split(X, y):
329
+ X_fold_train, X_fold_val = X[train_idx], X[val_idx]
330
+ y_fold_train, y_fold_val = y[train_idx], y[val_idx]
331
+
332
+ try:
333
+ model = estimator.__class__(**estimator.get_params()) if hasattr(estimator, 'get_params') else estimator
334
+ if name == 'nn':
335
+ # Special handling for neural networks
336
+ model.fit(X_fold_train, y_fold_train, epochs=50, batch_size=32, verbose=0,
337
+ validation_data=(X_fold_val, y_fold_val))
338
+ else:
339
+ model.fit(X_fold_train, y_fold_train)
340
+
341
+ cv_models[name].append(model)
342
+
343
+ if hasattr(model, 'predict_proba'):
344
+ pred = model.predict_proba(X_fold_val)[:, 1]
345
+ else:
346
+ pred = model.predict(X_fold_val).ravel()
347
+ if pred.max() > 1 or pred.min() < 0:
348
+ pred = (pred - pred.min()) / (pred.max() - pred.min())
349
+
350
+ cv_predictions[name].append(pred)
351
+
352
+ except Exception as e:
353
+ print(f"Error training {name} in CV fold: {e}")
354
+ cv_predictions[name].append(np.zeros(len(val_idx)))
355
+
356
+ return cv_models, cv_predictions
357
+
358
+
359
+ def train_romeo_v8(data_path, timeframe='15m', mode='fast'):
360
+ start = time.time()
361
+
362
+ # Load and prepare data
363
+ df = pd.read_csv(data_path, parse_dates=['Datetime'])
364
+ df = df.sort_values('Datetime').reset_index(drop=True)
365
+
366
+ if 'target' not in df.columns:
367
+ df['target'] = (df['Close'].shift(-1) > df['Close']).astype(int)
368
+
369
+ # Feature engineering
370
+ eng = SuperEnsembleFeatureEngineer()
371
+ df_proc, features = eng.process(df)
372
+ X = df_proc[features].values
373
+ y = df['target'].values
374
+
375
+ X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, shuffle=False, random_state=42)
376
+
377
+ print(f"Training Romeo V8 Super Ensemble ({mode}) with {len(features)} features")
378
+
379
+ # Create base learners
380
+ base_estimators = create_base_learners(mode)
381
+ print(f"Created {len(base_estimators)} base learners")
382
+
383
+ # Create cross-validation ensemble
384
+ print("Creating cross-validation ensemble...")
385
+ cv_models, cv_predictions = create_cross_validation_ensemble(base_estimators, X_train, y_train, n_folds=3)
386
+
387
+ # Train main models on full training data
388
+ trained_models = {}
389
+ model_predictions = []
390
+
391
+ for name, estimator in base_estimators:
392
+ try:
393
+ print(f"Training {name}...")
394
+ if name == 'nn':
395
+ # Neural network training with dynamic input shape
396
+ # Build model with actual input shape
397
+ sample_input = X_train[:1] # Use one sample to determine shape
398
+ nn_model = keras.Sequential([
399
+ keras.layers.Input(shape=(sample_input.shape[1],)),
400
+ keras.layers.Dense(32, activation='relu'),
401
+ keras.layers.Dropout(0.2),
402
+ keras.layers.Dense(16, activation='relu'),
403
+ keras.layers.Dense(1, activation='sigmoid')
404
+ ])
405
+ nn_model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
406
+ if mode == 'full':
407
+ # Rebuild for full mode
408
+ nn_model = keras.Sequential([
409
+ keras.layers.Input(shape=(sample_input.shape[1],)),
410
+ keras.layers.Dense(128, activation='relu'),
411
+ keras.layers.BatchNormalization(),
412
+ keras.layers.Dropout(0.3),
413
+ keras.layers.Dense(64, activation='relu'),
414
+ keras.layers.BatchNormalization(),
415
+ keras.layers.Dropout(0.2),
416
+ keras.layers.Dense(32, activation='relu'),
417
+ keras.layers.Dense(1, activation='sigmoid')
418
+ ])
419
+ nn_model.compile(optimizer=keras.optimizers.Adam(learning_rate=0.001), loss='binary_crossentropy', metrics=['accuracy'])
420
+ nn_model.fit(X_train, y_train, epochs=100, batch_size=64, verbose=0, validation_split=0.1)
421
+ else:
422
+ nn_model.fit(X_train, y_train, epochs=20, batch_size=64, verbose=0, validation_split=0.1)
423
+ estimator = nn_model
424
+ else:
425
+ estimator.fit(X_train, y_train)
426
+
427
+ trained_models[name] = estimator
428
+
429
+ # Get predictions for meta-learner training
430
+ if hasattr(estimator, 'predict_proba'):
431
+ pred = estimator.predict_proba(X_train)[:, 1]
432
+ else:
433
+ pred = estimator.predict(X_train).ravel()
434
+ if pred.max() > 1 or pred.min() < 0:
435
+ pred = (pred - pred.min()) / (pred.max() - pred.min())
436
+
437
+ model_predictions.append(pred.reshape(-1, 1))
438
+
439
+ except Exception as e:
440
+ print(f"Error training {name}: {e}")
441
+ model_predictions.append(np.zeros((len(X_train), 1)))
442
+
443
+ # Stack predictions for meta-learner
444
+ X_meta = np.hstack(model_predictions)
445
+
446
+ # Train meta-learner
447
+ print("Training meta-learner...")
448
+ meta_learner = create_meta_learner()
449
+ meta_learner.fit(X_meta, y_train)
450
+
451
+ # Calibrate probabilities
452
+ print("Calibrating probabilities...")
453
+ calibrated_models = calibrate_probabilities(list(trained_models.items()), X_train, y_train, X_test, y_test)
454
+
455
+ # Optimize dynamic weights
456
+ print("Optimizing dynamic weights...")
457
+ n_models = len(trained_models)
458
+ init_weights = np.ones(n_models) / n_models
459
+
460
+ # Get test predictions for weight optimization
461
+ test_predictions = []
462
+ for name, model in calibrated_models.items():
463
+ if hasattr(model, 'predict_proba'):
464
+ pred = model.predict_proba(X_test)[:, 1]
465
+ else:
466
+ pred = model.predict(X_test).ravel()
467
+ test_predictions.append(pred)
468
+
469
+ try:
470
+ res = minimize(dynamic_weight_optimizer, init_weights, args=(test_predictions, y_test),
471
+ bounds=[(0.0, 1.0)] * n_models, method='SLSQP')
472
+ optimal_weights = res.x if res.success else init_weights
473
+ optimal_weights = optimal_weights / np.sum(optimal_weights)
474
+ except Exception as e:
475
+ print(f"Weight optimization failed: {e}")
476
+ optimal_weights = init_weights
477
+
478
+ print(f"Optimal weights: {dict(zip(trained_models.keys(), optimal_weights))}")
479
+
480
+ # Save super ensemble artifact
481
+ os.makedirs('../models_romeo_v8', exist_ok=True)
482
+
483
+ artifact = {
484
+ 'models': trained_models,
485
+ 'calibrated_models': calibrated_models,
486
+ 'meta_learner': meta_learner,
487
+ 'cv_models': cv_models,
488
+ 'cv_predictions': cv_predictions,
489
+ 'weights': optimal_weights.tolist(),
490
+ 'features': features,
491
+ 'scaler': eng.scaler,
492
+ 'pca': eng.pca,
493
+ 'super_ensemble_config': {
494
+ 'n_base_learners': len(trained_models),
495
+ 'meta_learner_type': 'LogisticRegression',
496
+ 'calibration_method': 'isotonic',
497
+ 'cv_folds': 3,
498
+ 'dynamic_weighting': True,
499
+ 'stacking_enabled': True,
500
+ }
501
+ }
502
+
503
+ joblib.dump(artifact, f'../models_romeo_v8/trading_model_romeo_{timeframe}.pkl')
504
+
505
+ elapsed = time.time() - start
506
+ print(f"Finished training Romeo V8 Super Ensemble in {elapsed:.1f}s")
507
+ print(f"Super ensemble includes {len(trained_models)} algorithms working together")
508
+ print("Features: stacking, calibration, dynamic weighting, cross-validation")
509
+
510
+ return artifact
511
+
512
+
513
+ class SuperEnsemble:
514
+ """Super Ensemble combining 10+ algorithms with advanced collaboration features"""
515
+
516
+ def __init__(self, artifact):
517
+ self.models = artifact['models']
518
+ self.calibrated_models = artifact['calibrated_models']
519
+ self.meta_learner = artifact['meta_learner']
520
+ self.weights = np.array(artifact['weights'])
521
+ self.features = artifact['features']
522
+ self.scaler = artifact['scaler']
523
+ self.pca = artifact['pca']
524
+ self.cv_models = artifact.get('cv_models', {})
525
+ self.cv_predictions = artifact.get('cv_predictions', {})
526
+ self.config = artifact.get('super_ensemble_config', {})
527
+
528
+ def predict_proba(self, X):
529
+ """Generate probability predictions using super ensemble"""
530
+ if X.ndim == 1:
531
+ X = X.reshape(1, -1)
532
+
533
+ # Scale and PCA transform
534
+ X_scaled = self.scaler.transform(X)
535
+ X_pca = self.pca.transform(X_scaled)
536
+
537
+ # Combine original and PCA features
538
+ X_combined = np.hstack([X_scaled, X_pca])
539
+
540
+ # Get predictions from all calibrated models
541
+ model_predictions = []
542
+ for name, model in self.calibrated_models.items():
543
+ try:
544
+ if hasattr(model, 'predict_proba'):
545
+ pred = model.predict_proba(X_combined)[:, 1]
546
+ else:
547
+ pred = model.predict(X_combined).ravel()
548
+ if pred.max() > 1 or pred.min() < 0:
549
+ pred = (pred - pred.min()) / (pred.max() - pred.min())
550
+ model_predictions.append(pred.reshape(-1, 1))
551
+ except Exception as e:
552
+ print(f"Error predicting with {name}: {e}")
553
+ model_predictions.append(np.zeros((X_combined.shape[0], 1)))
554
+
555
+ # Stack predictions for meta-learner
556
+ X_meta = np.hstack(model_predictions)
557
+
558
+ # Meta-learner prediction
559
+ meta_proba = self.meta_learner.predict_proba(X_meta)[:, 1]
560
+
561
+ # Dynamic weighted ensemble
562
+ weighted_proba = np.zeros(X_combined.shape[0])
563
+ for i, pred in enumerate(model_predictions):
564
+ weighted_proba += self.weights[i] * pred.ravel()
565
+
566
+ # Fusion of meta-learner and weighted ensemble
567
+ final_proba = 0.7 * meta_proba + 0.3 * weighted_proba
568
+
569
+ # Confidence calibration using cross-validation ensemble
570
+ if self.cv_models:
571
+ cv_confidence = self._get_cv_confidence(X_combined)
572
+ final_proba = final_proba * cv_confidence + (1 - cv_confidence) * 0.5
573
+
574
+ return np.column_stack([1 - final_proba, final_proba])
575
+
576
+ def predict(self, X, threshold=0.5):
577
+ """Generate binary predictions"""
578
+ proba = self.predict_proba(X)[:, 1]
579
+ return (proba > threshold).astype(int)
580
+
581
+ def _get_cv_confidence(self, X):
582
+ """Get confidence from cross-validation ensemble"""
583
+ cv_probas = []
584
+ for name, models_list in self.cv_models.items():
585
+ fold_probas = []
586
+ for model in models_list:
587
+ try:
588
+ if hasattr(model, 'predict_proba'):
589
+ proba = model.predict_proba(X)[:, 1]
590
+ else:
591
+ proba = model.predict(X).ravel()
592
+ fold_probas.append(proba)
593
+ except:
594
+ continue
595
+ if fold_probas:
596
+ cv_probas.append(np.mean(fold_probas, axis=0))
597
+
598
+ if cv_probas:
599
+ mean_cv_proba = np.mean(cv_probas, axis=0)
600
+ confidence = 1 - np.abs(mean_cv_proba - 0.5) * 2 # Higher confidence when closer to 0 or 1
601
+ return confidence
602
+ else:
603
+ return np.full(X.shape[0], 0.5)
604
+
605
+ def get_feature_importance(self):
606
+ """Get feature importance from tree-based models"""
607
+ importance_dict = {}
608
+ tree_models = ['xgb', 'lgb', 'rf', 'et']
609
+
610
+ for name in tree_models:
611
+ if name in self.models and hasattr(self.models[name], 'feature_importances_'):
612
+ importance_dict[name] = self.models[name].feature_importances_
613
+
614
+ return importance_dict
615
+
616
+ def get_model_weights(self):
617
+ """Get the optimized weights for each model"""
618
+ return dict(zip(self.calibrated_models.keys(), self.weights))
619
+
620
+
621
+ def load_romeo_v8(model_path):
622
+ """Load Romeo V8 super ensemble"""
623
+ artifact = joblib.load(model_path)
624
+ return SuperEnsemble(artifact)
625
+
626
+
627
+ # Add this after the train_romeo_v8 function
628
+ def test_super_ensemble():
629
+ """Test the super ensemble on sample data"""
630
+ try:
631
+ # Load a trained model
632
+ model = load_romeo_v8('models_romeo_v8/trading_model_romeo_15m.pkl')
633
+
634
+ # Load test data
635
+ df = pd.read_csv('data_xauusd_v3/15m_data_v3.csv', parse_dates=['Datetime'])
636
+ df = df.sort_values('Datetime').reset_index(drop=True)
637
+
638
+ if 'target' not in df.columns:
639
+ df['target'] = (df['Close'].shift(-1) > df['Close']).astype(int)
640
+
641
+ # Feature engineering (same as training but without fitting scaler/PCA)
642
+ eng = SuperEnsembleFeatureEngineer()
643
+ df = eng.add_technical_indicators(df)
644
+ df = eng.add_quantum_features(df)
645
+ df = df.fillna(method='bfill').fillna(method='ffill').fillna(0)
646
+
647
+ exclude = ['Datetime', 'Open', 'High', 'Low', 'Close', 'Volume', 'Adj Close']
648
+ feature_cols = [c for c in df.columns if c not in exclude and not c.startswith('target')]
649
+
650
+ # Take last 100 samples for testing
651
+ X_test = df[feature_cols].values[-100:]
652
+ y_test = df['target'].values[-100:]
653
+
654
+ # Make predictions using the SuperEnsemble
655
+ proba = model.predict_proba(X_test)
656
+ preds = model.predict(X_test)
657
+
658
+ accuracy = accuracy_score(y_test, preds)
659
+ auc = roc_auc_score(y_test, proba[:, 1])
660
+
661
+ print(f"Super Ensemble Test Results:")
662
+ print(f"Accuracy: {accuracy:.4f}")
663
+ print(f"AUC: {auc:.4f}")
664
+ print(f"Model weights: {model.get_model_weights()}")
665
+
666
+ return accuracy, auc
667
+
668
+ except Exception as e:
669
+ print(f"Error testing super ensemble: {e}")
670
+ return None, None
671
+
672
+
673
+ # Update main function to include testing
674
+ def main():
675
+ parser = argparse.ArgumentParser()
676
+ parser.add_argument('--data', default='data_xauusd_v3/15m_data_v3.csv')
677
+ parser.add_argument('--timeframe', default='15m')
678
+ parser.add_argument('--mode', choices=['fast', 'full'], default='fast')
679
+ parser.add_argument('--test', action='store_true', help='Test the trained model')
680
+ args = parser.parse_args()
681
+
682
+ art = train_romeo_v8(args.data, timeframe=args.timeframe, mode=args.mode)
683
+ print('Saved artifact keys:', list(art.keys()))
684
+
685
+ if args.test:
686
+ print("\nTesting super ensemble...")
687
+ acc, auc = test_super_ensemble()
688
+ if acc is not None:
689
+ print(f"✓ Test completed - Accuracy: {acc:.4f}, AUC: {auc:.4f}")
690
+
691
+
692
+ if __name__ == '__main__':
693
+ main()