initial: import DOLPHIN baseline 2026-04-21 from dolphinng5_predict working tree

Includes core prod + GREEN/BLUE subsystems:
- prod/ (BLUE harness, configs, scripts, docs)
- nautilus_dolphin/ (GREEN Nautilus-native impl + dvae/ preserved)
- adaptive_exit/ (AEM engine + models/bucket_assignments.pkl)
- Observability/ (EsoF advisor, TUI, dashboards)
- external_factors/ (EsoF producer)
- mc_forewarning_qlabs_fork/ (MC regime/envelope)

Excludes runtime caches, logs, backups, and reproducible artifacts per .gitignore.
This commit is contained in:
hjnormey
2026-04-21 16:58:38 +02:00
commit 01c19662cb
643 changed files with 260241 additions and 0 deletions

View File

@@ -0,0 +1,271 @@
"""
Tests for Adaptive Circuit Breaker v5.
"""
import unittest
from datetime import datetime
from unittest.mock import patch, MagicMock
import numpy as np
from nautilus_dolphin.nautilus.adaptive_circuit_breaker import (
AdaptiveCircuitBreaker, ACBConfig, ACBPositionSizer, get_acb_cut_for_date
)
class TestACBConfig(unittest.TestCase):
"""Test ACB configuration."""
def test_default_config(self):
"""Test default configuration values."""
config = ACBConfig()
# Check cut rates (v5 configuration)
self.assertEqual(config.CUT_RATES[0], 0.00)
self.assertEqual(config.CUT_RATES[1], 0.15)
self.assertEqual(config.CUT_RATES[2], 0.45)
self.assertEqual(config.CUT_RATES[3], 0.55)
self.assertEqual(config.CUT_RATES[4], 0.75)
self.assertEqual(config.CUT_RATES[5], 0.80)
# Check thresholds
self.assertEqual(config.FUNDING_VERY_BEARISH, -0.0001)
self.assertEqual(config.DVOL_EXTREME, 80)
self.assertEqual(config.FNG_EXTREME_FEAR, 25)
class TestAdaptiveCircuitBreaker(unittest.TestCase):
"""Test Adaptive Circuit Breaker functionality."""
def setUp(self):
"""Set up test fixtures."""
self.acb = AdaptiveCircuitBreaker()
def test_signal_calculation_no_stress(self):
"""Test signal calculation with no stress factors."""
factors = {
'funding_btc': 0.0001, # Positive funding (bullish)
'dvol_btc': 40.0, # Low volatility
'fng': 60, # Greed (bullish)
'taker': 1.1 # Buying pressure
}
result = self.acb._calculate_signals(factors)
self.assertEqual(result['signals'], 0.0)
self.assertEqual(result['severity'], 0)
def test_signal_calculation_funding_only(self):
"""Test signal calculation with funding stress only."""
factors = {
'funding_btc': -0.00015, # Very bearish funding
'dvol_btc': 40.0,
'fng': 60,
'taker': 1.1
}
result = self.acb._calculate_signals(factors)
self.assertEqual(result['signals'], 1.0) # 1 signal from funding
self.assertEqual(result['severity'], 2)
def test_signal_calculation_multiple_stress(self):
"""Test signal calculation with multiple stress factors."""
factors = {
'funding_btc': -0.00015, # Very bearish funding (1.0 signals, sev 2)
'dvol_btc': 85.0, # Extreme volatility (1.0 signals, sev 2)
'fng': 20, # Extreme fear (confirmed, 1.0 signals, sev 2)
'taker': 0.75 # Selling pressure (1.0 signals, sev 1)
}
result = self.acb._calculate_signals(factors)
# Should have 4 signals (funding + dvol + fng + taker)
self.assertGreaterEqual(result['signals'], 3.0)
self.assertGreater(result['severity'], 0)
def test_cut_mapping(self):
"""Test signal to cut rate mapping."""
test_cases = [
(0.0, 0.00), # 0 signals -> 0% cut
(0.5, 0.00), # 0.5 signals -> 0% cut (below threshold)
(1.0, 0.15), # 1 signal -> 15% cut
(1.5, 0.15), # 1.5 signals -> 15% cut
(2.0, 0.45), # 2 signals -> 45% cut
(2.5, 0.45), # 2.5 signals -> 45% cut
(3.0, 0.55), # 3 signals -> 55% cut
(4.0, 0.75), # 4 signals -> 75% cut
(5.0, 0.80), # 5 signals -> 80% cut
]
for signals, expected_cut in test_cases:
cut = self.acb._get_cut_from_signals(signals)
self.assertEqual(cut, expected_cut,
f"Failed for signals={signals}: got {cut}, expected {expected_cut}")
def test_apply_cut_to_position_size(self):
"""Test applying cut to position size."""
base_size = 1000.0
# Test with no stress (0% cut)
with patch.object(self.acb, 'get_cut_for_date') as mock_get:
mock_get.return_value = {'cut': 0.0, 'signals': 0.0}
final_size, info = self.acb.apply_cut_to_position_size(base_size, '2026-02-06')
self.assertEqual(final_size, base_size)
# Test with moderate stress (45% cut)
with patch.object(self.acb, 'get_cut_for_date') as mock_get:
mock_get.return_value = {'cut': 0.45, 'signals': 2.0}
final_size, info = self.acb.apply_cut_to_position_size(base_size, '2026-02-06')
self.assertAlmostEqual(final_size, base_size * 0.55, places=10) # 1000 * (1 - 0.45)
# Test with extreme stress (80% cut)
with patch.object(self.acb, 'get_cut_for_date') as mock_get:
mock_get.return_value = {'cut': 0.80, 'signals': 5.0}
final_size, info = self.acb.apply_cut_to_position_size(base_size, '2026-02-06')
self.assertAlmostEqual(final_size, base_size * 0.20, places=10) # 1000 * (1 - 0.80)
def test_caching(self):
"""Test that results are cached."""
date_str = '2026-02-06'
# First call should cache
with patch.object(self.acb, '_load_external_factors') as mock_load:
mock_load.return_value = {
'funding_btc': -0.0001,
'dvol_btc': 60,
'fng': 40,
'taker': 0.95
}
result1 = self.acb.get_cut_for_date(date_str)
self.assertEqual(self.acb._stats['total_calls'], 1)
self.assertEqual(self.acb._stats['cache_hits'], 0)
# Second call should use cache
result2 = self.acb.get_cut_for_date(date_str)
self.assertEqual(self.acb._stats['total_calls'], 2)
self.assertEqual(self.acb._stats['cache_hits'], 1)
# Results should be identical
self.assertEqual(result1['cut'], result2['cut'])
def test_stats_tracking(self):
"""Test statistics tracking."""
# Clear stats
self.acb.reset_stats()
# Simulate some calls
with patch.object(self.acb, '_load_external_factors') as mock_load:
mock_load.return_value = {
'funding_btc': -0.0001,
'dvol_btc': 60,
'fng': 40,
'taker': 0.95
}
self.acb.get_cut_for_date('2026-02-06')
self.acb.get_cut_for_date('2026-02-07')
self.acb.get_cut_for_date('2026-02-06') # Should be cached
stats = self.acb.get_stats()
self.assertEqual(stats['total_calls'], 3)
self.assertEqual(stats['cache_hits'], 1)
self.assertGreater(stats['cache_hit_rate'], 0)
class TestACBPositionSizer(unittest.TestCase):
"""Test ACB Position Sizer."""
def setUp(self):
"""Set up test fixtures."""
self.sizer = ACBPositionSizer()
def test_enabled_disabled(self):
"""Test enabling/disabling ACB."""
self.assertTrue(self.sizer.is_enabled())
self.sizer.disable()
self.assertFalse(self.sizer.is_enabled())
# When disabled, should return base size unchanged
size, info = self.sizer.calculate_size(1000.0, '2026-02-06')
self.assertEqual(size, 1000.0)
self.assertFalse(info['enabled'])
self.sizer.enable()
self.assertTrue(self.sizer.is_enabled())
def test_calculate_size_with_acb(self):
"""Test calculate size with ACB enabled."""
base_size = 1000.0
with patch.object(self.sizer.acb, 'get_cut_for_date') as mock_get:
mock_get.return_value = {
'cut': 0.15,
'signals': 1.0,
'factors': {}
}
final_size, info = self.sizer.calculate_size(base_size, '2026-02-06')
self.assertAlmostEqual(final_size, base_size * 0.85, places=10) # 15% cut
self.assertEqual(info['base_size'], base_size)
self.assertAlmostEqual(info['final_size'], final_size, places=10)
self.assertAlmostEqual(info['reduction_pct'], 15.0, places=10)
class TestIntegration(unittest.TestCase):
"""Integration tests."""
def test_feb_6_scenario(self):
"""
Test Feb 6, 2026 scenario (actual crash day).
Expected signals:
- Funding: -0.000137 (very bearish)
- DVOL: 58.9 (elevated)
- FNG: 14 (extreme fear)
Expected: 3+ signals -> 55% cut
"""
acb = AdaptiveCircuitBreaker()
# Mock the external factors for Feb 6
with patch.object(acb, '_load_external_factors') as mock_load:
mock_load.return_value = {
'funding_btc': -0.000137, # Very bearish
'dvol_btc': 58.9, # Elevated
'fng': 14, # Extreme fear
'taker': 0.85, # Mild selling
'available': True
}
result = acb.get_cut_for_date('2026-02-06')
# Should detect 3+ signals
self.assertGreaterEqual(result['signals'], 2.0)
# Should apply 55% or higher cut
self.assertGreaterEqual(result['cut'], 0.45)
def test_normal_day_scenario(self):
"""Test normal market day scenario."""
acb = AdaptiveCircuitBreaker()
with patch.object(acb, '_load_external_factors') as mock_load:
mock_load.return_value = {
'funding_btc': 0.00005, # Slightly positive
'dvol_btc': 45.0, # Normal volatility
'fng': 55, # Neutral/greed
'taker': 1.05, # Slight buying
'available': True
}
result = acb.get_cut_for_date('2026-01-15')
# Should detect 0-1 signals
self.assertLessEqual(result['signals'], 1.0)
# Should apply 0% or 15% cut
self.assertLessEqual(result['cut'], 0.15)
if __name__ == '__main__':
unittest.main()