Bitcoin Miner Extreme Selling
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © SimpleCryptoLife
//@version=5
indicator("Bitcoin Miner Extreme Selling", overlay=false, precision=4)


// ABOUT
// This script is for identifying extreme selling. Judging by the chart, Bitcoin miners often (not always) sell hard for two reasons: to take profit into parabolic price rises, or to stay solvent when the price is very low.
// Extreme selling thus often coincides with long-term tops and bottoms in Bitcoin price. This can be a useful EXTRA data point when trying to time long-term Bitcoin spot or crypto equity investment (NOT advice, you remain responsible, etc).
// The difference between selling measured in BTC and in USD gives a reasonable idea of whether miners are selling to make a profit or to stay solvent.

// CREDITS
// The idea for using the ratio of miner outflows to reserves comes from the "Bitcoin Miner Sell Pressure" script by the pioneering capriole_charles. 
// The two request.security calls are identical. Another similarity is that you have to sum the outflows to make it make sense. But it doesn't make much difference, it turns out from testing, to use an average of the reserves, so I didn't. All other code is different.
// The script from capriole_charles uses Bollinger bands to highlight periods when sell pressure is high, uses a rolling 30-day sum, and only uses the BTC metrics.
// My script uses a configurable 2-6 week rolling sum (there's nothing magical about one month), uses different calculations, and uses BTC, USD, and composite metrics.

// INPUTS
// Rolling Time Basis: Determines how much data is rolled up. At the lowest level, daily data is too volatile. If you choose, e.g., 1 week, then the indicator displays the relative selling on a weekly basis. Longer time periods, obviously, are smoother but delayed, while shorter time periods are more reactive. There is no "real" time period, only an explicit interpretation.
// Show Data > Outflows: Displays the relative selling data, along with a long-term moving average. You might use this option if you want to compare the "real" heights of peaks across history.
// Show Data > Delta (the default): Only the difference between the relative selling and the long-term moving average is displayed, along with an average of *that*. This is more signal and less noise.
// Base Currency: Configure whether the calculations use BTC or USD as the metric. This setting doesn't use the BTC price at all; it switches the data requested from INTOTHEBLOCK.
//                  If you choose Composite (the default), the script combines BTC and USD together in a relative way (you can't simply add them, as USD is a much bigger absolute value).
//                      In Composite mode, the peaks are coloured red if BTC selling is higher than USD, which usually indicates forced selling, and green if USD is higher, which usually indicates profit-taking. This categorisation is not perfectly accurate but it is interesting insomuch as it is derived from block data and not Bitcoin price.
//                  In BTC or USD mode, a gradient is used to give a rough visual idea of how far from the average the current value is, and to make it look pretty.

// USAGE NOTES
// Because of the long-term moving averages, the length of the chart does make a difference. I recommend running the script on the longest Bitcoin chart, ticker BLX.
// To use it to compare selling with pivots in crypto equities, use a split chart: one BLX with the indicator applied, and one with the equity of your choice. Sync Interval, Crosshair, Time, and Date Range, but not Symbol.



// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
//                                            INPUTS                                                //
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //

int in_timeBasisWeeks = input.int(title="Rolling Time Basis (weeks)", minval=1, maxval=6, defval=2, tooltip="The time period for which miner outflows are summed.")
string in_showData = input.string(defval="Delta", title="Show Data", options=["Delta","Outflows"], tooltip="Outflows: Show the relative and average outflows. This is like the \"raw\" data. Delta: Show by how much the outflows are above the average \(and the average of that\). This is a bit more signal and less noise.")
string in_baseCurrency = input.string(defval="Composite", title="Base Currency", options=["Composite","BTC","USD"])

// === Variables derived solely from inputs ===
int timeBasisDays = in_timeBasisWeeks * 7
bool showOutflows = in_showData == "Outflows", bool showDelta = in_showData == "Delta"
bool showBTC = in_baseCurrency == "BTC", bool showUSD = in_baseCurrency == "USD", bool showComposite = in_baseCurrency == "Composite"


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
//                                          GET DATA                                                //
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //

float minerOutflowsBTC = request.security("INTOTHEBLOCK:BTC_MINEROUTFLOWS","D",close)  // Hardcoded to days. View this indicator on a chart at the daily interval.
float minerReservesBTC = request.security("INTOTHEBLOCK:BTC_MINERRESERVES","D",close)  

float minerOutflowsUSD = request.security("INTOTHEBLOCK:BTC_MINEROUTFLOWSUSD","D",close) 
float minerReservesUSD = request.security("INTOTHEBLOCK:BTC_MINERRESERVESUSD","D",close)  


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
//                                          CALCULATIONS                                            //
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //

f_ema(_source,_length) => _alpha = (2 / (_length + 1)), var float _ema = 0.0, _ema := na(_ema[1]) ? _source : _alpha * _source + (1 - _alpha) * nz(_ema[1])  // Function taken from the Pine docs for ema(). We can't use the built-in function because we want to change the length dynamically.

f_delta(_outflows,_reserves,_timeBasisDays) =>
    float _outflowsRelativeToReserves = ta.rma(_outflows,5)/_reserves  // How much selling is there *relative* to the size of the reserves. Smoothed because the raw data is extremely volatile, and smoothing makes it easier to work with and doesn't affect the timing significantly.
    float _sumOutflowsRelative = math.sum(_outflowsRelativeToReserves,_timeBasisDays)  // Sum the relative outflows over the time basis, because otherwise it doesn't form patterns.
    _sumOutflowsRelative := _sumOutflowsRelative/_timeBasisDays  // Adjust the sum for the size of the time basis to normalise the size of the peaks for different inputs.
    var int _dynamicLength = 1, _dynamicLength := bar_index > 1 and bar_index < 200 ? bar_index : 200  // Increase the long-term average MA length up to 200, so that we can start processing as early as possible.
    float _outflowsAverage = f_ema(_source=_sumOutflowsRelative,_length=_dynamicLength)  // Long-term average of relative outflows to use as a baseline.
    float _delta = _sumOutflowsRelative - _outflowsAverage  // Is the current relative selling more than (positive) or less than (negative) the baseline.
    var float _deltaPositiveAverage = na
    float _deltaPositiveSource = _delta > 0 ? _delta : _deltaPositiveAverage  // Modify the input conditionally so that we can call the function on every bar for consistency.
    _deltaPositiveAverage := f_ema(_source=_deltaPositiveSource,_length=_dynamicLength) 
    [_sumOutflowsRelative,_outflowsAverage,_delta,_deltaPositiveAverage]


[sumOutflowsRelativeBTC,outflowsAverageBTC,deltaBTC,deltaPositiveAverageBTC] = f_delta(minerOutflowsBTC,minerReservesBTC,timeBasisDays)
[sumOutflowsRelativeUSD,outflowsAverageUSD,deltaUSD,deltaPositiveAverageUSD] = f_delta(minerOutflowsUSD,minerReservesUSD,timeBasisDays)

// Create Composite delta. We don't create an average for the Composite Delta because it doesn't make sense.
float ratioOutflowsAverageBTC = (sumOutflowsRelativeBTC/outflowsAverageBTC)-1  // What is the BTC outflow as a ratio of its average? Positive if above the average, negative if below.
float sumOutflowsRelativeComposite = sumOutflowsRelativeUSD + (ratioOutflowsAverageBTC * outflowsAverageUSD)  // Use the USD as a base and add/subtract proportional BTC outflows. Just adding them wouldn't work.
float outflowsAverageComposite = outflowsAverageUSD  // We can use the USD average because of using the USD values as a base and it is the EXACT SAME as calculating it separately. Assigning it here doesn't make coding sense but is just for clarity of reading.
float deltaComposite = sumOutflowsRelativeComposite - outflowsAverageComposite  // Simple now we've done all the other stuff.


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
//                                             PLOTS                                                //
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //

// OUTFLOWS
plot(series=showOutflows and showBTC ? sumOutflowsRelativeBTC : showOutflows and showUSD ? sumOutflowsRelativeUSD : showOutflows and showComposite ? sumOutflowsRelativeComposite : na, title="Relative outflows", linewidth=1, color=color.yellow)  // Show outflows in the appropriate currency.
plot(series=showOutflows and showBTC ? outflowsAverageBTC : showOutflows and showUSD ? outflowsAverageUSD : showOutflows and showComposite ? outflowsAverageComposite : na, title="Average outflows", color=color.red, linewidth=2)  // Show the average

// DELTA PLOT
float deltaPositiveBTC = deltaBTC < 0 ? 0 : deltaBTC, float deltaPositiveUSD = deltaUSD < 0 ? 0 : deltaUSD, float deltaPositiveComposite = deltaComposite < 0 ? 0 : deltaComposite  // Suppress plots under zero
deltaPositivePlot = plot(series=showDelta and showBTC ? deltaPositiveBTC : showDelta and showUSD ? deltaPositiveUSD : showDelta and showComposite ? deltaPositiveComposite : na, title="Delta (selling > average)", color=color.new(color.white,50))
deltaPositiveAveragePlot = plot(series=showDelta and showBTC ? deltaPositiveAverageBTC : showDelta and showUSD ? deltaPositiveAverageUSD : na, title="Average of delta", color=color.red, linewidth=1)

// DELTA FILL
float zeroLineBTC = deltaBTC > 0 ? 0 : na, float zeroLineUSD = deltaUSD > 0 ? 0 : na, float zeroLineComposite = deltaComposite > 0 ? 0 : na
zeroPlot = plot(series=showDelta and showBTC ? zeroLineBTC : showDelta and showUSD ? zeroLineUSD : showComposite ? zeroLineComposite : na, color=na, style=plot.style_linebr, title="Phantom plot for 0")

float deltaOrAverageLowerBTC = math.min(deltaPositiveAverageBTC, deltaPositiveBTC), float deltaOrAverageLowerUSD = math.min(deltaPositiveAverageUSD, deltaPositiveUSD)
deltaOrAverageLowerPlot = plot(series=showDelta and showBTC ? deltaOrAverageLowerBTC : showDelta and showUSD ? deltaOrAverageLowerUSD : na, style=plot.style_linebr, color=na, title="Phantom plot for fill 1")
fill(zeroPlot,deltaOrAverageLowerPlot, title="Fill for lower part of delta", color=color.new(color.white,80))

float averageIfDeltaHigherBTC = deltaPositiveBTC > deltaPositiveAverageBTC ? deltaPositiveAverageBTC : na
float averageIfDeltaHigherUSD = deltaPositiveUSD > deltaPositiveAverageUSD ? deltaPositiveAverageUSD : na
averageIfDeltaHigherPlot = plot(series=showDelta and showBTC ? averageIfDeltaHigherBTC : showDelta and showUSD ? averageIfDeltaHigherUSD : na, style=plot.style_linebr, color=na, title="Phantom plot for fill 2")
deltaPositiveAverageResolved = showBTC ? deltaPositiveAverageBTC : showUSD ? deltaPositiveAverageUSD : na
averageIfDeltaHigherResolved = showBTC ? averageIfDeltaHigherBTC : showUSD ? averageIfDeltaHigherUSD : na
fill(plot1=averageIfDeltaHigherPlot,plot2=deltaPositivePlot, title="Fill for upper part of delta", top_value=deltaPositiveAverageResolved*3, bottom_value=averageIfDeltaHigherResolved, top_color=color.yellow, bottom_color=color.red)  // Make it look pretty

// COMPOSITE DELTA FILL
// Take a % of how much the positive delta is above the average.
float ratioDeltaBTC = deltaBTC/deltaPositiveAverageBTC, float ratioDeltaUSD = deltaUSD/deltaPositiveAverageUSD, 
float ratioDeltaBTCPositive = ratioDeltaBTC < 1 ? 0 : ratioDeltaBTC >= 1 ? ratioDeltaBTC : na, float ratioDeltaUSDPositive = ratioDeltaUSD < 1 ? 0 : ratioDeltaUSD >= 1 ? ratioDeltaUSD : na  // Not needed?
float ratioDelta = ratioDeltaBTC - ratioDeltaUSD

// Colour the composite Delta plot: If BTC % positive delta is higher, the gradient is red/yellow. If USD is higher, it's green-blue.
zeroLineCompositeFixed = showComposite ? zeroLineComposite : na  // So the fill scopes to only Composite
zeroLineCompositeFixedPlot = plot(series=zeroLineCompositeFixed, color=na, title="Phantom Plot for Composite Zero Line")

compositeFillColour = ratioDelta > 0.1 ? color.red : ratioDelta < 0.1 and ratioDelta > 0 ? color.new(color.red,50) : ratioDelta < -0 and ratioDelta > -0.1 ? color.new(color.green,50) : ratioDelta < -0.1 ? color.green : na
fill(plot1=zeroLineCompositeFixedPlot,plot2=deltaPositivePlot, title="Fill for Composite delta", color=compositeFillColour)


// =============== FIN ============== //
//                                    //
//      WAGMI ? wagyu : ramen         //
//                                    //
// ================================== //