Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions strategies/market_making_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -1211,10 +1211,13 @@ def _check_forager_health(self, coin: str) -> None:
return

# Avoid false positives on coins that are active but lack close
# history yet (quality dimension undefined).
# history yet (quality dimension undefined). The activity boundary
# is anchored to ``_NO_HISTORY_NEUTRAL_SCORE`` so the gate scales
# with the same midpoint the tracker uses for unknown coins —
# adjusting one place updates both.
if (
health.n_closes < cfg.min_closes_for_quality
and health.activity_score > 50.0
and health.activity_score > CoinHealthTracker._NO_HISTORY_NEUTRAL_SCORE
):
return

Expand Down
35 changes: 35 additions & 0 deletions tests/test_forager.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,41 @@ def test_triggers_when_inactive_even_without_close_data():
assert "BTC" in s._coin_cooldown_until


def test_quality_gate_boundary_at_neutral_score():
"""Activity exactly at the neutral score must NOT engage the gate.

The gate fires only when ``activity_score > _NO_HISTORY_NEUTRAL_SCORE``;
equality should not block trigger. Pinned here so a future refactor
of the boundary semantic (>= vs >) is detected.
"""
s = _make_strategy(forager_consecutive=3, forager_min_closes_for_quality=10)
tracker = MagicMock()
s._coin_health_tracker = tracker
# Insufficient close data + activity exactly at the neutral midpoint.
# With strict '>' the gate is NOT engaged, so trigger should fire.
tracker.get_health.return_value = _stub_health(
score=10.0, n_closes=2, activity=CoinHealthTracker._NO_HISTORY_NEUTRAL_SCORE,
)
for _ in range(3):
s._check_forager_health("BTC")
assert "BTC" in s._coin_cooldown_until


def test_quality_gate_just_above_neutral_score_blocks_trigger():
"""Activity just above the neutral score must engage the gate."""
s = _make_strategy(forager_consecutive=3, forager_min_closes_for_quality=10)
tracker = MagicMock()
s._coin_health_tracker = tracker
tracker.get_health.return_value = _stub_health(
score=10.0,
n_closes=2,
activity=CoinHealthTracker._NO_HISTORY_NEUTRAL_SCORE + 0.1,
)
for _ in range(5):
s._check_forager_health("BTC")
assert "BTC" not in s._coin_cooldown_until


# --------------------------------------------------------------------------- #
# Cooldown lifecycle
# --------------------------------------------------------------------------- #
Expand Down
Loading