Ethereum: Patience Engine

This lightweight script acts as a privacy-first, macro-focused tracker for Ethereum, stripping away the standard clutter and data-harvesting mechanisms of modern financial interfaces. By establishing a direct, local connection to a public data feed, the script pulls the closing prices of the last 100 weeks and calculates a mathematically precise 14-week Relative Strength Index (RSI) entirely within the browser. Because it operates completely client-side without requiring account registration, API keys, or tracking cookies, it serves as a clean, independent utility that loads instantly and respects user privacy.

The core innovation of the widget lies in its psychological design, specifically engineered to suppress the emotional anxiety triggered by intraday market volatility. When the "Noise Filter" is enabled, the interface hides the current price, shifting the focus away from short-term fluctuations and instead displaying a clear, high-timeframe classification of the market state, such as Value, Strategic Patience, or Distribution zones. This structural boundary helps long-term holders maintain ironclad discipline, allowing them to ignore daily market noise and focus strictly on multi-month technical execution targets.

WARNING: This widget is a high-timeframe informational tool and must not be treated as a definitive financial advisor or standalone execution trigger. Because the 14-week RSI relies strictly on weekly candle closes, it is inherently a lagging indicator that cannot capture fast, mid-week market volatility or sudden price spikes. Additionally, the interface is completely dependent on a public, third-party data feed that can experience unexpected downtime, rate limits, or data delays. Always verify these metrics across independent charting platforms and use them in conjunction with broader risk-management strategies before making any execution decisions.
Ethereum Patience Engine v1.0
Current Price
Loading...
Weekly RSI (14)
Loading...
Buy Trigger
≤ 35.00
Sell Trigger
≥ 75.00
Strategic Action
Analysing...
Hide short-term price noise
Ethereum Patience Engine v1. 0
<div id="patience-engine">
    <div class="pe-header">
        <span class="pe-title">Ethereum Patience Engine v1.0</span>
        <span id="pe-status-dot" class="pe-dot"></span>
    </div>
    
    <div id="pe-noise-container" class="pe-metric-section">
        <div class="pe-label">Current Price</div>
        <div id="pe-price" class="pe-value">Loading...</div>
    </div>
    
    <div class="pe-metric-section">
        <div class="pe-label">Weekly RSI (14)</div>
        <div id="pe-rsi" class="pe-value">Loading...</div>
    </div>
    
    <div class="pe-targets-grid">
        <div class="pe-target-box" id="pe-buy-box">
            <div class="pe-label">Buy Trigger</div>
            <div class="pe-target-val">&le; 35.00</div>
        </div>
        <div class="pe-target-box" id="pe-sell-box">
            <div class="pe-label">Sell Trigger</div>
            <div class="pe-target-val">&ge; 75.00</div>
        </div>
    </div>
    
    <div class="pe-metric-section" style="margin-top: 16px;">
        <div class="pe-label">Strategic Action</div>
        <div id="pe-state" class="pe-state-text">Analysing...</div>
    </div>
    
    <div class="pe-toggle-container">
        <label class="pe-switch">
            <input type="checkbox" id="pe-noise-toggle" checked>
            <span class="pe-slider"></span>
        </label>
        <span class="pe-toggle-label">Hide short-term price noise</span>
    </div>
</div>

<style>
#patience-engine {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    background-color: #1a1a1a;
    color: #ffffff;
    padding: 20px;
    border-radius: 8px;
    max-width: 320px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
    margin: 10px auto;
}
.pe-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    border-bottom: 1px solid #333;
    padding-bottom: 12px;
    margin-bottom: 16px;
}
.pe-title {
    font-size: 14px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: #aaa;
}
.pe-dot {
    width: 8px;
    height: 8px;
    background-color: #555;
    border-radius: 50%;
    display: inline-block;
}
.pe-dot.active { background-color: #00ff88; }
.pe-dot.error { background-color: #ff3333; }
.pe-metric-section { margin-bottom: 16px; }
.pe-label {
    font-size: 11px;
    color: #888;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    margin-bottom: 4px;
}
.pe-value {
    font-size: 24px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}
.pe-targets-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
    border-top: 1px solid #222;
    border-bottom: 1px solid #222;
    padding: 12px 0;
}
.pe-target-box {
    background-color: #222;
    padding: 8px;
    border-radius: 4px;
    text-align: center;
    border: 1px solid transparent;
}
.pe-target-val {
    font-size: 14px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    margin-top: 2px;
}
.pe-state-text {
    font-size: 16px;
    font-weight: 600;
    color: #ffffff;
}
.pe-toggle-container {
    display: flex;
    align-items: center;
    margin-top: 20px;
    padding-top: 12px;
    border-top: 1px solid #333;
}
.pe-toggle-label {
    font-size: 12px;
    color: #888;
    margin-left: 10px;
}
.pe-switch {
    position: relative;
    display: inline-block;
    width: 36px;
    height: 20px;
}
.pe-switch input { display: none; }
.pe-slider {
    position: absolute;
    cursor: pointer;
    top: 0; left: 0; right: 0; bottom: 0;
    background-color: #333;
    transition: .3s;
    border-radius: 20px;
}
.pe-slider:before {
    position: absolute;
    content: "";
    height: 14px;
    width: 14px;
    left: 3px;
    bottom: 3px;
    background-color: #aaa;
    transition: .3s;
    border-radius: 50%;
}
input:checked + .pe-slider { background-color: #00ff88; }
input:checked + .pe-slider:before {
    transform: translateX(16px);
    background-color: #1a1a1a;
}
.trigger-active-buy { border-color: #00ff88 !important; background-color: #0c2a18 !important; }
.trigger-active-sell { border-color: #ff3333 !important; background-color: #2a0c0c !important; }
</style>

<script>
(function() {
    const priceEl = document.getElementById('pe-price');
    const rsiEl = document.getElementById('pe-rsi');
    const stateEl = document.getElementById('pe-state');
    const dotEl = document.getElementById('pe-status-dot');
    const toggleEl = document.getElementById('pe-noise-toggle');
    const priceContainer = document.getElementById('pe-noise-container');
    const buyBox = document.getElementById('pe-buy-box');
    const sellBox = document.getElementById('pe-sell-box');

    const BUY_THRESHOLD = 35.0;
    const SELL_THRESHOLD = 75.0;

    toggleEl.addEventListener('change', function() {
        priceContainer.style.display = this.checked ? 'none' : 'block';
    });

    if (toggleEl.checked) { priceContainer.style.display = 'none'; }

    function calculateRSI(closes, period = 14) {
        if (closes.length <= period) return null;
        let changes = [];
        for (let i = 1; i < closes.length; i++) { changes.push(closes[i] - closes[i - 1]); }
        let gains = [], losses = [];
        for (let i = 0; i < changes.length; i++) {
            if (changes[i] > 0) { gains.push(changes[i]); losses.push(0); }
            else { gains.push(0); losses.push(Math.abs(changes[i])); }
        }
        let avgGain = gains.slice(0, period).reduce((a, b) => a + b, 0) / period;
        let avgLoss = losses.slice(0, period).reduce((a, b) => a + b, 0) / period;
        for (let i = period; i < changes.length; i++) {
            avgGain = (avgGain * (period - 1) + gains[i]) / period;
            avgLoss = (avgLoss * (period - 1) + losses[i]) / period;
        }
        if (avgLoss === 0) return 100;
        return 100 - (100 / (1 + (avgGain / avgLoss)));
    }

    async function fetchMacroData() {
        try {
            const [usdResponse, gbpResponse] = await Promise.all([
                fetch('https://api.binance.com/api/v3/klines?symbol=ETHUSDT&interval=1w&limit=100'),
                fetch('https://api.coinbase.com/v2/prices/ETH-GBP/spot')
            ]);

            if (!usdResponse.ok || !gbpResponse.ok) throw new Error('Data stream failed');
            
            const usdData = await usdResponse.json();
            const gbpData = await gbpResponse.json();
            
            const closesUSD = usdData.map(candle => parseFloat(candle[4]));
            const rsi = calculateRSI(closesUSD, 14);

            const currentPriceGBP = parseFloat(gbpData.data.amount);

            priceEl.innerText = '\u00A3' + currentPriceGBP.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
            
            buyBox.className = 'pe-target-box';
            sellBox.className = 'pe-target-box';

            if (rsi !== null) {
                rsiEl.innerText = rsi.toFixed(2);
                
                if (rsi >= SELL_THRESHOLD) {
                    stateEl.innerText = 'Sell Trigger Active (Scale Out)';
                    stateEl.style.color = '#ff3333';
                    sellBox.className = 'pe-target-box trigger-active-sell';
                } else if (rsi <= BUY_THRESHOLD) {
                    stateEl.innerText = 'Buy Trigger Active (Accumulate)';
                    stateEl.style.color = '#00ff88';
                    buyBox.className = 'pe-target-box trigger-active-buy';
                } else {
                    stateEl.innerText = 'Hold / Exercise Patience';
                    stateEl.style.color = '#aaa';
                }
            } else {
                rsiEl.innerText = 'Error processing metrics';
            }
            dotEl.className = 'pe-dot active';
        } catch (error) {
            console.error(error);
            stateEl.innerText = 'Connection Error';
            dotEl.className = 'pe-dot error';
        }
    }

    fetchMacroData();
    setInterval(fetchMacroData, 900000);
})();
</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. Spam will not be tolerated. My site. My rules.

Post a Comment (0)