Astro: Planetary Aspects v2.0
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © BarefootJoey

// With much respect to @BarefootJoey, the following small changes have been implemented by @Yevolution:
// 1. Overlay the indicator plot on top of the main chart, with the indicator's scale placed on the left - I found it easier to spot price reactions at a given planetary aspect vs seeing the plot in a separate frame
// 2. Add options to plot a vertical bar for every occurrence of chosen aspects
// 3. The "Precision" setting changes the width of each plotted bar to shade in the calculated window of time while the aspect is in progress.
//   - eg. 1: if "Precision" is set to 3 degrees, then the "Trine" aspect bar will appear when the aspect is between 117 & 123 degrees
//   - eg. 2: if "Precision" is set to 15 degrees, then the "Opposition" aspect bar will appear when the aspect is between 165 & 195 degrees
//   - eg. 3: if "Precision" is set to 6 degrees, then the "Conjunction" aspect bar will appear when the aspect is between 0 & 6 degrees (though technically the aspect moves from 253 to 0 to 6 degrees covering a span of 12 degrees)
// Additional comments for specific functions have been placed above the function in question

// ██████████████████████████████████████████████████████████████████████   _____ __   _      _______  _____  _______ _______ _______
// █▄─▄─▀██▀▄─██▄─▄▄▀█▄─▄▄─█▄─▄▄─█─▄▄─█─▄▄─█─▄─▄─███▄─▄█─▄▄─█▄─▄▄─█▄─█─▄█     |   | \  |      |______ |_____] |_____| |       |______
// ██─▄─▀██─▀─███─▄─▄██─▄█▀██─▄███─██─█─██─███─███─▄█─██─██─██─▄█▀██▄─▄██   __|__ |  \_|      ______| |       |     | |_____  |______ 
// █▄▄▄▄██▄▄█▄▄█▄▄█▄▄█▄▄▄▄▄█▄▄▄███▄▄▄▄█▄▄▄▄██▄▄▄██▄▄▄███▄▄▄▄█▄▄▄▄▄██▄▄▄██                                                            

//@version=5
indicator("Astro: Planetary Aspects v2.0", overlay=true, scale=scale.left, max_boxes_count = 500)

import BarefootJoey/AstroLib/1 as AL

interplanet = input.bool(false, "Interplanetary aspects?", tooltip="Leave this box unchecked for single planet aspects.")
planet1_in = input.string("☿ Mercury", "Which planets?", options=["☉︎ Sun", "☽︎ Moon", "☿ Mercury", "♀ Venus", "🜨 Earth", "♂ Mars", "♃ Jupiter", "♄ Saturn", "⛢ Uranus", "♆ Neptune", "♇ Pluto"], inline="3")
planet2_in = input.string("♂ Mars", " ", options=["☉︎ Sun", "☽︎ Moon", "☿ Mercury", "♀ Venus", "🜨 Earth", "♂ Mars", "♃ Jupiter", "♄ Saturn", "⛢ Uranus", "♆ Neptune", "♇ Pluto"], inline="3")
precision = input.float(6.0, "Aspect Precision (+/- °)", minval=0, maxval=15)
showlast = input.int(1000, "Show last?", minval=1, tooltip="Number of historical plots to display. The fewer plots, the faster the load time (especially on loweer timeframes).")
col_asp = input.color(color.white, "Aspect & Info Color")
iTxtSize=input.string("Normal", title="Text & Symbol Size", options=["Auto", "Tiny", "Small", "Normal", "Large"], inline="1")
vTxtSize=iTxtSize == "Auto" ? size.auto : iTxtSize == "Tiny" ? size.tiny : iTxtSize == "Small" ? size.small : iTxtSize == "Normal" ? size.normal : iTxtSize == "Large" ? size.large : size.small
position = input.string(position.top_center, "Aspect Info Position", [position.top_center, position.top_right, position.middle_right, position.bottom_right, position.bottom_center, position.bottom_left, position.middle_left, position.top_left])

//// The "Time Machine" function is no longer necessary and has been effectively replaced by drawing vertical bars at each occurence of an aspect. To re-enable it, simply uncomment lines #36 & #37 and comment lines #38 & #39. @Yevolution ////

grtm = "🕜 Time Machine 🕜"
//gsd = input.bool(false, "Activate Time Machine", group=grtm)
//sdms = input.time(timestamp("2022-04-20T00:00:00"), "Select Date", group=grtm)
gsd = false
sdms = timestamp("2022-04-20T00:00:00")
gt = gsd ? sdms : time

//// The latitude/longitude variables do not appear to have any effect, however I've left them enabled in order not to break anything. @Yevolution ////

grol = "🔭 Observer Location 🔭"
htz = input.float(0, "Hour", step=0.5, inline="2", group=grol)
mtz = input.int(0, "Minute", minval=0, maxval=45, inline="2", group=grol)
tz = htz + math.round(mtz / 60, 4)
latitude = input.float(0, "Latitude", inline="1", group=grol)
longitude = input.float(0, "Longitude", inline="1", group=grol)
geo = input.bool(false, "Geocentric?", tooltip="Leave this box unchecked for heliocentric.", group=grol)
geoout = geo ? 1 : 0

day = AL.J2000(AL.JDN(gt, 0, tz))
dayr = AL.J2000(AL.JDN(gt, 1, tz))

if interplanet and planet1_in == planet2_in 
    runtime.error("Planet cannot aspect itself. Please select 2 different planets for interplanetary analysis.")

long1_out = planet1_in == "☉︎ Sun" ? AL.getsun(geoout, day, dayr, latitude, longitude, tz) : planet1_in == "☽︎ Moon" ? AL.getmoon(geoout, day, dayr, latitude, longitude) : planet1_in == "☿ Mercury" ? AL.getplanet(1, geoout, day, dayr, latitude, longitude, tz) : planet1_in == "♀ Venus" ? AL.getplanet(2, geoout, day, dayr, latitude, longitude, tz) : planet1_in == "🜨 Earth" ? AL.getplanet(3, geoout, day, dayr, latitude, longitude, tz) : planet1_in == "♂ Mars" ? AL.getplanet(4, geoout, day, dayr, latitude, longitude, tz) : planet1_in == "♃ Jupiter" ? AL.getplanet(5, geoout, day, dayr, latitude, longitude, tz) : planet1_in == "♄ Saturn" ? AL.getplanet(6, geoout, day, dayr, latitude, longitude, tz) : planet1_in == "⛢ Uranus" ? AL.getplanet(7, geoout, day, dayr, latitude, longitude, tz) : planet1_in == "♆ Neptune" ? AL.getplanet(8, geoout, day, dayr, latitude, longitude, tz) : planet1_in == "♇ Pluto" ? AL.getplanet(9, geoout, day, dayr, latitude, longitude, tz) : na
long2_out = planet2_in == "☉︎ Sun" ? AL.getsun(geoout, day, dayr, latitude, longitude, tz) : planet2_in == "☽︎ Moon" ? AL.getmoon(geoout, day, dayr, latitude, longitude) : planet2_in == "☿ Mercury" ? AL.getplanet(1, geoout, day, dayr, latitude, longitude, tz) : planet2_in == "♀ Venus" ? AL.getplanet(2, geoout, day, dayr, latitude, longitude, tz) : planet2_in == "🜨 Earth" ? AL.getplanet(3, geoout, day, dayr, latitude, longitude, tz) : planet2_in == "♂ Mars" ? AL.getplanet(4, geoout, day, dayr, latitude, longitude, tz) : planet2_in == "♃ Jupiter" ? AL.getplanet(5, geoout, day, dayr, latitude, longitude, tz) : planet2_in == "♄ Saturn" ? AL.getplanet(6, geoout, day, dayr, latitude, longitude, tz) : planet2_in == "⛢ Uranus" ? AL.getplanet(7, geoout, day, dayr, latitude, longitude, tz) : planet2_in == "♆ Neptune" ? AL.getplanet(8, geoout, day, dayr, latitude, longitude, tz) : planet2_in == "♇ Pluto" ? AL.getplanet(9, geoout, day, dayr, latitude, longitude, tz) : na

p1p2=math.abs(AL.degtolowest180(AL.AngToCirc(long1_out - (interplanet?long2_out:0)))) 
plot(p1p2, color=col_asp, linewidth=2, show_last=showlast)

htrans=66
hline(0, color=color.new(color.red, htrans))
hline(30, color=color.new(color.orange, htrans))
hline(60, color=color.new(color.yellow, htrans))
hline(90, color=color.new(color.green, htrans))
hline(120, color=color.new(color.aqua, htrans))
hline(150, color=color.new(color.navy, htrans))
hline(180, color=color.new(color.purple, htrans))

var label conj = na
var label ssex = na
var label sext = na
var label squa = na
var label trin = na
var label inco = na
var label oppo = na

if barstate.islast
    conj := label.new(bar_index, y=0, text="☌", size=vTxtSize, style=label.style_label_left, color=color.new(color.white,100), textcolor=color.new(color.red,0), tooltip="☌ Conjunction 0°\n" + "")
    label.delete(conj[1])
    ssex := label.new(bar_index, y=30, text="⊻", size=vTxtSize, style=label.style_label_left, color=color.new(color.white,100), textcolor=color.new(color.orange,0), tooltip="⊻ Semi Sextile 30°\n" + "")
    label.delete(ssex[1])
    sext := label.new(bar_index, y=60, text="🞶", size=vTxtSize, style=label.style_label_left, color=color.new(color.white,100), textcolor=color.new(color.yellow,0), tooltip="🞶 Sextile 60°\n" + "")
    label.delete(sext[1])
    squa := label.new(bar_index, y=90, text="□", size=vTxtSize, style=label.style_label_left, color=color.new(color.white,100), textcolor=color.new(color.green,0), tooltip="□ Square 90°\n" + "")
    label.delete(squa[1])
    trin := label.new(bar_index, y=120, text="△", size=vTxtSize, style=label.style_label_left, color=color.new(color.white,100), textcolor=color.new(color.aqua,0), tooltip="△ Trine 120°\n" + "")
    label.delete(trin[1])
    inco := label.new(bar_index, y=150, text="⊼", size=vTxtSize, style=label.style_label_left, color=color.new(color.white,100), textcolor=color.new(color.navy,0), tooltip="⊼ Inconjunct 150°\n" + "")
    label.delete(inco[1])
    oppo := label.new(bar_index, y=180, text="☍", size=vTxtSize, style=label.style_label_left, color=color.new(color.white,100), textcolor=color.new(color.purple,0), tooltip="☍ Opposition 180°\n" + "")
    label.delete(oppo[1])

retro_tt(deg) => deg > deg[1] ? "" : " ℞" 
retro_tt_out = retro_tt(AL.AngToCirc(long1_out - (interplanet?long2_out:0)))

var table Info = na
table.delete(Info)
Info := table.new(position, 1, 1)
if barstate.islast
    table.cell(Info, 0, 0, 
      text = planet1_in + (interplanet?" ∡ "+planet2_in:"") + retro_tt_out, 
      text_size = vTxtSize, 
      text_color = color.new(col_asp,0),
      tooltip = AL.aspectsignprecisionV2ext(p1p2, precision) + "°\nPrecision: +/- " + str.tostring(precision) + "°")


//// Vertical bars for Aspect time windows by @Yevolution ////

// Inputs to allow (de)selection of individual aspects

grvb = "Aspects marked with a vertical Bar:"
ConjunctVertBar = input.bool(true, "Conjunction", group = grvb)
SemiSextVertBar = input.bool(true, "Semi Sextile", group = grvb)
SextVertBar = input.bool(true, "Sextile", group = grvb)
SquareVertBar = input.bool(true, "Square", group = grvb)
TrineVertBar = input.bool(true, "Trine", group = grvb)
InConjunctVertBar = input.bool(true, "Inconjunct", group = grvb)
OppositVertBar = input.bool(true, "Opposition", group = grvb)

// Logic to determine if an aspect is currently in progress

conjunctionActive = p1p2 <= (0 + precision)
semiSextActive = p1p2 >= (30 - precision) and p1p2 <= (30 + precision)
sextActive = p1p2 >= (60 - precision) and p1p2 <= (60 + precision)
squareActive = p1p2 >= (90 - precision) and p1p2 <= (90 + precision)
trineActive = p1p2 >= (120 - precision) and p1p2 <= (120 + precision)
inConjunctActive = p1p2 >= (150 - precision) and p1p2 <= (150 + precision)
oppositionActive = p1p2 >= (180 - precision)

// Plot vertical bars on chart

line1 = plot(conjunctionActive and ConjunctVertBar ? 0 : na, color = color.new(color.red, 100))
line2 = plot(conjunctionActive and ConjunctVertBar ? 180 : na, color = color.new(color.red, 100))

line3 = plot(semiSextActive and SemiSextVertBar ? 0 : na, color = color.new(color.orange, 100))
line4 = plot(semiSextActive and SemiSextVertBar ? 180 : na, color = color.new(color.orange, 100))

line5 = plot(sextActive and SextVertBar ? 0 : na, color = color.new(color.yellow, 100))
line6 = plot(sextActive and SextVertBar ? 180 : na, color = color.new(color.yellow, 100))

line7 = plot(squareActive and SquareVertBar ? 0 : na, color = color.new(color.green, 100))
line8 = plot(squareActive and SquareVertBar ? 180 : na, color = color.new(color.green, 100))

line9 = plot(trineActive and TrineVertBar ? 0 : na, color = color.new(color.aqua, 100))
line10 = plot(trineActive and TrineVertBar ? 180 : na, color = color.new(color.aqua, 100))

line11 = plot(inConjunctActive and InConjunctVertBar ? 0 : na, color = color.new(color.navy, 100))
line12 = plot(inConjunctActive and InConjunctVertBar ? 180 : na, color = color.new(color.navy, 100))

line13 = plot(oppositionActive and OppositVertBar ? 0 : na, color = color.new(color.purple, 100))
line14 = plot(oppositionActive and OppositVertBar ? 180 : na, color = color.new(color.purple, 100))


fill(line1, line2, color=color.new(color.red, 75))
fill(line3, line4, color=color.new(color.orange, 75))
fill(line5, line6, color=color.new(color.yellow, 75))
fill(line7, line8, color=color.new(color.green, 75))
fill(line9, line10, color=color.new(color.aqua, 75))
fill(line11, line12, color=color.new(color.navy, 75))
fill(line13, line14, color=color.new(color.purple, 75))


// EoS made w/ ❤ by @BarefootJoey ✌💗📈
// Respectfully updated with additional functions by @Yevolution