fix: NaN-safe correlation de-risk in book_series (zero-variance windows)
np.corrcoef on a window where a vol-normalized stream is constant (warmup / non-moving stream) divided by zero std -> RuntimeWarning + NaN in the corr matrix -> ac=NaN -> propagated via chist into the z-score and could poison leverage L and book with NaN (silent corruption). Fix: correlate only streams with std>0 in the window, nansum over the actual valid count, fall back to ac=0 when <2 vary. Verified: no RuntimeWarning (tested with warnings-as-errors), no NaN, identical L/weights. Affects all scripts importing book_series + the cluster ConfigMap. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -88,7 +88,12 @@ def book_series(R):
|
||||
L = min(L, max(KELLY_FLOOR, (emu * 252) / (evar * 252 + 1e-9)))
|
||||
dd = eq / peak - 1.0
|
||||
L *= float(np.clip(1 - DD_SENS * max(0.0, -dd - DD_DEADBAND), DD_FLOOR, 1.0))
|
||||
cm = np.corrcoef(np.nan_to_num(M[t - 63:t]).T); ac = (cm.sum() - S) / (S * S - S); chist.append(ac)
|
||||
sub = np.nan_to_num(M[t - 63:t]); vmask = sub.std(0) > 1e-12 # only streams that vary (avoid /0 in corrcoef -> NaN)
|
||||
if int(vmask.sum()) >= 2:
|
||||
k = int(vmask.sum()); cm = np.corrcoef(sub[:, vmask].T); ac = float((np.nansum(cm) - k) / (k * k - k))
|
||||
else:
|
||||
ac = 0.0
|
||||
chist.append(ac)
|
||||
if len(chist) > 60:
|
||||
h = np.array(chist[-120:]); z = (ac - h.mean()) / (h.std() + 1e-9)
|
||||
L *= float(np.clip(1 - 0.2 * max(0.0, z), 0.5, 1.0))
|
||||
|
||||
Reference in New Issue
Block a user