Tonic commited on
Commit
2384e68
·
1 Parent(s): 248f443

adds improved predictions for chronos model

Browse files
Files changed (1) hide show
  1. app.py +49 -12
app.py CHANGED
@@ -63,9 +63,14 @@ def load_pipeline():
63
  "amazon/chronos-t5-large",
64
  device_map="auto", # Let the machine choose the best device
65
  torch_dtype=torch.float16, # Use float16 for better memory efficiency
66
- low_cpu_mem_usage=True
 
67
  )
 
68
  pipeline.model = pipeline.model.eval()
 
 
 
69
  print("Chronos model loaded successfully")
70
  return pipeline
71
  except Exception as e:
@@ -293,8 +298,13 @@ def make_prediction(symbol: str, timeframe: str = "1d", prediction_days: int = 5
293
  # Use Close prices instead of returns for better prediction
294
  prices = df['Close'].values
295
 
 
 
 
 
296
  # Normalize the data using MinMaxScaler
297
  normalized_prices = scaler.fit_transform(prices.reshape(-1, 1)).flatten()
 
298
 
299
  # Ensure we have enough data points and pad if necessary
300
  min_data_points = 64 # Minimum required by Chronos
@@ -302,12 +312,18 @@ def make_prediction(symbol: str, timeframe: str = "1d", prediction_days: int = 5
302
  # Pad the data with the last value
303
  padding = np.full(min_data_points - len(normalized_prices), normalized_prices[-1])
304
  normalized_prices = np.concatenate([padding, normalized_prices])
 
 
305
  elif len(normalized_prices) > min_data_points:
306
  # Take the most recent data points
307
  normalized_prices = normalized_prices[-min_data_points:]
 
 
 
 
308
 
309
- # Reshape for Chronos (batch_size=1, sequence_length, features=1)
310
- context = torch.tensor(normalized_prices.reshape(1, -1, 1), dtype=torch.float32)
311
 
312
  # Make prediction with GPU acceleration
313
  pipe = load_pipeline()
@@ -333,18 +349,39 @@ def make_prediction(symbol: str, timeframe: str = "1d", prediction_days: int = 5
333
 
334
  with torch.inference_mode():
335
  try:
336
- prediction = pipe.predict(
337
- context=context,
338
- prediction_length=actual_prediction_length,
339
- num_samples=100
340
- ).detach().cpu().numpy()
341
 
342
- if prediction is None or prediction.size == 0:
343
- raise ValueError("Chronos returned empty prediction")
 
 
 
 
 
 
 
 
 
 
 
 
344
 
 
 
 
 
 
 
 
345
  # Denormalize predictions
346
- mean_pred = scaler.inverse_transform(prediction.mean(axis=0).reshape(-1, 1)).flatten()
347
- std_pred = prediction.std(axis=0) * (scaler.data_max_ - scaler.data_min_)
 
 
 
 
348
 
349
  # If we had to limit the prediction length, extend the prediction
350
  if actual_prediction_length < prediction_days:
 
63
  "amazon/chronos-t5-large",
64
  device_map="auto", # Let the machine choose the best device
65
  torch_dtype=torch.float16, # Use float16 for better memory efficiency
66
+ low_cpu_mem_usage=True,
67
+ trust_remote_code=True # Required for Chronos models
68
  )
69
+ # Set model to evaluation mode
70
  pipeline.model = pipeline.model.eval()
71
+ # Disable gradient computation
72
+ for param in pipeline.model.parameters():
73
+ param.requires_grad = False
74
  print("Chronos model loaded successfully")
75
  return pipeline
76
  except Exception as e:
 
298
  # Use Close prices instead of returns for better prediction
299
  prices = df['Close'].values
300
 
301
+ # Calculate returns for additional context
302
+ returns = np.diff(prices) / prices[:-1]
303
+ returns = np.insert(returns, 0, 0) # Add 0 for first day
304
+
305
  # Normalize the data using MinMaxScaler
306
  normalized_prices = scaler.fit_transform(prices.reshape(-1, 1)).flatten()
307
+ normalized_returns = scaler.fit_transform(returns.reshape(-1, 1)).flatten()
308
 
309
  # Ensure we have enough data points and pad if necessary
310
  min_data_points = 64 # Minimum required by Chronos
 
312
  # Pad the data with the last value
313
  padding = np.full(min_data_points - len(normalized_prices), normalized_prices[-1])
314
  normalized_prices = np.concatenate([padding, normalized_prices])
315
+ padding_returns = np.full(min_data_points - len(normalized_returns), normalized_returns[-1])
316
+ normalized_returns = np.concatenate([padding_returns, normalized_returns])
317
  elif len(normalized_prices) > min_data_points:
318
  # Take the most recent data points
319
  normalized_prices = normalized_prices[-min_data_points:]
320
+ normalized_returns = normalized_returns[-min_data_points:]
321
+
322
+ # Combine price and returns data
323
+ combined_data = np.column_stack((normalized_prices, normalized_returns))
324
 
325
+ # Reshape for Chronos (batch_size=1, sequence_length, features=2)
326
+ context = torch.tensor(combined_data.reshape(1, -1, 2), dtype=torch.float32)
327
 
328
  # Make prediction with GPU acceleration
329
  pipe = load_pipeline()
 
349
 
350
  with torch.inference_mode():
351
  try:
352
+ # Generate multiple predictions for ensemble
353
+ num_ensemble = 5
354
+ all_predictions = []
 
 
355
 
356
+ for _ in range(num_ensemble):
357
+ # Use predict_quantiles for probabilistic forecasts
358
+ quantiles, mean = pipe.predict_quantiles(
359
+ context=context,
360
+ prediction_length=actual_prediction_length,
361
+ quantile_levels=[0.1, 0.5, 0.9] # 10th, 50th, and 90th percentiles
362
+ )
363
+
364
+ if quantiles is None or mean is None:
365
+ raise ValueError("Chronos returned empty prediction")
366
+
367
+ # Convert to numpy arrays
368
+ quantiles = quantiles.detach().cpu().numpy()
369
+ mean = mean.detach().cpu().numpy()
370
 
371
+ # Store predictions
372
+ all_predictions.append((quantiles, mean))
373
+
374
+ # Ensemble the predictions
375
+ ensemble_quantiles = np.mean([p[0] for p in all_predictions], axis=0)
376
+ ensemble_mean = np.mean([p[1] for p in all_predictions], axis=0)
377
+
378
  # Denormalize predictions
379
+ mean_pred = scaler.inverse_transform(ensemble_mean.reshape(-1, 1)).flatten()
380
+ lower_bound = scaler.inverse_transform(ensemble_quantiles[0, :, 0].reshape(-1, 1)).flatten()
381
+ upper_bound = scaler.inverse_transform(ensemble_quantiles[0, :, 2].reshape(-1, 1)).flatten()
382
+
383
+ # Calculate standard deviation from quantiles
384
+ std_pred = (upper_bound - lower_bound) / (2 * 1.645) # 90% confidence interval
385
 
386
  # If we had to limit the prediction length, extend the prediction
387
  if actual_prediction_length < prediction_days: