Price Strength Index + RSI Buy/Sell Zones
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © tkarolak 
//@version=5

// ====================
// ==== Background ====
// ====================

// This Indicator assesses the strength and direction of a financial asset's price trend 
// by comparing the current price to a range of Volume Weighted Moving Averages (VWMAs) 
// with varying lengths and subsequently calculates the percentage of prices above these VWMAs.

// Insights:
// 1. Price Trend Strength: The indicator calculates multiple VWMAs based on historical price 
//    and volume data, allowing traders to assess the varying aspects of the price trend.
// 2. Buy and Sell Zones: User-defined thresholds create buy and sell zones, indicating potential 
//    entry and exit points.
// 3. RSI Integration: RSI conditions further refine signals. When the Price Strength Index
//    falls within the buy zone and RSI is below the lower RSI threshold, it may signal a buy opportunity. 
//    Conversely, when it falls within the sell zone and RSI is above the upper RSI threshold, 
//    it may indicate a sell opportunity.
// 4. Color Coding: The indicator employs color coding for visual clarity. 
//    - PSI Index Line: The PSI Index line color is used to signify trends. Green denotes an uptrend, 
//      indicating that the percentage of prices above VWMAs is increasing and the trend is strengthening. 
//      Conversely, red signifies a downtrend, indicating a decrease in the percentage of prices above VWMAs 
//      and a weakening trend.
//    - Background Color: The background color adapts based on PSI Index and RSI conditions. 
//      Red indicates a potential buy zone and oversold territory, while green highlights a potential sell zone 
//      and overbought territory.
//    - The middle horizontal orange line is used to mark the median and potential reversal points if crossed.


indicator("Price Strength Index + RSI Buy/Sell Zones", "Price Strength + B/S Zones", overlay = false)

// Custom types
type SettingsPsi
    float   source
    int     quantity
    int     lenght

type SettingsRsi
    bool    showInZones
    int     upperRsi
    int     lowerRsi

// Price Strength Index settings
string gPsi                 = "Price Strength Index (PS Index)"
string ttSource             = "Select the price source for VWMA and RSI calculations"
string ttQuantityPsi        = "Set the number of historical VWMA calculations"
string ttLenghtPsi          = "Specify the smoothing length for obtaining the Simple Moving Average of the Index"
string ttUpperPsi           = "Set the upper threshold for the Sell Zone (high percentage of prices above VWMA)"
string ttLowerPsi           = "Set the lower threshold for the Buy Zone (low percentage of prices above VWMA)."
SettingsPsi settingsPsi     =
 SettingsPsi.new(
   input.source  (close,      "Source",                                 group = gPsi, tooltip = ttSource),
   input.int     (400,        "Quantity of MA",                         group = gPsi, tooltip = ttQuantityPsi),
   input.int     (14,         "Strength Index's SMA Lenght",            group = gPsi, minval = 1, tooltip = ttLenghtPsi)
 )
settingsPsiUpperPsi         = input.int(90, "Zone Sell",                group = gPsi, minval = 50, maxval = 100, step = 1, tooltip = ttUpperPsi)
settingsPsiLowerPsi         = input.int(10, "Zone Buy",                 group = gPsi, minval = 0, maxval = 50, step = 1, tooltip = ttLowerPsi)

// Relative Strength Index settings
string gRsi                 = "RSI settings"
string ttLenghtRsi          = "Specify the length for calculating the Relative Strength Index (RSI)."
string ttUpperRsi           = "Set the upper threshold for the Overbought RSI."
string ttLowerRsi           = "Set the lower threshold for the Oversold RSI."
settingsRsiLengthRsi        = input.int(14,"RSI Length",                group = gRsi, minval = 3, tooltip = ttLenghtRsi)
SettingsRsi settingsRsi     = 
 SettingsRsi.new(
   input.bool    (false,      "Show extreem RSI in zones",              group = gRsi),
   input.int     (70,         "Overbought (OB) RSI",                    group = gRsi, minval = 50, maxval = 100, tooltip = ttUpperRsi),
   input.int     (30,         "Oversold (OS) RSI",                      group = gRsi, minval = 0, maxval = 50, tooltip = ttLowerRsi)
 )


// Runtime error indicating the absence of volume data from the data vendor
if barstate.islast and ta.cum(volume) == 0
    runtime.error("No volume is provided by the data vendor.")

// Calculate RSI
float   rsi             = ta.rsi(settingsPsi.source, settingsRsiLengthRsi)
int     priceAboveVWMA  = 0
float   weights         = 0.0
float   sum             = 0.0

for i = 0 to settingsPsi.quantity
    sum := sum + settingsPsi.source[i] * volume[i]
    weights := weights + volume[i]
    // Check if the current source value is above the weighted moving average
    if i > 0 and settingsPsi.source > sum / weights
        priceAboveVWMA := priceAboveVWMA + 1

// Calculate the percentage of prices above VWMA
float psi = priceAboveVWMA / settingsPsi.quantity * 100
// Calculate Result's SMA
float psiMa = ta.sma(psi, settingsPsi.lenght)

////////////////////////////////////////////////////////////////////////////////
//                     ====== DRAWING and PLOTTING ======                     //
////////////////////////////////////////////////////////////////////////////////

// Background Color
bgcolor = psiMa < settingsPsiLowerPsi ? color.new(color.red, 90) : psiMa > settingsPsiUpperPsi ? color.new(color.green, 90) : na
// Check RSI conditions and update bgcolor
bgcolor := settingsRsi.showInZones and psiMa < settingsPsiLowerPsi and rsi < settingsRsi.lowerRsi ? color.new(color.red, 50) : settingsRsi.showInZones and psiMa > settingsPsiUpperPsi and rsi > settingsRsi.upperRsi ? color.new(color.green, 50) : bgcolor
// Trend Color
trendColor = psiMa < 50 ? color.new(color.red, 0) : psiMa > 50 ? color.new(color.green, 0) : color.new(color.orange, 0)

//Hlines
median = hline(50, "Median", color.new(color.orange, 50), hline.style_dotted)
upperMA = hline(settingsPsiUpperPsi, "OB MA", color.new(color.red, 70))
lowerMA = hline(settingsPsiLowerPsi, "OS MA", color.new(color.green, 70))

//Plots
bgcolor(bgcolor)
plot(psi, "Percentage of Prices Above VWMA", color = color.new(color.yellow, 80), linewidth = 1, display = display.pane)
plot(psiMa, "Price Strength Index", color = trendColor, linewidth = 2)