in

Find two biggest values, issues with non bar history referencing, and % values in pine v5 strategy


I’m new to pinescript and working on my first full strategy script in pinescript v5. It’s still a work in progress and I’m trying to work out some bugs and the best way to calculate some of the logic … so any suggestions or help is greatly appreciated.

The script I’m coding uses MACD uptrend crossover points as potential buy points and trend lines (via pivot points on price and rsi) to determine if it should send a buy signal or not to a 3 commas dca bot.

I have a few errors in the code that I could use some help debugging (or please point me in the right direction):

RSIDropValue and PriceDropValue display wrong % on their labels and display NaN for the first pivot %

MACD Trend Fill (when toggled on) shows opposing color in parts of the up/downtrends

RSIBiggestPivotLabel seems to work fine until 10 or 14 bars since MACD started an uptrend

My thought process so far was:

Keep a counter of how many bars since MACD uptrend started (updates every bar) to be able to use the history reference [] for price/rsi per bar.

Assign value, time and count values to price and rsi variables in local loops when pivots are detected so I can reference them by their own history[] by their pivot occurrence which is different from bars back. So that it skips the times that pivots were not detected (na value) and I can reference the time value of the pivot I need for drawing a trend line.

I also am trying to figure out the best way to find the 2 biggest pivots (largest % change from the last reversal before them) for RSI and price, but they have to be from when the MACD last up crossed. Because that is not a set number I can’t use math.max or highest functions.

I’m also planning on doing some additional options and a couple functions to implement them for different pivot points in the near future for backtesting what works best.

If anyone can help with bug fixes, suggestions, more optimized code, any issues I will soon face with how I’m planning to implement this, or a better way to figure out what I need it is appreciated. Pine is still a learning process for me and I want to understand it more … why some things aren’t working as I expect them to and what I can do to get it working correctly.

Screenshot of Strategy

Here is the code I have so far:

//@version=5
//@natejohnson000 on TV
//
// RSI / SRSI / MACD XOVER FOR 3 COMMAS DCA BOT v 1.0 alpha
// This script uses MACD uptrend crossovers and RSI/Price trendlines to try to
// predict good long entry points on 1M/3M/5M crypto charts for sniping and can send
// Buy(at MACD upcross) and Sell(profit%-trail optional, RSI>Band Limit w trail,
// and stop loss) signals to a 3 Commas DCA Bot.
//
// Please bear with me as this is a work in progress
//
// Big thanks to LucF @ pinecoders for letting me use part of his stopwatch script.

strategy(title="Stoch RSI/SRSI/MACDx Trend", shorttitle="RSMx", format=format.price, precision=2, initial_capital=500, max_bars_back = 4999, calc_on_every_tick=true, max_labels_count=500)

// ====================
// INITIALIZE VARIABLES
// ====================

// RSI
Grp1 ="RSI OPTIONS"
showRSI = input(true, "Show RSI     ", "", "1", Grp1)
showRSIBand = input(true, "Show RSI Band   ", "", "1", Grp1)
showPivotPercents = input(true, "Show Pivot Percents", "", "1", Grp1)
lengthRSI = input(14, "RSI Length  ", "", "2", Grp1)
RSIHighLimit = input(80, "RSI High Band ", "", "3", Grp1)
RSILowLimit = input(20, "RSI Low Band", "", "3", Grp1)

// PIVOTS
var bool RSIPivot = na
var int RSIPivotPoint1 = na
var int RSIPivotPoint2 = na
var float RSIPivotPoint = na
var float RSIDropValue = na
var int RSIPivotTime = na
var int RSIPivotCount = 0
RSIPivot := false //Resets to flase if pivot was found on previous bar

var bool PricePivot = na
var float PriceLowPivot1 = na
var float PriceLowPivot2 = na
var float PricePivotPoint = na
var float PriceDropValue = na
var int PricePivotTime = na
var int PricePivotCount = 0
PricePivot := false //Resets to flase if pivot was found on previous bar

// STOCH RSI
Grp2 ="STOCH RSI OPTIONS"
showSRSI = input(true, "Show SRSI", "", "1", Grp2)
smoothSRSI = input(3, "Smooth SRSI", "", "2", Grp2)

// MACD
Grp3 = "MACD OPTIONS"
showMACDUpCross = input(true, "Show MACD Up Cross    ", "", "1", Grp3) // Shows potential entry point for buy (after one full up & downtrend)
showMACDDownCross = input(true, "Show MACD Down Cross    ", "", "1", Grp3)
showMACDcolor  = input(false, "Show MACD Color Bands", "", "1", Grp3) // Show MACD red and green color bands for up and downtrends  ----- Needs to be fixed

//  BACKTEST OPTIONS
Grp4 = "BACKTEST OPTIONS"
enableBacktest = input(true, "Enable Backtesting     ", "", "1", Grp4)
showDate = input(true, "Show Backtest Range", "","1", Grp4)
fromDate = input.time(timestamp("01 Jan 2022 00:00 EST"), "From Date  ", "", "1", Grp4)
toDate = input.time(timestamp("31 Jan 2122 23:59 EST"), "To Date   ", "", "1", Grp4)
// Function to determine if in backtest period
WithinBacktestPeriod() => time >= fromDate and time <= toDate ? true : false

// 3COMMAS OPTIONS
Grp5 = "3COMMAS OPTIONS"
BotId = input("", "Bot ID", "", "1", Grp5)
EmailToken = input("", "      Email Token", "", "1", Grp5)

// SELL OPTIONS
Grp6 = "SELL OPTIONS"
SellAtRSIPeak = input(true, "Force Sell at RSI Peak Over Band ", "", "1", Grp6)
TakeProfit = input(5.0, "Take Profit % ", "", "2", Grp6)
Trailing = input(2.0, "  Trailing %", "", "2", Grp6)
StopLoss = input(2.5, "Stop Loss %  ", "", "2", Grp6)
var float LastBuyPrice = 0.0

// OPTIMIZE OPTIONS
Grp7 = "OPTIMIZE OPTIONS"
ShowOptimizeInfo = input(false, "Show Optimize Info", "", "1", Grp7)

// Big thanks to LucF @ pinecoders
// —————————— ▼▼▼▼▼ Calculate run time of the script (insert this snippet after your "input()" calls).
var _timeBegin = timenow
var _timeElapsed = 0.
if not barstate.islast
    _timeElapsed := timenow - _timeBegin
var _msPerBar = 0.
var _barsTimed = 0
var _barsNotTimed = 0
if ta.change(_timeElapsed)
    _barsTimed := bar_index + 1
    _msPerBar  := _timeElapsed / _barsTimed
if not barstate.islast
    _barsNotTimed := bar_index  + 1 - _barsTimed
_totalTime = _timeElapsed + (_barsNotTimed * _msPerBar)
if barstate.islast and ShowOptimizeInfo
    var _label_text = str.tostring(_msPerBar, "Avg time per bar: #.#### msn") + str.tostring(_totalTime / 1000, "Time elapsed: #.#### secondsn") + str.tostring(_barsTimed + _barsNotTimed, "Bars analyzed: #")
    var label _timeLabel = label.new(bar_index, na, _label_text, xloc.bar_index, yloc.price, color.silver, label.style_label_up, color.black, size = size.tiny)
    label.set_xy(_timeLabel, bar_index, _totalTime)
// —————————— ▲▲▲▲▲


// ============
// CALCULATIONS
// ============

// CALC RSI
RSISignal = ta.rsi(close, lengthRSI)

// CALC SRSI
SRSI = ta.ema(ta.stoch(RSISignal, RSISignal, RSISignal, lengthRSI), smoothSRSI)
SRSISignal = ta.ema(SRSI, smoothSRSI)

// CALC MACD
[MACD, MACDSignal, MACDHist] = ta.macd(close, 12, 26, 9)

// CALC RANGE & RESET PIVOT COUNT
BarsSinceMACDXOver = ta.barssince(ta.crossover(MACD, MACDSignal))
if BarsSinceMACDXOver == 0
    RSIPivotCount := 0
    PricePivotCount := 0

// IDENTIFY RSI PIVOTS: FINDS LOW RSI PIVOTS AND RECORDS THE CHART VALUE, TIME AND COUNT SINCE MACD UPCROSS
if ta.pivotlow(RSISignal, 1, 1) and BarsSinceMACDXOver > 1 
    RSIPivot := true
    RSIPivotPoint := RSISignal[1]
    RSIPivotTime := time[1]
    RSIPivotCount += 1

// MEASURE RSI PIVOT AMOUNT: MEASURES THE DIFFERENCE FROM LATEST DETECTED PIVOT BACK BARS UNTIL PRICE REVERSED ************** Displays wrong % needs to be fixed
if RSIPivot
    for i = 2 to BarsSinceMACDXOver
        if RSISignal[i-1] < RSISignal[i]
            RSIDropValue := RSISignal[i] - RSISignal[1]

// IDENTIFY PRICE PIVOTS: FINDS LOW PRICE PIVOTS AND RECORDS THE CHART VALUE, TIME AND COUNT SINCE MACD UPCROSS
if BarsSinceMACDXOver > 1 and ta.pivotlow(close, 1, 1)
    PricePivot := true
    PricePivotPoint := close[1]
    PricePivotTime := time[1]
    PricePivotCount := PricePivotCount + 1

// MEASURE & LABEL PRICE PIVOT %: MEASURES THE DIFFERENCE FROM LATEST DETECTED PIVOT BACK BARS UNTIL PRICE REVERSED ************** Displays wrong % needs to be fixed?
if PricePivot
    for i = 2 to BarsSinceMACDXOver
        if close[i-1] < close[i]
            PriceDropValue := PricePivotPoint[1] - close[i]

// FIND BIGGEST RSI PIVOT
if RSIPivot and RSIPivotCount == 1 // SETS THE FIRST RSI PIVOT POINT [X] LOCATION AS THE BIGGEST WHEN IT'S FIRST DETECTED (FOR TREND LINE)
    RSIPivotPoint1 := RSIPivotCount
else
    if RSIDropValue > RSIDropValue[RSIPivotPoint1] // CHECKS IF A NEW RSI PIVOT IS BIGGER THE LOCATION [X] TO THAT (FOR TREND LINE) 
        RSIPivotPoint1 := RSIPivotCount

// TO DO:
// FIND 2 BIGGEST PRICE PIVOT LOCATIONS
// FIND 2 BIGGEST RSI PIVOT LOCATIONS
// PLOT RSI/PRICE TREND LINES
// CHECK DIVERGENCE
// TRIGGER SIGNALS IF CONDITIONS MET 
// BUY
//    strategy.entry("BUY", strategy.long, alert_message = "{"message_type": "bot", "bot_id": "+BotId+", "email_token": ""+EmailToken+"", "delay_seconds": 0, "pair": "USD_"+syminfo.basecurrency+""}")
// SELL (RSA 80+ OR % PROFIT W TRAILING)
// TRIGGER TRAILING
// STOP LOSS
//SETUP FUNCTIONS AND ADDITIONAL DIFFERENT PIVOT POINT OPTIONS IN MENU FOR BACKTESTING 


// =========
// PLOT DATA
// =========

//PLOT RSI: WHITE CHART LINE
plot(showRSI ? RSISignal : na, "RSI", color=color.new(color.white, 0))
RSIUpBand = hline(RSIHighLimit, "RSI Up Band Limit", color=color.new(color.blue, 90))
RSILowBand = hline(RSILowLimit, "RSI Low Band Limit", color=color.new(color.blue, 90))
fill(RSIUpBand, RSILowBand, color= showRSIBand ? color.new(color.blue, 80) : color.new(color.blue, 100), title = "RSI Band Fill")

// PLOT SRSI: BLUE/ORANGE LINES FOR SRSI
plot(showSRSI ? SRSI : na, "SRSI", color=#0094FF)
plot(showSRSI ? SRSISignal : na, "SRSI Signal", color=#FF6A00)

// PLOT MACD: YELLOW VERICAL LINE FOR MACD UPCROSS, ORANGE VERICAL LINE FOR MACD DOWNCROSS, **************** GREEN AND RED TREND FILL IS BROKEN
plot(showMACDUpCross and ta.crossover(MACD, MACDSignal) ? 100 : na, "MACD Up Trend", style=plot.style_histogram, color=color.new(color.yellow, 0), linewidth = 2)
plot(showMACDDownCross and ta.crossunder(MACD, MACDSignal) ? RSISignal : na, "MACD Down Trend", style=plot.style_histogram, color=color.new(color.orange, 0), linewidth = 2)
plot(showMACDcolor ? 100 : na, "MACD Backround Trend Fill", style=plot.style_area, color=(MACDHist > MACDHist[1] ? color.new(color.green, 85) : color.new(color.red, 85)))

// PLOT RANGE COUNTER: DIPLAYS THE BARS SINCE MACD UPCROSS ON BOTTOM RIGHT OF INDICATOR
var RangeCounter = label.new(na, na, color = color.new(color.silver, 75), style = label.style_label_down, textcolor = color.white, size = size.tiny)
if barstate.islast
    label.set_xy(RangeCounter, bar_index, 0)
    label.set_text(RangeCounter, str.tostring(BarsSinceMACDXOver, format.volume))

// PLOT RSI PIVOT DETECTED ON RSI LINE: GREEN DOT ON RSI LINE
plot(RSIPivot ? RSISignal[1] : na, title = "RSI Pivot", color=color.new(color.green, 0), style = plot.style_circles, offset = -1, linewidth = 2)

// PLOT RSI PIVOT % LABEL: GREEN LABEL OVER RSI LINE ************ BROKEN DISPLAYS WRONG % DUE TO RSIDropValue CALC?
RSIPivotDropPercentLabel = label.new(showPivotPercents and RSIPivot ? bar_index[1] : na, (RSISignal[1] + 2), text=str.tostring(RSIDropValue, format.percent), style = label.style_label_down, color = color.new(color.green, 75), textcolor = color.white, size = size.tiny)

// PLOT PRICE PIVOT DETECTED AT BOTTOM OF INDICATOR: RED DOT
plot(PricePivot ? 0[1] : na, title = "Price Pivot", color=color.new(color.red, 0), style = plot.style_circles, offset = -1, linewidth = 2)

// PLOT PRICE PIVOT % LABEL: RED LABEL UNDER RSI LINE *********** BROKEN DISPLAYS WRONG % DUE TO PriceDropValue CALC? ALSO DOES NaN FOR FIRST PIVOT %
PricePivotDropPercentLabel = label.new(showPivotPercents and PricePivot ? bar_index[1] : na, (RSISignal[1] - 2), text=str.tostring(PriceDropValue, format.percent), style = label.style_label_up, color = color.new(color.red, 75), textcolor = color.white, size = size.tiny)

// PLOT BACKTEST PERIOD: LIGHTLY GREYS BACKROUND OF BACKTEST PERIOD
bgcolor(color = showDate and enableBacktest and WithinBacktestPeriod() ? color.new(color.gray, 95) : na, title = "Backtest Period Fill")

// PLOT BIGGEST RSI PIVOT # LABEL: SHOWS THE NUMBER OF THE PIVOT AS IT IS DETECTED AT THE TOP OF THE INDICATOR (CURRENTLY THE 2ND ROW DOWN) FOR DEBUG PURPOSES BUT MAY KEEP IT IN FINAL SCRIPT
RSIPivotCounterLabel = label.new(RSIPivot ? bar_index[1] : na, 100[1], text=str.tostring(PricePivotCount), style = label.style_none, textcolor = color.white, size = size.tiny)

// PLOT BIGGEST RSI PIVOT # LABEL: SHOULD BE DISPLAYING THE BIGGEST % PIVOT # DETECTED SINCE MACD UPCROSS (YELLOW VERTICAL LINE) ************* BROKEN DUE TO RSIPivotPoint1 VARIABLE?
RSIBiggestPivotLabel = label.new(bar_index[1], 115, text=str.tostring(RSIPivotPoint1), style = label.style_none, textcolor = color.white, size = size.tiny)


//RESET VARIABLES (FOR NEW PERIOD) WHEN MACD CROSSES TO UPTREND
if barstate.isconfirmed and BarsSinceMACDXOver == 0
    RSIPivotPoint1 := na
    RSIPivotPoint2 := na
    RSIPivotPoint := na
    RSIDropValue := na
    RSIPivotTime := na
    PriceLowPivot1 := na
    PriceLowPivot2 := na
    PricePivotPoint := na
    PriceDropValue := na
    PricePivotTime := na
    BarsSinceMACDXOver := na



Source: https://stackoverflow.com/questions/70632733/find-two-biggest-values-issues-with-non-bar-history-referencing-and-values-i

Python – How to hide API Requests in Console

API for Speech Analytics integration with FreePBX/Asterisk