AoDivergenceLibrary_
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © sammie123567858

//@version=5

// @description this has functions which calculate and plot divergences which are used for ao divergences. essentially, this finds divergences by using the ao divergence logic. this logic has been used in "AO Hid & Reg Div with LC & Kernel".
library("AoDivergenceLibrary_", true)








// UTILITY FUNCTIONS

//to find where the ao range starts (the bar at which the ao first started being negative)
findBuyStartPoint(float osc) =>
    int shift = 0

    int i = 0
    while osc[i] < 0 
        shift := i
        i := i + 1
    //

    shift 
//

// @function to find the lowest ao in the given range (starting from _end upto end)
// @param start is for the left side. the parameter value is supposed to be a bar index
// @param _end is for the right side. the parameter value is supposed to be a shift
export findLowestAo(int start, float osc, int _end=0) =>
    float lowest = 0
    int end = bar_index - start

    for i = _end to end
        if osc[i] < lowest
            lowest := osc[i]
    //

    lowest
//

// @function to find the lowest price in the given range (starting from start upto end)
// @param start is for the right side. the parameter value is supposed to be a bar index
// @param end is for the left side. the parameter value is supposed to be a bar index
export findLowestPrice(int start, int end) =>
    int endShift = bar_index - end
    int startShift = bar_index - start
    float lowest = low[startShift]
    int barTime = 0
    
    for i = startShift to endShift 
        if low[i] < lowest
            lowest := low[i]
            barTime := i
        //
    //

    [lowest, barTime]
// 

//to find lowest low (in price) in a given price range for updating the trendline
findLowest(int start, int end) =>
    int endShift = end
    int startShift = start
    float lowest = low[startShift]
    int barTime = 0
    
    for i = startShift to endShift 
        if low[i] < lowest
            lowest := low[i]
            barTime := i
        //
    //

    [lowest, barTime]
// 

//------------------------------------------------------------------------------------------------------------------------------------


//to find where the ao range starts (the bar at which the ao first started being negative)
findSellStartPoint(float osc) =>
    int shift = 0

    int i = 0
    while osc[i] > 0 
        shift := i
        i := i + 1
    //

    shift 
//

// @function to find the highes ao in the given range (starting from _end upto end)
// @param start is for the left side. the parameter value is supposed to be a bar index
// @param _end is for the right side. the parameter value is supposed to be a shift
export findHighestAo(int start, float osc, int _end=0) =>
    float highest = 0
    int end = bar_index - start

    for i = _end to end
        if osc[i] > highest
            highest := osc[i]
    //

    highest
//

// @function to find the highest price in the given range (starting from start upto end)
// @param start is for the right side. the parameter value is supposed to be a bar index
// @param end is for the left side. the parameter value is supposed to be a bar index
export findHighestPrice(int start, int end) =>
    int endShift = bar_index - end
    int startShift = bar_index - start
    float highest = high[startShift]
    int barTime = 0
    
    for i = startShift to endShift 
        if high[i] > highest
            highest := high[i]
            barTime := i
        //
    //

    [highest, barTime]
// 

//to find lowest low (in price) in a given price range for updating the trendline
findHighest(int start, int end) =>
    int endShift = end
    int startShift = start
    float highest = high[startShift]
    int barTime = 0
    
    for i = startShift to endShift 
        if high[i] > highest
            highest := high[i]
            barTime := i
        //
    //

    [highest, barTime]
// 























// FOR CALCULATING AND PLOTTING REGULAR BULL DIVERGENCES
export regBullDivergence(bool swingLow, float osc, color colour) =>

	bool lookingForBull = false
    int _check = 0

	float curr_lowest = 0
	int bullStart = 0

	var int bullishTrendlineStart = 0

	var latestBullLine = line(na)
	var bullLineCounter = -1


	bullStart := bar_index - findBuyStartPoint(osc)
	curr_lowest := findLowestAo(bullStart, osc)

	var minRangesLowest = array.new_float(0)
	var minRangesEnd = array.new_int(0)
	var minRangesStart = array.new_int(0)

	var int lowestIndex = -1
	var float lowestVal = 0
	var int lowestStart = -1

	//start afresh when there is a a zigzag down
	if not swingLow
		bullStart := 0
		curr_lowest := 0
		array.clear(minRangesLowest)
		array.clear(minRangesEnd)
		array.clear(minRangesStart)
		lowestIndex := -1
		lowestVal := 0
		lowestStart := -1
	//


	if swingLow
		if osc < 0 
			lookingForBull := true
	//
				


	//storing the lowest value, start shift & end shift of the current range & updating it as values change
	if lookingForBull == true  
		int lastIndex = array.size(minRangesStart) - 1

		if array.size(minRangesStart) >= 1 
			if array.get(minRangesStart, lastIndex) != bullStart
				array.push(minRangesStart, 0)
				array.push(minRangesEnd, 0)
				array.push(minRangesLowest, 0)
			//
		//

		if array.size(minRangesStart) == 0
			array.push(minRangesStart, 0)
			array.push(minRangesEnd, 0)
			array.push(minRangesLowest, 0)
		//

		lastIndex := array.size(minRangesStart)-1
		array.set(minRangesLowest, lastIndex, curr_lowest) //set the lowest value for the current range
		array.set(minRangesStart, lastIndex, bullStart) //set the start shift for the current range
		array.set(minRangesEnd, lastIndex, bar_index) //set the end shift for the current range
	//


	//checking if there is more than 1 range to compare with the lowest ao val up untill the current bar
	if array.size(minRangesStart) >= 1 
		int lastIndex = array.size(minRangesStart) - 1

		if array.get(minRangesLowest, lastIndex) < lowestVal
			lowestVal := array.get(minRangesLowest, lastIndex)
			lowestIndex := lastIndex
		//
	//


	//checking if there are more than 2 ranges to compare/ delete lines
	if array.size(minRangesStart) >= 2 
		int lastIndex = array.size(minRangesStart) - 1

		//get the start and end shifts of the 2 ranges
		int start2 = array.get(minRangesStart, lastIndex)
		int end2 = array.get(minRangesEnd, lastIndex)

		int start1 = array.get(minRangesStart, lowestIndex)
		int end1 = array.get(minRangesEnd, lowestIndex)

		//if lowest in range 2 is greater than lowest in range 1
		if array.get(minRangesLowest, lastIndex) > lowestVal
			[lowest1, index1] = findLowestPrice(start1, end1)
			[lowest2, index2] = findLowestPrice(start2, end2)

			//check if lowest price in range 1 > lowest price in range 2
			if lowest1 > lowest2 

				if lowestStart != start1
					latestBullLine := line.new(time[index2], lowest2, time[index1], lowest1, xloc = xloc.bar_time, extend = extend.none, color = colour, style = line.style_solid, width = 1)
					bullLineCounter := bullLineCounter + 1
					lowestStart := start1
				//

				if lowestStart == start1

					//to find the lowest low from the start of trendline to current bar
					[currLowest, currLowestIndex] = findLowest(index1, 0)
					
					line.set_x1(latestBullLine, time[currLowestIndex])
					line.set_y1(latestBullLine, currLowest)
				//

			//

		//

	//

    _check
//





// -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------





// FOR CALCULATING AND PLOTTING REGULAR BEAR DIVERGENCES
export regBearDivergence(bool swingHigh, float osc, color colour) =>

	bool lookingForBear = false
    int _check = 0

	float curr_highest = 0
	int bearStart = 0

	var int bearishTrendlineStart = 0

	var latestBearLine = line(na)
	var bearLineCounter = -1


	bearStart := bar_index - findSellStartPoint(osc)
	curr_highest := findHighestAo(bearStart, osc)

	var maxRangesHighest = array.new_float(0)
	var maxRangesEnd = array.new_int(0)
	var maxRangesStart = array.new_int(0)

	var int highestIndex = -1
	var float highestVal = 0
	var int highestStart = -1


	//start afresh when there is a a zigzag up
	if swingHigh
		bearStart := 0
		curr_highest := 0
		array.clear(maxRangesHighest)
		array.clear(maxRangesEnd)
		array.clear(maxRangesStart)
		highestIndex := -1
		highestVal := 0
		highestStart := -1
	//


	if not swingHigh
		if osc > 0 
			lookingForBear := true
	//
				


	//storing the highest value, start shift & end shift of the current range & updating it as values change
	if lookingForBear == true 
		int lastIndex = array.size(maxRangesStart) - 1

		if array.size(maxRangesStart) >= 1 
			if array.get(maxRangesStart, lastIndex) != bearStart
				array.push(maxRangesStart, 0)
				array.push(maxRangesEnd, 0)
				array.push(maxRangesHighest, 0)
			//
		//

		if array.size(maxRangesStart) == 0
			array.push(maxRangesStart, 0)
			array.push(maxRangesEnd, 0)
			array.push(maxRangesHighest, 0)
		//

		lastIndex := array.size(maxRangesStart)-1
		array.set(maxRangesHighest, lastIndex, curr_highest) //set the highest value for the current range
		array.set(maxRangesStart, lastIndex, bearStart) //set the start shift for the current range
		array.set(maxRangesEnd, lastIndex, bar_index) //set the end shift for the current range
	//


	//checking if there is more than 1 range to compare with the highest ao val up untill the current bar
	if array.size(maxRangesStart) >= 1 
		int lastIndex = array.size(maxRangesStart) - 1

		if array.get(maxRangesHighest, lastIndex) > highestVal
			highestVal := array.get(maxRangesHighest, lastIndex)
			highestIndex := lastIndex
		//
	//


	//checking if there are more than 2 ranges to compare/ delete lines
	if array.size(maxRangesStart) >= 2  

		int lastIndex = array.size(maxRangesStart) - 1
		
		//get the start and end shifts of the 2 ranges
		int start2 = array.get(maxRangesStart, lastIndex)
		int end2 = array.get(maxRangesEnd, lastIndex)

		int start1 = array.get(maxRangesStart, highestIndex)
		int end1 = array.get(maxRangesEnd, highestIndex)

		//if highest in range 2 is lower than highest in range 1
		if array.get(maxRangesHighest, lastIndex) < highestVal
			[highest1, index1] = findHighestPrice(start1, end1)
			[highest2, index2] = findHighestPrice(start2, end2)

			//check if highest price in range 1 < highest price in range 2
			if highest1 < highest2 

				if highestStart != start1
					latestBearLine := line.new(time[index2], highest2, time[index1], highest1, xloc = xloc.bar_time, extend = extend.none, color = colour, style = line.style_solid, width = 1)
					bearLineCounter := bearLineCounter + 1
					highestStart := start1
				//

				if highestStart == start1
					
					//to find the highest high from the start of trendline to current bar
					[currHighest, currHighestIndex] = findHighest(index1, 0)

					line.set_x1(latestBearLine, time[currHighestIndex])
					line.set_y1(latestBearLine, currHighest)
				//

			//

		//

	//

    _check
//

























// -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------






























// FOR CALCULATING AND PLOTTING HIDDEN BULL DIVERGENCES
export hidBullDivergence(bool swingHigh, float osc, color colour) =>

	bool lookingForBull2 = false

	float curr_lowest2 = 0
	int bullStart2 = 0
	int _check = 0

	var minRangesLowest2 = array.new_float(0)
	var minRangesHighest2 = array.new_float(0)
	var minRangesEnd2 = array.new_int(0)
	var minRangesStart2 = array.new_int(0)

	var int lowestIndex2 = -1
	var int highestLowestIndex2 = -1

	var float lowestVal2 = 0
	var float highestLowestVal2 = -100000

	var int highestStartHid2 = -1

	var latestBullLine2 = line(na)




	//LOGIC FOR HIDDEN DIV

	bullStart2 := bar_index - findBuyStartPoint(osc)

	float lowest = 0
	int end = bar_index - bullStart2

	for i = 0 to end
		if osc[i] < lowest
			lowest := osc[i]
	//
	curr_lowest2 := lowest

	//start afresh when there is a a zigzag down
	if swingHigh
		bullStart2 := 0
		curr_lowest2 := 0
		array.clear(minRangesLowest2)
		array.clear(minRangesEnd2)
		array.clear(minRangesStart2)
		lowestIndex2 := -1
		highestLowestIndex2 := -1
		lowestVal2 := 0
		highestLowestVal2 := -100000
		highestStartHid2 := -1
	//


	if not swingHigh
		if osc < 0
			lookingForBull2 := true
	//


	//storing the lowest value, start shift & end shift of the current range & updating it as values change
	if lookingForBull2 == true  
		int lastIndex = array.size(minRangesStart2) - 1

		if array.size(minRangesStart2) >= 1  //if there are elements in the arrays already, 
			if array.get(minRangesStart2, lastIndex) != bullStart2 //and a new range has started, push new elements in the arrays which are made for that range specifically
				array.push(minRangesStart2, 0)
				array.push(minRangesEnd2, 0)
				array.push(minRangesLowest2, 0)
			//
		//

		if array.size(minRangesStart2) == 0  //if there is nothing in the array, push a new element in to store values
			array.push(minRangesStart2, 0)
			array.push(minRangesEnd2, 0)
			array.push(minRangesLowest2, 0)
		//

		lastIndex := array.size(minRangesStart2)-1
		array.set(minRangesLowest2, lastIndex, curr_lowest2) //set the lowest value for the current range
		array.set(minRangesStart2, lastIndex, bullStart2) //set the start shift for the current range
		array.set(minRangesEnd2, lastIndex, bar_index) //set the end shift for the current range
	//


	if array.size(minRangesLowest2) >= 2 and osc[1] < osc and osc >= 0
		int lastIndex = array.size(minRangesLowest2) - 1

		//only compare the lowest value of R2 (current range) with lowest value of R1 (previous range) once R2 is complete
		if array.get(minRangesLowest2, lastIndex) > array.get(minRangesLowest2, lastIndex- 1) and array.get(minRangesLowest2, lastIndex) > highestLowestVal2 //checking if there is a higher low in the current range
			highestLowestVal2 := array.get(minRangesLowest2, lastIndex)
			highestLowestIndex2 := lastIndex
		//
	//


	bool bullHidDiv = false
	float _lowest1 = 0, float _lowest2 = 0
	int _startShift1 = 0, int _startShift2 = 0
	int _index1 = 0, int _index2 = 0
	int _endShift1 = 0, int _endShift2 = 0

	if array.size(minRangesLowest2) >= 2 and highestLowestIndex2 >= 0

		int lastIndex = array.size(minRangesLowest2) - 1
		
		//get the start and end shifts of the 2 ranges
		int start2 = array.get(minRangesStart2, lastIndex)
		int end2 = array.get(minRangesEnd2, lastIndex)

		int start1 = array.get(minRangesStart2, highestLowestIndex2)
		int end1 = array.get(minRangesEnd2, highestLowestIndex2)

		//if lowest in range 2 is lesser than lowest in range 1
		if array.get(minRangesLowest2, lastIndex) < highestLowestVal2 and highestLowestIndex2 < lastIndex

			_endShift1 := bar_index - end1
			_startShift1 := bar_index - start1
			_lowest1 := low[_startShift1]
			
			//DO NOT CHANGE THE WAY THE 2ND IF CONDITION IS WRITTEN, BECAUSE IT ONLY WORKS THIS WAY... I DONT KNOW WHY THOUGH
			for i = _startShift1 to _endShift1 
				if low[i] < _lowest1
					_lowest1 := low[i]
				//

				if low[i] <= _lowest1
					_index1 := i
				//
			//

			_endShift2 := bar_index - end2
			_startShift2 := bar_index - start2
			_lowest2 := low[_startShift2]
			
			//DO NOT CHANGE THE WAY THE 2ND IF CONDITION IS WRITTEN, BECAUSE IT ONLY WORKS THIS WAY... I DONT KNOW WHY THOUGH
			for i = _endShift2 to _startShift2 

				if low[i] < _lowest2
					_lowest2 := low[i]
				//

				if low[i] <= _lowest2
					_index2 := i
				//
			//



			//check if lowest price in range 1 < lowest price in range 2
			if _lowest1 < _lowest2 
				bullHidDiv := true

				//x1, y1 is the left most part
				//x2, y2 is the right most part
				latestBullLine2 := line.new(time[_index1], _lowest1, time[_index2], _lowest2, xloc = xloc.bar_time, extend = extend.none, color = colour, style = line.style_solid, width = 1)
			//

		//
	//

	_check
//







// -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------







// FOR CALCULATING AND PLOTTING HIDDEN BEAR DIVERGENCES
export hidBearDivergence(bool swingHigh, float osc, color colour) =>

	bool lookingForBear2 = false

	float curr_highest2 = 0
	int bearStart2 = 0
	int _check = 0

	var maxRangesHighest2 = array.new_float(0)
	var maxRangesLowest2 = array.new_float(0)
	var maxRangesEnd2 = array.new_int(0)
	var maxRangesStart2 = array.new_int(0)

	var int highestIndex2 = -1
	var int lowestHighestIndex2 = -1

	var float lowestHighestVal2 = 100000

	var int lowestStartHid2 = -1

	var latestBearLine2 = line(na)




	//LOGIC FOR HIDDEN DIV

	bearStart2 := bar_index - findSellStartPoint(osc)

	float highest = 0
	int end3 = bar_index - bearStart2

	for i = 0 to end3
		if osc[i] > highest
			highest := osc[i]
	//
	curr_highest2 := highest

	//start afresh when there is a a zigzag down
	if not swingHigh
		bearStart2 := 0
		curr_highest2 := 0
		array.clear(maxRangesHighest2)
		array.clear(maxRangesEnd2)
		array.clear(maxRangesStart2)
		highestIndex2 := -1
		lowestHighestIndex2 := -1
		lowestHighestVal2 := 100000
		lowestStartHid2 := -1
	//


	if swingHigh
		if osc > 0
			lookingForBear2 := true
	//


	//storing the highest value, start shift & end shift of the current range & updating it as values change
	if lookingForBear2 == true  
		int lastIndex = array.size(maxRangesStart2) - 1

		if array.size(maxRangesStart2) >= 1  //if there are elements in the arrays already, 
			if array.get(maxRangesStart2, lastIndex) != bearStart2 //and a new range has started, push new elements in the arrays which are made for that range specifically
				array.push(maxRangesStart2, 0)
				array.push(maxRangesEnd2, 0)
				array.push(maxRangesHighest2, 0)
			//
		//

		if array.size(maxRangesStart2) == 0  //if there is nothing in the array, push a new element in to store values
			array.push(maxRangesStart2, 0)
			array.push(maxRangesEnd2, 0)
			array.push(maxRangesHighest2, 0)
		//

		lastIndex := array.size(maxRangesStart2)-1
		array.set(maxRangesHighest2, lastIndex, curr_highest2) //set the highest value for the current range
		array.set(maxRangesStart2, lastIndex, bearStart2) //set the start shift for the current range
		array.set(maxRangesEnd2, lastIndex, bar_index) //set the end shift for the current range
	//


	if array.size(maxRangesHighest2) >= 2 and osc[1] > osc and osc < 0
		int lastIndex = array.size(maxRangesHighest2) - 1

		//only compare the highest value of R2 (current range) with highest value of R1 (previous range) once R2 is complete
		if array.get(maxRangesHighest2, lastIndex) < array.get(maxRangesHighest2, lastIndex- 1) and array.get(maxRangesHighest2, lastIndex) < lowestHighestVal2 //checking if there is a lower high in the current range
			lowestHighestVal2 := array.get(maxRangesHighest2, lastIndex)
			lowestHighestIndex2 := lastIndex
		//
	//


	bool bearHidDiv = false
	float _highest1 = 0, float _highest2 = 0
	int _startShift1 = 0, _startShift2 = 0
	int _index1 = 0, _index2 = 0
	int _endShift1 = 0, _endShift2 = 0

	if array.size(maxRangesHighest2) >= 2 and lowestHighestIndex2 >= 0

		int lastIndex = array.size(maxRangesHighest2) - 1
		
		//get the start and end shifts of the 2 ranges
		int start2 = array.get(maxRangesStart2, lastIndex)
		int end2 = array.get(maxRangesEnd2, lastIndex)

		int start1 = array.get(maxRangesStart2, lowestHighestIndex2)
		int end1 = array.get(maxRangesEnd2, lowestHighestIndex2)

		//if highest in range 2 is greater than highest in range 1
		if array.get(maxRangesHighest2, lastIndex) > lowestHighestVal2 and lowestHighestIndex2 < lastIndex

			_startShift1 := bar_index - start1
			_endShift1 := bar_index - end1
			_highest1 := high[_startShift1]

			for i = _startShift1 to _endShift1 
				if high[i] > _highest1
					_highest1 := high[i]
				//

				if high[i] >= _highest1
					_index1 := i
				//
			//

			_startShift2 := bar_index - start2
			_endShift2 := bar_index - end2
			_highest2 := high[_startShift2]

			for i = _startShift2 to _endShift2 
				if high[i] > _highest2
					_highest2 := high[i]
				//

				if high[i] >= _highest2
					_index2 := i
				//
			//



			//check if highest price in range 1 < highest price in range 2
			if _highest1 > _highest2 
				bearHidDiv := true

				//x1, y1 is the left most part
				//x2, y2 is the right most part
				latestBearLine2 := line.new(time[_index1], _highest1, time[_index2], _highest2, xloc = xloc.bar_time, extend = extend.none, color = colour, style = line.style_solid, width = 1)
			//

		//
	//

	_check
//
