Buying/Selling Pressure Cycle (PreCy)
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © ALifeToMake

//@version=5
indicator(title="Buying/Selling Pressure Cycle (PreCy) V6.8", shorttitle="PreCy", overlay=false, precision=0)

// INPUTS --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
showSignal = input.bool(true, title = "The SIGNAL line", group="Display")
signalVisibility = showSignal == true ? 0 : 100

showSignalPivots = input.bool(true, title = "The pivot lines of the SIGNAL", group="Display")
signalPivotsVisibility = showSignalPivots == true ? 70 : 100

showCycle = input.bool(true, title = "The CYCLE areas", group="Display")
cycleVisibility = showCycle == true ? 0 : 100
cycleBGVisibility = showCycle == true ? 25 : 100

UPchecked = input.bool(true, title = "UP", group="Alerts", tooltip ="Show alerts of bullish nature.")
DOWNchecked = input.bool(true, title = "DOWN", group="Alerts", tooltip ="Show alerts of bearish nature.")
EXCELLchecked = input.bool(true, title = "Excellent signals", group="Alerts", tooltip ="Triggered when the SIGNAL and the CYCLE go in the same direction, and the CYCLE crosses 50 or -50, in a relatively active market.")
GOODchecked = input.bool(true, title = "Good signals", group="Alerts", tooltip ="Triggered after 3 consecutive SIGNALS above 0 or under 0, in a relatively active market.")
Increased3checked = input.bool(true, title = "3 increases of pressure", group="Alerts", tooltip ="Early sign detection. Triggered after 3 consecutive SIGNAL increases of pressure, in a relatively active market.")
Increased2checked = input.bool(true, title = "2 increases of pressure", group="Alerts", tooltip ="Early sign detection. Triggered after 3 consecutive SIGNALS above or below 0 with an increase of pressure between the 1st and the 3rd, in a relatively active market.")

SignalLength = input.int(1, minval = 1, title = "Smoothing length", group="SIGNAL (Goes from 100 to -100)")
SignalMAbullColor=input.color(#22ab94, title="Increase of buying pressure", tooltip = "Highlights the buying pressure of the SIGNAL (% of buying pressure - % of selling pressure).", group="SIGNAL (Goes from 100 to -100)")
SignalMAbullDecColor=input.color(#22ab94, title="Buying pressure slowing down", group="SIGNAL (Goes from 100 to -100)")
SignalMAMixColor=input.color(#787b86, title="When pressures are equal", group="SIGNAL (Goes from 100 to -100)")
SignalMAbearColor=input.color(color.new(#f7525f, 0), title="Increase of selling pressure", tooltip = "Highlights the selling pressure of the SIGNAL (% of buying pressure - % of selling pressure).", group="SIGNAL (Goes from 100 to -100)")
SignalMAbearDecColor=input.color(color.new(#f7525f, 0), title="Selling pressure slowing down", group="SIGNAL (Goes from 100 to -100)")

cycleLength = input.int(6, minval = 1, title = "Length", group="CYCLE (Goes from 200 to -200)", tooltip = "Moving Average of the SIGNAL * (body of this candle / 200 period Moving Average of the candle's bodies)")
cycleTrigger = input.int(40, minval = 1, title = "Trigger alert distance from zero line", group="CYCLE (Goes from 200 to -200)", tooltip = "Used to estimate the activity of the market. 1 of the last 3 candles has to be above this value (or below negative this value) for the alerts to trigger.")
increaseColor=input.color(#ce93d8, title="Increase of pressure", group="Cycle (from 200 to -200)")
decreaseColor=input.color(#d1c4e9, title="Decrease of pressure", group="Cycle (from 200 to -200)")
levelOne = input.float(120, title = "Top level", group="Cycle (from 200 to -200)", tooltip = "Where the gradient stops increasing.")
levelTwo = input.float(-120, title = "Bottom level", group="Cycle (from 200 to -200)", tooltip = "Where the gradient stops increasing.")
increaseBGColor=input.color(#d1c4e9, title="Increase of pressure's background", group="Cycle (from 200 to -200)")
decreaseBGColor=input.color(#c5b7df, title="Decrease of pressure's background", group="Cycle (from 200 to -200)")
halfBGColor=input.color(#cdbee6, title="Crossing zeroline background", group="Cycle (from 200 to -200)")

lowZoneBGColor=input.color(color.new(#d1d4dc, 0), title="Low intensity zone's background")
topLine =input.color(color.new(#70ccbd, 0), title="100% line")
botLine =input.color(color.new(#faa1a4, 0), title="-100% line")
zeroLineColor=input.color(#9598a1, title="Zeroline")


// CANDLE calculations  ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
topwick = open<close ? high - close : high - open
bottomwick = open<close ? open-low : close-low
body = open<close ? close-open : open-close

fractionup = open<close ? (topwick + bottomwick + 2*body)/(2*topwick + 2*bottomwick + 2*body) : (topwick + bottomwick)/(2*topwick + 2*bottomwick + 2*body)
fractiondown = open<close ? (topwick + bottomwick)/(2*topwick + 2*bottomwick + 2*body) : (topwick + bottomwick + 2*body)/(2*topwick + 2*bottomwick + 2*body)

// Percentages -----------------------------------------------------------------------------------------
totalOfFractions = fractionup + fractiondown
percentUp=(100*fractionup)/totalOfFractions
percentDown=(100*fractiondown)/totalOfFractions

// PRECY Calculations --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
float bodyMA = ta.sma(body, 200)
velocityRatio = body/bodyMA
velocityRatioApply = velocityRatio > 1 ? velocityRatio : 1

Signal = percentUp-percentDown
SignalMA = ta.sma(Signal,SignalLength)
pressureCycle = (Signal*velocityRatioApply)
preCycleMA = ta.sma(pressureCycle,cycleLength)

// CYCLE LIMITER (+-200) -------------------------------------------------------------------------------
float pressureCycleOverTopRatio = 0.0
float pressureCycleExcess = 0.0
preCycleMAlimited=preCycleMA
if preCycleMA>100
    pressureCycleExcess:=(preCycleMA-100)
    pressureCycleOverTopRatio:=(pressureCycleExcess/100)+1
    preCycleMAlimited:=100+(pressureCycleExcess/pressureCycleOverTopRatio)
else if preCycleMA<-100
    pressureCycleExcess:=(math.abs(preCycleMA)-100)
    pressureCycleOverTopRatio:=(pressureCycleExcess/100)+1
    preCycleMAlimited:=(-pressureCycleExcess/pressureCycleOverTopRatio)-100


// PLOTS ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// Horizontal scale lines-------------------------------------------------------------------------------
plot(150, "+150 line", color=color.new(#003a7c, 100), linewidth=1, style=plot.style_line)
plot(100, "+100 line", color=topLine, linewidth=1, style=plot.style_circles)
plusTWline=plot(50, "+50 line", color=color.new(#002c5e, 100), linewidth=1)
zeroline=plot(0, "Zeroline", color=zeroLineColor, linewidth=1)
minusTWline=plot(-50, "-50 line", color=color.new(#42000a, 100), linewidth=1)
plot(-100, "-100 line", color=botLine, linewidth=1, style=plot.style_circles)
plot(-150, "-150 line", color=color.new(#58000d, 100), linewidth=1, style=plot.style_line)

// SIGNAL COLORS ---------------------------------------------------------------------------------------
SignalMAcolor =SignalMA[1] > 0 and SignalMA > 0 and SignalMA >= SignalMA[1] ? SignalMAbullColor : SignalMA[1] > 0 and SignalMA > 0 and SignalMA < SignalMA[1] ? SignalMAbullDecColor : SignalMA[1] < 0 and SignalMA < 0 and SignalMA <= SignalMA[1] ? SignalMAbearColor : SignalMA[1] < 0 and SignalMA < 0 and SignalMA > SignalMA[1] ? SignalMAbearDecColor : SignalMA[1] >= 0 and SignalMA <= 0  and SignalMA > -50 and math.abs(SignalMA[1]) < math.abs(SignalMA) ? SignalMAbearColor : SignalMA[1] >= 0 and SignalMA <= 0  and SignalMA > -50 and math.abs(SignalMA[1]) > math.abs(SignalMA) ? SignalMAbullDecColor : SignalMA[1] >= 0 and SignalMA <= -50 ? SignalMAbearColor : SignalMA[1] <= 0 and SignalMA >= 0  and SignalMA < 50 and math.abs(SignalMA) < math.abs(SignalMA[1]) ? SignalMAbearDecColor : SignalMA[1] <= 0 and SignalMA >= 0  and SignalMA < 50 and math.abs(SignalMA) > math.abs(SignalMA[1]) ? SignalMAbullColor : SignalMA[1] <= 0 and SignalMA >= 50 ? SignalMAbullColor : SignalMAMixColor

// CYCLE COLORS ----------------------------------------------------------------------------------------
// Color generator
gradientGen(signal) =>
    returnedColor= signal>=0 ? color.from_gradient(signal, 0, levelOne, decreaseColor, increaseColor) : signal<0 ? color.from_gradient(signal, levelTwo, 0, increaseColor, decreaseColor) : #FFFFFF
pressureCycleBGColor=preCycleMA > 0 and preCycleMA[1] < 0 ? halfBGColor : preCycleMA < 0 and preCycleMA[1] > 0 ? halfBGColor : preCycleMA > 0 and preCycleMA > preCycleMA[1] ? increaseBGColor : preCycleMA > 0 and preCycleMA < preCycleMA[1] ? decreaseBGColor : preCycleMA < 0 and preCycleMA < preCycleMA[1] ? increaseBGColor : preCycleMA < 0 and preCycleMA > preCycleMA[1] ? decreaseBGColor : decreaseBGColor

// PLOTTING & FILLING the Signal and the Cycle ---------------------------------------------------------
fill(plusTWline, minusTWline, color=lowZoneBGColor)
cycleLine=plot(preCycleMAlimited, color=color.new(gradientGen(preCycleMAlimited), cycleVisibility), linewidth=2, style=plot.style_line)
plot(SignalMA, color=color.new(SignalMAcolor, signalVisibility), linewidth=2, style=plot.style_line)
fill(zeroline, cycleLine, color=color.new(pressureCycleBGColor, cycleBGVisibility))


// PIVOTS --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
leftHighBars = input(1, title="High left look back", group="Pivot lines")
rightHighBars = input(1, title="High right look back", group="Pivot lines")
highBarsColor=input.color(#828282, title="High", group="Pivot lines")
plHigh = ta.pivothigh(SignalMA, leftHighBars, rightHighBars)
plot(plHigh, style=plot.style_line, linewidth=1, color=color.new(highBarsColor, signalPivotsVisibility), offset=-rightHighBars)
leftLowBars = input(1, title="Low left look back", group="Pivot lines")
rightLowBars = input(1, title="Low right look back", group="Pivot lines")
lowBarsColor=input.color(#828282, title="Low", group="Pivot lines")
plLow = ta.pivotlow(SignalMA, leftLowBars, rightLowBars)
plot(plLow, style=plot.style_line, linewidth=1, color=color.new(lowBarsColor, signalPivotsVisibility), offset=-rightLowBars)


// ALERTS -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// AMPLITUDES Calculations -----------------------------------------------------------------------------
amp = (high - low)/((high+low)/2)
float ampMA = ta.sma(amp, 250)
ampGlobal = amp > ampMA

ampLocal = preCycleMA > cycleTrigger or preCycleMA < -cycleTrigger or preCycleMA[1] > cycleTrigger or preCycleMA[1] < -cycleTrigger or preCycleMA[2] > cycleTrigger or preCycleMA[2] < -cycleTrigger


// Conditions ------------------------------------------------------------------------------------------
crossOverCurrPos = ta.crossover(preCycleMAlimited,50) and preCycleMAlimited[1]<0
crossOverCurrNeg = ta.crossover(preCycleMAlimited,-50) and preCycleMAlimited[1]<-50
crossOverOnePos = ta.crossover(preCycleMAlimited[1],50) and preCycleMAlimited[2]<0
crossOverOneNeg = ta.crossover(preCycleMAlimited[1],-50) and preCycleMAlimited[2]<-50

crossUnderCurrPos = ta.crossunder(preCycleMAlimited,50) and preCycleMAlimited[1]>50
crossUnderCurrNeg = ta.crossunder(preCycleMAlimited,-50) and preCycleMAlimited[1]<0
crossUnderOnePos = ta.crossunder(preCycleMAlimited[1],50) and preCycleMAlimited[2]>50
crossUnderOneNeg = ta.crossunder(preCycleMAlimited[1],-50) and preCycleMAlimited[2]<0

excellentUp = false
goodUp = false
increase3Up = false
increase2Up = false
excellentDown = false
goodDown =  false
increase3Down = false
increase2Down = false

// UP -----------------------------------------------
if UPchecked and ampGlobal and ampLocal and Signal > 0 and barstate.isconfirmed
    excellentUp := EXCELLchecked and crossOverCurrPos or crossOverCurrNeg or crossOverOnePos or crossOverOneNeg
    goodUp := GOODchecked and Signal[1] > 0 and Signal[2] > 0
    increase3Up := Increased3checked and Signal > Signal[1] and Signal[1] > Signal[2] and Signal[2] > Signal[3]
    increase2Up := Increased2checked and Signal >= 0 and Signal[1] >= 0 and Signal[2] >= 0 and Signal > Signal[2]
// DOWN ---------------------------------------------
if DOWNchecked and ampGlobal and ampLocal and Signal < 0 and barstate.isconfirmed
    excellentDown := EXCELLchecked and crossUnderCurrPos or crossUnderCurrNeg or crossUnderOnePos or crossUnderOneNeg
    goodDown := GOODchecked and Signal[1] < 0 and Signal[2] < 0
    increase3Down := Increased3checked and Signal[3] > Signal[2] and Signal[2] > Signal[1] and Signal[1] > Signal
    increase2Down := Increased2checked and Signal <= 0 and Signal[1] <= 0 and Signal[2] <= 0 and Signal[2] > Signal


// TRIGGERS and messages -------------------------------------------------------------------------------
if excellentUp
    alert('Excellent ▲ signals on TF:'+timeframe.period)
else if excellentDown
    alert('Excellent ▼ signals on TF:'+timeframe.period)
else if goodUp
    alert('Good ▲ signals on TF:'+timeframe.period)
else if goodDown
    alert('Good ▼ signals on TF:'+timeframe.period)
else if increase3Up
    alert('3 increases of bullish pressure on TF:'+timeframe.period)
else if increase3Down
    alert('3 increases of bearish pressure on TF:'+timeframe.period)
else if increase2Up
    alert('2 increases of bullish pressure on TF:'+timeframe.period)
else if increase2Down
    alert('2 increases of bearish pressure on TF:'+timeframe.period)