shaibu01/Titan-Engine
0
1"""2TITAN BLOCKCHAIN ALPHA (v50.0 - INSTITUTIONAL ORDER FLOW & FUNDING PROXY)3TYPE: Institutional Whale Tracking & Microstructure Engine4DATA: Perpetual Funding Rates / Aggregated Taker Volume Imbalance5MATH: Z-Score Acceleration & Non-Linear Flow Normalization6DESCRIPTION: 100% Non-Blocking. Thread-Safe. True Microstructure Edge.7"""8 9import requests10import json11import time12import logging13import numpy as np14import pandas as pd15import threading16import concurrent.futures17 18# --- C++ CORE INJECTION ---19try:20 import titan_engine21 HAS_CPP_CORE = True22except ImportError:23 HAS_CPP_CORE = False24 25logger = logging.getLogger("TITAN")26 27CACHE_DURATION = 60 # 1 Minute Cache for high-frequency relevance28 29class FastMath:30 @staticmethod31 def get_zscore(array):32 """Calculates Z-Score of an array. Prioritizes C++ for speed."""33 if HAS_CPP_CORE and hasattr(titan_engine, 'calc_zscore'):34 try:35 np_arr = np.ascontiguousarray(array, dtype=np.float64)36 return float(titan_engine.calc_zscore(np_arr))37 except Exception:38 pass39 40 if len(array) == 0: return 0.041 std = np.std(array)42 if std == 0: return 0.043 return float((array[-1] - np.mean(array)) / std)44 45fast_math = FastMath()46 47class BlockchainAlpha:48 def __init__(self):49 logger.info("⛓️ BLOCKCHAIN ALPHA: ENGAGING MICROSTRUCTURE & FUNDING RADAR (ASYNC MODE)...")50 self.cache = {}51 self.cache_lock = threading.Lock()52 53 self.session = requests.Session()54 self.http_executor = concurrent.futures.ThreadPoolExecutor(max_workers=3)55 56 # Test Public API Connection57 try:58 res = self.session.get("https://fapi.binance.com/fapi/v1/time", timeout=3)59 if res.status_code == 200:60 logger.info("✅ PUBLIC ORDER FLOW RADAR: SECURE & ACTIVE")61 self.mode = "LIVE_PUBLIC"62 else:63 raise Exception("Public API Blocked")64 except Exception:65 logger.warning("⚠️ PUBLIC API BLOCKED: FALLING BACK TO MT5 MATH PROXY")66 self.mode = "PROXY"67 68 def _fetch_funding_and_flow(self, symbol):69 """70 Pulls Perpetual Funding Rates and Taker Buy/Sell Volume ratios.71 This provides a true indication of institutional leverage and aggression.72 """73 # Map MT5 symbol to Binance Futures symbol74 if "BTC" in symbol: base_sym = "BTCUSDT"75 elif "ETH" in symbol: base_sym = "ETHUSDT"76 elif "SOL" in symbol: base_sym = "SOLUSDT"77 else: return 0.078 79 try:80 # 1. Fetch Funding Rate (Indicates over-leveraged side)81 # Positive funding means Longs pay Shorts (Market is overly Bullish/Greedy)82 # Negative funding means Shorts pay Longs (Market is overly Bearish/Fearful)83 fund_url = f"https://fapi.binance.com/fapi/v1/premiumIndex?symbol={base_sym}"84 fund_resp = self.session.get(fund_url, timeout=3)85 funding_rate = 0.086 87 if fund_resp.status_code == 200:88 funding_rate = float(fund_resp.json().get('lastFundingRate', 0.0))89 90 # 2. Fetch Aggregated Taker Volume (Microstructure Aggression)91 # Pull the last 20 15-minute candles to calculate the Z-score of Taker Volume92 klines_url = f"https://fapi.binance.com/fapi/v1/klines?symbol={base_sym}&interval=15m&limit=20"93 klines_resp = self.session.get(klines_url, timeout=3)94 95 flow_score = 0.096 if klines_resp.status_code == 200:97 klines = klines_resp.json()98 taker_buy_vols = np.array([float(k[9]) for k in klines]) # Taker buy base asset volume99 total_vols = np.array([float(k[5]) for k in klines]) # Total volume100 101 # Calculate Taker Sell Volume102 taker_sell_vols = total_vols - taker_buy_vols103 104 # Calculate Imbalance Ratio (Buy / Sell)105 imbalance_ratios = taker_buy_vols / (taker_sell_vols + 1e-9)106 107 # Z-Score the Imbalance108 flow_score = fast_math.get_zscore(imbalance_ratios)109 110 # 3. Synthesize the Alpha Score (-1.0 to 1.0)111 # High positive flow_score + High negative funding = MASSIVE BUY SIGNAL112 # High negative flow_score + High positive funding = MASSIVE SELL SIGNAL113 114 # Normalize funding rate (Usually between -0.01% and 0.01%)115 norm_funding = max(-1.0, min(1.0, funding_rate * 10000)) 116 117 # Normalize flow score (Usually between -3 and 3)118 norm_flow = max(-1.0, min(1.0, flow_score / 3.0))119 120 # Opposing forces: We want to trade WITH the flow, but AGAINST the crowd's leverage121 final_score = (norm_flow * 0.7) - (norm_funding * 0.3)122 123 return float(max(-1.0, min(1.0, final_score)))124 125 except Exception as e:126 logger.debug(f"Order Flow Fetch Error for {symbol}: {e}")127 return 0.0128 129 def _calculate_proxy_score(self, df):130 """Fallback: Uses C++ Accelerated Volatility Z-Score if API is blocked."""131 if df is None or df.empty or len(df) < 20: 132 return 0.0133 134 try:135 closes = df['close'].values136 returns = np.diff(closes) / (closes[:-1] + 1e-9)137 138 if len(returns) < 20: 139 return 0.0140 141 vol_z = fast_math.get_zscore(returns[-20:])142 143 if abs(vol_z) < 2.0: 144 return 0.0 145 146 close = closes[-1]147 open_p = df['open'].iloc[-1]148 direction = 1.0 if close > open_p else -1.0149 150 return float(min(abs(vol_z) / 5.0, 1.0) * direction)151 152 except Exception: 153 return 0.0154 155 def _async_update_cache(self, symbol, df):156 """Background thread worker to update the cache."""157 if self.mode == "LIVE_PUBLIC":158 score = self._fetch_funding_and_flow(symbol)159 if score == 0.0:160 score = self._calculate_proxy_score(df)161 else:162 score = self._calculate_proxy_score(df)163 164 with self.cache_lock:165 self.cache[symbol] = {'score': score, 'time': time.time()}166 167 def get_onchain_sentiment(self, symbol, df=None):168 """169 Returns a raw sentiment score from -1.0 (Heavy Selling) to 1.0 (Heavy Buying).170 🚨 NON-BLOCKING UPGRADE: Instantly returns cached value while updating in background.171 """172 if "BTC" not in symbol and "ETH" not in symbol and "SOL" not in symbol:173 return 0.0174 175 now = time.time()176 177 with self.cache_lock:178 cached_data = self.cache.get(symbol)179 180 # 1. Fresh Cache: Return immediately181 if cached_data and (now - cached_data['time']) < CACHE_DURATION:182 return float(cached_data['score'])183 184 # 2. Stale/Empty Cache: Fire background update to prevent blocking185 instant_proxy_score = self._calculate_proxy_score(df)186 self.http_executor.submit(self._async_update_cache, symbol, df)187 188 # Return whatever we have right now189 if cached_data:190 return float(cached_data['score'])191 return float(instant_proxy_score)192 193 def get_onchain_signal(self, symbol, df=None):194 """Converts the raw sentiment score into a definitive Action."""195 score = self.get_onchain_sentiment(symbol, df)196 197 if score >= 0.50:198 return "STRONG_BUY"199 elif score <= -0.50:200 return "STRONG_SELL"201 else:202 return "NEUTRAL"203 204# Singleton Instance for Global Imports205alpha_oracle = BlockchainAlpha()