CI/CD / build-and-push (push) Successful in 7s
Phase 3 excluded momentum and Fear & Greed from politics/tech/events
markets; Phase 4 fixed ticker detection. But the BTC-dominance signal was
still applied to non-price markets that legitimately mention a ticker
('Will the ETH ETF be approved?'), despite having no demonstrated causality
for non-price outcomes. Reuse the existing is_non_price gate so the
contribution stays 0.0 -> feat_btc_dom_lo = 0.0 for those markets.
Price-market behavior unchanged: ETH/altcoin/general-crypto markets keep
the +/-0.03 dominance adjustment.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
131 lines
4.9 KiB
Python
131 lines
4.9 KiB
Python
"""
|
|
Tests for FASE 5 — BTC-dominance signal must not apply to non-price markets.
|
|
|
|
FASE 3 gated momentum and Fear & Greed behind is_non_price (politics / tech /
|
|
events); FASE 4 fixed ticker detection so non-crypto questions no longer flag
|
|
crypto assets by accident. But a non-price market that LEGITIMATELY mentions
|
|
a ticker ("Will the ETH ETF be approved?") still armed the BTC-dominance
|
|
signal, which has no demonstrated causality for non-price outcomes. FASE 5
|
|
applies the same is_non_price gate to that signal.
|
|
|
|
Note: the dominance signal only fires for is_eth / is_altcoin /
|
|
is_general_crypto markets — a pure-BTC question never receives it, so the
|
|
pro-Bitcoin test below is a regression guard rather than a gate exercise;
|
|
the ETH-ETF test is the one that fails without the gate.
|
|
|
|
Same caplog technique as test_bayesian_asset_detection.py: btc_dom_lo is
|
|
parsed from the structured audit log, with btc_dominance=65 (>55) so the
|
|
signal fires whenever it is allowed to.
|
|
"""
|
|
import asyncio
|
|
import logging
|
|
import re
|
|
|
|
import pytest
|
|
|
|
from bot.data.external import ExternalSignals
|
|
from bot.data.polymarket import Market
|
|
from bot.strategy.bayesian import BayesianStrategy
|
|
|
|
BTC_DOM_RE = re.compile(r"btc_dom_lo=([+-]\d+\.\d+)")
|
|
|
|
|
|
def _make_market(question: str, category: str) -> Market:
|
|
return Market(
|
|
id="mkt-test-1",
|
|
condition_id="cond-test-1",
|
|
question=question,
|
|
yes_token_id="yes-tok",
|
|
no_token_id="no-tok",
|
|
yes_price=0.50,
|
|
no_price=0.50,
|
|
volume_24h=50_000.0,
|
|
end_date="2026-07-15T00:00:00Z",
|
|
active=True,
|
|
category=category,
|
|
)
|
|
|
|
|
|
def _make_signals() -> ExternalSignals:
|
|
# btc_dominance=65 (>55) arms the dominance signal wherever it is allowed.
|
|
# Momentum kept below the 2% threshold so price-market tests isolate the
|
|
# dominance contribution.
|
|
return ExternalSignals(
|
|
btc_price=100_000.0,
|
|
btc_change_24h=1.0,
|
|
eth_price=4_000.0,
|
|
eth_change_24h=1.0,
|
|
btc_dominance=65.0,
|
|
fear_greed_index=50,
|
|
fear_greed_label="neutral",
|
|
total_market_cap_change=1.0,
|
|
valid=True,
|
|
)
|
|
|
|
|
|
def _evaluate(question: str, category: str, caplog) -> tuple[float, str]:
|
|
"""Run evaluate() and return (btc_dom_lo, full_log) from the audit log."""
|
|
strategy = BayesianStrategy(news=None, manifold=None, db=None)
|
|
market = _make_market(question, category)
|
|
with caplog.at_level(logging.INFO, logger="bot.strategy.bayesian"):
|
|
asyncio.run(
|
|
strategy.evaluate(market, _make_signals(), occupied_families=set())
|
|
)
|
|
full_log = "\n".join(r.getMessage() for r in caplog.records)
|
|
for record in caplog.records:
|
|
m = BTC_DOM_RE.search(record.getMessage())
|
|
if m:
|
|
return float(m.group(1)), full_log
|
|
pytest.fail(
|
|
"No SKIP_EDGE_NET/TRADE log line with btc_dom_lo found; "
|
|
f"got: {[r.getMessage() for r in caplog.records]}"
|
|
)
|
|
|
|
|
|
# ── Non-price markets: gate must zero the signal ─────────────────────────────
|
|
|
|
def test_politics_market_mentioning_eth_gets_no_btc_dom(caplog):
|
|
"""Legitimate ETH mention in a politics market → btc_dom_lo == 0.0."""
|
|
btc_dom_lo, full_log = _evaluate(
|
|
"Will the ETH ETF be approved?", "politics", caplog
|
|
)
|
|
assert btc_dom_lo == 0.0
|
|
assert "BTC dom" not in full_log
|
|
|
|
def test_politics_market_mentioning_bitcoin_gets_no_btc_dom(caplog):
|
|
"""Legitimate Bitcoin mention in a politics market → btc_dom_lo == 0.0."""
|
|
btc_dom_lo, full_log = _evaluate(
|
|
"Will a pro-Bitcoin candidate win the election?", "politics", caplog
|
|
)
|
|
assert btc_dom_lo == 0.0
|
|
assert "BTC dom" not in full_log
|
|
|
|
def test_tech_and_events_markets_get_no_btc_dom(caplog):
|
|
for category in ("tech", "events"):
|
|
caplog.clear()
|
|
btc_dom_lo, full_log = _evaluate(
|
|
"Will the ETH foundation launch the product?", category, caplog
|
|
)
|
|
assert btc_dom_lo == 0.0, f"BTC dominance applied to {category} market"
|
|
assert "BTC dom" not in full_log
|
|
|
|
|
|
# ── Price markets: current behavior preserved ───────────────────────────────
|
|
|
|
def test_eth_price_market_keeps_btc_dom(caplog):
|
|
"""ETH price market with dominance 65 → signal fires as before."""
|
|
btc_dom_lo, full_log = _evaluate(
|
|
"Will ETH be above $5000?", "crypto/finance", caplog
|
|
)
|
|
# 'above' → is_price_above, dominance 65 > 55 → -0.03 → -0.06 log-odds
|
|
assert btc_dom_lo == pytest.approx(-0.06, abs=1e-4)
|
|
assert "BTC dom: 65.0% (high → alt pressure)" in full_log
|
|
|
|
def test_altcoin_price_market_keeps_btc_dom(caplog):
|
|
"""SOL price market with dominance 65 → signal fires as before."""
|
|
btc_dom_lo, full_log = _evaluate(
|
|
"Will SOL reach $200?", "crypto/finance", caplog
|
|
)
|
|
assert btc_dom_lo == pytest.approx(-0.06, abs=1e-4)
|
|
assert "BTC dom: 65.0% (high → alt pressure)" in full_log
|