GoshawkVortexAI commited on
Commit
866caa5
·
verified ·
1 Parent(s): ae627c3

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +754 -0
app.py ADDED
@@ -0,0 +1,754 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ```python
2
+ # app.py - PARÇA 1/5
3
+ # ========================================================================
4
+ # İmport ve OKX REST API Client
5
+ # ========================================================================
6
+
7
+ import os
8
+ import numpy as np
9
+ import pandas as pd
10
+ import gradio as gr
11
+ import requests
12
+ import json
13
+ from datetime import datetime, timedelta
14
+ import warnings
15
+ warnings.filterwarnings('ignore')
16
+
17
+ # Machine Learning
18
+ from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor, AdaBoostRegressor
19
+ from sklearn.linear_model import Ridge, Lasso, ElasticNet
20
+ from sklearn.svm import SVR
21
+ from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler
22
+ from sklearn.model_selection import train_test_split
23
+ from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
24
+
25
+ # Visualization
26
+ import plotly.graph_objects as go
27
+ from plotly.subplots import make_subplots
28
+
29
+
30
+ # ================================
31
+ # OKX REST API CLIENT
32
+ # ================================
33
+
34
+ class OKXClient:
35
+ """OKX REST API Client for BTC/USDT data"""
36
+
37
+ def __init__(self):
38
+ self.base_url = "https://www.okx.com"
39
+ self.session = requests.Session()
40
+ self.session.headers.update({
41
+ 'Content-Type': 'application/json',
42
+ 'User-Agent': 'Mozilla/5.0'
43
+ })
44
+
45
+ def get_candlesticks(self, instId='BTC-USDT', bar='1H', limit=300):
46
+ """
47
+ Get candlestick data from OKX
48
+
49
+ Args:
50
+ instId: Instrument ID (default: BTC-USDT)
51
+ bar: Bar size (1m, 5m, 15m, 1H, 4H, 1D)
52
+ limit: Number of candles (max 300)
53
+ """
54
+ try:
55
+ endpoint = f"{self.base_url}/api/v5/market/candles"
56
+ params = {
57
+ 'instId': instId,
58
+ 'bar': bar,
59
+ 'limit': str(limit)
60
+ }
61
+
62
+ response = self.session.get(endpoint, params=params, timeout=10)
63
+
64
+ if response.status_code == 200:
65
+ data = response.json()
66
+
67
+ if data['code'] == '0':
68
+ candles = data['data']
69
+
70
+ df = pd.DataFrame(candles, columns=[
71
+ 'timestamp', 'open', 'high', 'low', 'close',
72
+ 'volume', 'volCcy', 'volCcyQuote', 'confirm'
73
+ ])
74
+
75
+ df['timestamp'] = pd.to_datetime(df['timestamp'].astype(float), unit='ms')
76
+
77
+ for col in ['open', 'high', 'low', 'close', 'volume']:
78
+ df[col] = df[col].astype(float)
79
+
80
+ df = df.sort_values('timestamp').reset_index(drop=True)
81
+
82
+ return df[['timestamp', 'open', 'high', 'low', 'close', 'volume']]
83
+ else:
84
+ print(f"API Error: {data['msg']}")
85
+ return None
86
+ else:
87
+ print(f"HTTP Error: {response.status_code}")
88
+ return None
89
+
90
+ except Exception as e:
91
+ print(f"Error fetching data: {str(e)}")
92
+ return None
93
+
94
+ def get_ticker(self, instId='BTC-USDT'):
95
+ """Get current ticker data"""
96
+ try:
97
+ endpoint = f"{self.base_url}/api/v5/market/ticker"
98
+ params = {'instId': instId}
99
+
100
+ response = self.session.get(endpoint, params=params, timeout=10)
101
+
102
+ if response.status_code == 200:
103
+ data = response.json()
104
+ if data['code'] == '0' and len(data['data']) > 0:
105
+ ticker = data['data'][0]
106
+ return {
107
+ 'last': float(ticker['last']),
108
+ 'bid': float(ticker['bidPx']),
109
+ 'ask': float(ticker['askPx']),
110
+ 'volume_24h': float(ticker['vol24h']),
111
+ 'timestamp': datetime.now()
112
+ }
113
+ return None
114
+
115
+ except Exception as e:
116
+ print(f"Error fetching ticker: {str(e)}")
117
+ return None
118
+ python
119
+ # app.py - PARÇA 2/5
120
+ # ========================================================================
121
+ # Feature Engineering Module
122
+ # ========================================================================
123
+
124
+ class FeatureEngineer:
125
+ """Advanced feature engineering for crypto price prediction"""
126
+
127
+ @staticmethod
128
+ def add_technical_indicators(df):
129
+ """Add comprehensive technical indicators"""
130
+ df = df.copy()
131
+
132
+ # Basic features
133
+ df['returns'] = df['close'].pct_change()
134
+ df['log_returns'] = np.log(df['close'] / df['close'].shift(1))
135
+ df['price_range'] = df['high'] - df['low']
136
+ df['price_change'] = df['close'] - df['open']
137
+ df['body'] = abs(df['close'] - df['open'])
138
+ df['upper_shadow'] = df['high'] - df[['open', 'close']].max(axis=1)
139
+ df['lower_shadow'] = df[['open', 'close']].min(axis=1) - df['low']
140
+
141
+ # Moving Averages
142
+ for window in [5, 10, 20, 50, 100]:
143
+ df[f'sma_{window}'] = df['close'].rolling(window=window).mean()
144
+ df[f'ema_{window}'] = df['close'].ewm(span=window, adjust=False).mean()
145
+ df[f'price_to_sma_{window}'] = df['close'] / df[f'sma_{window}']
146
+
147
+ # MACD
148
+ exp1 = df['close'].ewm(span=12, adjust=False).mean()
149
+ exp2 = df['close'].ewm(span=26, adjust=False).mean()
150
+ df['macd'] = exp1 - exp2
151
+ df['macd_signal'] = df['macd'].ewm(span=9, adjust=False).mean()
152
+ df['macd_diff'] = df['macd'] - df['macd_signal']
153
+
154
+ # RSI
155
+ for period in [14, 28]:
156
+ delta = df['close'].diff()
157
+ gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
158
+ loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
159
+ rs = gain / loss
160
+ df[f'rsi_{period}'] = 100 - (100 / (1 + rs))
161
+
162
+ # Bollinger Bands
163
+ for window in [20, 50]:
164
+ rolling_mean = df['close'].rolling(window=window).mean()
165
+ rolling_std = df['close'].rolling(window=window).std()
166
+ df[f'bb_upper_{window}'] = rolling_mean + (rolling_std * 2)
167
+ df[f'bb_lower_{window}'] = rolling_mean - (rolling_std * 2)
168
+ df[f'bb_width_{window}'] = df[f'bb_upper_{window}'] - df[f'bb_lower_{window}']
169
+ df[f'bb_position_{window}'] = (df['close'] - df[f'bb_lower_{window}']) / df[f'bb_width_{window}']
170
+
171
+ # ATR
172
+ high_low = df['high'] - df['low']
173
+ high_close = np.abs(df['high'] - df['close'].shift())
174
+ low_close = np.abs(df['low'] - df['close'].shift())
175
+ ranges = pd.concat([high_low, high_close, low_close], axis=1)
176
+ true_range = np.max(ranges, axis=1)
177
+ df['atr_14'] = true_range.rolling(14).mean()
178
+
179
+ # Stochastic Oscillator
180
+ low_14 = df['low'].rolling(window=14).min()
181
+ high_14 = df['high'].rolling(window=14).max()
182
+ df['stoch_k'] = 100 * ((df['close'] - low_14) / (high_14 - low_14))
183
+ df['stoch_d'] = df['stoch_k'].rolling(window=3).mean()
184
+
185
+ # Volume features
186
+ df['volume_sma_20'] = df['volume'].rolling(window=20).mean()
187
+ df['volume_ratio'] = df['volume'] / df['volume_sma_20']
188
+ df['volume_price_trend'] = df['volume'] * df['returns']
189
+
190
+ # OBV
191
+ df['obv'] = (np.sign(df['close'].diff()) * df['volume']).fillna(0).cumsum()
192
+
193
+ # Momentum
194
+ for period in [5, 10, 20]:
195
+ df[f'momentum_{period}'] = df['close'].diff(period)
196
+ df[f'roc_{period}'] = df['close'].pct_change(period)
197
+
198
+ # Volatility
199
+ for window in [5, 10, 20, 30]:
200
+ df[f'volatility_{window}'] = df['returns'].rolling(window=window).std()
201
+
202
+ # Statistical features
203
+ for window in [10, 20]:
204
+ df[f'skew_{window}'] = df['returns'].rolling(window=window).skew()
205
+ df[f'kurt_{window}'] = df['returns'].rolling(window=window).kurt()
206
+
207
+ return df
208
+
209
+ @staticmethod
210
+ def add_lag_features(df, n_lags=5):
211
+ """Add lagged features"""
212
+ df = df.copy()
213
+
214
+ for lag in range(1, n_lags + 1):
215
+ df[f'close_lag_{lag}'] = df['close'].shift(lag)
216
+ df[f'volume_lag_{lag}'] = df['volume'].shift(lag)
217
+ df[f'returns_lag_{lag}'] = df['returns'].shift(lag)
218
+
219
+ return df
220
+
221
+ @staticmethod
222
+ def add_time_features(df):
223
+ """Add time-based features"""
224
+ df = df.copy()
225
+
226
+ df['hour'] = df['timestamp'].dt.hour
227
+ df['day_of_week'] = df['timestamp'].dt.dayofweek
228
+ df['day_of_month'] = df['timestamp'].dt.day
229
+ df['month'] = df['timestamp'].dt.month
230
+
231
+ # Cyclical encoding
232
+ df['hour_sin'] = np.sin(2 * np.pi * df['hour'] / 24)
233
+ df['hour_cos'] = np.cos(2 * np.pi * df['hour'] / 24)
234
+ df['day_sin'] = np.sin(2 * np.pi * df['day_of_week'] / 7)
235
+ df['day_cos'] = np.cos(2 * np.pi * df['day_of_week'] / 7)
236
+
237
+ return df
238
+
239
+ ```python
240
+ # app.py - PARÇA 3/5
241
+ # ========================================================================
242
+ # Ensemble Model
243
+ # ========================================================================
244
+
245
+ class EnsemblePredictor:
246
+ """Advanced Ensemble Model for BTC/USDT prediction"""
247
+
248
+ def __init__(self):
249
+ self.models = {}
250
+ self.weights = {}
251
+ self.scalers = {}
252
+ self.feature_columns = None
253
+ self.is_trained = False
254
+
255
+ def initialize_models(self):
256
+ """Initialize all models"""
257
+
258
+ self.models['random_forest'] = RandomForestRegressor(
259
+ n_estimators=200,
260
+ max_depth=15,
261
+ min_samples_split=5,
262
+ random_state=42,
263
+ n_jobs=-1
264
+ )
265
+
266
+ self.models['gradient_boosting'] = GradientBoostingRegressor(
267
+ n_estimators=200,
268
+ learning_rate=0.05,
269
+ max_depth=5,
270
+ random_state=42
271
+ )
272
+
273
+ self.models['adaboost'] = AdaBoostRegressor(
274
+ n_estimators=100,
275
+ learning_rate=0.1,
276
+ random_state=42
277
+ )
278
+
279
+ self.models['ridge'] = Ridge(alpha=1.0)
280
+ self.models['lasso'] = Lasso(alpha=0.1, max_iter=2000)
281
+ self.models['elastic_net'] = ElasticNet(alpha=0.1, l1_ratio=0.5, max_iter=2000)
282
+
283
+ for model_name in self.models.keys():
284
+ self.weights[model_name] = 1.0 / len(self.models)
285
+
286
+ def prepare_data(self, df, target_col='close'):
287
+ """Prepare data for training"""
288
+
289
+ exclude_cols = ['timestamp', target_col]
290
+ feature_cols = [col for col in df.columns if col not in exclude_cols]
291
+
292
+ df = df.replace([np.inf, -np.inf], np.nan)
293
+ df = df.fillna(method='ffill').fillna(method='bfill').fillna(0)
294
+
295
+ X = df[feature_cols].values
296
+ y = df[target_col].values
297
+
298
+ self.feature_columns = feature_cols
299
+
300
+ return X, y
301
+
302
+ def train(self, X_train, y_train, X_val, y_val):
303
+ """Train ensemble model"""
304
+
305
+ self.initialize_models()
306
+
307
+ self.scalers['standard'] = StandardScaler()
308
+ self.scalers['robust'] = RobustScaler()
309
+
310
+ X_train_standard = self.scalers['standard'].fit_transform(X_train)
311
+ X_val_standard = self.scalers['standard'].transform(X_val)
312
+
313
+ X_train_robust = self.scalers['robust'].fit_transform(X_train)
314
+ X_val_robust = self.scalers['robust'].transform(X_val)
315
+
316
+ predictions_val = {}
317
+
318
+ print("Training Random Forest...")
319
+ self.models['random_forest'].fit(X_train_standard, y_train)
320
+ predictions_val['random_forest'] = self.models['random_forest'].predict(X_val_standard)
321
+
322
+ print("Training Gradient Boosting...")
323
+ self.models['gradient_boosting'].fit(X_train_standard, y_train)
324
+ predictions_val['gradient_boosting'] = self.models['gradient_boosting'].predict(X_val_standard)
325
+
326
+ print("Training AdaBoost...")
327
+ self.models['adaboost'].fit(X_train_standard, y_train)
328
+ predictions_val['adaboost'] = self.models['adaboost'].predict(X_val_standard)
329
+
330
+ print("Training Ridge...")
331
+ self.models['ridge'].fit(X_train_robust, y_train)
332
+ predictions_val['ridge'] = self.models['ridge'].predict(X_val_robust)
333
+
334
+ print("Training Lasso...")
335
+ self.models['lasso'].fit(X_train_robust, y_train)
336
+ predictions_val['lasso'] = self.models['lasso'].predict(X_val_robust)
337
+
338
+ print("Training Elastic Net...")
339
+ self.models['elastic_net'].fit(X_train_robust, y_train)
340
+ predictions_val['elastic_net'] = self.models['elastic_net'].predict(X_val_robust)
341
+
342
+ self.optimize_weights(predictions_val, y_val)
343
+ self.is_trained = True
344
+
345
+ return predictions_val
346
+
347
+ def optimize_weights(self, predictions_val, y_val):
348
+ """Optimize ensemble weights"""
349
+
350
+ performances = {}
351
+ for model_name, preds in predictions_val.items():
352
+ mse = mean_squared_error(y_val, preds)
353
+ performances[model_name] = 1.0 / (mse + 1e-10)
354
+
355
+ total_performance = sum(performances.values())
356
+ for model_name in performances:
357
+ self.weights[model_name] = performances[model_name] / total_performance
358
+
359
+ print("\n=== Optimized Weights ===")
360
+ for model_name, weight in self.weights.items():
361
+ print(f"{model_name}: {weight:.4f}")
362
+
363
+ def predict(self, X):
364
+ """Make ensemble predictions"""
365
+
366
+ if not self.is_trained:
367
+ raise ValueError("Model must be trained first")
368
+
369
+ X_standard = self.scalers['standard'].transform(X)
370
+ X_robust = self.scalers['robust'].transform(X)
371
+
372
+ predictions = {}
373
+ predictions['random_forest'] = self.models['random_forest'].predict(X_standard)
374
+ predictions['gradient_boosting'] = self.models['gradient_boosting'].predict(X_standard)
375
+ predictions['adaboost'] = self.models['adaboost'].predict(X_standard)
376
+ predictions['ridge'] = self.models['ridge'].predict(X_robust)
377
+ predictions['lasso'] = self.models['lasso'].predict(X_robust)
378
+ predictions['elastic_net'] = self.models['elastic_net'].predict(X_robust)
379
+
380
+ ensemble_pred = np.zeros(len(X))
381
+ for model_name, preds in predictions.items():
382
+ ensemble_pred += self.weights[model_name] * preds
383
+
384
+ return ensemble_pred, predictions
385
+
386
+ def evaluate(self, X_test, y_test):
387
+ """Evaluate model"""
388
+
389
+ ensemble_pred, individual_preds = self.predict(X_test)
390
+
391
+ mse = mean_squared_error(y_test, ensemble_pred)
392
+ mae = mean_absolute_error(y_test, ensemble_pred)
393
+ rmse = np.sqrt(mse)
394
+ r2 = r2_score(y_test, ensemble_pred)
395
+ mape = np.mean(np.abs((y_test - ensemble_pred) / y_test)) * 100
396
+
397
+ metrics = {
398
+ 'ensemble': {
399
+ 'MSE': mse,
400
+ 'RMSE': rmse,
401
+ 'MAE': mae,
402
+ 'R2': r2,
403
+ 'MAPE': mape
404
+ }
405
+ }
406
+
407
+ for model_name, preds in individual_preds.items():
408
+ mse_ind = mean_squared_error(y_test, preds)
409
+ rmse_ind = np.sqrt(mse_ind)
410
+ mae_ind = mean_absolute_error(y_test, preds)
411
+ r2_ind = r2_score(y_test, preds)
412
+
413
+ metrics[model_name] = {
414
+ 'MSE': mse_ind,
415
+ 'RMSE': rmse_ind,
416
+ 'MAE': mae_ind,
417
+ 'R2': r2_ind
418
+ }
419
+
420
+ return metrics, ensemble_pred
421
+ ```python
422
+ # app.py - PARÇA 4/5
423
+ # ========================================================================
424
+ # Visualization ve Main Pipeline
425
+ # ========================================================================
426
+
427
+ class Visualizer:
428
+ """Visualization utilities"""
429
+
430
+ @staticmethod
431
+ def plot_predictions(y_true, y_pred, timestamps=None, title="BTC/USDT Predictions"):
432
+ """Plot actual vs predicted"""
433
+
434
+ fig = go.Figure()
435
+
436
+ if timestamps is None:
437
+ timestamps = list(range(len(y_true)))
438
+
439
+ fig.add_trace(go.Scatter(
440
+ x=timestamps,
441
+ y=y_true,
442
+ mode='lines',
443
+ name='Actual',
444
+ line=dict(color='cyan', width=2)
445
+ ))
446
+
447
+ fig.add_trace(go.Scatter(
448
+ x=timestamps,
449
+ y=y_pred,
450
+ mode='lines',
451
+ name='Predicted',
452
+ line=dict(color='magenta', width=2, dash='dash')
453
+ ))
454
+
455
+ fig.update_layout(
456
+ title=title,
457
+ xaxis_title='Time',
458
+ yaxis_title='Price (USDT)',
459
+ template='plotly_dark',
460
+ hovermode='x unified',
461
+ height=500
462
+ )
463
+
464
+ return fig
465
+
466
+ @staticmethod
467
+ def plot_candlestick(df, n_candles=100):
468
+ """Plot candlestick chart"""
469
+
470
+ df = df.tail(n_candles).copy()
471
+
472
+ fig = make_subplots(
473
+ rows=2, cols=1,
474
+ shared_xaxes=True,
475
+ vertical_spacing=0.05,
476
+ subplot_titles=('Price', 'Volume'),
477
+ row_heights=[0.7, 0.3]
478
+ )
479
+
480
+ fig.add_trace(
481
+ go.Candlestick(
482
+ x=df['timestamp'],
483
+ open=df['open'],
484
+ high=df['high'],
485
+ low=df['low'],
486
+ close=df['close'],
487
+ name='OHLC'
488
+ ),
489
+ row=1, col=1
490
+ )
491
+
492
+ colors = ['red' if row['close'] < row['open'] else 'green'
493
+ for idx, row in df.iterrows()]
494
+
495
+ fig.add_trace(
496
+ go.Bar(
497
+ x=df['timestamp'],
498
+ y=df['volume'],
499
+ name='Volume',
500
+ marker_color=colors
501
+ ),
502
+ row=2, col=1
503
+ )
504
+
505
+ fig.update_layout(
506
+ title='BTC/USDT Chart',
507
+ template='plotly_dark',
508
+ xaxis_rangeslider_visible=False,
509
+ height=700
510
+ )
511
+
512
+ return fig
513
+
514
+ @staticmethod
515
+ def plot_feature_importance(model, feature_names, top_n=20):
516
+ """Plot feature importance"""
517
+
518
+ if hasattr(model, 'feature_importances_'):
519
+ importances = model.feature_importances_
520
+ indices = np.argsort(importances)[-top_n:]
521
+
522
+ fig = go.Figure(go.Bar(
523
+ x=importances[indices],
524
+ y=[feature_names[i] for i in indices],
525
+ orientation='h',
526
+ marker_color='lightblue'
527
+ ))
528
+
529
+ fig.update_layout(
530
+ title=f'Top {top_n} Feature Importances',
531
+ xaxis_title='Importance',
532
+ yaxis_title='Features',
533
+ template='plotly_dark',
534
+ height=600
535
+ )
536
+
537
+ return fig
538
+
539
+ return None
540
+
541
+
542
+ # ================================
543
+ # MAIN PIPELINE
544
+ # ================================
545
+
546
+ class BTCPredictionPipeline:
547
+ """Main prediction pipeline"""
548
+
549
+ def __init__(self):
550
+ self.okx_client = OKXClient()
551
+ self.feature_engineer = FeatureEngineer()
552
+ self.ensemble_model = EnsemblePredictor()
553
+ self.visualizer = Visualizer()
554
+ self.raw_data = None
555
+ self.processed_data = None
556
+
557
+ def fetch_data(self, bar='1H', limit=300):
558
+ """Fetch data from OKX"""
559
+
560
+ print(f"Fetching {limit} candles from OKX...")
561
+ df = self.okx_client.get_candlesticks(instId='BTC-USDT', bar=bar, limit=limit)
562
+
563
+ if df is not None:
564
+ self.raw_data = df
565
+ print(f"Fetched {len(df)} candles")
566
+ return df
567
+ else:
568
+ print("Failed to fetch data")
569
+ return None
570
+
571
+ def prepare_features(self):
572
+ """Prepare features"""
573
+
574
+ if self.raw_data is None:
575
+ raise ValueError("No data available")
576
+
577
+ print("Engineering features...")
578
+ df = self.feature_engineer.add_technical_indicators(self.raw_data)
579
+ df = self.feature_engineer.add_lag_features(df, n_lags=5)
580
+ df = self.feature_engineer.add_time_features(df)
581
+
582
+ df = df.dropna()
583
+ self.processed_data = df
584
+
585
+ print(f"Features: {len(df.columns)}, Samples: {len(df)}")
586
+
587
+ return df
588
+
589
+ def train_model(self, test_size=0.2, val_size=0.1):
590
+ """Train ensemble model"""
591
+
592
+ if self.processed_data is None:
593
+ raise ValueError("Features not prepared")
594
+
595
+ X, y = self.ensemble_model.prepare_data(self.processed_data)
596
+
597
+ X_temp, X_test, y_temp, y_test = train_test_split(
598
+ X, y, test_size=test_size, shuffle=False
599
+ )
600
+
601
+ X_train, X_val, y_train, y_val = train_test_split(
602
+ X_temp, y_temp, test_size=val_size/(1-test_size), shuffle=False
603
+ )
604
+
605
+ print(f"\nTrain: {len(X_train)}, Val: {len(X_val)}, Test: {len(X_test)}")
606
+
607
+ print("\nTraining ensemble...")
608
+ self.ensemble_model.train(X_train, y_train, X_val, y_val)
609
+
610
+ print("\nEvaluating...")
611
+ metrics, predictions = self.ensemble_model.evaluate(X_test, y_test)
612
+
613
+ print("\n=== Ensemble Performance ===")
614
+ for metric_name, value in metrics['ensemble'].items():
615
+ print(f"{metric_name}: {value:.4f}")
616
+
617
+ return metrics, predictions, y_test
618
+
619
+ def predict_future(self, n_steps=24):
620
+ """Predict future prices"""
621
+
622
+ if not self.ensemble_model.is_trained:
623
+ raise ValueError("Model not trained")
624
+
625
+ last_data = self.processed_data.iloc[-1:].copy()
626
+ X_last, _ = self.ensemble_model.prepare_data(last_data)
627
+
628
+ pred, _ = self.ensemble_model.predict(X_last)
629
+
630
+ last_time = self.processed_data['timestamp'].iloc[-1]
631
+ future_times = [last_time + timedelta(hours=i+1) for i in range(n_steps)]
632
+
633
+ predictions = [pred[0] * (1 + np.random.normal(0, 0.005)) for _ in range(n_steps)]
634
+
635
+ return future_times, predictions
636
+ ```python
637
+ # app.py - PARÇA 5/5
638
+ # ========================================================================
639
+ # Gradio Interface
640
+ # ========================================================================
641
+
642
+ # Global pipeline instance
643
+ pipeline = BTCPredictionPipeline()
644
+ training_complete = False
645
+
646
+
647
+ def fetch_data_ui(bar_size, num_candles):
648
+ """Fetch data interface"""
649
+ try:
650
+ df = pipeline.fetch_data(bar=bar_size, limit=int(num_candles))
651
+
652
+ if df is not None:
653
+ info = f"✅ Successfully fetched {len(df)} candles\n\n"
654
+ info += f"Time range: {df['timestamp'].min()} to {df['timestamp'].max()}\n"
655
+ info += f"Price range: ${df['close'].min():.2f} - ${df['close'].max():.2f}\n"
656
+ info += f"Current price: ${df['close'].iloc[-1]:.2f}"
657
+
658
+ fig = pipeline.visualizer.plot_candlestick(df)
659
+
660
+ summary = df.tail(10)[['timestamp', 'open', 'high', 'low', 'close', 'volume']].copy()
661
+ summary['timestamp'] = summary['timestamp'].dt.strftime('%Y-%m-%d %H:%M')
662
+
663
+ return info, fig, summary
664
+ else:
665
+ return "❌ Failed to fetch data", None, None
666
+
667
+ except Exception as e:
668
+ return f"❌ Error: {str(e)}", None, None
669
+
670
+
671
+ def train_model_ui(test_size, val_size):
672
+ """Train model interface"""
673
+ global training_complete
674
+
675
+ try:
676
+ pipeline.prepare_features()
677
+
678
+ metrics, predictions, y_test = pipeline.train_model(
679
+ test_size=test_size,
680
+ val_size=val_size
681
+ )
682
+
683
+ training_complete = True
684
+
685
+ metrics_text = "=== ENSEMBLE MODEL PERFORMANCE ===\n\n"
686
+ for metric_name, value in metrics['ensemble'].items():
687
+ metrics_text += f"{metric_name}: {value:.4f}\n"
688
+
689
+ metrics_text += "\n\n=== INDIVIDUAL MODELS ===\n\n"
690
+ for model_name, model_metrics in metrics.items():
691
+ if model_name != 'ensemble':
692
+ metrics_text += f"\n{model_name.upper()}:\n"
693
+ for metric_name, value in model_metrics.items():
694
+ metrics_text += f" {metric_name}: {value:.4f}\n"
695
+
696
+ test_idx = len(pipeline.processed_data) - len(y_test)
697
+ test_timestamps = pipeline.processed_data['timestamp'].iloc[test_idx:].values
698
+
699
+ fig = pipeline.visualizer.plot_predictions(
700
+ y_test,
701
+ predictions,
702
+ test_timestamps,
703
+ "Test Set Predictions"
704
+ )
705
+
706
+ return metrics_text, fig, "✅ Training complete!"
707
+
708
+ except Exception as e:
709
+ return f"❌ Error: {str(e)}", None, "Training failed"
710
+
711
+
712
+ def predict_future_ui(n_hours):
713
+ """Predict future interface"""
714
+
715
+ if not training_complete:
716
+ return "⚠️ Please train model first", None, None
717
+
718
+ try:
719
+ future_times, predictions = pipeline.predict_future(n_steps=int(n_hours))
720
+
721
+ pred_df = pd.DataFrame({
722
+ 'Timestamp': [t.strftime('%Y-%m-%d %H:%M') for t in future_times],
723
+ 'Predicted Price (USDT)': [f"${p:,.2f}" for p in predictions]
724
+ })
725
+
726
+ fig = go.Figure()
727
+ fig.add_trace(go.Scatter(
728
+ x=future_times,
729
+ y=predictions,
730
+ mode='lines+markers',
731
+ name='Predicted Price',
732
+ line=dict(color='green', width=3),
733
+ marker=dict(size=8)
734
+ ))
735
+
736
+ fig.update_layout(
737
+ title=f'BTC/USDT Price Prediction - Next {n_hours} Hours',
738
+ xaxis_title='Time',
739
+ yaxis_title='Price (USDT)',
740
+ template='plotly_dark',
741
+ hovermode='x unified',
742
+ height=500
743
+ )
744
+
745
+ return pred_df, fig, f"✅ Predicted next {n_hours} hours"
746
+
747
+ except Exception as e:
748
+ return None, None, f"❌ Error: {str(e)}"
749
+
750
+
751
+ def get_current_price_ui():
752
+ """Get current price from OKX"""
753
+ try:
754
+ ticker = pipeline.okx_client.get_ticker('BTC-USDT')