ETH Macro Engine

The ETH Macro Engine is a bespoke analytical dashboard designed to provide a comprehensive overview of Ethereum’s market state by aggregating a wide array of technical, macro, and on-chain metrics into a single, cohesive view. By pulling data from multiple live feeds - including spot pricing, order flow dynamics, liquidity cycles, and broader sentiment indices - it removes the burden of monitoring disparate charting platforms.

The engine's primary objective is to identify market confluence, allowing you to gauge whether the asset is in an expansion or contraction phase based on the collective weight of multiple evidence-based indicators.

  • Long-Term Structural Focus: Engineered specifically for a multi-year horizon, the engine is calibrated for full-cycle structural analysis rather than risk-on, intraday speculation; by prioritising macro trends over short-term volatility, it frequently identifies high-conviction accumulation opportunities even during periods of significant geopolitical or macroeconomic distress.

At its core, the tool functions through a unique combination of algorithmic logic that evaluates market health by blending indicators such as RSI, volume delta, and moving average crossovers.

Instead of relying on a single data point, the engine weights these signals against one another to output an actionable 'Accumulate', 'Scale Out', or 'Neutral' trigger. This custom approach provides a clear, objective perspective on the market, filtering out short-term noise to help you make decisions grounded in a holistic view of the macro environment.

PLEASE NOTE: This Ethereum price panel uses caching to manage API usage limits, so the displayed values may differ slightly from the real-time market price. This widget also carries out complex calculations, thus loading can be slow at times. Please be patient.


MORE TOOLS:

ETH Macro Engine

Synchronising data feeds...
Weekly RSI (14) --
Trend Forecast --
Macro Multiplier 1.00x
Market Phase Neutral
Primary Engine Output
Awaiting system compilation...
Structure--
Linear / Log Combined--
DXY / VIX Composite--
Market Yield (vs T-Bill)--
Pi Cycle Top--
Bayesian Flow--
Volume Delta--
Cumulative Volume--
5Y Liquidity Cycle--
Asset Rotation (GOLD)--
Daily Trend (50/200D)--
Weekly Divergence--
Market Sentiment--
BTC/ETH Ratio--
Deriv Funding--%
Gas Network State--
DeFi TVL (14D)--%
Net Impact--

WARNING: This Macro Engine is an informational tool designed for macro trend analysis and must not be treated as definitive financial advice or a standalone trade execution trigger. Because the compounding calculations rely on a multi-layered matrix of daily metrics - including the weekly RSI, macro-multiplier phases, derivatives funding rates, live gas fees, DeFi TVL trends, and net validator queue impact - it is inherently a lagging macro tool that cannot capture rapid intra-day volatility or sudden, high-speed price spikes. Furthermore, the interface depends on multiple third-party aggregated historical feeds, public blockchain endpoints, and proxy-routed queue trackers that can experience API reporting latency, network throttling, or unexpected structural inconsistencies. Always verify these metrics across independent charting platforms, and use them strictly in conjunction with broader, personal risk-management strategies before making any investment decisions.

Historical Market Extremes

Traditional structural flow indicators

Weekly RSI (14) Momentum Oscillator Traditional Bottom ≤ 30 Oversold Traditional Top ≥ 70 Overbought
Fear & Greed Sentiment Index Traditional Bottom 5 to 15 Extreme Fear Traditional Top 85 to 100 Extreme Greed
Caveat: These boundaries represent historical tendencies and structural flow extremes. Cryptocurrency markets are highly volatile, and actual asset prices frequently overextend significantly higher or lower than these indicators imply before a true macro reversal occurs.

Core Functional Objectives

Noise Reduction: By incorporating a native "Hide Noise" feature, the widget allows for a clean view of essential data, filtering out the constant, distracting ticker updates when the user requires a focused view.

Weekly RSI (14): Tracks long-term momentum on a weekly timeframe. If the RSI drops to 35 (deeply oversold), it triggers an immediate +0.20 boost to the multiplier. If it climbs to 75 (extremely overbought), it cuts the multiplier by -0.20.

Trend Forecast: The Trend Forecast function utilises a unique blend of MACD momentum, Stochastic oscillators, and candlestick pattern recognition to generate a directional bias. By combining these distinct technical signals into a single score, this custom-built logic allows the engine to flag potential trend shifts or volatility more effectively than relying on the limitations of any standard, single-source indicator.

Macro Multiplier:  This is the mathematical anchor of the entire engine. It starts at a baseline of 1.00x. Every backend component acts as an independent weight, adding to or subtracting from this number. The final figure directly dictates the primary engine output.

Market Phase: The structural macro regime. Derived directly from the final Multiplier score. A score of 1.20x prints Expansion (Bull market environment). A score 0.80x prints Contraction (Bear market/Liquidity drain environment). Anything else defaults to Neutral. 

Structure: This functions as an algorithmic gatekeeper, merging Ichimoku trend boundaries with Wyckoff volume footprints to strip out short-term noise and ensure the engine only increases allocation when structural equilibrium and institutional money are perfectly aligned.

DXY / VIX Composite: Global systemic fear and fiat liquidity strength. Combines the US Dollar Index and Volatility Index into a single score. Extreme panic triggers a Contrarian Setup (+0.15x to multiplier), while extreme complacency flags Systemic Overextension (-0.15x to multiplier).

Market Yield (vs T-Bill): Benchmarks an on-chain implied economic velocity yield against the risk-free rate of the traditional US 3-Month Treasury Bill. Derived from a combined calculation of stablecoin capital velocity and recent DeFi TVL expansion, this indicator determines the financial incentive for capital to stay within the crypto ecosystem. A substantial on-chain premium reflects a highly favourable, risk-on environment, triggering a progressive boost of up to +0.10 to the multiplier. Conversely, an on-chain deficit signals a systemic liquidity drain toward sovereign bonds, stripping up to -0.10 from the core engine score.

Pi Cycle Top: Compares the 111-day moving average against a 2-year moving average (350 days x2). When the Pi Cycle ratio hits the 90% threshold, the engine automatically flags the market as "Approaching" and applies a 0.15 penalty to the multiplier, effectively scaling down risk exposure. If the moving averages cross, it flags Crossed (TOP) and slashes the multiplier by -0.50x to forcefully trigger capital preservation mode.

Bayesian Flow: Combines Hurst Exponent trend analysis, macro-weighted probabilities, funding rates, and market convergence to determine the probability of upward price expansion and flag potential liquidity squeezes.

Volume Delta: Measures the net difference between buying and selling volume over the last 14 days to identify short-term momentum shifts.

Cumulative Volume: Tracks the running total of volume delta to highlight broader buying or selling trends and spot divergences from price action.

5Y Liquidity Cycle: Tracks global central bank intervention regimes modeled on a fixed 5-year timeline from the November 2022 market trough. Modifies the multiplier based on the structural liquidity phase (e.g., Expansion adds +0.20x, while Contraction subtracts -0.20x).

Global Liquidity Cycle

Tracking the 5-year macroeconomic capital flow cycle, anchored to its foundational 2022 trough.

Total 5-Year Cycle Progress
0%
Contraction Phase (Peak to Trough)
0%
-- -- --
Calculating current phase...
Asset Rotation (GOLD): Tracks Institutional capital movement out of risk assets and into safe havens. Analyses the moving averages of the ETH/PAXG ratio. If capital is aggressively moving into gold while gold volatility is rising, it flags Heavy Safety and cuts the multiplier.

Daily Trend (50/200D): Macro technical trend confirmation. Evaluates the standard daily moving averages. A Golden Cross adds +0.10x to the multiplier, while a Death Cross subtracts -0.10x.

Weekly Divergence: Tracks structural trend reversals. Scans the last 20 weeks of price data against the weekly RSI. If price is making lower lows but the RSI is making higher lows, it prints Bullish (and vice versa for Bearish).

Market Sentiment: Tracks Retail crowd psychology. Pulls the live Crypto Fear & Greed Index. Extreme Fear (25) gives a contrarian boost of +0.10x to the multiplier; Extreme Greed (75) penalizes it by -0.10x.

BTC/ETH Ratio: Tracks internal crypto ecosystem risk appetite. Calculates the raw price ratio of ETH relative to BTC. A rising ratio indicates a healthy altcoin environment, while a falling ratio indicates capital fleeing back to the safety of Bitcoin.

Today 30 Days Ago BTC ETH
Deriv Funding: Tracks Perpetual futures market leverage. Measures the premium paid by long traders. Overleveraged markets (0.05%) penalise the multiplier by -0.15x due to high liquidation risk, while negative funding (shorts paying longs) rewards it by +0.15x.

Gas Network State: Tracks raw on-chain demand and congestion. Reads live base fees in Gwei. Extremely quiet gas flags an Accumulation state (great time to deploy on-chain capital cheaply), while sustained high gas flags a network topping out.

DeFi TVL (14D): Velocity of capital locked inside smart contracts. Tracks the net percentage change of capital entering or leaving Ethereum DeFi protocols over a rolling 14-day window.

Net Impact: Final momentum validation. If the 14D forecast is expanding and the multiplier confirms macro conditions are strong, it flashes Accelerated. If both are contracting, it flashes Decelerated.

Operational Logic

The engine utilises an asynchronous, parallel fetching architecture to ensure the widget remains fast and responsive. It queries multiple distinct APIs simultaneously - including CoinGecko, Binance, Alternative.me, and DefiLlama - to compile a unified, intelligent overview.

The widget is configured to refresh every 15 minutes, maintaining a balance between data accuracy and system efficiency.

This widget provides the analytical clarity required to maintain a grounded perspective on investment targets, ensuring that daily pressures do not interfere with the long-term objective of scaling out at a high weekly RSI.

You are welcome to copy, analyse, and use this code for your own website. I do not offer any warranty. It may break.
ETH MACRO ENGINE
<style>
    .ese-container { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: #18181b; color: #f4f4f5; max-width: 480px; padding: 20px; border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.5); }
    .ese-header { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #3f3f46; padding-bottom: 12px; margin-bottom: 16px; }
    .ese-title { font-size: 16px; font-weight: 600; color: #a1a1aa; margin: 0; }
    .ese-controls { display: flex; align-items: center; gap: 12px; }
    .ese-toggle-lbl { font-size: 12px; color: #a1a1aa; display: flex; align-items: center; cursor: pointer; gap: 6px; }
    .ese-status { height: 10px; width: 10px; background: #00ff88; border-radius: 50%; box-shadow: 0 0 8px #00ff88; cursor: help; }
    
    .ese-price-container { margin: 10px 0 15px 0; min-height: 48px; }
    .ese-main-price { font-size: 32px; font-weight: 700; display: flex; align-items: baseline; gap: 8px; transition: filter 0.3s; }
    .ese-sub-price { font-size: 16px; color: #a1a1aa; font-weight: 400; }
    .ese-blur { filter: blur(8px); opacity: 0.6; }
    .ese-loading-text { font-size: 18px; color: #a1a1aa; font-style: italic; animation: esePulse 1.5s infinite; }

    .ese-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 20px; }
    .ese-box { background: #27272a; padding: 12px; border-radius: 8px; border-left: 4px solid #3f3f46; display: flex; flex-direction: column; justify-content: space-between; min-height: 52px; transition: all 0.3s ease; }
    .ese-box-title { font-size: 11px; text-transform: uppercase; letter-spacing: 1px; color: #a1a1aa; margin-bottom: 4px; display: block; }
    .ese-box-val { font-size: 18px; font-weight: 600; }
    
    .ese-box-crash { border: 1px solid #ef4444 !important; background: rgba(239, 68, 68, 0.15) !important; border-left: 4px solid #ef4444 !important; box-shadow: inset 0 0 12px rgba(239, 68, 68, 0.2); }
    
    .ese-state-panel { background: #222225; padding: 16px; border-radius: 8px; border-left: 4px solid #3f3f46; margin-bottom: 20px; text-align: center; box-shadow: inset 0 0 20px rgba(0,0,0,0.2); transition: all 0.3s ease; }
    .ese-state-title { font-size: 12px; text-transform: uppercase; letter-spacing: 1.5px; color: #a1a1aa; margin-bottom: 8px; display: block; }
    .ese-state-value { font-size: 22px; font-weight: 700; }
    
    .ese-data-list { background: #27272a; border-radius: 8px; padding: 12px; margin-bottom: 20px; font-size: 13px; }
    .ese-row { display: flex; justify-content: space-between; padding: 7px 0; border-bottom: 1px solid #3f3f46; }
    .ese-row:last-child { border-bottom: none; }
    
    .ese-footer { text-align: center; padding-top: 16px; border-top: 1px solid #3f3f46; font-size: 11px; font-weight: 400; color: #52525b; }
    
    .text-green { color: #00ff88; } .text-red { color: #ef4444; } .text-yellow { color: #fbbf24; }
    .border-green { border-left-color: #00ff88 !important; } .border-red { border-left-color: #ef4444 !important; } .border-yellow { border-left-color: #fbbf24 !important; }

    @keyframes esePulse { 0% { opacity: 0.5; } 50% { opacity: 1; } 100% { opacity: 0.5; } }
</style>

<div class="ese-container">
    <div class="ese-header">
        <h2 class="ese-title">ETH Macro Engine</h2>
        <div class="ese-controls">
            <label class="ese-toggle-lbl">
                <input type="checkbox" id="ese-noise-toggle"> Hide Noise
            </label>
            <div id="ese-status-dot" class="ese-status"></div>
        </div>
    </div>

    <div class="ese-price-container">
        <div id="ese-price-display" class="ese-main-price">
            <span id="ese-loader" class="ese-loading-text">Synchronising data feeds...</span>
            <span id="ese-gbp" style="display:none;">&amp;pound;--</span>
            <span class="ese-sub-price" id="ese-usd" style="display:none;">&amp;#36;--</span>
        </div>
    </div>

    <div class="ese-grid">
        <div class="ese-box" id="ese-box-rsi">
            <span class="ese-box-title">Weekly RSI (14)</span>
            <span class="ese-box-val" id="ese-rsi">--</span>
        </div>
        <div class="ese-box" id="ese-box-forecast">
            <span class="ese-box-title">Trend Forecast</span>
            <span class="ese-box-val" id="ese-forecast">--</span>
        </div>
        <div class="ese-box">
            <span class="ese-box-title">Macro Multiplier</span>
            <span class="ese-box-val" id="ese-multiplier">1.00x</span>
        </div>
        <div class="ese-box">
            <span class="ese-box-title">Market Phase</span>
            <span class="ese-box-val" id="ese-phase">Neutral</span>
        </div>
    </div>

    <div class="ese-state-panel" id="ese-state-panel">
        <span class="ese-state-title">Primary Engine Output</span>
        <div class="ese-state-value" id="ese-action">Awaiting system compilation...</div>
    </div>

    <div class="ese-data-list">
        <div class="ese-row"><span>Structural Integrity</span><span id="ese-structural">--</span></div>
        <div class="ese-row"><span>Linear / Log Combined</span><span id="ese-linlog">--</span></div>
        <div class="ese-row"><span>DXY / VIX Composite</span><span id="ese-macrorisk">--</span></div>
        <div class="ese-row"><span>Market Yield (vs T-Bill)</span><span id="ese-myield">--</span></div>
        <div class="ese-row"><span>Pi Cycle Top</span><span id="ese-picycle">--</span></div>
        <div class="ese-row"><span>Bayesian Flow Dynamics</span><span id="ese-bayes">--</span></div>
        <div class="ese-row"><span>Volume Delta (14D)</span><span id="ese-delta">--</span></div>
        <div class="ese-row"><span>Cumulative Volume (CVD)</span><span id="ese-cvd">--</span></div>
        <div class="ese-row"><span>5Y Liquidity Cycle</span><span id="ese-liquidity">--</span></div>
        <div class="ese-row"><span>Asset Rotation (GOLD)</span><span id="ese-rotation">--</span></div>
        <div class="ese-row"><span>Daily Trend (50/200D)</span><span id="ese-cross">--</span></div>
        <div class="ese-row"><span>Weekly Divergence</span><span id="ese-div">--</span></div>
        <div class="ese-row"><span>Market Sentiment</span><span id="ese-sentiment">--</span></div>
        <div class="ese-row"><span>BTC/ETH Ratio</span><span id="ese-ethbtc">--</span></div>
        <div class="ese-row"><span>Deriv Funding</span><span id="ese-funding">--%</span></div>
        <div class="ese-row"><span>Gas Network State</span><span id="ese-gas">--</span></div>
        <div class="ese-row"><span>DeFi TVL (14D)</span><span id="ese-tvl">--%</span></div>
        <div class="ese-row"><span>Net Impact</span><span id="ese-impact">--</span></div>
    </div>

    <div class="ese-footer">Live Data Feed &amp;bull; Auto-updating</div>
</div>

<script>
(function() {
    const CACHE_KEY = 'eth_macro_engine_data';
    const CACHE_EXPIRY = 900000;

    async function safeFetch(url, options = {}) {
        try {
            const controller = new AbortController();
            const timeoutId = setTimeout(() => controller.abort(), 8000); 
            const res = await fetch(url, { cache: 'no-cache', ...options, signal: controller.signal });
            clearTimeout(timeoutId);
            return res.ok ? await res.json() : null;
        } catch (e) { return null; }
    }

    async function fetchWithProxy(url) {
        const primaryProxy = `https://corsproxy.io/?${encodeURIComponent(url)}`;
        const fallbackProxy = `https://api.allorigins.win/raw?url=${encodeURIComponent(url)}`;
        
        let data = await safeFetch(primaryProxy);
        if (!data) {
            data = await safeFetch(fallbackProxy);
        }
        return data;
    }

    async function fetchRobustKlines(symbol, interval, limit) {
        const binanceUrl = `https://api.binance.com/api/v3/klines?symbol=${symbol}&amp;interval=${interval}&amp;limit=${limit}`;
        let data = await safeFetch(binanceUrl);
        if (!data) data = await fetchWithProxy(binanceUrl);
        
        if (!data &amp;&amp; symbol === 'ETHUSDT') {
            const agg = interval === '1w' ? '7' : '1';
            const ccUrl = `https://min-api.cryptocompare.com/data/v2/histoday?fsym=ETH&amp;tsym=USD&amp;limit=${limit}&amp;aggregate=${agg}`;
            const ccRes = await safeFetch(ccUrl);
            if (ccRes &amp;&amp; ccRes.Data &amp;&amp; ccRes.Data.Data) {
                return ccRes.Data.Data.map(d => {
                    const t = d.time * 1000;
                    const volQuote = (d.volumeto || (d.volumefrom * d.close)) || 1;
                    const range = (d.high - d.low) || 1;
                    const buyRatio = Math.max(0.1, Math.min(0.9, 0.5 + ((d.close - d.open) / (2 * range))));
                    const takerBuyQuote = volQuote * buyRatio;
                    return [t, d.open, d.high, d.low, d.close, d.volumefrom, t, volQuote, 0, d.volumefrom * buyRatio, takerBuyQuote, 0];
                });
            }
        } else if (!data &amp;&amp; symbol === 'PAXGUSDT') {
            const ccUrl = `https://min-api.cryptocompare.com/data/v2/histoday?fsym=PAXG&amp;tsym=USD&amp;limit=${limit}`;
            const ccRes = await safeFetch(ccUrl);
            if (ccRes &amp;&amp; ccRes.Data &amp;&amp; ccRes.Data.Data) {
                return ccRes.Data.Data.map(d => [d.time * 1000, d.open, d.high, d.low, d.close, d.volumefrom, d.time * 1000, d.volumeto, 0, 0, 0, 0]);
            }
        }
        return data;
    }

    async function fetchRobustFunding() {
        const url = 'https://fapi.binance.com/fapi/v1/premiumIndex?symbol=ETHUSDT';
        let data = await safeFetch(url);
        if (!data) data = await fetchWithProxy(url);
        if (!data) {
            const bybitRes = await safeFetch('https://api.bybit.com/v5/market/tickers?category=linear&amp;symbol=ETHUSDT');
            if (bybitRes &amp;&amp; bybitRes.result &amp;&amp; bybitRes.result.list &amp;&amp; bybitRes.result.list[0]) {
                return { lastFundingRate: bybitRes.result.list[0].fundingRate };
            }
        }
        return data;
    }

    function calculateRSIArray(closes, period = 14) {
        if (closes.length <= period) return [];
        let rsiArr = new Array(period).fill(null);
        let gains = 0, losses = 0;
        for (let i = 1; i <= period; i++) {
            let diff = closes[i] - closes[i - 1];
            if (diff >= 0) gains += diff; else losses -= diff;
        }
        let avgGain = gains / period; let avgLoss = losses / period;
        rsiArr.push(100 - (100 / (1 + (avgGain / (avgLoss || 1e-10)))));
        for (let i = period + 1; i < closes.length; i++) {
            let diff = closes[i] - closes[i - 1];
            let gain = diff >= 0 ? diff : 0; let loss = diff < 0 ? -diff : 0;
            avgGain = ((avgGain * (period - 1)) + gain) / period;
            avgLoss = ((avgLoss * (period - 1)) + loss) / period;
            rsiArr.push(100 - (100 / (1 + (avgGain / (avgLoss || 1e-10)))));
        }
        return rsiArr;
    }

    const getHighLowRange = (klinesArray, startIdx, endIdx) => {
        let maxH = -Infinity, minL = Infinity;
        for (let i = startIdx; i <= endIdx; i++) {
            let h = parseFloat(klinesArray[i][2]), l = parseFloat(klinesArray[i][3]);
            if (h > maxH) maxH = h; if (l < minL) minL = l;
        }
        return { maxH, minL };
    };

    const getEMAArray = (prices, period) => {
        const k = 2 / (period + 1);
        let emaArr = [prices[0]];
        for (let i = 1; i < prices.length; i++) {
            emaArr.push((prices[i] * k) + (emaArr[i - 1] * (1 - k)));
        }
        return emaArr;
    };

    const getMACD = (prices) => {
        const ema12 = getEMAArray(prices, 12);
        const ema26 = getEMAArray(prices, 26);
        let macdLine = prices.map((_, i) => ema12[i] - ema26[i]);
        const signalLine = getEMAArray(macdLine, 9);
        const currMacd = macdLine[macdLine.length - 1];
        const currSig = signalLine[signalLine.length - 1];
        return { macd: currMacd, signal: currSig, hist: currMacd - currSig };
    };

    const getStochastic = (klines, period = 14) => {
        const recent = klines.slice(-period);
        const currentClose = parseFloat(recent[recent.length - 1][4]);
        const highs = recent.map(k => parseFloat(k[2]));
        const lows = recent.map(k => parseFloat(k[3]));
        const maxH = Math.max(...highs);
        const minL = Math.min(...lows);
        return ((currentClose - minL) / (maxH - minL || 1)) * 100;
    };

    const analyzePatterns = (klines) => {
        if (klines.length < 3) return 0;
        const last = klines[klines.length - 1];
        const prev = klines[klines.length - 2];
        const pPrev = klines[klines.length - 3];

        const o1 = parseFloat(prev[1]), c1 = parseFloat(prev[4]), h1 = parseFloat(prev[2]), l1 = parseFloat(prev[3]);
        const o2 = parseFloat(last[1]), c2 = parseFloat(last[4]), h2 = parseFloat(last[2]), l2 = parseFloat(last[3]);
        
        let score = 0;
        const body1 = Math.abs(c1 - o1);
        const body2 = Math.abs(c2 - o2);

        if (c1 < o1 &amp;&amp; c2 > o2 &amp;&amp; c2 >= o1 &amp;&amp; o2 <= c1) score += 3.5; 
        if (c1 > o1 &amp;&amp; c2 < o2 &amp;&amp; c2 <= o1 &amp;&amp; o2 >= c1) score -= 3.5; 
        
        const lowerWick2 = Math.min(o2, c2) - l2;
        const upperWick2 = h2 - Math.max(o2, c2);
        if (lowerWick2 > body2 * 2.5 &amp;&amp; upperWick2 < body2) score += 2.5; 
        if (upperWick2 > body2 * 2.5 &amp;&amp; lowerWick2 < body2) score -= 2.5;

        if (h1 < parseFloat(pPrev[2]) &amp;&amp; l1 > parseFloat(pPrev[3])) { 
            if (c2 > h1) score += 2.0;
            else if (c2 < l1) score -= 2.0;
        }

        return score;
    };

    const calculateBackendForecast = (dailyKlines) => {
        if (!dailyKlines || dailyKlines.length < 35) return 0;
        const closes = dailyKlines.map(k => parseFloat(k[4]));
        
        const macdData = getMACD(closes);
        const currentPrice = closes[closes.length - 1];
        const macdMomentum = (macdData.hist / currentPrice) * 100 * 6.5; 
        
        const stochK = getStochastic(dailyKlines, 14);
        let stochModifier = 0;
        if (stochK < 20) stochModifier = 3.5; 
        else if (stochK > 80) stochModifier = -3.5; 
        else stochModifier = (50 - stochK) * 0.116; 
        
        const patternScore = analyzePatterns(dailyKlines);
        
        let forecastScore = macdMomentum + stochModifier + patternScore;
        return Math.max(-15, Math.min(15, forecastScore)); 
    };

    function updateDOM(m) {
        document.getElementById('ese-loader')?.remove(); 
        document.getElementById('ese-gbp').style.display = 'inline'; 
        document.getElementById('ese-usd').style.display = 'inline';
        
        document.getElementById('ese-gbp').innerHTML = m.gbp ? `&amp;pound;${m.gbp.toLocaleString(undefined, {minimumFractionDigits:2, maximumFractionDigits:2})}` : '&amp;pound;--';
        document.getElementById('ese-usd').innerHTML = m.usd ? `&amp;#36;${m.usd.toLocaleString(undefined, {minimumFractionDigits:2, maximumFractionDigits:2})}` : '&amp;#36;--';
        
        const rsiEl = document.getElementById('ese-rsi'), rsiBox = document.getElementById('ese-box-rsi');
        rsiEl.innerText = m.wRsi.toFixed(1); 
        rsiEl.className = `ese-box-val ${m.wRsi <= 35 ? 'text-green' : m.wRsi >= 75 ? 'text-red' : ''}`; 
        rsiBox.className = `ese-box ${m.wRsi <= 35 ? 'border-green' : m.wRsi >= 75 ? 'border-red' : ''}`;
        
        const foreEl = document.getElementById('ese-forecast'), foreBox = document.getElementById('ese-box-forecast');
        if (m.forecast <= -10.0) { 
            foreEl.innerText = m.forecast.toFixed(1) + '% (CRASH)'; foreEl.className = 'ese-box-val text-red'; foreBox.className = 'ese-box ese-box-crash';
        } else {
            foreEl.innerText = (m.forecast >= 0 ? '+' : '') + m.forecast.toFixed(1) + '%';
            foreEl.className = `ese-box-val ${m.forecast >= 5 ? 'text-green' : m.forecast < 0 ? 'text-red' : ''}`;
            foreBox.className = `ese-box ${m.forecast >= 5 ? 'border-green' : m.forecast <= -5 ? 'border-red' : ''}`;
        }
        
        document.getElementById('ese-multiplier').innerText = m.multi.toFixed(2) + 'x'; 
        document.getElementById('ese-phase').className = `ese-box-val ${m.phase === 'Expansion' ? 'text-green' : m.phase === 'Contraction' ? 'text-red' : ''}`; 
        document.getElementById('ese-phase').innerText = m.phase;
        
        document.getElementById('ese-structural').innerText = m.structText; 
        document.getElementById('ese-structural').className = m.structClass;
        document.getElementById('ese-linlog').innerText = m.linLogText; 
        document.getElementById('ese-linlog').className = m.linLogClass;
        document.getElementById('ese-macrorisk').innerText = m.stressText; 
        document.getElementById('ese-macrorisk').className = m.stressClass;
        document.getElementById('ese-myield').innerText = m.mYieldText; 
        document.getElementById('ese-myield').className = m.mYieldClass;
        document.getElementById('ese-picycle').innerText = m.piText; 
        document.getElementById('ese-picycle').className = m.piClass;
        document.getElementById('ese-bayes').innerText = m.bayesText; 
        document.getElementById('ese-bayes').className = m.bayesClass;
        document.getElementById('ese-delta').innerText = m.deltaText;
        document.getElementById('ese-delta').className = m.deltaClass;
        document.getElementById('ese-cvd').innerText = m.cvdText;
        document.getElementById('ese-cvd').className = m.cvdClass;
        document.getElementById('ese-liquidity').innerText = m.liqState; 
        document.getElementById('ese-liquidity').className = m.liqClass;
        document.getElementById('ese-rotation').innerText = m.rotText; 
        document.getElementById('ese-rotation').className = m.rotClass;
        
        document.getElementById('ese-cross').className = (m.cross === 'Golden Cross' ? 'text-green' : m.cross === 'Death Cross' ? 'text-red' : ''); 
        document.getElementById('ese-cross').innerText = m.cross;
        document.getElementById('ese-div').className = (m.div === 'Bullish' ? 'text-green' : m.div === 'Bearish' ? 'text-red' : ''); 
        document.getElementById('ese-div').innerText = m.div;
        document.getElementById('ese-sentiment').innerText = `${m.fngVal} (${m.fngText})`; 
        document.getElementById('ese-sentiment').className = (m.fngVal <= 25 ? 'text-green' : m.fngVal >= 75 ? 'text-red' : '');
        document.getElementById('ese-ethbtc').innerText = m.ethbtc ? m.ethbtc.toFixed(5) : '--'; 
        document.getElementById('ese-tvl').innerText = (m.tvl >= 0 ? '+' : '') + m.tvl.toFixed(1) + '%'; 
        document.getElementById('ese-tvl').className = (m.tvl >= 0 ? 'text-green' : 'text-red');
        document.getElementById('ese-funding').innerText = m.funding.toFixed(4) + '%'; 
        document.getElementById('ese-gas').innerText = m.gasText; 
        document.getElementById('ese-gas').className = m.gasClass; 
        document.getElementById('ese-impact').innerText = m.impactText; 
        document.getElementById('ese-impact').className = m.impactClass;
        
        const act = document.getElementById('ese-action'); 
        const statePanel = document.getElementById('ese-state-panel');
        
        if (m.wRsi >= 75) {
            act.innerText = 'Sell Trigger Active (Macro Exit)';
            act.className = 'ese-state-value text-red';
            statePanel.className = 'ese-state-panel border-red';
        } else if (m.wRsi <= 35) {
            act.innerText = 'Buy Trigger Active (Macro Accumulate)';
            act.className = 'ese-state-value text-green';
            statePanel.className = 'ese-state-panel border-green';
        } else if (m.multi <= 0.8 || m.forecast <= -10) {
            act.innerText = 'Short-Term Weakness (Macro Hold)';
            act.className = 'ese-state-value text-yellow';
            statePanel.className = 'ese-state-panel border-yellow';
        } else if (m.multi >= 1.2 || m.forecast >= 10) {
            act.innerText = 'Short-Term Strength (Macro Hold)';
            act.className = 'ese-state-value text-yellow';
            statePanel.className = 'ese-state-panel border-yellow';
        } else {
            act.innerText = 'Neutral State (Patience)';
            act.className = 'ese-state-value text-yellow';
            statePanel.className = 'ese-state-panel border-yellow';
        }
    }

    async function executeSynthesis() {
        const cachedData = localStorage.getItem(CACHE_KEY);
        if (cachedData) {
            try {
                const parsed = JSON.parse(cachedData);
                if (Date.now() - parsed.timestamp < CACHE_EXPIRY) {
                    updateDOM(parsed.data);
                    return;
                }
            } catch (e) { localStorage.removeItem(CACHE_KEY); }
        }

        let m = { usd: null, gbp: null, ethbtc: null, wRsi: 50, forecast: 0, funding: 0.01, gas: 1.5, tvl: 0, cross: 'Neutral', div: 'None', phase: 'Neutral', fngVal: 50, fngText: 'Neutral', multi: 1.00, structText: 'Awaiting Confluence', structClass: '', stressText: 'Neutral State', stressClass: '', mYieldVal: 0, mYieldText: 'Awaiting Data', mYieldClass: '', piText: 'No Data', piClass: '', linLogText: 'Awaiting Data', linLogClass: '', bayesText: 'Awaiting Data', bayesClass: '', deltaText: 'Awaiting Data', deltaClass: '', cvdText: 'Awaiting Data', cvdClass: '', liqState: 'Neutral', liqClass: '', rotText: 'No Data', rotClass: '', gasText: 'Normal', gasClass: '', impactText: 'Normal', impactClass: '' };
        
        const [spot, binance, cg200, fund, gasRes, tvlRes, fngRes, eth365, gold365, vixRes, dxyRes, ethDaily, irxRes] = await Promise.all([
            safeFetch('https://api.coingecko.com/api/v3/simple/price?ids=ethereum,bitcoin,tether,usd-coin&amp;vs_currencies=usd,gbp&amp;include_market_cap=true&amp;include_24hr_vol=true'),
            fetchRobustKlines('ETHUSDT', '1w', 120),
            safeFetch('https://api.coingecko.com/api/v3/coins/ethereum/market_chart?vs_currency=gbp&amp;days=200&amp;interval=daily'),
            fetchRobustFunding(),
            safeFetch('https://eth.llamarpc.com', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ jsonrpc: "2.0", method: "eth_gasPrice", params: [], id: 1 }) }),
            safeFetch('https://api.llama.fi/charts/Ethereum'),
            safeFetch('https://api.alternative.me/fng/?limit=1'),
            safeFetch('https://api.coingecko.com/api/v3/coins/ethereum/market_chart?vs_currency=usd&amp;days=365&amp;interval=daily'),
            fetchRobustKlines('PAXGUSDT', '1d', 365),
            fetchWithProxy('https://query1.finance.yahoo.com/v8/finance/chart/^VIX?range=1d&amp;interval=1d'),
            fetchWithProxy('https://query1.finance.yahoo.com/v8/finance/chart/DX-Y.NYB?range=1d&amp;interval=1d'),
            fetchRobustKlines('ETHUSDT', '1d', 100),
            fetchWithProxy('https://query1.finance.yahoo.com/v8/finance/chart/^IRX?range=1d&amp;interval=1d')
        ]);
        
        if (spot &amp;&amp; spot.ethereum) { m.usd = spot.ethereum.usd; m.gbp = spot.ethereum.gbp; if (spot.bitcoin &amp;&amp; spot.bitcoin.usd) m.ethbtc = m.usd / spot.bitcoin.usd; }
        
        let closes = [];

        if (Array.isArray(binance) &amp;&amp; binance.length >= 110) { 
            closes = binance.map(k => parseFloat(k[4])); 
            
            const rsi = calculateRSIArray(closes, 14); 
            m.wRsi = rsi[rsi.length - 1]; 
            
            const recent = closes.slice(-20); 
            const rsiRecent = rsi.slice(-20); 
            
            const half = 10;
            const olderPrices = recent.slice(0, half);
            const newerPrices = recent.slice(half);
            const olderRSI = rsiRecent.slice(0, half);
            const newerRSI = rsiRecent.slice(half);

            const minP1 = Math.min(...olderPrices);
            const rsiAtMinP1 = olderRSI[olderPrices.indexOf(minP1)];
            const minP2 = Math.min(...newerPrices);
            const rsiAtMinP2 = newerRSI[newerPrices.indexOf(minP2)];

            const maxP1 = Math.max(...olderPrices);
            const rsiAtMaxP1 = olderRSI[olderPrices.indexOf(maxP1)];
            const maxP2 = Math.max(...newerPrices);
            const rsiAtMaxP2 = newerRSI[newerPrices.indexOf(maxP2)];

            if (minP2 < minP1 &amp;&amp; rsiAtMinP2 > rsiAtMinP1) {
                m.div = 'Bullish';
            } else if (maxP2 > maxP1 &amp;&amp; rsiAtMaxP2 < rsiAtMaxP1) {
                m.div = 'Bearish';
            } else {
                m.div = 'None';
            }

            let wyckoffPhase = 'Re-Accumulation';
            if (m.wRsi < 45) wyckoffPhase = 'Accumulation';
            else if (m.wRsi > 65) wyckoffPhase = 'Distribution';

            const currentIdx = binance.length - 1;
            const projectIdx = currentIdx - 26; 

            const tRange = getHighLowRange(binance, projectIdx - 8, projectIdx);
            const kRange = getHighLowRange(binance, projectIdx - 25, projectIdx);
            const sBRange = getHighLowRange(binance, projectIdx - 51, projectIdx);

            const tenkan = (tRange.maxH + tRange.minL) / 2;
            const kijun = (kRange.maxH + kRange.minL) / 2;
            const spanA = (tenkan + kijun) / 2;
            const spanB = (sBRange.maxH + sBRange.minL) / 2;

            const curPrice = closes[currentIdx];
            let ichimokuState = 'Neutral';
            if (curPrice > spanA &amp;&amp; curPrice > spanB) ichimokuState = 'Bullish';
            else if (curPrice < spanA &amp;&amp; curPrice < spanB) ichimokuState = 'Deeply Bearish';

            if (wyckoffPhase === 'Accumulation' &amp;&amp; ichimokuState === 'Deeply Bearish') {
                m.structText = 'Noise Filtered (Patience)';
                m.structClass = 'text-yellow';
            } else if (wyckoffPhase === 'Accumulation' &amp;&amp; ichimokuState !== 'Deeply Bearish') {
                m.structText = 'Accumulation Aligned';
                m.structClass = 'text-green';
                m.multi += 0.15;
            } else if (wyckoffPhase === 'Distribution' &amp;&amp; ichimokuState === 'Deeply Bearish') {
                m.structText = 'Distribution Confirmed';
                m.structClass = 'text-red';
                m.multi -= 0.15;
            } else if (wyckoffPhase === 'Distribution' &amp;&amp; ichimokuState === 'Bullish') {
                m.structText = 'Noise Filtered (Skepticism)';
                m.structClass = 'text-yellow';
            } else {
                m.structText = `${wyckoffPhase} (${ichimokuState})`;
                m.structClass = ichimokuState === 'Bullish' ? 'text-green' : ichimokuState === 'Deeply Bearish' ? 'text-red' : 'text-yellow';
                if (ichimokuState === 'Bullish') m.multi += 0.05;
                if (ichimokuState === 'Deeply Bearish') m.multi -= 0.05;
            }
        }
        
        if (cg200?.prices &amp;&amp; Array.isArray(cg200.prices)) { 
            const dp = cg200.prices.map(p => p[1]); 
            if(dp.length >= 200) {
                m.cross = (dp.slice(-50).reduce((a,b)=>a+b,0)/50 >= dp.slice(-200).reduce((a,b)=>a+b,0)/200) ? 'Golden Cross' : 'Death Cross'; 
            }
        }

        if (Array.isArray(ethDaily) &amp;&amp; ethDaily.length >= 35) {
            m.forecast = calculateBackendForecast(ethDaily);
            
            let sumDelta14D = 0;
            let sumVol14D = 0;
            let cvdHistory = [];
            let runningCVD = 0;
            
            for (let i = 0; i < ethDaily.length; i++) {
                const k = ethDaily[i];
                const open = parseFloat(k[1]), high = parseFloat(k[2]), low = parseFloat(k[3]), close = parseFloat(k[4]);
                const totalQuoteVol = parseFloat(k[7]) || (parseFloat(k[5]) * close) || 1;
                let deltaQuote = 0;
                
                if (k[10] !== undefined &amp;&amp; k[10] !== null &amp;&amp; parseFloat(k[10]) > 0) {
                    const buyQuoteVol = parseFloat(k[10]);
                    deltaQuote = (2 * buyQuoteVol) - totalQuoteVol;
                } else {
                    const range = (high - low) || 1;
                    const buyPressure = (close - low) / range;
                    const sellPressure = (high - close) / range;
                    deltaQuote = totalQuoteVol * (buyPressure - sellPressure);
                }
                
                runningCVD += deltaQuote;
                cvdHistory.push({ delta: deltaQuote, cvd: runningCVD, vol: totalQuoteVol, close: close });
                
                if (i >= ethDaily.length - 14) {
                    sumDelta14D += deltaQuote;
                    sumVol14D += totalQuoteVol;
                }
            }

            const deltaRatio14D = sumVol14D > 0 ? (sumDelta14D / sumVol14D) * 100 : 0;
            if (deltaRatio14D >= 4.0) {
                m.deltaText = `Net Inflow (+${deltaRatio14D.toFixed(1)}%)`;
                m.deltaClass = 'text-green';
                m.multi += 0.10;
            } else if (deltaRatio14D >= 1.5) {
                m.deltaText = `Accumulation (+${deltaRatio14D.toFixed(1)}%)`;
                m.deltaClass = 'text-green';
                m.multi += 0.05;
            } else if (deltaRatio14D <= -4.0) {
                m.deltaText = `Heavy Outflow (${deltaRatio14D.toFixed(1)}%)`;
                m.deltaClass = 'text-red';
                m.multi -= 0.10;
            } else if (deltaRatio14D <= -1.5) {
                m.deltaText = `Distribution (${deltaRatio14D.toFixed(1)}%)`;
                m.deltaClass = 'text-red';
                m.multi -= 0.05;
            } else {
                m.deltaText = `Balanced Flow (${deltaRatio14D > 0 ? '+' : ''}${deltaRatio14D.toFixed(1)}%)`;
                m.deltaClass = 'text-yellow';
            }

            if (cvdHistory.length >= 20) {
                const nowIdx = cvdHistory.length - 1;
                const prevIdx = Math.max(0, nowIdx - 20);
                const cvdDiff = cvdHistory[nowIdx].cvd - cvdHistory[prevIdx].cvd;
                
                let vol20D = 0;
                for (let j = prevIdx; j <= nowIdx; j++) vol20D += cvdHistory[j].vol;
                const cvdSlope = vol20D > 0 ? (cvdDiff / vol20D) * 100 : 0;
                
                const priceChg20D = ((cvdHistory[nowIdx].close - cvdHistory[prevIdx].close) / cvdHistory[prevIdx].close) * 100;
                
                if (priceChg20D < -2.0 &amp;&amp; cvdSlope > 2.0) {
                    m.cvdText = `Bullish Absorption (+${cvdSlope.toFixed(1)}%)`;
                    m.cvdClass = 'text-green';
                    m.multi += 0.15; 
                } else if (priceChg20D > 2.0 &amp;&amp; cvdSlope < -2.0) {
                    m.cvdText = `Bearish Exhaustion (${cvdSlope.toFixed(1)}%)`;
                    m.cvdClass = 'text-red';
                    m.multi -= 0.15; 
                } else if (cvdSlope >= 1.5) {
                    m.cvdText = `Confirmed Expansion (+${cvdSlope.toFixed(1)}%)`;
                    m.cvdClass = 'text-green';
                    m.multi += 0.05;
                } else if (cvdSlope <= -1.5) {
                    m.cvdText = `Confirmed Contraction (${cvdSlope.toFixed(1)}%)`;
                    m.cvdClass = 'text-red';
                    m.multi -= 0.05;
                } else {
                    m.cvdText = `Neutral Tracking (${cvdSlope > 0 ? '+' : ''}${cvdSlope.toFixed(1)}%)`;
                    m.cvdClass = 'text-yellow';
                }
            }
        }

        if (m.forecast > 5.0) m.multi += 0.10;
        else if (m.forecast > 2.0) m.multi += 0.05;
        else if (m.forecast < -5.0) m.multi -= 0.10;
        else if (m.forecast < -2.0) m.multi -= 0.05;

        if (fund?.lastFundingRate) m.funding = parseFloat(fund.lastFundingRate) * 100;
        if (gasRes?.result) m.gas = parseInt(gasRes.result, 16) / 1e9;
        
        if (Array.isArray(tvlRes) &amp;&amp; tvlRes.length > 14) {
            m.tvl = ((tvlRes[tvlRes.length-1].totalLiquidityUSD - tvlRes[tvlRes.length-15].totalLiquidityUSD) / tvlRes[tvlRes.length-15].totalLiquidityUSD) * 100;
        }

        let tBillYield = 4.35;
        try { if (irxRes?.chart?.result?.[0]?.meta?.regularMarketPrice) tBillYield = irxRes.chart.result[0].meta.regularMarketPrice; } catch(e){}
        
        let stableVol = 0, stableCap = 0;
        if (spot?.tether) { stableVol += spot.tether.usd_24h_vol || 0; stableCap += spot.tether.usd_market_cap || 1; }
        if (spot?.['usd-coin']) { stableVol += spot['usd-coin'].usd_24h_vol || 0; stableCap += spot['usd-coin'].usd_market_cap || 1; }
        
        const velocityYield = (stableCap > 0) ? ((stableVol / stableCap) * 100 * 0.25) : 3.5;
        const tvlGrowthYield = (m.tvl || 0) * 0.5;
        const onChainYield = velocityYield + Math.max(-5, Math.min(10, tvlGrowthYield));
        m.mYieldVal = onChainYield - tBillYield;

        if (m.mYieldVal >= 2.0) {
            m.mYieldText = 'Strong Premium (Risk-On)';
            m.mYieldClass = 'text-green';
            m.multi += 0.10;
        } else if (m.mYieldVal >= 0.5) {
            m.mYieldText = 'Slight Premium (Favourable)';
            m.mYieldClass = 'text-green';
            m.multi += 0.05;
        } else if (m.mYieldVal <= -2.0) {
            m.mYieldText = 'Heavy Deficit (T-Bill Drain)';
            m.mYieldClass = 'text-red';
            m.multi -= 0.10;
        } else if (m.mYieldVal <= -0.5) {
            m.mYieldText = 'Slight Deficit (Unfavourable)';
            m.mYieldClass = 'text-red';
            m.multi -= 0.05;
        } else {
            m.mYieldText = 'Parity (Neutral Flow)';
            m.mYieldClass = 'text-yellow';
        }
        
        if (fngRes?.data &amp;&amp; Array.isArray(fngRes.data) &amp;&amp; fngRes.data.length > 0) { 
            m.fngVal = parseInt(fngRes.data[0].value); 
            m.fngText = fngRes.data[0].value_classification; 
        }

        let dxy = 104.0, vix = 15.0;
        try { if (dxyRes?.chart?.result?.[0]?.meta?.regularMarketPrice) dxy = dxyRes.chart.result[0].meta.regularMarketPrice; } catch(e){}
        try { if (vixRes?.chart?.result?.[0]?.meta?.regularMarketPrice) vix = vixRes.chart.result[0].meta.regularMarketPrice; } catch(e){}
        
        const stressIndex = (vix / 20) + (dxy / 100);
        if (stressIndex >= 2.2) { m.stressText = 'Contrarian Setup'; m.stressClass = 'text-green'; m.multi += 0.15; }
        else if (stressIndex >= 1.9) { m.stressText = 'Macro-Value Zone'; m.stressClass = 'text-green'; m.multi += 0.05; }
        else if (stressIndex < 1.7) { m.stressText = 'Systemic Overextension'; m.stressClass = 'text-red'; m.multi -= 0.15; }
        
        let piRatio = 0;
        if (eth365?.prices &amp;&amp; Array.isArray(eth365.prices) &amp;&amp; eth365.prices.length >= 350) {
            const ePrices = eth365.prices.map(p => p[1]);
            const sma111 = ePrices.slice(-111).reduce((a, b) => a + b, 0) / 111;
            const sma350 = ePrices.slice(-350).reduce((a, b) => a + b, 0) / 350;
            piRatio = sma111 / (sma350 * 2);
            if (piRatio >= 1.0) { m.piText = 'Crossed (TOP)'; m.piClass = 'text-red'; m.multi -= 0.50; } 
            else if (piRatio >= 0.85) { m.piText = `Approaching ${(piRatio * 100).toFixed(1)}%`; m.piClass = 'text-red'; m.multi -= 0.15; } 
            else { m.piText = `Safe ${(piRatio * 100).toFixed(1)}%`; m.piClass = 'text-green'; if (piRatio < 0.60) m.multi += 0.10; }
        }

        if (closes.length > 50) {
            const currentClose = closes[closes.length - 1];
            let sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0; let sumLogY = 0, sumXLogY = 0; const n = closes.length;
            for (let i = 0; i < n; i++) {
                const x = i + 1; const y = closes[i]; const logY = Math.log(y);
                sumX += x; sumY += y; sumXY += x * y; sumX2 += x * x; sumLogY += logY; sumXLogY += x * logY;
            }
            const linSlope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
            const linIntercept = (sumY - linSlope * sumX) / n; const linearPred = linSlope * n + linIntercept;
            const logSlope = (n * sumXLogY - sumX * sumLogY) / (n * sumX2 - sumX * sumX);
            const logIntercept = (sumLogY - logSlope * sumX) / n; const logPred = Math.exp(logSlope * n + logIntercept);
            const combinedPred = (linearPred + logPred) / 2; const deviation = ((currentClose - combinedPred) / combinedPred) * 100;
            
            if (deviation > 35) { m.linLogText = `Overextended (+${deviation.toFixed(1)}%)`; m.linLogClass = 'text-red'; m.multi -= 0.15; } 
            else if (deviation < -25) { m.linLogText = `Undervalued (${deviation.toFixed(1)}%)`; m.linLogClass = 'text-green'; m.multi += 0.15; } 
            else { m.linLogText = `Fair Value (${deviation > 0 ? '+' : ''}${deviation.toFixed(1)}%)`; m.linLogClass = 'text-yellow'; }
        }

        if (closes.length > 60) {
            let hurst = calculateHurst(closes.slice(-60)); let probUp = 0.5; 
            if (hurst > 0.6) { if (closes[closes.length-1] > closes[closes.length-20]) probUp += 0.15; else probUp -= 0.15; } 
            else if (hurst < 0.4) { probUp -= 0.1; }
            if (piRatio < 0.6) probUp += 0.15; else if (piRatio > 0.85) probUp -= 0.2;
            if (stressIndex >= 2.2) probUp += 0.1; else if (stressIndex < 1.7) probUp -= 0.1;
            probUp = Math.max(0.05, Math.min(0.95, probUp)); 

            let flowCondition = 'Balanced';
            if (m.funding > 0.05) { flowCondition = 'Long Liq. Risk'; m.bayesClass = 'text-red'; } 
            else if (m.funding < -0.02) { flowCondition = 'Short Squeeze'; m.bayesClass = 'text-green'; } 
            else if (hurst > 0.65) { flowCondition = 'Trending Flow'; m.bayesClass = probUp > 0.5 ? 'text-green' : 'text-red'; } 
            else if (hurst < 0.4) { flowCondition = 'Chop / Reverting'; m.bayesClass = 'text-yellow'; }
            
            if (hurst > 0.65 &amp;&amp; probUp > 0.6) m.multi += 0.15;
            else if (hurst > 0.65 &amp;&amp; probUp < 0.4) m.multi -= 0.15;
            else if (hurst < 0.4) m.multi = 1 + ((m.multi - 1) * 0.7); 
            m.bayesText = `${(probUp * 100).toFixed(0)}% Prob (${flowCondition})`;
        }
        
        const msIn5Y = 5 * 365.25 * 24 * 60 * 60 * 1000;
        const timeSinceTrough = Date.now() - new Date('2022-11-20T00:00:00Z').getTime();
        const cycleProgress = (timeSinceTrough % msIn5Y) / msIn5Y;
        if (cycleProgress < 0.35) { m.liqState = 'Expansion'; m.liqClass = 'text-green'; m.multi += 0.20; }
        else if (cycleProgress < 0.50) { m.liqState = 'Peak Liquidity'; m.liqClass = 'text-green'; m.multi += 0.10; }
        else if (cycleProgress < 0.85) { m.liqState = 'Contraction'; m.liqClass = 'text-red'; m.multi -= 0.20; }
        else { m.liqState = 'Trough / Accum.'; m.liqClass = 'text-green'; m.multi += 0.15; }

        if (eth365?.prices &amp;&amp; Array.isArray(eth365.prices) &amp;&amp; eth365.prices.length >= 200 &amp;&amp; Array.isArray(gold365) &amp;&amp; gold365.length >= 200) {
            const eth200 = eth365.prices.map(p => p[1]).slice(-200); const gld200 = gold365.map(k => parseFloat(k[4])).slice(-200);
            let ratios = []; for (let i = 0; i < 200; i++) ratios.push(eth200[i] / (gld200[i] || 1));
            const sma50_ratio = ratios.slice(-50).reduce((a,b) => a + b, 0) / 50; const sma200_ratio = ratios.reduce((a,b) => a + b, 0) / 200;
            const sma14_ratio = ratios.slice(-14).reduce((a,b) => a + b, 0) / 14; const sma30_ratio = ratios.slice(-30).reduce((a,b) => a + b, 0) / 30;
            const gld30 = gld200.slice(-30); const gldMean = gld30.reduce((a,b) => a + b, 0) / 30;
            const gldStdDev = Math.sqrt(gld30.reduce((sq, n) => sq + Math.pow(n - gldMean, 2), 0) / 30); const gldVolPct = (gldStdDev / gldMean) * 100;
            const rNow = ratios[199], r30d = ratios[169];
            
            if (sma50_ratio < sma200_ratio) {
                const rotationWeight = ((r30d - rNow) / r30d) * 100;
                if (rotationWeight >= 1.0) { 
                    m.rotText = `Heavy Safety (+${rotationWeight.toFixed(1)}%)`; 
                    m.rotClass = 'text-red'; 
                    m.multi -= 0.15; 
                    if (gldVolPct > 2.5) m.multi -= 0.05; 
                } else { 
                    let rotLabel = 'Rotation Exhausted';
                    if (rotationWeight > -3.0) {
                        rotLabel = 'Rotation Cooling';
                    } else if (rotationWeight > -7.0) {
                        rotLabel = 'Active Unwind';
                    } else {
                        rotLabel = 'Rotation Exhausted';
                    }
                    m.rotText = `${rotLabel} (${rotationWeight > 0 ? '+' : ''}${rotationWeight.toFixed(1)}%)`; 
                    m.rotClass = 'text-yellow'; 
                    m.multi -= 0.05; 
                }
            } else if (sma14_ratio < sma30_ratio) {
                m.rotText = `Early Rotation (${gldVolPct.toFixed(1)}% Vol)`; m.rotClass = 'text-yellow'; m.multi -= 0.05; if (gldVolPct > 2.0) { m.multi -= 0.05; m.rotClass = 'text-red'; }
            } else {
                const riskOnWeight = ((rNow - r30d) / r30d) * 100; m.rotText = `Risk-On (${riskOnWeight > 0 ? '+' : ''}${riskOnWeight.toFixed(1)}%)`; m.rotClass = 'text-green'; if (riskOnWeight > 10) m.multi += 0.10;
            }
        }

        if (m.funding > 0.05) m.multi -= 0.15; else if (m.funding < 0) m.multi += 0.15;
        if (m.wRsi < 40) m.multi += 0.20; else if (m.wRsi > 70) m.multi -= 0.20;
        if (m.cross === 'Golden Cross') m.multi += 0.10; else if (m.cross === 'Death Cross') m.multi -= 0.10;
        if (m.fngVal <= 25) m.multi += 0.10; else if (m.fngVal >= 75) m.multi -= 0.10;
        
        m.phase = (m.multi >= 1.20) ? 'Expansion' : (m.multi <= 0.80) ? 'Contraction' : 'Neutral';
        if (m.gas <= 1.0) { m.gasText = 'Accumulation'; m.gasClass = 'text-green'; } else if (m.gas >= 10.0) { m.gasText = 'Topping Out'; m.gasClass = 'text-red'; }
        if (m.forecast >= 2.0 &amp;&amp; m.multi >= 1.1) { m.impactText = 'Accelerated'; m.impactClass = 'text-green'; } else if (m.forecast <= -2.0 &amp;&amp; m.multi <= 0.9) { m.impactText = 'Decelerated'; m.impactClass = 'text-red'; }
        
        function calculateHurst(prices) {
            if (prices.length < 10) return 0.5;
            let returns = []; for (let i = 1; i < prices.length; i++) returns.push(Math.log(prices[i] / prices[i-1]));
            let mean = returns.reduce((a,b)=>a+b,0) / returns.length; let dev = returns.map(r => r - mean);
            let sum = 0, maxZ = -Infinity, minZ = Infinity;
            for (let i = 0; i < dev.length; i++) { sum += dev[i]; if (sum > maxZ) maxZ = sum; if (sum < minZ) minZ = sum; }
            let R = maxZ - minZ; let S = Math.sqrt(dev.reduce((a,b)=>a+Math.pow(b,2),0) / returns.length);
            if (S === 0 || R === 0) return 0.5; return Math.log(R/S) / Math.log(returns.length);
        }

        localStorage.setItem(CACHE_KEY, JSON.stringify({ timestamp: Date.now(), data: m }));
        updateDOM(m);
    }
    
    document.getElementById('ese-noise-toggle').addEventListener('change', (e) => document.getElementById('ese-price-display').classList.toggle('ese-blur', e.target.checked));
    
    executeSynthesis(); 
    setInterval(executeSynthesis, 900000);

    try {
        if (!document.querySelector('.ese-footer a[data-engine-ref]')) {
            const engineUrl = 'https://www.seanduffy.uk/';
            const engineAnchor = document.createElement('a');
            engineAnchor.setAttribute('data-engine-ref', 'v1');
            engineAnchor.href = engineUrl;
            engineAnchor.target = '_blank';
            engineAnchor.style.textDecoration = 'none';
            engineAnchor.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="#52525b" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:middle;margin-left:5px;opacity:0.7;transition:stroke 0.2s;cursor:pointer;" onmouseover="this.style.stroke=\'#a1a1aa\'" onmouseout="this.style.stroke=\'#52525b\'"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>';
            document.querySelector('.ese-footer')?.appendChild(engineAnchor);
        }
    } catch (e) {}
})();
</script>

    

Post a Comment

0 Comments

Notice:
Comments are moderated and may not appear immediately. Please keep your comments respectful, and relevant to the post. My site. My rules.

Post a Comment (0)