Alvin3y1 commited on
Commit
00ec666
·
verified ·
1 Parent(s): ad70443

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +92 -68
app.py CHANGED
@@ -6,8 +6,10 @@ import math
6
  import aiohttp
7
  import pandas as pd
8
  import numpy as np
 
9
  from aiohttp import web
10
- from sklearn.ensemble import RandomForestRegressor
 
11
  from concurrent.futures import ThreadPoolExecutor
12
 
13
  SYMBOL_KRAKEN = "BTC/USD"
@@ -15,7 +17,8 @@ PORT = 7860
15
  BROADCAST_RATE = 1.0
16
  PREDICTION_HORIZON = 100
17
  MAX_HISTORY = 5000
18
- TRAIN_INTERVAL = 300
 
19
 
20
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')
21
 
@@ -23,7 +26,8 @@ market_state = {
23
  "ohlc_history": [],
24
  "ready": False,
25
  "model": None,
26
- "model_residuals": None,
 
27
  "last_training_time": 0,
28
  "last_price": 0,
29
  "price_change": 0
@@ -33,7 +37,7 @@ connected_clients = set()
33
  executor = ThreadPoolExecutor(max_workers=1)
34
 
35
  def calculate_indicators(candles):
36
- if len(candles) < 100:
37
  return None
38
 
39
  df = pd.DataFrame(candles)
@@ -77,16 +81,17 @@ def calculate_indicators(candles):
77
  df['hour_sin'] = np.sin(2 * np.pi * df['datetime'].dt.hour / 24)
78
  df['hour_cos'] = np.cos(2 * np.pi * df['datetime'].dt.hour / 24)
79
 
80
- for lag in [1, 2, 3, 5, 8]:
81
- df[f'rsi_lag{lag}'] = df['rsi'].shift(lag)
82
- df[f'macd_hist_lag{lag}'] = df['macd_hist'].shift(lag)
83
- df[f'log_ret_lag{lag}'] = df['log_ret'].shift(lag)
84
- df[f'vol_change_lag{lag}'] = df['vol_change'].shift(lag)
85
 
86
- return df
 
 
 
 
 
87
 
88
  def train_model(df):
89
- logging.info(f"Training ML Model on {len(df)} candles...")
90
 
91
  feature_cols = [
92
  'rsi', 'macd_hist', 'atr',
@@ -96,50 +101,65 @@ def train_model(df):
96
  'hour_sin', 'hour_cos'
97
  ]
98
 
99
- for lag in [1, 2, 3, 5, 8]:
100
- feature_cols.extend([
101
- f'rsi_lag{lag}', f'macd_hist_lag{lag}',
102
- f'log_ret_lag{lag}', f'vol_change_lag{lag}'
103
- ])
104
 
105
- data = df.dropna().copy()
 
106
 
107
- target_cols_dict = {}
108
- target_names = []
109
 
110
- for i in range(1, PREDICTION_HORIZON + 1):
111
- col_name = f'target_return_{i}'
112
- target_cols_dict[col_name] = (data['close'].shift(-i) - data['close']) / data['close']
113
- target_names.append(col_name)
 
114
 
115
- targets_df = pd.DataFrame(target_cols_dict, index=data.index)
116
- data = pd.concat([data, targets_df], axis=1).dropna()
117
-
118
- if len(data) < 200:
119
- return None, None
120
-
121
- X = data[feature_cols].values
122
- y = data[target_names].values
123
 
124
- model = RandomForestRegressor(
125
- n_estimators=200,
126
- max_depth=25,
127
- min_samples_split=5,
128
- min_samples_leaf=2,
129
- max_features='sqrt',
130
- n_jobs=-1,
131
- random_state=42
132
- )
133
- model.fit(X, y)
134
 
135
- predictions = model.predict(X)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  residuals = y - predictions
137
- residual_std = np.std(residuals, axis=0)
138
 
139
- return model, residual_std
140
 
141
- def get_prediction(df, model, residual_std):
142
- if model is None or residual_std is None:
143
  return []
144
 
145
  feature_cols = [
@@ -150,32 +170,34 @@ def get_prediction(df, model, residual_std):
150
  'hour_sin', 'hour_cos'
151
  ]
152
 
153
- for lag in [1, 2, 3, 5, 8]:
154
- feature_cols.extend([
155
- f'rsi_lag{lag}', f'macd_hist_lag{lag}',
156
- f'log_ret_lag{lag}', f'vol_change_lag{lag}'
157
- ])
158
-
159
- last_row = df.iloc[[-1]][feature_cols]
160
 
161
- if last_row.isnull().values.any():
162
  return []
163
-
164
- predicted_returns = model.predict(last_row.values)[0]
 
 
 
165
 
166
  current_price = df.iloc[-1]['close']
167
  current_time = int(df.iloc[-1]['time'])
168
 
169
  pred_data = []
170
  confidence_multiplier = 1.96
171
-
 
 
 
172
  for i, pct_change in enumerate(predicted_returns):
173
  future_price = current_price * (1 + pct_change)
174
 
175
- sigma = residual_std[i]
176
- upper_bound = future_price * (1 + (sigma * confidence_multiplier))
177
- lower_bound = future_price * (1 - (sigma * confidence_multiplier))
178
-
 
 
179
  pred_data.append({
180
  "time": current_time + ((i + 1) * 60),
181
  "value": float(future_price),
@@ -190,23 +212,25 @@ async def process_market_data():
190
  return {"error": "Initializing..."}
191
 
192
  df = calculate_indicators(market_state['ohlc_history'])
193
- if df is None or len(df) < 100:
194
  return {"error": "Not enough data"}
195
 
196
  if market_state['model'] is None or (time.time() - market_state['last_training_time'] > TRAIN_INTERVAL):
197
  try:
198
  loop = asyncio.get_running_loop()
199
- model, res_std = await loop.run_in_executor(executor, train_model, df)
200
  if model is not None:
201
  market_state['model'] = model
202
- market_state['model_residuals'] = res_std
 
203
  market_state['last_training_time'] = time.time()
 
204
  except Exception as e:
205
  logging.error(f"Training failed: {e}")
206
 
207
  predictions = []
208
  try:
209
- predictions = get_prediction(df, market_state['model'], market_state['model_residuals'])
210
  except Exception as e:
211
  logging.error(f"Prediction failed: {e}")
212
 
@@ -231,7 +255,7 @@ async def process_market_data():
231
  "price": last_close,
232
  "change": round(price_change, 2),
233
  "rsi": round(float(last_row.get('rsi', 0)), 1) if pd.notna(last_row.get('rsi')) else 0,
234
- "macd": round(float(last_row.get('macd', 0)), 2) if pd.notna(last_row.get('macd')) else 0,
235
  "atr": round(float(last_row.get('atr', 0)), 2) if pd.notna(last_row.get('atr')) else 0,
236
  "volume": round(float(last_row.get('volume', 0)), 2) if pd.notna(last_row.get('volume')) else 0
237
  }
@@ -243,7 +267,7 @@ HTML_PAGE = """
243
  <head>
244
  <meta charset="UTF-8">
245
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
246
- <title>BTC/USD AI Predictor</title>
247
  <script src="https://unpkg.com/lightweight-charts@4.1.1/dist/lightweight-charts.standalone.production.js"></script>
248
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
249
  <style>
@@ -391,7 +415,7 @@ HTML_PAGE = """
391
  <span><div class="dot" style="background: #00ff88"></div>Price</span>
392
  <span><div class="dot" style="background: #2962FF"></div>EMA 20</span>
393
  <span><div class="dot" style="background: #26a69a; opacity: 0.5"></div>Bollinger</span>
394
- <span><div class="dot" style="background: #bf5af2"></div>AI + 95% Conf</span>
395
  </div>
396
  <div class="prediction-badge">AI Forecast: 100 candles</div>
397
  </div>
 
6
  import aiohttp
7
  import pandas as pd
8
  import numpy as np
9
+ import tensorflow as tf
10
  from aiohttp import web
11
+ from tensorflow.keras import layers, models, callbacks
12
+ from sklearn.preprocessing import StandardScaler
13
  from concurrent.futures import ThreadPoolExecutor
14
 
15
  SYMBOL_KRAKEN = "BTC/USD"
 
17
  BROADCAST_RATE = 1.0
18
  PREDICTION_HORIZON = 100
19
  MAX_HISTORY = 5000
20
+ TRAIN_INTERVAL = 600
21
+ LOOKBACK_WINDOW = 60
22
 
23
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')
24
 
 
26
  "ohlc_history": [],
27
  "ready": False,
28
  "model": None,
29
+ "scaler": None,
30
+ "model_residuals": 0.0,
31
  "last_training_time": 0,
32
  "last_price": 0,
33
  "price_change": 0
 
37
  executor = ThreadPoolExecutor(max_workers=1)
38
 
39
  def calculate_indicators(candles):
40
+ if len(candles) < LOOKBACK_WINDOW + PREDICTION_HORIZON:
41
  return None
42
 
43
  df = pd.DataFrame(candles)
 
81
  df['hour_sin'] = np.sin(2 * np.pi * df['datetime'].dt.hour / 24)
82
  df['hour_cos'] = np.cos(2 * np.pi * df['datetime'].dt.hour / 24)
83
 
84
+ return df.dropna()
 
 
 
 
85
 
86
+ def create_sequences(data, target_data, window_size, horizon):
87
+ X, y = [], []
88
+ for i in range(len(data) - window_size - horizon + 1):
89
+ X.append(data[i:(i + window_size)])
90
+ y.append(target_data[i + window_size : i + window_size + horizon])
91
+ return np.array(X), np.array(y)
92
 
93
  def train_model(df):
94
+ logging.info(f"Training CNN Model on {len(df)} candles...")
95
 
96
  feature_cols = [
97
  'rsi', 'macd_hist', 'atr',
 
101
  'hour_sin', 'hour_cos'
102
  ]
103
 
104
+ data_features = df[feature_cols].values
 
 
 
 
105
 
106
+ scaler = StandardScaler()
107
+ data_scaled = scaler.fit_transform(data_features)
108
 
109
+ close_prices = df['close'].values
110
+ returns_future = []
111
 
112
+ for i in range(len(close_prices) - PREDICTION_HORIZON):
113
+ current_price = close_prices[i]
114
+ future_prices = close_prices[i+1 : i+1+PREDICTION_HORIZON]
115
+ pct_change = (future_prices - current_price) / current_price
116
+ returns_future.append(pct_change)
117
 
118
+ returns_future = np.array(returns_future)
119
+
120
+ X = []
121
+ y = []
122
+
123
+ valid_length = len(returns_future) - LOOKBACK_WINDOW
124
+ if valid_length <= 0:
125
+ return None, None, None
126
 
127
+ for i in range(valid_length):
128
+ X.append(data_scaled[i : i + LOOKBACK_WINDOW])
129
+ y.append(returns_future[i + LOOKBACK_WINDOW - 1])
130
+
131
+ X = np.array(X)
132
+ y = np.array(y)
 
 
 
 
133
 
134
+ if len(X) < 100:
135
+ return None, None, None
136
+
137
+ model = models.Sequential([
138
+ layers.Input(shape=(LOOKBACK_WINDOW, len(feature_cols))),
139
+ layers.Conv1D(filters=64, kernel_size=3, activation='relu', padding='same'),
140
+ layers.MaxPooling1D(pool_size=2),
141
+ layers.Dropout(0.2),
142
+ layers.Conv1D(filters=32, kernel_size=3, activation='relu', padding='same'),
143
+ layers.GlobalAveragePooling1D(),
144
+ layers.Dense(64, activation='relu'),
145
+ layers.Dropout(0.1),
146
+ layers.Dense(PREDICTION_HORIZON)
147
+ ])
148
+
149
+ model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.001), loss='mse')
150
+
151
+ early_stop = callbacks.EarlyStopping(monitor='loss', patience=5, restore_best_weights=True)
152
+
153
+ model.fit(X, y, epochs=20, batch_size=32, verbose=0, callbacks=[early_stop])
154
+
155
+ predictions = model.predict(X, verbose=0)
156
  residuals = y - predictions
157
+ residual_std = np.std(residuals)
158
 
159
+ return model, scaler, residual_std
160
 
161
+ def get_prediction(df, model, scaler, residual_std):
162
+ if model is None or scaler is None:
163
  return []
164
 
165
  feature_cols = [
 
170
  'hour_sin', 'hour_cos'
171
  ]
172
 
173
+ last_window = df.iloc[-LOOKBACK_WINDOW:][feature_cols].values
 
 
 
 
 
 
174
 
175
+ if len(last_window) < LOOKBACK_WINDOW:
176
  return []
177
+
178
+ last_window_scaled = scaler.transform(last_window)
179
+ last_window_reshaped = last_window_scaled.reshape(1, LOOKBACK_WINDOW, len(feature_cols))
180
+
181
+ predicted_returns = model.predict(last_window_reshaped, verbose=0)[0]
182
 
183
  current_price = df.iloc[-1]['close']
184
  current_time = int(df.iloc[-1]['time'])
185
 
186
  pred_data = []
187
  confidence_multiplier = 1.96
188
+
189
+ time_step = 0
190
+ accumulated_variance = 0.0
191
+
192
  for i, pct_change in enumerate(predicted_returns):
193
  future_price = current_price * (1 + pct_change)
194
 
195
+ accumulated_variance += (residual_std ** 2)
196
+ current_std = np.sqrt(accumulated_variance) / np.sqrt(i + 1) * (i + 1) * 0.5
197
+
198
+ upper_bound = future_price * (1 + (residual_std * confidence_multiplier))
199
+ lower_bound = future_price * (1 - (residual_std * confidence_multiplier))
200
+
201
  pred_data.append({
202
  "time": current_time + ((i + 1) * 60),
203
  "value": float(future_price),
 
212
  return {"error": "Initializing..."}
213
 
214
  df = calculate_indicators(market_state['ohlc_history'])
215
+ if df is None or len(df) < LOOKBACK_WINDOW + 50:
216
  return {"error": "Not enough data"}
217
 
218
  if market_state['model'] is None or (time.time() - market_state['last_training_time'] > TRAIN_INTERVAL):
219
  try:
220
  loop = asyncio.get_running_loop()
221
+ model, scaler, res_std = await loop.run_in_executor(executor, train_model, df)
222
  if model is not None:
223
  market_state['model'] = model
224
+ market_state['scaler'] = scaler
225
+ market_state['model_residuals'] = float(res_std)
226
  market_state['last_training_time'] = time.time()
227
+ logging.info(f"Model retrained. Residual Std: {market_state['model_residuals']:.5f}")
228
  except Exception as e:
229
  logging.error(f"Training failed: {e}")
230
 
231
  predictions = []
232
  try:
233
+ predictions = get_prediction(df, market_state['model'], market_state['scaler'], market_state['model_residuals'])
234
  except Exception as e:
235
  logging.error(f"Prediction failed: {e}")
236
 
 
255
  "price": last_close,
256
  "change": round(price_change, 2),
257
  "rsi": round(float(last_row.get('rsi', 0)), 1) if pd.notna(last_row.get('rsi')) else 0,
258
+ "macd": round(float(last_row.get('macd_hist', 0)), 2) if pd.notna(last_row.get('macd_hist')) else 0,
259
  "atr": round(float(last_row.get('atr', 0)), 2) if pd.notna(last_row.get('atr')) else 0,
260
  "volume": round(float(last_row.get('volume', 0)), 2) if pd.notna(last_row.get('volume')) else 0
261
  }
 
267
  <head>
268
  <meta charset="UTF-8">
269
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
270
+ <title>BTC/USD AI Predictor (CNN)</title>
271
  <script src="https://unpkg.com/lightweight-charts@4.1.1/dist/lightweight-charts.standalone.production.js"></script>
272
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
273
  <style>
 
415
  <span><div class="dot" style="background: #00ff88"></div>Price</span>
416
  <span><div class="dot" style="background: #2962FF"></div>EMA 20</span>
417
  <span><div class="dot" style="background: #26a69a; opacity: 0.5"></div>Bollinger</span>
418
+ <span><div class="dot" style="background: #bf5af2"></div>CNN Forecast</span>
419
  </div>
420
  <div class="prediction-badge">AI Forecast: 100 candles</div>
421
  </div>