]> Piment Noir Git Repositories - freqai-strategies.git/commitdiff
feat(weights): add compose_sample_weights helper with mean=1 multiplicative composition
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Sat, 23 May 2026 23:49:34 +0000 (01:49 +0200)
committerJérôme Benoit <jerome.benoit@piment-noir.org>
Sat, 23 May 2026 23:49:34 +0000 (01:49 +0200)
AFML §4.10 / mlfinpy canonical: per-label mean=1 normalization, multiplicative composition with temporal decay, geometric-mean aggregation for multi-label, NaN/inf handling, all-zero degenerate fallback. Validated locally with pytest (evidence: .omo/evidence/task-5-{red,green}.txt).

quickadapter/user_data/strategies/Utils.py

index a1def001e1f331e1d15219f061152338961d1cb1..92ab4ec149409a9dac773d7873810086ca062345 100644 (file)
@@ -680,6 +680,31 @@ def midpoint(value1: T, value2: T) -> T:
     return (value1 + value2) / 2
 
 
+def compose_sample_weights(
+    temporal: NDArray[np.floating],
+    label_weights_map: dict[str, NDArray[np.floating]],
+) -> NDArray[np.floating]:
+    if not label_weights_map:
+        return temporal
+    normalized_per_label: list[NDArray[np.floating]] = []
+    for w in label_weights_map.values():
+        arr = np.asarray(w, dtype=float)
+        arr = np.where(np.isfinite(arr) & (arr > 0), arr, 1.0)
+        total = arr.sum()
+        if total <= 0 or not np.isfinite(total):
+            arr = np.ones_like(arr)
+        else:
+            arr = arr * (len(arr) / total)
+        normalized_per_label.append(arr)
+    stacked = np.vstack(normalized_per_label)
+    agg = np.exp(np.log(stacked).mean(axis=0))
+    combined = np.asarray(temporal, dtype=float) * agg
+    combined_sum = combined.sum()
+    if combined_sum <= 0 or not np.isfinite(combined_sum):
+        return np.asarray(temporal, dtype=float)
+    return combined * (len(combined) / combined_sum)
+
+
 def nan_average(
     values: NDArray[np.floating],
     weights: NDArray[np.floating] | None = None,