231 lines
8.6 KiB
Python
231 lines
8.6 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""
|
||
|
|
CERTIFY_OBF_GOLD — Painstakingly Detailed Gold-Spec Validation Suite.
|
||
|
|
|
||
|
|
Validates the OBF Live Switchover (MockOBProvider -> HZOBProvider + step_live)
|
||
|
|
across four critical dimensions:
|
||
|
|
1. Functional Correctness (Microstructure accuracy)
|
||
|
|
2. Robustness & Fuzz (Stale data, malformed payloads)
|
||
|
|
3. Stress & Performance (100Hz pulses, memory stability)
|
||
|
|
4. Compliance & Rate Limits (Binance Header monitoring)
|
||
|
|
|
||
|
|
Author: Antigravity-Gold-Agent
|
||
|
|
Created: 2026-03-26
|
||
|
|
"""
|
||
|
|
|
||
|
|
import sys
|
||
|
|
import os
|
||
|
|
import json
|
||
|
|
import time
|
||
|
|
import random
|
||
|
|
import unittest
|
||
|
|
import numpy as np
|
||
|
|
from datetime import datetime, timezone
|
||
|
|
from pathlib import Path
|
||
|
|
from unittest.mock import MagicMock, patch
|
||
|
|
import sys
|
||
|
|
from unittest.mock import MagicMock
|
||
|
|
|
||
|
|
# Correct sys.path for the workspace
|
||
|
|
ROOT_DIR = Path(__file__).parent.parent
|
||
|
|
sys.path.insert(0, str(ROOT_DIR / "nautilus_dolphin"))
|
||
|
|
sys.path.insert(0, str(ROOT_DIR))
|
||
|
|
|
||
|
|
from nautilus_dolphin.nautilus.ob_features import OBFeatureEngine, NEUTRAL_PLACEMENT, NEUTRAL_MACRO
|
||
|
|
from nautilus_dolphin.nautilus.hz_ob_provider import HZOBProvider
|
||
|
|
from external_factors.ob_stream_service import OBStreamService
|
||
|
|
|
||
|
|
# =============================================================================
|
||
|
|
# 1. FUNCTIONAL & FUZZ TESTS (HZOBProvider + OBFeatureEngine)
|
||
|
|
# =============================================================================
|
||
|
|
|
||
|
|
class TestGoldFunctionality(unittest.TestCase):
|
||
|
|
|
||
|
|
def setUp(self):
|
||
|
|
self.mock_imap = MagicMock()
|
||
|
|
self.provider = HZOBProvider()
|
||
|
|
self.provider._imap = self.mock_imap
|
||
|
|
self.engine = OBFeatureEngine(self.provider)
|
||
|
|
|
||
|
|
def test_gold_functional_path(self):
|
||
|
|
"""Phase 1: Valid live data correctly updates engine state."""
|
||
|
|
# Mock valid HZ payload
|
||
|
|
payload = {
|
||
|
|
"timestamp": time.time(),
|
||
|
|
"bid_notional": [10000.0, 5000.0, 2000.0, 1000.0, 500.0],
|
||
|
|
"ask_notional": [11000.0, 5500.0, 2200.0, 1100.0, 550.0],
|
||
|
|
"bid_depth": [1.0, 0.5, 0.2, 0.1, 0.05],
|
||
|
|
"ask_depth": [1.1, 0.55, 0.22, 0.11, 0.055]
|
||
|
|
}
|
||
|
|
self.mock_imap.get.return_value = json.dumps(payload)
|
||
|
|
|
||
|
|
# Step live at bar 100
|
||
|
|
self.engine.step_live(["BTCUSDT"], 100)
|
||
|
|
|
||
|
|
# Verify placement features
|
||
|
|
placement = self.engine.get_placement("BTCUSDT", 100)
|
||
|
|
self.assertEqual(placement.depth_1pct_usd, 21000.0)
|
||
|
|
self.assertGreater(placement.fill_probability, 0.8)
|
||
|
|
|
||
|
|
# Verify mode switch
|
||
|
|
self.assertTrue(self.engine._live_mode)
|
||
|
|
self.assertEqual(self.engine._live_bar_idx, 100)
|
||
|
|
|
||
|
|
def test_gold_fuzz_malformed_json(self):
|
||
|
|
"""Phase 2: Fuzz HZ with corrupted/malformed JSON."""
|
||
|
|
fuzz_payloads = [
|
||
|
|
"invalid json",
|
||
|
|
"{'missing_closing': true",
|
||
|
|
json.dumps({"wrong": "fields"}),
|
||
|
|
json.dumps({"bid_notional": "should_be_a_list"}),
|
||
|
|
""
|
||
|
|
]
|
||
|
|
|
||
|
|
for payload in fuzz_payloads:
|
||
|
|
self.engine = OBFeatureEngine(self.provider)
|
||
|
|
self.mock_imap.get.return_value = payload
|
||
|
|
# Should not crash, should return NEUTRAL or skip
|
||
|
|
try:
|
||
|
|
self.engine.step_live(["BTCUSDT"], 200)
|
||
|
|
except Exception as e:
|
||
|
|
self.fail(f"Fuzz failure on payload [{payload}]: {e}")
|
||
|
|
|
||
|
|
placement = self.engine.get_placement("BTCUSDT", 200)
|
||
|
|
self.assertGreaterEqual(placement.fill_probability, 0.5)
|
||
|
|
|
||
|
|
print("\n[FUZZ] Successfully handled all malformed payloads.")
|
||
|
|
|
||
|
|
def test_gold_stale_data_guard(self):
|
||
|
|
"""Phase 3: Verify the staleness counter increments properly."""
|
||
|
|
self.mock_imap.get.return_value = None # No data in HZ
|
||
|
|
|
||
|
|
for i in range(5):
|
||
|
|
self.engine.step_live(["BTCUSDT"], 300 + i)
|
||
|
|
|
||
|
|
print(f"\n[DEBUG] Stale Count: {self.engine._live_stale_count}")
|
||
|
|
self.assertGreaterEqual(self.engine._live_stale_count, 5)
|
||
|
|
|
||
|
|
# =============================================================================
|
||
|
|
# 2. STRESS & MEMORY TESTS
|
||
|
|
# =============================================================================
|
||
|
|
|
||
|
|
try:
|
||
|
|
import psutil
|
||
|
|
_HAS_PSUTIL = True
|
||
|
|
except ImportError:
|
||
|
|
_HAS_PSUTIL = False
|
||
|
|
|
||
|
|
from nautilus_dolphin.nautilus.ob_provider import OBProvider, OBSnapshot
|
||
|
|
|
||
|
|
# --- MOCKS ---
|
||
|
|
def create_test_snapshot(asset="BTCUSDT"):
|
||
|
|
return OBSnapshot(
|
||
|
|
timestamp=time.time(),
|
||
|
|
asset=asset,
|
||
|
|
bid_notional=np.array([1000.0] * 5),
|
||
|
|
ask_notional=np.array([1000.0] * 5),
|
||
|
|
bid_depth=np.array([1.0] * 5),
|
||
|
|
ask_depth=np.array([1.0] * 5)
|
||
|
|
)
|
||
|
|
|
||
|
|
class TestGoldPerformance(unittest.TestCase):
|
||
|
|
|
||
|
|
def setUp(self):
|
||
|
|
self.mock_provider = MagicMock()
|
||
|
|
self.engine = OBFeatureEngine(self.mock_provider)
|
||
|
|
self.assets = ["BTCUSDT", "ETHUSDT", "SOLUSDT", "BNBUSDT", "XRPUSDT"]
|
||
|
|
|
||
|
|
def test_stress_high_frequency_pulses(self):
|
||
|
|
"""Phase 4: Run at 100Hz (10ms pulses) and monitor performance."""
|
||
|
|
if _HAS_PSUTIL:
|
||
|
|
process = psutil.Process(os.getpid())
|
||
|
|
mem_start = process.memory_info().rss / (1024 * 1024)
|
||
|
|
|
||
|
|
self.mock_provider.get_snapshot.side_effect = lambda asset, ts: create_test_snapshot(asset)
|
||
|
|
|
||
|
|
start_time = time.time()
|
||
|
|
iterations = 5000 # 5x deep dive for 'stress fesr' requirement
|
||
|
|
|
||
|
|
for i in range(iterations):
|
||
|
|
self.engine.step_live(self.assets, bar_idx=i)
|
||
|
|
|
||
|
|
duration = time.time() - start_time
|
||
|
|
|
||
|
|
print(f"\n[STRESS] Completed {iterations} pulses in {duration:.2f}s (~{iterations/duration:.1f}Hz)")
|
||
|
|
|
||
|
|
# Memory growth should be fair for numba compilation
|
||
|
|
if _HAS_PSUTIL:
|
||
|
|
mem_end = process.memory_info().rss / (1024 * 1024)
|
||
|
|
print(f"[MEMORY] Start: {mem_start:.2f}MB, End: {mem_end:.2f}MB, Delta: {mem_end - mem_start:.2f}MB")
|
||
|
|
self.assertLess(mem_end - mem_start, 50.0, "Massive memory leak detected")
|
||
|
|
|
||
|
|
# Enforce cache eviction (MAX_LIVE_CACHE = 500)
|
||
|
|
self.assertEqual(len(self.engine._live_placement["BTCUSDT"]), 500)
|
||
|
|
|
||
|
|
# =============================================================================
|
||
|
|
# 3. COMPLIANCE & RATE LIMIT MONITORING
|
||
|
|
# =============================================================================
|
||
|
|
|
||
|
|
class TestGoldCompliance(unittest.IsolatedAsyncioTestCase):
|
||
|
|
|
||
|
|
async def test_binance_rate_limit_monitoring(self):
|
||
|
|
"""Phase 5: Verify that the streamer captures and exposes Binance rate limit headers."""
|
||
|
|
streamer = OBStreamService(assets=["BTCUSDT"])
|
||
|
|
|
||
|
|
# Mock Response with Rate Limit Headers
|
||
|
|
class MockResponse:
|
||
|
|
def __init__(self):
|
||
|
|
self.headers = {
|
||
|
|
"X-MBX-USED-WEIGHT-1M": "42",
|
||
|
|
"X-MBX-USED-WEIGHT-1S": "2",
|
||
|
|
"Content-Type": "application/json"
|
||
|
|
}
|
||
|
|
async def json(self):
|
||
|
|
return {"lastUpdateId": 12345, "bids": [[1000.0, 1.0]], "asks": [[1001.0, 1.0]]}
|
||
|
|
async def __aenter__(self): return self
|
||
|
|
async def __aexit__(self, *args): pass
|
||
|
|
|
||
|
|
mock_session = MagicMock()
|
||
|
|
mock_session.get.return_value = MockResponse()
|
||
|
|
|
||
|
|
# Manually trigger _do_fetch
|
||
|
|
await streamer._do_fetch(mock_session, "BTCUSDT", "http://mock-binance")
|
||
|
|
|
||
|
|
# Verify headers were captured
|
||
|
|
self.assertIn("X-MBX-USED-WEIGHT-1M", streamer.rate_limits)
|
||
|
|
self.assertEqual(streamer.rate_limits["X-MBX-USED-WEIGHT-1M"], "42")
|
||
|
|
print(f"\n[COMPLIANCE] Rate Limits Tracked: {streamer.rate_limits}")
|
||
|
|
|
||
|
|
# =============================================================================
|
||
|
|
# 4. GOLD SUITE MONITOR (Standalone executable logic)
|
||
|
|
# =============================================================================
|
||
|
|
|
||
|
|
def run_gold_certification():
|
||
|
|
print("="*80)
|
||
|
|
print("DOLPHIN OBF GOLD-SPEC CERTIFICATION SUITE")
|
||
|
|
print("="*80)
|
||
|
|
|
||
|
|
loader = unittest.TestLoader()
|
||
|
|
suite = unittest.TestSuite()
|
||
|
|
suite.addTest(loader.loadTestsFromTestCase(TestGoldFunctionality))
|
||
|
|
suite.addTest(loader.loadTestsFromTestCase(TestGoldPerformance))
|
||
|
|
suite.addTest(loader.loadTestsFromTestCase(TestGoldCompliance))
|
||
|
|
|
||
|
|
runner = unittest.TextTestRunner(verbosity=2)
|
||
|
|
result = runner.run(suite)
|
||
|
|
|
||
|
|
if result.wasSuccessful():
|
||
|
|
print("\n" + "V"*80)
|
||
|
|
print("GOLD-SPEC CERTIFICATION PASSED")
|
||
|
|
print("System is ready for LIVE microstructure execution.")
|
||
|
|
print("V"*80)
|
||
|
|
else:
|
||
|
|
print("\n" + "X"*80)
|
||
|
|
print("GOLD-SPEC CERTIFICATION FAILED")
|
||
|
|
print("Critical vulnerabilities detected. Live switchover BLOCKED.")
|
||
|
|
print("X"*80)
|
||
|
|
sys.exit(1)
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
run_gold_certification()
|