ifieryarrows commited on
Commit
225d4a2
·
verified ·
1 Parent(s): 27d35ae

Sync from GitHub (tests passed)

Browse files
Files changed (1) hide show
  1. deep_learning/models/tft_copper.py +15 -8
deep_learning/models/tft_copper.py CHANGED
@@ -250,21 +250,28 @@ def format_prediction(
250
  n_days = pred.shape[0]
251
  median_idx = len(quantiles) // 2
252
 
 
 
 
 
 
 
253
  # T+1 quantile spreads (return-space distance from median).
254
  # Used as the base width for confidence bands; scaled by sqrt(d) for
255
  # later days so uncertainty grows realistically instead of compounding
256
  # tail quantiles exponentially (which would produce absurd bands).
257
- med_0 = float(pred[0, median_idx])
258
- spread_q10 = (float(pred[0, 1]) - med_0) if len(quantiles) > 2 else 0.0
259
- spread_q90 = (float(pred[0, -2]) - med_0) if len(quantiles) > 2 else 0.0
260
- spread_q02 = float(pred[0, 0]) - med_0
261
- spread_q98 = float(pred[0, -1]) - med_0
 
262
 
263
  daily_forecasts = []
264
  cum_price_med = baseline_price
265
 
266
  for d in range(n_days):
267
- med = float(pred[d, median_idx])
268
  cum_price_med *= (1 + med)
269
  cum_return = (cum_price_med / baseline_price) - 1.0
270
 
@@ -288,8 +295,8 @@ def format_prediction(
288
 
289
  return {
290
  "predicted_return_median": first["daily_return"],
291
- "predicted_return_q10": float(pred[0, 1]) if len(quantiles) > 2 else first["daily_return"],
292
- "predicted_return_q90": float(pred[0, -2]) if len(quantiles) > 2 else first["daily_return"],
293
  "predicted_price_median": first["price_median"],
294
  "predicted_price_q10": first["price_q10"],
295
  "predicted_price_q90": first["price_q90"],
 
250
  n_days = pred.shape[0]
251
  median_idx = len(quantiles) // 2
252
 
253
+ # Hard clamp: prevents overconfident models (VR >> 1) from producing
254
+ # absurd compound prices. Copper's actual daily σ ≈ 0.024; capping at
255
+ # ~1.25σ keeps the 5-day compound under ≈16 %. The clamp is inactive
256
+ # once the model is retrained with a healthy VR (0.5–1.5).
257
+ _MAX_DAILY_RET = 0.03
258
+
259
  # T+1 quantile spreads (return-space distance from median).
260
  # Used as the base width for confidence bands; scaled by sqrt(d) for
261
  # later days so uncertainty grows realistically instead of compounding
262
  # tail quantiles exponentially (which would produce absurd bands).
263
+ med_0 = float(np.clip(pred[0, median_idx], -_MAX_DAILY_RET, _MAX_DAILY_RET))
264
+ _raw_med_0 = float(pred[0, median_idx])
265
+ spread_q10 = np.clip(float(pred[0, 1]) - _raw_med_0, -_MAX_DAILY_RET, 0) if len(quantiles) > 2 else 0.0
266
+ spread_q90 = np.clip(float(pred[0, -2]) - _raw_med_0, 0, _MAX_DAILY_RET) if len(quantiles) > 2 else 0.0
267
+ spread_q02 = np.clip(float(pred[0, 0]) - _raw_med_0, -_MAX_DAILY_RET * 1.5, 0)
268
+ spread_q98 = np.clip(float(pred[0, -1]) - _raw_med_0, 0, _MAX_DAILY_RET * 1.5)
269
 
270
  daily_forecasts = []
271
  cum_price_med = baseline_price
272
 
273
  for d in range(n_days):
274
+ med = float(np.clip(pred[d, median_idx], -_MAX_DAILY_RET, _MAX_DAILY_RET))
275
  cum_price_med *= (1 + med)
276
  cum_return = (cum_price_med / baseline_price) - 1.0
277
 
 
295
 
296
  return {
297
  "predicted_return_median": first["daily_return"],
298
+ "predicted_return_q10": float(np.clip(pred[0, 1], -_MAX_DAILY_RET * 2, _MAX_DAILY_RET * 2)) if len(quantiles) > 2 else first["daily_return"],
299
+ "predicted_return_q90": float(np.clip(pred[0, -2], -_MAX_DAILY_RET * 2, _MAX_DAILY_RET * 2)) if len(quantiles) > 2 else first["daily_return"],
300
  "predicted_price_median": first["price_median"],
301
  "predicted_price_q10": first["price_q10"],
302
  "predicted_price_q90": first["price_q90"],