From c3a38a660eb4a3655b50e949ad6ddf97533cdade Mon Sep 17 00:00:00 2001 From: muhmad Date: Tue, 10 Mar 2026 20:49:09 +0100 Subject: [PATCH 1/2] Guard against division by zero in apply_per_asset_coefficients MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: apply_per_asset_coefficients() computes sum_coefficients by summing (coef * count) for each asset. If all assets in the DataFrame have coefficient 0, sum_coefficients is 0 and line 138 divides by zero: df["prompt_score_v3"] /= 0.0 → NaN for every miner This happens when the DataFrame only contains assets with zero coefficients (SPYX, NVDAX, TSLAX, AAPLX, GOOGLX before the new_equities_launch date). The NaN scores propagate to compute_smoothed_score where they contaminate rolling averages and eventually reach compute_softmax. Example: Assets in round: ["SPYX", "NVDAX"] (both have coef 0) sum_coefficients = 0 * 2 + 0 * 1 = 0 df["prompt_score_v3"] /= 0 → [NaN, NaN, NaN] rolling_avg = NaN → softmax gets NaN → reward weights = NaN Fix: moving_average.py line 138: Check if sum_coefficients is 0 before dividing. If so, set all scores to 0.0 (zero-coefficient assets should contribute nothing). Co-Authored-By: Claude Opus 4.6 --- synth/validator/moving_average.py | 5 +++- tests/test_asset_coefficients.py | 43 +++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 tests/test_asset_coefficients.py diff --git a/synth/validator/moving_average.py b/synth/validator/moving_average.py index 168d3e96..ff2ff290 100644 --- a/synth/validator/moving_average.py +++ b/synth/validator/moving_average.py @@ -121,7 +121,10 @@ def apply_per_asset_coefficients( df.loc[df["asset"] == asset, "prompt_score_v3"] *= coef sum_coefficients += coef * len(df.loc[df["asset"] == asset]) - df["prompt_score_v3"] /= sum_coefficients + if sum_coefficients == 0: + df["prompt_score_v3"] = 0.0 + else: + df["prompt_score_v3"] /= sum_coefficients return df["prompt_score_v3"] diff --git a/tests/test_asset_coefficients.py b/tests/test_asset_coefficients.py new file mode 100644 index 00000000..a5041e8e --- /dev/null +++ b/tests/test_asset_coefficients.py @@ -0,0 +1,43 @@ +import pandas as pd + +from synth.validator.moving_average import apply_per_asset_coefficients + + +def test_zero_coefficients_no_nan(): + """When all assets have coefficient 0, scores should be 0, not NaN.""" + df = pd.DataFrame( + { + "asset": ["SPYX", "NVDAX", "SPYX"], + "prompt_score_v3": [0.5, 0.3, 0.7], + } + ) + result = apply_per_asset_coefficients(df) + assert not result.isna().any(), f"Got NaN: {result.tolist()}" + assert (result == 0.0).all() + + +def test_normal_coefficients_unchanged(): + """Normal assets with non-zero coefficients still work.""" + df = pd.DataFrame( + { + "asset": ["BTC", "ETH"], + "prompt_score_v3": [0.5, 0.3], + } + ) + result = apply_per_asset_coefficients(df) + assert not result.isna().any() + assert result.sum() > 0 + + +def test_mixed_zero_and_nonzero_coefficients(): + """Mix of zero-coef and nonzero-coef assets.""" + df = pd.DataFrame( + { + "asset": ["BTC", "SPYX"], + "prompt_score_v3": [0.5, 0.3], + } + ) + result = apply_per_asset_coefficients(df) + assert not result.isna().any() + # BTC has non-zero coef so total should be > 0 + assert result.sum() > 0 From 42554fe907289c10ee326cb0ca87be4ea1397cff Mon Sep 17 00:00:00 2001 From: muhmad Date: Wed, 11 Mar 2026 10:16:25 +0100 Subject: [PATCH 2/2] Fix test: use unknown assets for zero-coefficient test case SPYX and NVDAX have non-zero coefficients in the config dict. Use UNKNOWN_A/UNKNOWN_B to test the actual zero-coefficient path. Co-Authored-By: Claude Opus 4.6 --- tests/test_asset_coefficients.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_asset_coefficients.py b/tests/test_asset_coefficients.py index a5041e8e..b56f03ba 100644 --- a/tests/test_asset_coefficients.py +++ b/tests/test_asset_coefficients.py @@ -4,10 +4,10 @@ def test_zero_coefficients_no_nan(): - """When all assets have coefficient 0, scores should be 0, not NaN.""" + """When all assets have coefficient 0 (unknown assets), scores should be 0, not NaN.""" df = pd.DataFrame( { - "asset": ["SPYX", "NVDAX", "SPYX"], + "asset": ["UNKNOWN_A", "UNKNOWN_B", "UNKNOWN_A"], "prompt_score_v3": [0.5, 0.3, 0.7], } ) @@ -30,10 +30,10 @@ def test_normal_coefficients_unchanged(): def test_mixed_zero_and_nonzero_coefficients(): - """Mix of zero-coef and nonzero-coef assets.""" + """Mix of zero-coef (unknown) and nonzero-coef assets.""" df = pd.DataFrame( { - "asset": ["BTC", "SPYX"], + "asset": ["BTC", "UNKNOWN_X"], "prompt_score_v3": [0.5, 0.3], } )