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:
602
nautilus_dolphin/tests/test_acb_nautilus_vs_reference.py
Executable file
602
nautilus_dolphin/tests/test_acb_nautilus_vs_reference.py
Executable file
@@ -0,0 +1,602 @@
|
||||
"""
|
||||
ACB Nautilus vs Reference Implementation - Identity Test
|
||||
=========================================================
|
||||
|
||||
Verifies that the Nautilus-Dolphin ACB implementation produces
|
||||
IDENTICAL results to the tested reference implementation.
|
||||
|
||||
This test runs WITHOUT requiring Nautilus Trader to be installed,
|
||||
using only the core ACB logic.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict, Tuple
|
||||
|
||||
# Add parent to path
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# REFERENCE IMPLEMENTATION (Copied from tested ACB)
|
||||
# ============================================================================
|
||||
|
||||
@dataclass
|
||||
class ReferenceACBConfig:
|
||||
"""Reference ACB v5 Configuration - THE GROUND TRUTH."""
|
||||
|
||||
CUT_RATES = {
|
||||
0: 0.00,
|
||||
1: 0.15,
|
||||
2: 0.45,
|
||||
3: 0.55,
|
||||
4: 0.75,
|
||||
5: 0.80,
|
||||
}
|
||||
|
||||
FUNDING_VERY_BEARISH = -0.0001
|
||||
FUNDING_BEARISH = 0.0
|
||||
DVOL_EXTREME = 80
|
||||
DVOL_ELEVATED = 55
|
||||
FNG_EXTREME_FEAR = 25
|
||||
FNG_FEAR = 40
|
||||
TAKER_SELLING = 0.8
|
||||
TAKER_MILD_SELLING = 0.9
|
||||
|
||||
|
||||
class ReferenceAdaptiveCircuitBreaker:
|
||||
"""
|
||||
Reference implementation - THE GROUND TRUTH.
|
||||
This is the tested, validated implementation.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.config = ReferenceACBConfig()
|
||||
|
||||
def calculate_signals(self, factors: Dict) -> Dict:
|
||||
"""Calculate signals - REFERENCE implementation."""
|
||||
signals = 0.0
|
||||
severity = 0
|
||||
|
||||
# Funding
|
||||
funding = factors.get('funding_btc', 0)
|
||||
if funding < self.config.FUNDING_VERY_BEARISH:
|
||||
signals += 1.0
|
||||
severity += 2
|
||||
elif funding < self.config.FUNDING_BEARISH:
|
||||
signals += 0.5
|
||||
severity += 1
|
||||
|
||||
# DVOL
|
||||
dvol = factors.get('dvol_btc', 50)
|
||||
if dvol > self.config.DVOL_EXTREME:
|
||||
signals += 1.0
|
||||
severity += 2
|
||||
elif dvol > self.config.DVOL_ELEVATED:
|
||||
signals += 0.5
|
||||
severity += 1
|
||||
|
||||
# FNG
|
||||
fng = factors.get('fng', 50)
|
||||
if fng < self.config.FNG_EXTREME_FEAR:
|
||||
if signals >= 1:
|
||||
signals += 1.0
|
||||
severity += 2
|
||||
elif fng < self.config.FNG_FEAR:
|
||||
if signals >= 0.5:
|
||||
signals += 0.5
|
||||
severity += 1
|
||||
|
||||
# Taker
|
||||
taker = factors.get('taker', 1.0)
|
||||
if taker < self.config.TAKER_SELLING:
|
||||
signals += 1.0
|
||||
severity += 1
|
||||
elif taker < self.config.TAKER_MILD_SELLING:
|
||||
signals += 0.5
|
||||
|
||||
return {'signals': signals, 'severity': severity}
|
||||
|
||||
def get_cut_from_signals(self, signals: float) -> float:
|
||||
"""Map signals to cut - REFERENCE implementation."""
|
||||
if signals >= 5.0:
|
||||
return self.config.CUT_RATES[5]
|
||||
elif signals >= 4.0:
|
||||
return self.config.CUT_RATES[4]
|
||||
elif signals >= 3.0:
|
||||
return self.config.CUT_RATES[3]
|
||||
elif signals >= 2.0:
|
||||
return self.config.CUT_RATES[2]
|
||||
elif signals >= 1.0:
|
||||
return self.config.CUT_RATES[1]
|
||||
else:
|
||||
return self.config.CUT_RATES[0]
|
||||
|
||||
def get_cut_for_factors(self, factors: Dict) -> Dict:
|
||||
"""Get complete cut info - REFERENCE implementation."""
|
||||
signal_info = self.calculate_signals(factors)
|
||||
cut = self.get_cut_from_signals(signal_info['signals'])
|
||||
|
||||
return {
|
||||
'cut': cut,
|
||||
'signals': signal_info['signals'],
|
||||
'severity': signal_info['severity'],
|
||||
'factors': factors
|
||||
}
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# NAUTILUS IMPLEMENTATION (Import the actual Nautilus ACB)
|
||||
# ============================================================================
|
||||
|
||||
# Try to import Nautilus ACB
|
||||
try:
|
||||
from nautilus_dolphin.nautilus_dolphin.nautilus.adaptive_circuit_breaker import (
|
||||
AdaptiveCircuitBreaker as NautilusACB,
|
||||
ACBConfig as NautilusACBConfig
|
||||
)
|
||||
NAUTILUS_AVAILABLE = True
|
||||
except ImportError as e:
|
||||
print(f"Warning: Nautilus ACB not available: {e}")
|
||||
NAUTILUS_AVAILABLE = False
|
||||
|
||||
# Create placeholder for testing
|
||||
class NautilusACB:
|
||||
pass
|
||||
class NautilusACBConfig:
|
||||
pass
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# TEST CASES
|
||||
# ============================================================================
|
||||
|
||||
class TestACBIdentity(unittest.TestCase):
|
||||
"""
|
||||
Verify Nautilus ACB produces IDENTICAL results to reference.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""Set up both implementations."""
|
||||
cls.reference = ReferenceAdaptiveCircuitBreaker()
|
||||
|
||||
if NAUTILUS_AVAILABLE:
|
||||
cls.nautilus = NautilusACB()
|
||||
else:
|
||||
cls.nautilus = None
|
||||
|
||||
def _compare_results(self, factors: Dict, test_name: str):
|
||||
"""Compare reference vs Nautilus results."""
|
||||
# Get reference result
|
||||
ref_result = self.reference.get_cut_for_factors(factors)
|
||||
|
||||
# Get Nautilus result (if available)
|
||||
if self.nautilus is None:
|
||||
self.skipTest("Nautilus ACB not available")
|
||||
|
||||
# Mock the external factor loading
|
||||
self.nautilus._load_external_factors = lambda date: factors
|
||||
naut_result = self.nautilus.get_cut_for_date('2026-02-06')
|
||||
|
||||
# Compare cut rates (MUST be identical)
|
||||
self.assertAlmostEqual(
|
||||
ref_result['cut'], naut_result['cut'],
|
||||
places=6,
|
||||
msg=f"{test_name}: Cut rate mismatch - "
|
||||
f"Ref: {ref_result['cut']}, Naut: {naut_result['cut']}"
|
||||
)
|
||||
|
||||
# Compare signals (MUST be identical)
|
||||
self.assertAlmostEqual(
|
||||
ref_result['signals'], naut_result['signals'],
|
||||
places=6,
|
||||
msg=f"{test_name}: Signal count mismatch - "
|
||||
f"Ref: {ref_result['signals']}, Naut: {naut_result['signals']}"
|
||||
)
|
||||
|
||||
return ref_result, naut_result
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Test Case 1: No Stress (0 signals)
|
||||
# -------------------------------------------------------------------------
|
||||
def test_no_stress_all_normal(self):
|
||||
"""Test normal market conditions - 0 signals expected."""
|
||||
factors = {
|
||||
'funding_btc': 0.0001, # Positive (bullish)
|
||||
'dvol_btc': 40.0, # Low volatility
|
||||
'fng': 60, # Greed
|
||||
'taker': 1.1 # Buying pressure
|
||||
}
|
||||
|
||||
ref, naut = self._compare_results(factors, "No Stress")
|
||||
|
||||
self.assertEqual(ref['signals'], 0.0)
|
||||
self.assertEqual(ref['cut'], 0.0)
|
||||
print(f" No Stress: signals={ref['signals']:.1f}, cut={ref['cut']*100:.0f}% - MATCH")
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Test Case 2: Single Signal Variations
|
||||
# -------------------------------------------------------------------------
|
||||
def test_funding_stress_only(self):
|
||||
"""Test funding stress only - 1 signal expected."""
|
||||
factors = {
|
||||
'funding_btc': -0.00015, # Very bearish
|
||||
'dvol_btc': 40.0,
|
||||
'fng': 60,
|
||||
'taker': 1.1
|
||||
}
|
||||
|
||||
ref, naut = self._compare_results(factors, "Funding Stress")
|
||||
|
||||
self.assertEqual(ref['signals'], 1.0)
|
||||
self.assertEqual(ref['cut'], 0.15)
|
||||
print(f" Funding Stress: signals={ref['signals']:.1f}, cut={ref['cut']*100:.0f}% - MATCH")
|
||||
|
||||
def test_dvol_stress_only(self):
|
||||
"""Test DVOL stress only - 1 signal expected."""
|
||||
factors = {
|
||||
'funding_btc': 0.0001,
|
||||
'dvol_btc': 85.0, # Extreme volatility
|
||||
'fng': 60,
|
||||
'taker': 1.1
|
||||
}
|
||||
|
||||
ref, naut = self._compare_results(factors, "DVOL Stress")
|
||||
|
||||
self.assertEqual(ref['signals'], 1.0)
|
||||
self.assertEqual(ref['cut'], 0.15)
|
||||
print(f" DVOL Stress: signals={ref['signals']:.1f}, cut={ref['cut']*100:.0f}% - MATCH")
|
||||
|
||||
def test_taker_stress_only(self):
|
||||
"""Test taker stress only - 1 signal expected."""
|
||||
factors = {
|
||||
'funding_btc': 0.0001,
|
||||
'dvol_btc': 40.0,
|
||||
'fng': 60,
|
||||
'taker': 0.75 # Selling pressure
|
||||
}
|
||||
|
||||
ref, naut = self._compare_results(factors, "Taker Stress")
|
||||
|
||||
self.assertEqual(ref['signals'], 1.0)
|
||||
self.assertEqual(ref['cut'], 0.15)
|
||||
print(f" Taker Stress: signals={ref['signals']:.1f}, cut={ref['cut']*100:.0f}% - MATCH")
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Test Case 3: FNG Confirmation Logic
|
||||
# -------------------------------------------------------------------------
|
||||
def test_fng_no_confirmation(self):
|
||||
"""Test FNG without confirmation - should NOT count."""
|
||||
factors = {
|
||||
'funding_btc': 0.0001, # No other signals
|
||||
'dvol_btc': 40.0,
|
||||
'fng': 20, # Extreme fear (but no confirmation)
|
||||
'taker': 1.1
|
||||
}
|
||||
|
||||
ref, naut = self._compare_results(factors, "FNG No Confirmation")
|
||||
|
||||
# FNG requires confirmation, so should be 0 signals
|
||||
self.assertEqual(ref['signals'], 0.0)
|
||||
self.assertEqual(ref['cut'], 0.0)
|
||||
print(f" FNG No Conf: signals={ref['signals']:.1f}, cut={ref['cut']*100:.0f}% - MATCH")
|
||||
|
||||
def test_fng_with_confirmation(self):
|
||||
"""Test FNG WITH confirmation - requires signals >= 1 from other factors."""
|
||||
factors = {
|
||||
'funding_btc': -0.00015, # Strong funding signal (1.0) to confirm FNG
|
||||
'dvol_btc': 40.0,
|
||||
'fng': 20, # Extreme fear (confirmed by funding)
|
||||
'taker': 1.1
|
||||
}
|
||||
|
||||
ref, naut = self._compare_results(factors, "FNG With Confirmation")
|
||||
|
||||
# 1.0 from funding + 1.0 from confirmed FNG = 2.0 signals
|
||||
self.assertEqual(ref['signals'], 2.0)
|
||||
self.assertEqual(ref['cut'], 0.45) # 2 signals = 45% cut
|
||||
print(f" FNG With Conf: signals={ref['signals']:.1f}, cut={ref['cut']*100:.0f}% - MATCH")
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Test Case 4: Two Signals (45% Cut)
|
||||
# -------------------------------------------------------------------------
|
||||
def test_two_signals_funding_dvol(self):
|
||||
"""Test funding + DVOL stress - 2 signals expected."""
|
||||
factors = {
|
||||
'funding_btc': -0.00015, # Very bearish (1.0)
|
||||
'dvol_btc': 85.0, # Extreme (1.0)
|
||||
'fng': 60,
|
||||
'taker': 1.1
|
||||
}
|
||||
|
||||
ref, naut = self._compare_results(factors, "Two Signals (Funding+DVOL)")
|
||||
|
||||
self.assertEqual(ref['signals'], 2.0)
|
||||
self.assertEqual(ref['cut'], 0.45)
|
||||
print(f" 2 Signals: signals={ref['signals']:.1f}, cut={ref['cut']*100:.0f}% - MATCH")
|
||||
|
||||
def test_two_signals_funding_taker(self):
|
||||
"""Test funding + taker stress - 2 signals expected."""
|
||||
factors = {
|
||||
'funding_btc': -0.00015, # Very bearish (1.0)
|
||||
'dvol_btc': 40.0,
|
||||
'fng': 60,
|
||||
'taker': 0.75 # Selling (1.0)
|
||||
}
|
||||
|
||||
ref, naut = self._compare_results(factors, "Two Signals (Funding+Taker)")
|
||||
|
||||
self.assertEqual(ref['signals'], 2.0)
|
||||
self.assertEqual(ref['cut'], 0.45)
|
||||
print(f" 2 Signals: signals={ref['signals']:.1f}, cut={ref['cut']*100:.0f}% - MATCH")
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Test Case 5: Three Signals (55% Cut - Crash Level)
|
||||
# -------------------------------------------------------------------------
|
||||
def test_three_signals_feb6_scenario(self):
|
||||
"""
|
||||
Test Feb 6, 2026 scenario - THE CRASH DAY.
|
||||
|
||||
Actual values from Feb 6:
|
||||
- Funding: -0.000137 (very bearish)
|
||||
- DVOL: 58.9 (elevated)
|
||||
- FNG: 14 (extreme fear - confirmed)
|
||||
- Taker: ~0.85 (mild selling)
|
||||
|
||||
Expected: 3 signals, 55% cut
|
||||
"""
|
||||
factors = {
|
||||
'funding_btc': -0.000137, # Very bearish (1.0)
|
||||
'dvol_btc': 58.9, # Elevated (0.5)
|
||||
'fng': 14, # Extreme fear, confirmed (1.0)
|
||||
'taker': 0.85 # Mild selling (0.5)
|
||||
}
|
||||
|
||||
ref, naut = self._compare_results(factors, "Feb 6 Crash Scenario")
|
||||
|
||||
# 1.0 + 0.5 + 1.0 + 0.5 = 3.0 signals
|
||||
self.assertEqual(ref['signals'], 3.0)
|
||||
self.assertEqual(ref['cut'], 0.55)
|
||||
print(f" Feb 6: signals={ref['signals']:.1f}, cut={ref['cut']*100:.0f}% - MATCH")
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Test Case 6: Four+ Signals (75-80% Cut)
|
||||
# -------------------------------------------------------------------------
|
||||
def test_four_signals_extreme(self):
|
||||
"""Test extreme stress - 4 signals expected."""
|
||||
factors = {
|
||||
'funding_btc': -0.0002, # Very bearish (1.0)
|
||||
'dvol_btc': 95.0, # Extreme (1.0)
|
||||
'fng': 10, # Extreme fear, confirmed (1.0)
|
||||
'taker': 0.7 # Strong selling (1.0)
|
||||
}
|
||||
|
||||
ref, naut = self._compare_results(factors, "Four Signals (Extreme)")
|
||||
|
||||
self.assertEqual(ref['signals'], 4.0)
|
||||
self.assertEqual(ref['cut'], 0.75)
|
||||
print(f" 4 Signals: signals={ref['signals']:.1f}, cut={ref['cut']*100:.0f}% - MATCH")
|
||||
|
||||
def test_five_signals_apocalypse(self):
|
||||
"""Test apocalyptic stress - 5+ signals expected."""
|
||||
# Add even more extreme conditions
|
||||
factors = {
|
||||
'funding_btc': -0.0003, # Extremely bearish (1.0)
|
||||
'dvol_btc': 100.0, # Max volatility (1.0)
|
||||
'fng': 5, # Max fear, confirmed (1.0)
|
||||
'taker': 0.6 # Extreme selling (1.0)
|
||||
}
|
||||
|
||||
ref, naut = self._compare_results(factors, "Five+ Signals (Apocalypse)")
|
||||
|
||||
self.assertGreaterEqual(ref['signals'], 4.0)
|
||||
self.assertIn(ref['cut'], [0.75, 0.80])
|
||||
print(f" 5+ Signals: signals={ref['signals']:.1f}, cut={ref['cut']*100:.0f}% - MATCH")
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Test Case 7: Edge Cases
|
||||
# -------------------------------------------------------------------------
|
||||
def test_boundary_funding_exact(self):
|
||||
"""Test exact funding boundary (-0.0001)."""
|
||||
factors = {
|
||||
'funding_btc': -0.0001, # Exactly at threshold
|
||||
'dvol_btc': 40.0,
|
||||
'fng': 60,
|
||||
'taker': 1.1
|
||||
}
|
||||
|
||||
ref, naut = self._compare_results(factors, "Funding Boundary (-0.0001)")
|
||||
|
||||
# -0.0001 is the threshold, should trigger 1 signal
|
||||
self.assertEqual(ref['signals'], 1.0)
|
||||
self.assertEqual(ref['cut'], 0.15)
|
||||
print(f" Boundary: signals={ref['signals']:.1f}, cut={ref['cut']*100:.0f}% - MATCH")
|
||||
|
||||
def test_boundary_dvol_exact(self):
|
||||
"""Test exact DVOL boundary (55 and 80)."""
|
||||
# At 55 threshold
|
||||
factors = {
|
||||
'funding_btc': 0.0001,
|
||||
'dvol_btc': 55.0, # Exactly at elevated threshold
|
||||
'fng': 60,
|
||||
'taker': 1.1
|
||||
}
|
||||
|
||||
ref, naut = self._compare_results(factors, "DVOL Boundary (55)")
|
||||
|
||||
# 55 is elevated threshold, should trigger 0.5 signal
|
||||
self.assertEqual(ref['signals'], 0.5)
|
||||
print(f" DVOL 55: signals={ref['signals']:.1f} - MATCH")
|
||||
|
||||
# At 80 threshold
|
||||
factors['dvol_btc'] = 80.0 # Exactly at extreme threshold
|
||||
ref, naut = self._compare_results(factors, "DVOL Boundary (80)")
|
||||
|
||||
# 80 is extreme threshold, should trigger 1.0 signal
|
||||
self.assertEqual(ref['signals'], 1.0)
|
||||
self.assertEqual(ref['cut'], 0.15)
|
||||
print(f" DVOL 80: signals={ref['signals']:.1f}, cut={ref['cut']*100:.0f}% - MATCH")
|
||||
|
||||
def test_signal_thresholds_exact(self):
|
||||
"""Test exact signal count thresholds (1.0, 2.0, 3.0, 4.0, 5.0)."""
|
||||
test_cases = [
|
||||
(1.0, 0.15, "Exactly 1 signal"),
|
||||
(2.0, 0.45, "Exactly 2 signals"),
|
||||
(3.0, 0.55, "Exactly 3 signals"),
|
||||
(4.0, 0.75, "Exactly 4 signals"),
|
||||
(5.0, 0.80, "Exactly 5 signals"),
|
||||
]
|
||||
|
||||
for signals, expected_cut, description in test_cases:
|
||||
ref_cut = self.reference.get_cut_from_signals(signals)
|
||||
|
||||
if self.nautilus:
|
||||
naut_cut = self.nautilus._get_cut_from_signals(signals)
|
||||
self.assertEqual(ref_cut, naut_cut,
|
||||
f"{description}: Ref={ref_cut}, Naut={naut_cut}")
|
||||
|
||||
self.assertEqual(ref_cut, expected_cut,
|
||||
f"{description}: Expected {expected_cut}, got {ref_cut}")
|
||||
|
||||
print(f" {description}: cut={ref_cut*100:.0f}% - MATCH")
|
||||
|
||||
|
||||
class TestACBPositionSizing(unittest.TestCase):
|
||||
"""Test position sizing with ACB."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""Set up implementations."""
|
||||
cls.reference = ReferenceAdaptiveCircuitBreaker()
|
||||
|
||||
def test_position_sizing_calculation(self):
|
||||
"""Test that position sizing math is correct."""
|
||||
base_size = 1000.0
|
||||
|
||||
test_cases = [
|
||||
(0.0, 1000.0, "0% cut"),
|
||||
(0.15, 850.0, "15% cut"),
|
||||
(0.45, 550.0, "45% cut"),
|
||||
(0.55, 450.0, "55% cut"),
|
||||
(0.75, 250.0, "75% cut"),
|
||||
(0.80, 200.0, "80% cut"),
|
||||
]
|
||||
|
||||
for cut, expected_size, description in test_cases:
|
||||
actual_size = base_size * (1 - cut)
|
||||
self.assertAlmostEqual(
|
||||
actual_size, expected_size,
|
||||
places=2,
|
||||
msg=f"{description}: Expected ${expected_size}, got ${actual_size}"
|
||||
)
|
||||
print(f" {description}: ${base_size} * (1 - {cut}) = ${actual_size} - MATCH")
|
||||
|
||||
|
||||
class TestConfigurationIdentity(unittest.TestCase):
|
||||
"""Test that configurations are identical."""
|
||||
|
||||
def test_cut_rates_identical(self):
|
||||
"""Verify cut rates are identical between implementations."""
|
||||
ref_config = ReferenceACBConfig()
|
||||
|
||||
if not NAUTILUS_AVAILABLE:
|
||||
self.skipTest("Nautilus not available")
|
||||
|
||||
naut_config = NautilusACBConfig()
|
||||
|
||||
for signals, ref_cut in ref_config.CUT_RATES.items():
|
||||
naut_cut = naut_config.CUT_RATES.get(signals)
|
||||
self.assertIsNotNone(naut_cut, f"Nautilus missing cut rate for {signals} signals")
|
||||
self.assertEqual(ref_cut, naut_cut,
|
||||
f"Cut rate mismatch at {signals} signals: Ref={ref_cut}, Naut={naut_cut}")
|
||||
print(f" {signals} signals: Ref={ref_cut}, Naut={naut_cut} - MATCH")
|
||||
|
||||
def test_thresholds_identical(self):
|
||||
"""Verify thresholds are identical."""
|
||||
ref_config = ReferenceACBConfig()
|
||||
|
||||
if not NAUTILUS_AVAILABLE:
|
||||
self.skipTest("Nautilus not available")
|
||||
|
||||
naut_config = NautilusACBConfig()
|
||||
|
||||
thresholds = [
|
||||
('FUNDING_VERY_BEARISH', 'FUNDING_VERY_BEARISH'),
|
||||
('FUNDING_BEARISH', 'FUNDING_BEARISH'),
|
||||
('DVOL_EXTREME', 'DVOL_EXTREME'),
|
||||
('DVOL_ELEVATED', 'DVOL_ELEVATED'),
|
||||
('FNG_EXTREME_FEAR', 'FNG_EXTREME_FEAR'),
|
||||
('FNG_FEAR', 'FNG_FEAR'),
|
||||
('TAKER_SELLING', 'TAKER_SELLING'),
|
||||
('TAKER_MILD_SELLING', 'TAKER_MILD_SELLING'),
|
||||
]
|
||||
|
||||
for ref_attr, naut_attr in thresholds:
|
||||
ref_val = getattr(ref_config, ref_attr)
|
||||
naut_val = getattr(naut_config, naut_attr)
|
||||
self.assertEqual(ref_val, naut_val,
|
||||
f"Threshold mismatch for {ref_attr}: Ref={ref_val}, Naut={naut_val}")
|
||||
print(f" {ref_attr}: Ref={ref_val}, Naut={naut_val} - MATCH")
|
||||
|
||||
|
||||
def run_identity_tests():
|
||||
"""Run all identity tests and print summary."""
|
||||
print("=" * 80)
|
||||
print("ACB NAUTILUS vs REFERENCE - IDENTITY TEST SUITE")
|
||||
print("=" * 80)
|
||||
print()
|
||||
|
||||
# Create test suite
|
||||
loader = unittest.TestLoader()
|
||||
suite = unittest.TestSuite()
|
||||
|
||||
suite.addTests(loader.loadTestsFromTestCase(TestACBIdentity))
|
||||
suite.addTests(loader.loadTestsFromTestCase(TestACBPositionSizing))
|
||||
suite.addTests(loader.loadTestsFromTestCase(TestConfigurationIdentity))
|
||||
|
||||
# Run tests
|
||||
runner = unittest.TextTestRunner(verbosity=2)
|
||||
result = runner.run(suite)
|
||||
|
||||
# Print summary
|
||||
print()
|
||||
print("=" * 80)
|
||||
print("TEST SUMMARY")
|
||||
print("=" * 80)
|
||||
print(f"Tests Run: {result.testsRun}")
|
||||
print(f"Failures: {len(result.failures)}")
|
||||
print(f"Errors: {len(result.errors)}")
|
||||
print(f"Skipped: {len(result.skipped)}")
|
||||
|
||||
if result.wasSuccessful():
|
||||
print()
|
||||
print("✓ ALL TESTS PASSED - NAUTILUS ACB PRODUCES IDENTICAL RESULTS TO REFERENCE")
|
||||
print()
|
||||
print("The Nautilus-Dolphin ACB implementation is verified to be:")
|
||||
print(" - Mathematically identical to the tested reference")
|
||||
print(" - Producing the same cut rates for all signal combinations")
|
||||
print(" - Using the same thresholds and logic")
|
||||
print(" - Safe for production deployment")
|
||||
else:
|
||||
print()
|
||||
print("✗ TESTS FAILED - REVIEW IMPLEMENTATION")
|
||||
print()
|
||||
if result.failures:
|
||||
print("Failures:")
|
||||
for test, trace in result.failures:
|
||||
print(f" - {test}")
|
||||
if result.errors:
|
||||
print("Errors:")
|
||||
for test, trace in result.errors:
|
||||
print(f" - {test}")
|
||||
|
||||
return result.wasSuccessful()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
success = run_identity_tests()
|
||||
sys.exit(0 if success else 1)
|
||||
Reference in New Issue
Block a user