Spaces:
Running
Running
Sync from GitHub (tests passed)
Browse files
deep_learning/models/tft_copper.py
CHANGED
|
@@ -262,44 +262,17 @@ def format_prediction(
|
|
| 262 |
baseline_price,
|
| 263 |
)
|
| 264 |
|
| 265 |
-
#
|
| 266 |
-
#
|
| 267 |
-
#
|
| 268 |
-
#
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
#
|
| 272 |
-
#
|
| 273 |
-
#
|
| 274 |
-
#
|
| 275 |
-
|
| 276 |
-
_SOFT_ZONE = 0.010 # pass-through zone: Β±1 % unchanged
|
| 277 |
-
_SOFT_MAX = 0.020 # compress between 1 % and 2 %; beyond β stage 2
|
| 278 |
-
_MAX_DAILY_RET = 0.030 # hard clamp backstop
|
| 279 |
-
|
| 280 |
-
def _dampen(r: float) -> float:
|
| 281 |
-
"""Soft-then-hard attenuation of daily return r."""
|
| 282 |
-
sign = 1.0 if r >= 0 else -1.0
|
| 283 |
-
abs_r = abs(r)
|
| 284 |
-
if abs_r <= _SOFT_ZONE: # pass-through zone
|
| 285 |
-
return r
|
| 286 |
-
if abs_r <= _SOFT_MAX: # soft zone: linear taper to 70 %
|
| 287 |
-
# interpolate attenuation from 100 % β 70 % as abs_r goes from
|
| 288 |
-
# _SOFT_ZONE to _SOFT_MAX
|
| 289 |
-
t = (abs_r - _SOFT_ZONE) / (_SOFT_MAX - _SOFT_ZONE) # 0 β 1
|
| 290 |
-
scale = 1.0 - 0.30 * t
|
| 291 |
-
return sign * abs_r * scale
|
| 292 |
-
if abs_r <= _MAX_DAILY_RET: # extended zone: sqrt compression
|
| 293 |
-
# At _SOFT_MAX the value is 70 % of _SOFT_MAX.
|
| 294 |
-
# Beyond that, sqrt-taper ensures diminishing returns.
|
| 295 |
-
base = _SOFT_MAX * 0.70
|
| 296 |
-
excess = abs_r - _SOFT_MAX
|
| 297 |
-
budget = _MAX_DAILY_RET - _SOFT_MAX
|
| 298 |
-
compressed = base + excess ** 0.5 * budget ** 0.5 * 0.5
|
| 299 |
-
return sign * min(compressed, _MAX_DAILY_RET)
|
| 300 |
-
return sign * _MAX_DAILY_RET # hard clamp backstop
|
| 301 |
-
|
| 302 |
-
med_0 = _dampen(float(pred[0, median_idx]))
|
| 303 |
_raw_med_0 = float(pred[0, median_idx])
|
| 304 |
spread_q10 = np.clip(float(pred[0, 1]) - _raw_med_0, -_MAX_DAILY_RET, 0) if len(quantiles) > 2 else 0.0
|
| 305 |
spread_q90 = np.clip(float(pred[0, -2]) - _raw_med_0, 0, _MAX_DAILY_RET) if len(quantiles) > 2 else 0.0
|
|
@@ -310,7 +283,7 @@ def format_prediction(
|
|
| 310 |
cum_price_med = baseline_price
|
| 311 |
|
| 312 |
for d in range(n_days):
|
| 313 |
-
med =
|
| 314 |
cum_price_med *= (1 + med)
|
| 315 |
cum_return = (cum_price_med / baseline_price) - 1.0
|
| 316 |
|
|
|
|
| 262 |
baseline_price,
|
| 263 |
)
|
| 264 |
|
| 265 |
+
# Hard clamp: prevents overconfident models (VR >> 1) from producing
|
| 266 |
+
# absurd compound prices. Copper's actual daily Ο β 0.024; capping at
|
| 267 |
+
# ~1.25Ο keeps the 5-day compound under β16 %. The clamp is inactive
|
| 268 |
+
# once the model is retrained with a healthy VR (0.5β1.5).
|
| 269 |
+
_MAX_DAILY_RET = 0.03
|
| 270 |
+
|
| 271 |
+
# T+1 quantile spreads (return-space distance from median).
|
| 272 |
+
# Used as the base width for confidence bands; scaled by sqrt(d) for
|
| 273 |
+
# later days so uncertainty grows realistically instead of compounding
|
| 274 |
+
# tail quantiles exponentially (which would produce absurd bands).
|
| 275 |
+
med_0 = float(np.clip(pred[0, median_idx], -_MAX_DAILY_RET, _MAX_DAILY_RET))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 276 |
_raw_med_0 = float(pred[0, median_idx])
|
| 277 |
spread_q10 = np.clip(float(pred[0, 1]) - _raw_med_0, -_MAX_DAILY_RET, 0) if len(quantiles) > 2 else 0.0
|
| 278 |
spread_q90 = np.clip(float(pred[0, -2]) - _raw_med_0, 0, _MAX_DAILY_RET) if len(quantiles) > 2 else 0.0
|
|
|
|
| 283 |
cum_price_med = baseline_price
|
| 284 |
|
| 285 |
for d in range(n_days):
|
| 286 |
+
med = float(np.clip(pred[d, median_idx], -_MAX_DAILY_RET, _MAX_DAILY_RET))
|
| 287 |
cum_price_med *= (1 + med)
|
| 288 |
cum_return = (cum_price_med / baseline_price) - 1.0
|
| 289 |
|