Files
sentiment-engine/MALKHUT/malkhut/tests/test_mutation_litmus.py

109 lines
5.1 KiB
Python
Raw Normal View History

"""
Mutation-litmus tests for fee sensitivity.
LITMUS: if changing fees 10x does NOT change score, the reward ignores fees.
These tests MUST go RED before fee fix, GREEN after.
"""
import pytest
from malkhut.training.cma_trainer import ScenarioFactory, PolicyEvaluator
from malkhut.cwm.core import MinimalCryptoLOBCWM
from malkhut.state import FulfilmentPolicyParams
from malkhut.training.asset_classification import (
ASSET_PROFILES, _profile, Sector, TokenRole, SupplyModel,
ConsensusFamily, SmartContractCapability, MarketCapTier,
VolatilityProfile, LiquidityProfile, DerivativeAccess,
)
def _baseline_params():
return FulfilmentPolicyParams(
version='litmus', ucb_c=1.414, max_sims=4, max_depth=1,
rollout_depth=1, root_temperature=0.5, min_root_entropy=0.25,
quote_offsets_ticks=(0, 1), quote_size_fractions=(0.25, 0.50),
passive_ttl_ms=200, aggressive_ttl_ms=50,
maker_edge_min_bps=0.5, cross_spread_edge_min_bps=5.0,
adverse_toxicity_cancel_threshold=0.5, queue_churn_cancel_threshold=0.5,
mae_tail_cut_bps=50.0, mfe_giveback_cut_fraction=0.5,
max_time_in_loss_s=300.0, failed_recovery_cut_count=3,
recovery_velocity_min_bps_per_s=0.0,
max_symbol_notional_fraction=0.20, max_single_order_notional_fraction=0.05,
reduce_when_global_up_fraction=0.30, session_profit_lock_fraction=0.02,
w_expected_pnl=1.0, w_fill_probability=0.5, w_adverse_selection=2.0,
w_queue_priority=0.5, w_inventory_risk=1.5, w_tail_loss=5.0,
w_fee_quality=0.5, w_time_decay=0.3, w_policy_entropy=0.5,
robust_tail_weight=2.0, toxic_counterparty_weight=3.0,
low_liquidity_weight=2.0, latency_stress_weight=1.0,
)
def _make_btc_with_fees(taker_fee, maker_fee):
return _profile(
symbol="BTCUSDT", sectors=[Sector.CURRENCY],
token_roles=[TokenRole.STORE_OF_VALUE],
supply_model=SupplyModel.FIXED_CAP, consensus=ConsensusFamily.POW,
smart_contracts=SmartContractCapability.NONE,
market_cap_tier=MarketCapTier.MEGA,
volatility_profile=VolatilityProfile.LOW,
liquidity_profile=LiquidityProfile.DEEP,
derivative_access=DerivativeAccess.PERPS_AND_OPTIONS,
tick_size=0.1, lot_size=0.001, price_decimals=1,
maker_fee_bps=maker_fee, taker_fee_bps=taker_fee,
typical_spread_bps=0.3, typical_depth_usd=5_000_000,
typical_daily_volume_usd=30_000_000_000,
coingecko_id="bitcoin", cmc_id=1,
has_funding=True, has_options=True,
)
class TestFeeMutationLitmus:
"""LITMUS: fee change MUST affect score. These tests go RED if fees are ignored."""
def test_taker_fee_10x_changes_score(self):
"""Change fee BEFORE building scenarios — score must differ."""
factory = ScenarioFactory()
evaluator = PolicyEvaluator(cwm_factory=MinimalCryptoLOBCWM)
params = _baseline_params()
# Correct fees: build scenarios THEN evaluate
ASSET_PROFILES["BTCUSDT"] = _make_btc_with_fees(taker_fee=5.0, maker_fee=2.0)
suite_correct = factory.build_suite(symbols=('BTCUSDT',), steps_per_scenario=3)
score_correct, _ = evaluator.evaluate_candidate(
params=params, scenarios=suite_correct, rng_seed=42, workers=0)
# Wrong fees: rebuild scenarios with wrong fees
ASSET_PROFILES["BTCUSDT"] = _make_btc_with_fees(taker_fee=0.1, maker_fee=0.0)
suite_wrong = factory.build_suite(symbols=('BTCUSDT',), steps_per_scenario=3)
score_wrong, _ = evaluator.evaluate_candidate(
params=params, scenarios=suite_wrong, rng_seed=42, workers=0)
# Restore
ASSET_PROFILES["BTCUSDT"] = _make_btc_with_fees(taker_fee=5.0, maker_fee=2.0)
print(f' Correct fees score: {score_correct:.0f}')
print(f' Wrong fees score: {score_wrong:.0f}')
assert score_correct != score_wrong, (
"LITMUS FAILED: Fee change did NOT affect score. Reward ignores fees.")
def test_zero_fees_vs_correct_fees(self):
"""Zero fees vs 5bps — must produce different scores."""
factory = ScenarioFactory()
evaluator = PolicyEvaluator(cwm_factory=MinimalCryptoLOBCWM)
params = _baseline_params()
ASSET_PROFILES["BTCUSDT"] = _make_btc_with_fees(taker_fee=5.0, maker_fee=2.0)
suite_correct = factory.build_suite(symbols=('BTCUSDT',), steps_per_scenario=3)
score_correct, _ = evaluator.evaluate_candidate(
params=params, scenarios=suite_correct, rng_seed=42, workers=0)
ASSET_PROFILES["BTCUSDT"] = _make_btc_with_fees(taker_fee=0.0, maker_fee=0.0)
suite_zero = factory.build_suite(symbols=('BTCUSDT',), steps_per_scenario=3)
score_zero, _ = evaluator.evaluate_candidate(
params=params, scenarios=suite_zero, rng_seed=42, workers=0)
ASSET_PROFILES["BTCUSDT"] = _make_btc_with_fees(taker_fee=5.0, maker_fee=2.0)
print(f' Correct fees score: {score_correct:.0f}')
print(f' Zero fees score: {score_zero:.0f}')
assert score_correct != score_zero, (
"LITMUS FAILED: Zero fees and 5bps produce same score.")