Indicator · Pine Script v6 · On the chart

Price and Longitude Angles

by blueprintresearch-dev· code credited to BlueprintResearch / Javonnii

Price and Longitude Angles maps planetary longitude to price and plots Gann fan lines from a selected high or low pivot on the price chart.

View on GitHub

GitHub stars42on the repository
Pine versionv6current
Lines of code1,020
Settings28inputs
LicenceNone statedfile: MPL-2.0
Last update2 weeks ago12 Oct 2026

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.

Source Code

1,020 lines of Pine Script v6 from scripts/price and longitude angles.pine, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.

price and longitude angles.pine
//   ════════════════════════════════════════════════════════════
//      BLUEPRINT RESEARCH • PRICE AND LONGITUDE ANGLES
//   ════════════════════════════════════════════════════════════
//
//                  ★       ★       ★
//                      ╱   │   ╲
//                     ─    ☉    ─
//                      ╲   │   ╱
//                  ★       ★       ★
//
//   ════════════════════════════════════════════════════════════
//
// @description  Maps planetary longitude to price using Gann fan angles.
//               Supports single-planet or dual-planet modes with Average
//               and Synodic calculation options. Features future projections
//               and orbital bar coloring.
//
// @author       BlueprintResearch (Javonnii)
// @license      CC BY-NC-SA 4.0 (original work) + MPL-2.0 (HSV Color Tools)
// @version      3.0 (January 2026)
//
// @theory       Uses VSOP87, ELP2000-82, and Meeus algorithms via
//               Blueprint Research ephemeris libraries for accurate
//               planetary longitude calculations.
//
// @features     • Gann fan lines (1x8, 1x4, 1x3, 1x2, 1x1, 2x1, 3x1, 4x1, 8x1)
//               • Average mode: Uses average and select the same for Single planet longitudes or between different planets
//               • Synodic mode: Uses difference between planet longitudes
//                 (select innermost planet first for proper calculations)
//               • Future projections up to 500 bars
//               • Geocentric and heliocentric modes
//               • Orbital bar coloring by zodiac sign
//
// @accuracy     High accuracy using VSOP87/ELP2000-82 truncated series.
//               Within 1-10 arcseconds for most planets.
//
//
// @credits      HSV and HSL Color Tools by kaigouthro/Cairo (MPL-2.0)
//               https://mozilla.org/MPL/2.0/
//
// ════════════════════════════════════════════════════════════════
//   © 2025-2026 BlueprintResearch / Javonnii
//   Licensed under CC BY-NC-SA 4.0 (Non-Commercial, ShareAlike)
// ════════════════════════════════════════════════════════════════

//@version=6
indicator("Price and Longitude Angles", overlay=true, max_lines_count=500, max_bars_back=500, max_polylines_count=100)
import BlueprintResearch/lib_ephemeris/1 as eph
import BlueprintResearch/lib_vsop_core/1 as core
import kaigouthro/waves/1 as wv
import kaigouthro/hsvColor/17 as color

// ═══════════════════════════════════════════════════════════════════
// Input Settings
// ═══════════════════════════════════════════════════════════════════

// 1. Anchor Point
group_anchor = "1. Anchor Point"
high_low_bar_time = input.time(0, "Select a Significant High or Low Pivot", confirm = true, group = group_anchor)
selectHL1 = input.string("Low", "Select High/Low of pivot", options=["High", "Low"], group = group_anchor)

// 2. Planet Selection
group_planet = "2. Planet Selection"
planet1_in = input.string("☉︎ Sun", "Primary", options=["☉︎ Sun", "☽︎ Moon", "☿ Mercury", "♀ Venus", "♂ Mars", "♃ Jupiter", "♄ Saturn", "⛢ Uranus", "♆ Neptune", "♇ Pluto"], inline="planets", group = group_planet, tooltip = "Select the primary planet. For Single mode, only this planet is used.")
planet2_in = input.string("☉︎ Sun", "Secondary", options=["☉︎ Sun", "☽︎ Moon", "☿ Mercury", "♀ Venus", "♂ Mars", "♃ Jupiter", "♄ Saturn", "⛢ Uranus", "♆ Neptune", "♇ Pluto"], inline="planets", group = group_planet, tooltip = "Secondary planet for Average/Synodic modes. Ignored in Single mode.")
geo = input.bool(true, "Use Geocentric", group = group_planet, tooltip = "Checked: Earth-centered (geocentric). Unchecked: Sun-centered (heliocentric).")

// 3. Settings
group_settings = "3. Settings"
line_type = input.string("Single", "Calculation Mode", options = ["Single", "Average", "Synodic"], group = group_settings, tooltip = "Single: Primary planet only. Average: Midpoint of two planets. Synodic: Angular separation between them.")
scale_ratio1 = input.float(1.0, "Price per 1° Longitude", step=0.5, group = group_settings, tooltip = "How much price moves per degree of planetary longitude change.")
mirror_in = input.bool(false, "Mirror Fan Direction", group = group_settings, tooltip = "Flip the direction of fan lines.")
degree_per1 = 1

// 4. Fan Lines
group_fan = "4. Fan Lines"
show_1x8 = input.bool(true, title="Show 1x8 Fan Line", group = group_fan)
show_1x4 = input.bool(true, title="Show 1x4 Fan Line", group = group_fan)
show_1x3 = input.bool(true, title="Show 1x3 Fan Line", group = group_fan)
show_1x2 = input.bool(true, title="Show 1x2 Fan Line", group = group_fan)
show_1x1 = input.bool(true, title="Show 1x1 Fan Line", group = group_fan)
show_2x1 = input.bool(true, title="Show 2x1 Fan Line", group = group_fan)
show_3x1 = input.bool(true, title="Show 3x1 Fan Line", group = group_fan)
show_4x1 = input.bool(true, title="Show 4x1 Fan Line", group = group_fan)
show_8x1 = input.bool(true, title="Show 8x1 Fan Line", group = group_fan)

// 5. Display Options
group_display = "5. Display Options"
show_info_table = input.bool(true, "Show Info Table", group = group_display, tooltip = "Display summary table with price/degree, mode, planets, and traveled degrees")
label_text_size = input.string(size.small, "Text Size", options=[size.auto, size.tiny, size.small, size.normal, size.large, size.huge], group = group_display, tooltip = "Text size for info table, fan line labels, and aspect table")
cycleUnit = input.float(360.0, minval=1, title="Cycle Unit (degrees)", group = group_display, tooltip = "Number of degrees per cycle (360° = one full rotation)")
showCycles = input.bool(true, title="Show Cycles in Info Table", group = group_display)

// 6. Future Projections
group_proj = "6. Future Projections"
show_projections = input.bool(true, "Enable Future Projections", group = group_proj, tooltip = "Project fan lines into the future using calculated planetary positions")
look_ahead = input.int(250, "Look Ahead Bars", minval=1, maxval=250, group = group_proj, tooltip = "Number of bars to project fan lines into the future")
proj_transparency = input.int(0, "Projection Transparency", minval=0, maxval=100, group = group_proj, tooltip = "Transparency of projected lines (0=solid, 100=invisible)")
show_proj_labels = input.bool(true, "Show Projection End Labels", group = group_proj, tooltip = "Display labels at the end of projected lines")

// 7. Aspect Table
group_tab = "7. Aspect Table"
zodiac_tooltip = "The Zodiac Table shows the current planetary longitude mapped to zodiac signs. When 'Orbital Bar Color' is enabled, bars are colored based on which zodiac sign the planetary angle is currently in. Use this table as a reference to understand the bar coloring."
show_orbital_clr = input.bool(false, title="Enable Orbital Bar Coloring", group = group_tab, tooltip = "Color price bars based on the zodiac sign of the current planetary angle")
show_clr_tab = input.bool(false, "Show Zodiac Aspect Table", group = group_tab, tooltip = zodiac_tooltip)
tab_text_clr = input.color(color.white, "Table Text Color", group = group_tab)


mirror_out = mirror_in ? -1 : 1
centricSign = geo ? "𝒢" : "ℋ"

// Timestamp to bar index conversion
timestamp_to_index(_time) =>
    var int index = na
    if na(index) and time >= _time
        index := bar_index
    index

high_low_bar_index = timestamp_to_index(high_low_bar_time)



// === Normalize angle to 0–360° for continuity ===
normalize360(lon) =>
    wrapped360 = core.mod360(lon)
    wrapped360 := wrapped360 < 0 ? wrapped360 + 360 : wrapped360
    wrapped360

// Function to return hue, saturation, and value individually
planet_hue(planet) =>
    switch planet
        "☉︎ Sun"   => 50   // Golden Yellow
        "☽︎ Moon"  => 0    // White/Silver
        "☿ Mercury" => 300 // Purple/Maroon
        "♀ Venus"  => 120  // Green
        "♂ Mars"   => 0    // Red
        "♃ Jupiter" => 30   // Orange
        "♄ Saturn"  => 45   // Gray/Yellowish
        "⛢ Uranus"  => 200  // Deep Blue/Navy
        "♆ Neptune" => 180  // Cyan
        "♇ Pluto"   => 260  // Dark Violet
        => na  // Default case

planet_saturation(planet) =>
    switch planet
        "☉︎ Sun"   => 1  
        "☽︎ Moon"  => 0  // Silver (low saturation)
        "☿ Mercury" => 0.6  
        "♀ Venus"  => 1   
        "♂ Mars"   => 1  
        "♃ Jupiter" => 1  
        "♄ Saturn"  => 0.3  // Gray (low saturation)
        "⛢ Uranus"  => 1  
        "♆ Neptune" => 1  
        "♇ Pluto"   => 0.6  
        => na  

planet_value(planet) =>
    switch planet
        "☉︎ Sun"   => 1  
        "☽︎ Moon"  => 0.85  // Bright but not pure white
        "☿ Mercury" => 0.8  
        "♀ Venus"  => 1  
        "♂ Mars"   => 1  
        "♃ Jupiter" => 1  
        "♄ Saturn"  => 0.8  
        "⛢ Uranus"  => 1  
        "♆ Neptune" => 1  
        "♇ Pluto"   => 0.6  
        => na  

// Get HSV values for each planet
hue1 = planet_hue(planet1_in)
sat1 = planet_saturation(planet1_in)
val1 = planet_value(planet1_in)

hue2 = planet_hue(planet2_in)
sat2 = planet_saturation(planet2_in)
val2 = planet_value(planet2_in)

// Assign colors only if values are not `na`
col1_out = not na(hue1) and not na(sat1) and not na(val1) ? color.hsv(hue1, sat1, val1, 1) : na
// col2_out = not na(hue2) and not na(sat2) and not na(val2) ? color.hsv(hue2, sat2, val2, 1) : na



// Function to get the price based on selection (High or Low)
get_price(selection) =>
    selection == "High" ? high : low


// Function to get planet longitude (simplified using lib_ephemeris)
f_get_planet_longitude(string planet_str, bool use_geo, int timestamp_ms) =>
    planet_enum = eph.string_to_planet(planet_str)
    eph.get_longitude(planet_enum, timestamp_ms, use_geo)

// Convert longitude to degree-sign format (e.g., "15°Ari32'")
f_degsignf(float lon) =>
    signs = array.from("Ari", "Tau", "Gem", "Can", "Leo", "Vir", "Lib", "Sco", "Sag", "Cap", "Aqu", "Pis")
    norm = lon % 360
    norm := norm < 0 ? norm + 360 : norm
    sign_idx = int(norm / 30)
    deg_in_sign = norm % 30
    deg = int(deg_in_sign)
    min = int((deg_in_sign - deg) * 60)
    str.tostring(deg) + "°" + array.get(signs, sign_idx) + str.tostring(min) + "'"



// Initialize variables
var float accumulated_change1 = na
var float accumulated_change2 = na
var float previous_long = na
var float previous_long_2 = na
var int targetBar1 = na
var float current_long = na
var float current_long_2 = na
var float long_delta = na 
var float long_delta_2 = na 
var float accumulated_syn = na
var float previous_syn = na
var float current_syn = na
var float anchor_syn = na

// Label variables
var label label1x1 = na
var label label1x2 = na
var label label2x1 = na
var label label1x3 = na
var label label3x1 = na
var label label1x4 = na
var label label4x1 = na
var label label1x8 = na
var label label8x1 = na

// Info table (top right corner)
var table infoTable = table.new(position.top_right, 2, 5, bgcolor=color.new(color.black, 80), border_width=1)

// Fan variables for Planet 1
var float targetPrice1 = na
var float targetPrice_1x2 = na
var float targetPrice_2x1 = na
var float targetPrice_3x1 = na
var float targetPrice_4x1 = na
var float targetPrice_8x1 = na
var float targetPrice_1x3 = na
var float targetPrice_1x4 = na
var float targetPrice_1x8 = na

var float diagonal_value_1x1 = na
var float diagonal_value_2x1 = na
var float diagonal_value_3x1 = na
var float diagonal_value_4x1 = na
var float diagonal_value_8x1 = na
var float diagonal_value_1x2 = na
var float diagonal_value_1x3 = na
var float diagonal_value_1x4 = na
var float diagonal_value_1x8 = na

var float degree_counter = na 

// Fan variables for Planet 2 (similar structure for the second planet)
var float targetPrice2 = na
var float targetPrice2_1x2 = na
var float targetPrice2_2x1 = na
var float targetPrice2_3x1 = na
var float targetPrice2_4x1 = na
var float targetPrice2_8x1 = na
var float targetPrice2_1x3 = na
var float targetPrice2_1x4 = na
var float targetPrice2_1x8 = na

var float diagonal_value_2_1x1 = na
var float diagonal_value_2_2x1 = na
var float diagonal_value_2_3x1 = na
var float diagonal_value_2_4x1 = na
var float diagonal_value_2_8x1 = na
var float diagonal_value_2_1x2 = na
var float diagonal_value_2_1x3 = na
var float diagonal_value_2_1x4 = na
var float diagonal_value_2_1x8 = na
var float anchor_long = na
var float anchor_long_2 = na

// Future projection polylines (one per fan ratio)
var polyline proj_1x1 = na
var polyline proj_1x2 = na
var polyline proj_2x1 = na
var polyline proj_1x3 = na
var polyline proj_3x1 = na
var polyline proj_1x4 = na
var polyline proj_4x1 = na
var polyline proj_1x8 = na
var polyline proj_8x1 = na

// Future projection end labels
var label proj_label_1x1 = na
var label proj_label_1x2 = na
var label proj_label_2x1 = na
var label proj_label_1x3 = na
var label proj_label_3x1 = na
var label proj_label_1x4 = na
var label proj_label_4x1 = na
var label proj_label_1x8 = na
var label proj_label_8x1 = na

if (time == high_low_bar_time)
    // Store initial values for Planet 1
    targetBar1 := bar_index
    targetPrice1 := get_price(selectHL1)
    targetPrice_1x2 := get_price(selectHL1)
    targetPrice_2x1 := get_price(selectHL1)
    targetPrice_3x1 := get_price(selectHL1)
    targetPrice_4x1 := get_price(selectHL1)
    targetPrice_8x1 := get_price(selectHL1)
    targetPrice_1x3 := get_price(selectHL1)
    targetPrice_1x4 := get_price(selectHL1)
    targetPrice_1x8 := get_price(selectHL1)

    // Store initial values for Planet 2
    targetPrice2 := get_price(selectHL1)
    targetPrice2_1x2 := get_price(selectHL1)
    targetPrice2_2x1 := get_price(selectHL1)
    targetPrice2_3x1 := get_price(selectHL1)
    targetPrice2_4x1 := get_price(selectHL1)
    targetPrice2_8x1 := get_price(selectHL1)
    targetPrice2_1x3 := get_price(selectHL1)
    targetPrice2_1x4 := get_price(selectHL1)
    targetPrice2_1x8 := get_price(selectHL1)

    // Reset previous longitudes and accumulated changes
    previous_long := na
    previous_long_2 := na
    previous_syn := na
    accumulated_change1 := 0.0
    accumulated_change2 := 0.0
    accumulated_syn := 0.0
    degree_counter := 0.0

    // Set initial diagonal values for both planets
    diagonal_value_1x1 := targetPrice1
    diagonal_value_1x2 := targetPrice_1x2
    diagonal_value_2x1 := targetPrice_2x1
    diagonal_value_3x1 := targetPrice_3x1
    diagonal_value_4x1 := targetPrice_4x1
    diagonal_value_8x1 := targetPrice_8x1
    diagonal_value_1x3 := targetPrice_1x3
    diagonal_value_1x4 := targetPrice_1x4
    diagonal_value_1x8 := targetPrice_1x8

    diagonal_value_2_1x1 := targetPrice2
    diagonal_value_2_1x2 := targetPrice2_1x2
    diagonal_value_2_2x1 := targetPrice2_2x1
    diagonal_value_2_3x1 := targetPrice2_3x1
    diagonal_value_2_4x1 := targetPrice2_4x1
    diagonal_value_2_8x1 := targetPrice2_8x1
    diagonal_value_2_1x3 := targetPrice2_1x3
    diagonal_value_2_1x4 := targetPrice2_1x4
    diagonal_value_2_1x8 := targetPrice2_1x8

    anchor_long := f_get_planet_longitude(planet1_in, geo, time)
    anchor_long_2 := f_get_planet_longitude(planet2_in, geo, time)
    anchor_syn := normalize360(anchor_long - anchor_long_2)



// New variables for degree counting
var float degree_counter_custom = 0.0
var float previous_long_custom = na
var float current_long_custom = na 
var float long_deltas = na 
var float average_accum = na 


if (time >= high_low_bar_time)
    // Get the current planetary longitudes for both planets
    current_long := f_get_planet_longitude(planet1_in, geo, time)
    current_long_2 := f_get_planet_longitude(planet2_in, geo, time)
    current_syn := normalize360(current_long - current_long_2)

    if line_type == "Single"
        // Single mode: Only use Planet 1's accumulated change
        if not na(current_long) and not na(previous_long)
            long_delta := current_long - previous_long
            if (long_delta > 180)
                long_delta -= 360
            else if (long_delta < -180)
                long_delta += 360
            accumulated_change1 += long_delta

        // Update all diagonal values using only Planet 1's accumulation
        diagonal_value_1x1 := targetPrice1 + (scale_ratio1 * accumulated_change1) * mirror_out
        diagonal_value_1x2 := targetPrice1 + (scale_ratio1 / 2) * accumulated_change1 * mirror_out
        diagonal_value_2x1 := targetPrice1 + (scale_ratio1 * 2) * accumulated_change1 * mirror_out
        diagonal_value_3x1 := targetPrice1 + (scale_ratio1 * 3) * accumulated_change1 * mirror_out
        diagonal_value_4x1 := targetPrice1 + (scale_ratio1 * 4) * accumulated_change1 * mirror_out
        diagonal_value_8x1 := targetPrice1 + (scale_ratio1 * 8) * accumulated_change1 * mirror_out
        diagonal_value_1x3 := targetPrice1 + (scale_ratio1 / 3) * accumulated_change1 * mirror_out
        diagonal_value_1x4 := targetPrice1 + (scale_ratio1 / 4) * accumulated_change1 * mirror_out
        diagonal_value_1x8 := targetPrice1 + (scale_ratio1 / 8) * accumulated_change1 * mirror_out

        // Set second set equal to first (so averaging in plots gives same result)
        diagonal_value_2_1x1 := diagonal_value_1x1
        diagonal_value_2_1x2 := diagonal_value_1x2
        diagonal_value_2_2x1 := diagonal_value_2x1
        diagonal_value_2_3x1 := diagonal_value_3x1
        diagonal_value_2_4x1 := diagonal_value_4x1
        diagonal_value_2_8x1 := diagonal_value_8x1
        diagonal_value_2_1x3 := diagonal_value_1x3
        diagonal_value_2_1x4 := diagonal_value_1x4
        diagonal_value_2_1x8 := diagonal_value_1x8

        average_accum := accumulated_change1

    else if line_type == "Average"
        if not na(current_long) and not na(previous_long)
            // Planet 1: Calculate the difference between the current and previous longitudes
            long_delta := current_long - previous_long
            if (long_delta > 180)
                long_delta -= 360
            else if (long_delta < -180)
                long_delta += 360
            accumulated_change1 += long_delta

        if not na(current_long_2) and not na(previous_long_2)
            // Planet 2: Calculate the difference between the current and previous longitudes
            long_delta_2 := current_long_2 - previous_long_2
            if (long_delta_2 > 180)
                long_delta_2 -= 360
            else if (long_delta_2 < -180)
                long_delta_2 += 360
            accumulated_change2 += long_delta_2

        // Update diagonal values for both planets
        diagonal_value_1x1 := targetPrice1 + (scale_ratio1 * accumulated_change1) * mirror_out
        diagonal_value_1x2 := targetPrice1 + (scale_ratio1 / 2) * accumulated_change1 * mirror_out
        diagonal_value_2x1 := targetPrice1 + (scale_ratio1 * 2) * accumulated_change1 * mirror_out
        diagonal_value_3x1 := targetPrice1 + (scale_ratio1 * 3) * accumulated_change1 * mirror_out
        diagonal_value_4x1 := targetPrice1 + (scale_ratio1 * 4) * accumulated_change1 * mirror_out
        diagonal_value_8x1 := targetPrice1 + (scale_ratio1 * 8) * accumulated_change1 * mirror_out
        diagonal_value_1x3 := targetPrice1 + (scale_ratio1 / 3) * accumulated_change1 * mirror_out
        diagonal_value_1x4 := targetPrice1 + (scale_ratio1 / 4) * accumulated_change1 * mirror_out
        diagonal_value_1x8 := targetPrice1 + (scale_ratio1 / 8) * accumulated_change1 * mirror_out

        diagonal_value_2_1x1 := targetPrice2 + (scale_ratio1 * accumulated_change2) * mirror_out
        diagonal_value_2_1x2 := targetPrice2 + (scale_ratio1 / 2) * accumulated_change2 * mirror_out
        diagonal_value_2_2x1 := targetPrice2 + (scale_ratio1 * 2) * accumulated_change2 * mirror_out
        diagonal_value_2_3x1 := targetPrice2 + (scale_ratio1 * 3) * accumulated_change2 * mirror_out
        diagonal_value_2_4x1 := targetPrice2 + (scale_ratio1 * 4) * accumulated_change2 * mirror_out
        diagonal_value_2_8x1 := targetPrice2 + (scale_ratio1 * 8) * accumulated_change2 * mirror_out
        diagonal_value_2_1x3 := targetPrice2 + (scale_ratio1 / 3) * accumulated_change2 * mirror_out
        diagonal_value_2_1x4 := targetPrice2 + (scale_ratio1 / 4) * accumulated_change2 * mirror_out
        diagonal_value_2_1x8 := targetPrice2 + (scale_ratio1 / 8) * accumulated_change2 * mirror_out

        average_accum := math.avg(accumulated_change1, accumulated_change2)

    else  // Synodic
        if not na(current_syn) and not na(previous_syn)
            float diff = current_syn - previous_syn
            float adjusted_diff = diff
            if diff < -180
                adjusted_diff += 360.0
            else if diff > 180
                adjusted_diff -= 360.0
            accumulated_syn += adjusted_diff

        // Update diagonal values using accumulated_syn for both sets (to allow averaging)
        diagonal_value_1x1 := targetPrice1 + (scale_ratio1 * accumulated_syn) * mirror_out
        diagonal_value_1x2 := targetPrice1 + (scale_ratio1 / 2) * accumulated_syn * mirror_out
        diagonal_value_2x1 := targetPrice1 + (scale_ratio1 * 2) * accumulated_syn * mirror_out
        diagonal_value_3x1 := targetPrice1 + (scale_ratio1 * 3) * accumulated_syn * mirror_out
        diagonal_value_4x1 := targetPrice1 + (scale_ratio1 * 4) * accumulated_syn * mirror_out
        diagonal_value_8x1 := targetPrice1 + (scale_ratio1 * 8) * accumulated_syn * mirror_out
        diagonal_value_1x3 := targetPrice1 + (scale_ratio1 / 3) * accumulated_syn * mirror_out
        diagonal_value_1x4 := targetPrice1 + (scale_ratio1 / 4) * accumulated_syn * mirror_out
        diagonal_value_1x8 := targetPrice1 + (scale_ratio1 / 8) * accumulated_syn * mirror_out

        diagonal_value_2_1x1 := targetPrice2 + (scale_ratio1 * accumulated_syn) * mirror_out
        diagonal_value_2_1x2 := targetPrice2 + (scale_ratio1 / 2) * accumulated_syn * mirror_out
        diagonal_value_2_2x1 := targetPrice2 + (scale_ratio1 * 2) * accumulated_syn * mirror_out
        diagonal_value_2_3x1 := targetPrice2 + (scale_ratio1 * 3) * accumulated_syn * mirror_out
        diagonal_value_2_4x1 := targetPrice2 + (scale_ratio1 * 4) * accumulated_syn * mirror_out
        diagonal_value_2_8x1 := targetPrice2 + (scale_ratio1 * 8) * accumulated_syn * mirror_out
        diagonal_value_2_1x3 := targetPrice2 + (scale_ratio1 / 3) * accumulated_syn * mirror_out
        diagonal_value_2_1x4 := targetPrice2 + (scale_ratio1 / 4) * accumulated_syn * mirror_out
        diagonal_value_2_1x8 := targetPrice2 + (scale_ratio1 / 8) * accumulated_syn * mirror_out

        average_accum := accumulated_syn

    // Calculate the average accumulated change
    // average_accum := math.avg(accumulated_change1, accumulated_change2)
    cycles = math.round(average_accum / cycleUnit, 2)

    // Conditionally create display string
    cycleStr = showCycles ? " - Num of Cycles " + str.tostring(cycles, "#.##") : " "


    // Extract the symbol (everything before the space)
    planet_symbol = str.substring(planet1_in, 0, 1)
    planet_symbol2 = str.substring(planet2_in, 0, 1)
    // Determine abbreviation based on selection
    abbr = line_type == "Single" ? "SGL" : (line_type == "Average" ? "AVG" : "SYN")

    // Build the dynamic string
    planet_string = line_type == "Single" ? planet_symbol : (planet_symbol == planet_symbol2 ? planet_symbol : planet_symbol + " | " + planet_symbol2 + " " + abbr + " :")


    // Populate info table (top right corner)
    if show_info_table
        // Build mode string
        mode_str = centricSign + " " + abbr

        // Build planets string (Single mode shows only Planet 1)
        planets_str = line_type == "Single" ? planet_symbol : (planet_symbol == planet_symbol2 ? planet_symbol : planet_symbol + " | " + planet_symbol2)

        // Clear and populate table
        table.cell(infoTable, 0, 0, "$/°", text_color=color.gray, text_size=label_text_size, text_halign=text.align_right)
        table.cell(infoTable, 1, 0, "$" + str.tostring(scale_ratio1), text_color=col1_out, text_size=label_text_size, text_halign=text.align_left)

        table.cell(infoTable, 0, 1, "Mode", text_color=color.gray, text_size=label_text_size, text_halign=text.align_right)
        table.cell(infoTable, 1, 1, mode_str, text_color=col1_out, text_size=label_text_size, text_halign=text.align_left)

        table.cell(infoTable, 0, 2, "Planets", text_color=color.gray, text_size=label_text_size, text_halign=text.align_right)
        table.cell(infoTable, 1, 2, planets_str, text_color=col1_out, text_size=label_text_size, text_halign=text.align_left)

        table.cell(infoTable, 0, 3, "Traveled", text_color=color.gray, text_size=label_text_size, text_halign=text.align_right)
        table.cell(infoTable, 1, 3, str.tostring(average_accum, "#.##") + "°", text_color=col1_out, text_size=label_text_size, text_halign=text.align_left)

        if showCycles
            table.cell(infoTable, 0, 4, "Cycles", text_color=color.gray, text_size=label_text_size, text_halign=text.align_right)
            table.cell(infoTable, 1, 4, str.tostring(cycles, "#.##"), text_color=col1_out, text_size=label_text_size, text_halign=text.align_left)

    // Conditional label creation for each fan line using diagonal values
    if (show_1x1)
        label1x1 := label.new(x=bar_index + 5, y= math.avg(diagonal_value_1x1, diagonal_value_2_1x1), text="1x1", color=color.new(color.white, 100), textcolor=col1_out, size=label_text_size, style=label.style_label_up)
        label.delete(label1x1[1])

    if (show_1x2)
        label1x2 := label.new(x=bar_index + 5, y=math.avg(diagonal_value_1x2, diagonal_value_2_1x2), text="1x2", color=color.new(color.white, 100), textcolor=col1_out, size=label_text_size, style=label.style_label_up)
        label.delete(label1x2[1])

    if (show_2x1)
        label2x1 := label.new(x=bar_index + 5, y=math.avg(diagonal_value_2x1, diagonal_value_2_2x1), text="2x1", color=color.new(color.white, 100), textcolor=col1_out, size=label_text_size, style=label.style_label_up)
        label.delete(label2x1[1])

    if (show_1x3)
        label1x3 := label.new(x=bar_index + 5, y=math.avg(diagonal_value_1x3, diagonal_value_2_1x3), text="1x3", color=color.new(color.white, 100), textcolor=col1_out, size=label_text_size, style=label.style_label_up)
        label.delete(label1x3[1])

    if (show_3x1)
        label3x1 := label.new(x=bar_index + 5, y=math.avg(diagonal_value_3x1, diagonal_value_2_3x1), text="3x1", color=color.new(color.white, 100), textcolor=col1_out, size=label_text_size, style=label.style_label_up)
        label.delete(label3x1[1])

    if (show_1x4)
        label1x4 := label.new(x=bar_index + 5, y=math.avg(diagonal_value_1x4, diagonal_value_2_1x4), text="1x4", color=color.new(color.white, 100), textcolor=col1_out, size=label_text_size, style=label.style_label_up)
        label.delete(label1x4[1])

    if (show_4x1)
        label4x1 := label.new(x=bar_index + 5, y=math.avg(diagonal_value_4x1, diagonal_value_2_4x1), text="4x1", color=color.new(color.white, 100), textcolor=col1_out, size=label_text_size, style=label.style_label_up)
        label.delete(label4x1[1])

    if (show_1x8)
        label1x8 := label.new(x=bar_index + 5, y=math.avg(diagonal_value_1x8, diagonal_value_2_1x8), text="1x8", color=color.new(color.white, 100), textcolor=col1_out, size=label_text_size, style=label.style_label_up)
        label.delete(label1x8[1])

    if (show_8x1)
        label8x1 := label.new(x=bar_index + 5, y=math.avg(diagonal_value_8x1, diagonal_value_2_8x1), text="8x1", color=color.new(color.white, 100), textcolor=col1_out, size=label_text_size, style=label.style_label_up)
        label.delete(label8x1[1])


        // Store the current longitudes for the next iteration
    previous_long := current_long
    previous_long_2 := current_long_2
    previous_syn := current_syn


// Plotting with conditionals based on user input and adding alerts
p1x1 = show_1x1 ? math.avg(diagonal_value_1x1, diagonal_value_2_1x1): na
p1x8 = show_1x8 ? math.avg(diagonal_value_1x8, diagonal_value_2_1x8): na
p1x4 = show_1x4 ? math.avg(diagonal_value_1x4, diagonal_value_2_1x4): na
p1x3 = show_1x3 ? math.avg(diagonal_value_1x3, diagonal_value_2_1x3): na
p1x2 = show_1x2 ? math.avg(diagonal_value_1x2, diagonal_value_2_1x2): na
p2x1 = show_2x1 ? math.avg(diagonal_value_2x1, diagonal_value_2_2x1): na
p3x1 = show_3x1 ? math.avg(diagonal_value_3x1, diagonal_value_2_3x1): na
p4x1 = show_4x1 ? math.avg(diagonal_value_4x1, diagonal_value_2_4x1): na
p8x1 = show_8x1 ? math.avg(diagonal_value_8x1, diagonal_value_2_8x1): na


// 1x1 Line uses the average planetary longitude for hue
// plot(p1x1, color=color.hsv(math.avg(current_long, current_long_2), 1, 1, 1), title="1x1 Line")


//
hue_1x1 = line_type == "Average" ? math.avg(current_long, current_long_2) : current_syn
anchor_hue = line_type == "Average" ? math.avg(anchor_long, anchor_long_2) : anchor_syn

// // Plot the 1x1 line only when show_1x1 is true
// plot(p1x1, color=not na(hue_1x1) ? color.hsv(hue_1x1, 1, 1, 1) : na, title="1x1 Line")

// Conditional background color based on p1x1
// bgcolor(not na(p1x1) ? color.new(color.hsv(hue_1x1, 1, 1, 1), 80) : na, title="Planetary Zone")


// Other lines use planetary colors
plot(p1x1, color=col1_out, title="1x1 Line")
plot(p1x8, color=col1_out, title="1x8 Line")
plot(p1x4, color=col1_out, title="1x4 Line")
plot(p1x3, color=col1_out, title="1x3 Line")
plot(p1x2, color=col1_out, title="1x2 Line")
plot(p2x1, color=col1_out, title="2x1 Line")
plot(p3x1, color=col1_out, title="3x1 Line")
plot(p4x1, color=col1_out, title="4x1 Line")
plot(p8x1, color=col1_out, title="8x1 Line")


// ═══════════════════════════════════════════════════════════════════
// Future Projections
// ═══════════════════════════════════════════════════════════════════

if show_projections and barstate.islast and time >= high_low_bar_time
    // Clean up previous polylines
    polyline.delete(proj_1x1)
    polyline.delete(proj_1x2)
    polyline.delete(proj_2x1)
    polyline.delete(proj_1x3)
    polyline.delete(proj_3x1)
    polyline.delete(proj_1x4)
    polyline.delete(proj_4x1)
    polyline.delete(proj_1x8)
    polyline.delete(proj_8x1)

    // Clean up previous projection labels
    label.delete(proj_label_1x1)
    label.delete(proj_label_1x2)
    label.delete(proj_label_2x1)
    label.delete(proj_label_1x3)
    label.delete(proj_label_3x1)
    label.delete(proj_label_1x4)
    label.delete(proj_label_4x1)
    label.delete(proj_label_1x8)
    label.delete(proj_label_8x1)

    // Calculate bar gap in milliseconds
    bar_gap = timeframe.in_seconds(timeframe.period) * 1000

    // Initialize point arrays for each visible fan line
    array<chart.point> pts_1x1 = array.new<chart.point>()
    array<chart.point> pts_1x2 = array.new<chart.point>()
    array<chart.point> pts_2x1 = array.new<chart.point>()
    array<chart.point> pts_1x3 = array.new<chart.point>()
    array<chart.point> pts_3x1 = array.new<chart.point>()
    array<chart.point> pts_1x4 = array.new<chart.point>()
    array<chart.point> pts_4x1 = array.new<chart.point>()
    array<chart.point> pts_1x8 = array.new<chart.point>()
    array<chart.point> pts_8x1 = array.new<chart.point>()

    // Add current bar as first point (connects to historical plot)
    if show_1x1
        array.push(pts_1x1, chart.point.from_time(time, p1x1))
    if show_1x2
        array.push(pts_1x2, chart.point.from_time(time, p1x2))
    if show_2x1
        array.push(pts_2x1, chart.point.from_time(time, p2x1))
    if show_1x3
        array.push(pts_1x3, chart.point.from_time(time, p1x3))
    if show_3x1
        array.push(pts_3x1, chart.point.from_time(time, p3x1))
    if show_1x4
        array.push(pts_1x4, chart.point.from_time(time, p1x4))
    if show_4x1
        array.push(pts_4x1, chart.point.from_time(time, p4x1))
    if show_1x8
        array.push(pts_1x8, chart.point.from_time(time, p1x8))
    if show_8x1
        array.push(pts_8x1, chart.point.from_time(time, p8x1))

    // Track accumulated changes for projection
    float proj_accum1 = accumulated_change1
    float proj_accum2 = accumulated_change2
    float proj_accum_syn = accumulated_syn
    float proj_prev_long1 = current_long
    float proj_prev_long2 = current_long_2
    float proj_prev_syn = current_syn
    float proj_avg_accum = average_accum

    // Track final values for end labels
    float final_1x1 = p1x1
    float final_1x2 = p1x2
    float final_2x1 = p2x1
    float final_1x3 = p1x3
    float final_3x1 = p3x1
    float final_1x4 = p1x4
    float final_4x1 = p4x1
    float final_1x8 = p1x8
    float final_8x1 = p8x1

    for i = 1 to look_ahead
        future_time = time + (bar_gap * i)

        // Get future planetary longitudes
        future_long1 = f_get_planet_longitude(planet1_in, geo, future_time)
        future_long2 = f_get_planet_longitude(planet2_in, geo, future_time)

        if line_type == "Single"
            // Single mode: Only use Planet 1's longitude
            float delta1 = future_long1 - proj_prev_long1
            if delta1 > 180
                delta1 -= 360
            else if delta1 < -180
                delta1 += 360
            proj_accum1 += delta1
            proj_avg_accum := proj_accum1

        else if line_type == "Average"
            // Calculate longitude deltas with wraparound handling
            float delta1 = future_long1 - proj_prev_long1
            if delta1 > 180
                delta1 -= 360
            else if delta1 < -180
                delta1 += 360
            proj_accum1 += delta1

            float delta2 = future_long2 - proj_prev_long2
            if delta2 > 180
                delta2 -= 360
            else if delta2 < -180
                delta2 += 360
            proj_accum2 += delta2

            proj_avg_accum := math.avg(proj_accum1, proj_accum2)

        else // Synodic
            float future_syn = normalize360(future_long1 - future_long2)
            float syn_delta = future_syn - proj_prev_syn
            if syn_delta < -180
                syn_delta += 360
            else if syn_delta > 180
                syn_delta -= 360
            proj_accum_syn += syn_delta
            proj_avg_accum := proj_accum_syn
            proj_prev_syn := future_syn

        // Calculate projected fan values using same formulas as historical
        float proj_1x1_val = targetPrice1 + (scale_ratio1 * proj_avg_accum) * mirror_out
        float proj_1x2_val = targetPrice1 + (scale_ratio1 / 2) * proj_avg_accum * mirror_out
        float proj_2x1_val = targetPrice1 + (scale_ratio1 * 2) * proj_avg_accum * mirror_out
        float proj_1x3_val = targetPrice1 + (scale_ratio1 / 3) * proj_avg_accum * mirror_out
        float proj_3x1_val = targetPrice1 + (scale_ratio1 * 3) * proj_avg_accum * mirror_out
        float proj_1x4_val = targetPrice1 + (scale_ratio1 / 4) * proj_avg_accum * mirror_out
        float proj_4x1_val = targetPrice1 + (scale_ratio1 * 4) * proj_avg_accum * mirror_out
        float proj_1x8_val = targetPrice1 + (scale_ratio1 / 8) * proj_avg_accum * mirror_out
        float proj_8x1_val = targetPrice1 + (scale_ratio1 * 8) * proj_avg_accum * mirror_out

        // Average with planet 2 diagonal values
        float proj_2_1x1_val = targetPrice2 + (scale_ratio1 * proj_avg_accum) * mirror_out
        float proj_2_1x2_val = targetPrice2 + (scale_ratio1 / 2) * proj_avg_accum * mirror_out
        float proj_2_2x1_val = targetPrice2 + (scale_ratio1 * 2) * proj_avg_accum * mirror_out
        float proj_2_1x3_val = targetPrice2 + (scale_ratio1 / 3) * proj_avg_accum * mirror_out
        float proj_2_3x1_val = targetPrice2 + (scale_ratio1 * 3) * proj_avg_accum * mirror_out
        float proj_2_1x4_val = targetPrice2 + (scale_ratio1 / 4) * proj_avg_accum * mirror_out
        float proj_2_4x1_val = targetPrice2 + (scale_ratio1 * 4) * proj_avg_accum * mirror_out
        float proj_2_1x8_val = targetPrice2 + (scale_ratio1 / 8) * proj_avg_accum * mirror_out
        float proj_2_8x1_val = targetPrice2 + (scale_ratio1 * 8) * proj_avg_accum * mirror_out

        // Final averaged values
        final_1x1 := math.avg(proj_1x1_val, proj_2_1x1_val)
        final_1x2 := math.avg(proj_1x2_val, proj_2_1x2_val)
        final_2x1 := math.avg(proj_2x1_val, proj_2_2x1_val)
        final_1x3 := math.avg(proj_1x3_val, proj_2_1x3_val)
        final_3x1 := math.avg(proj_3x1_val, proj_2_3x1_val)
        final_1x4 := math.avg(proj_1x4_val, proj_2_1x4_val)
        final_4x1 := math.avg(proj_4x1_val, proj_2_4x1_val)
        final_1x8 := math.avg(proj_1x8_val, proj_2_1x8_val)
        final_8x1 := math.avg(proj_8x1_val, proj_2_8x1_val)

        // Add points to arrays
        if show_1x1
            array.push(pts_1x1, chart.point.from_time(future_time, final_1x1))
        if show_1x2
            array.push(pts_1x2, chart.point.from_time(future_time, final_1x2))
        if show_2x1
            array.push(pts_2x1, chart.point.from_time(future_time, final_2x1))
        if show_1x3
            array.push(pts_1x3, chart.point.from_time(future_time, final_1x3))
        if show_3x1
            array.push(pts_3x1, chart.point.from_time(future_time, final_3x1))
        if show_1x4
            array.push(pts_1x4, chart.point.from_time(future_time, final_1x4))
        if show_4x1
            array.push(pts_4x1, chart.point.from_time(future_time, final_4x1))
        if show_1x8
            array.push(pts_1x8, chart.point.from_time(future_time, final_1x8))
        if show_8x1
            array.push(pts_8x1, chart.point.from_time(future_time, final_8x1))

        // Update previous values for next iteration
        proj_prev_long1 := future_long1
        proj_prev_long2 := future_long2

    // Create polylines with dotted style and transparency
    proj_col = color.new(col1_out, proj_transparency)

    if show_1x1 and array.size(pts_1x1) > 1
        proj_1x1 := polyline.new(pts_1x1, xloc=xloc.bar_time, line_color=proj_col, line_style=line.style_solid)
    if show_1x2 and array.size(pts_1x2) > 1
        proj_1x2 := polyline.new(pts_1x2, xloc=xloc.bar_time, line_color=proj_col, line_style=line.style_solid)
    if show_2x1 and array.size(pts_2x1) > 1
        proj_2x1 := polyline.new(pts_2x1, xloc=xloc.bar_time, line_color=proj_col, line_style=line.style_solid)
    if show_1x3 and array.size(pts_1x3) > 1
        proj_1x3 := polyline.new(pts_1x3, xloc=xloc.bar_time, line_color=proj_col, line_style=line.style_solid)
    if show_3x1 and array.size(pts_3x1) > 1
        proj_3x1 := polyline.new(pts_3x1, xloc=xloc.bar_time, line_color=proj_col, line_style=line.style_solid)
    if show_1x4 and array.size(pts_1x4) > 1
        proj_1x4 := polyline.new(pts_1x4, xloc=xloc.bar_time, line_color=proj_col, line_style=line.style_solid)
    if show_4x1 and array.size(pts_4x1) > 1
        proj_4x1 := polyline.new(pts_4x1, xloc=xloc.bar_time, line_color=proj_col, line_style=line.style_solid)
    if show_1x8 and array.size(pts_1x8) > 1
        proj_1x8 := polyline.new(pts_1x8, xloc=xloc.bar_time, line_color=proj_col, line_style=line.style_solid)
    if show_8x1 and array.size(pts_8x1) > 1
        proj_8x1 := polyline.new(pts_8x1, xloc=xloc.bar_time, line_color=proj_col, line_style=line.style_solid)

    // Add dual labels at projection end (current bar labels already exist above)
    future_end_time = time + (bar_gap * look_ahead)
    if show_proj_labels
        if show_1x1
            proj_label_1x1 := label.new(x=future_end_time, y=final_1x1, text="1x1", xloc=xloc.bar_time, color=color.new(color.white, 100), textcolor=proj_col, size=label_text_size, style=label.style_label_up)
        if show_1x2
            proj_label_1x2 := label.new(x=future_end_time, y=final_1x2, text="1x2", xloc=xloc.bar_time, color=color.new(color.white, 100), textcolor=proj_col, size=label_text_size, style=label.style_label_up)
        if show_2x1
            proj_label_2x1 := label.new(x=future_end_time, y=final_2x1, text="2x1", xloc=xloc.bar_time, color=color.new(color.white, 100), textcolor=proj_col, size=label_text_size, style=label.style_label_up)
        if show_1x3
            proj_label_1x3 := label.new(x=future_end_time, y=final_1x3, text="1x3", xloc=xloc.bar_time, color=color.new(color.white, 100), textcolor=proj_col, size=label_text_size, style=label.style_label_up)
        if show_3x1
            proj_label_3x1 := label.new(x=future_end_time, y=final_3x1, text="3x1", xloc=xloc.bar_time, color=color.new(color.white, 100), textcolor=proj_col, size=label_text_size, style=label.style_label_up)
        if show_1x4
            proj_label_1x4 := label.new(x=future_end_time, y=final_1x4, text="1x4", xloc=xloc.bar_time, color=color.new(color.white, 100), textcolor=proj_col, size=label_text_size, style=label.style_label_up)
        if show_4x1
            proj_label_4x1 := label.new(x=future_end_time, y=final_4x1, text="4x1", xloc=xloc.bar_time, color=color.new(color.white, 100), textcolor=proj_col, size=label_text_size, style=label.style_label_up)
        if show_1x8
            proj_label_1x8 := label.new(x=future_end_time, y=final_1x8, text="1x8", xloc=xloc.bar_time, color=color.new(color.white, 100), textcolor=proj_col, size=label_text_size, style=label.style_label_up)
        if show_8x1
            proj_label_8x1 := label.new(x=future_end_time, y=final_8x1, text="8x1", xloc=xloc.bar_time, color=color.new(color.white, 100), textcolor=proj_col, size=label_text_size, style=label.style_label_up)



/// COLOR WORKS 
var bool color_bar = false 

if (time >= high_low_bar_time) and show_orbital_clr
    color_bar := true


getHue(sign) =>
    var float hue = na
    if sign == "Aries"
        hue := 0
    else if sign == "Taurus"
        hue := 30
    else if sign == "Gemini"
        hue := 60
    else if sign == "Cancer"
        hue := 90
    else if sign == "Leo"
        hue := 120
    else if sign == "Virgo"
        hue := 150
    else if sign == "Libra"
        hue := 180
    else if sign == "Scorpio"
        hue := 210
    else if sign == "Sagittarius"
        hue := 240
    else if sign == "Capricorn"
        hue := 270
    else if sign == "Aquarius"
        hue := 300
    else if sign == "Pisces"
        hue := 330
    hue


getSignFromDegree(deg) =>
    d = deg % 360  // wrap around if deg ≥ 360
    sign = d >= 0   and d < 30  ? "Aries" :
           d >= 30  and d < 60  ? "Taurus" :
           d >= 60  and d < 90  ? "Gemini" :
           d >= 90  and d < 120 ? "Cancer" :
           d >= 120 and d < 150 ? "Leo" :
           d >= 150 and d < 180 ? "Virgo" :
           d >= 180 and d < 210 ? "Libra" :
           d >= 210 and d < 240 ? "Scorpio" :
           d >= 240 and d < 270 ? "Sagittarius" :
           d >= 270 and d < 300 ? "Capricorn" :
           d >= 300 and d < 330 ? "Aquarius" :
           d >= 330 and d < 360 ? "Pisces" :
           na


getColor(sign) =>
    // Base hue from sign name
    baseHue = getHue(sign)

    // Custom hue tweaks
    hue = sign == "Cancer"      ? 80 :  // green-blue
          sign == "Leo"         ? 115 :  // sharp electric lime
          sign == "Virgo"       ? 155 :  // teal-aqua
          sign == "Pisces"      ? 325 :  // shift away from Aries
          sign == "Aries"       ? 0   :  // keep Aries bold red
          baseHue

    // Custom saturation per sign
    sat = sign == "Leo"         ? 1.0  :
          sign == "Pisces"      ? 0.9  :
          sign == "Cancer"      ? 0.85 :
          sign == "Virgo"       ? 0.9  :
          sign == "Aquarius"    ? 0.95 :  // soft magenta
          1.0

    // Custom brightness per sign
    val = sign == "Virgo"       ? 0.8  :
          sign == "Pisces"      ? 0.8  :
          sign == "Cancer"      ? 0.95 :
          1.0

    color.hsv(hue, sat, val, 1)

sign_bar = getSignFromDegree(hue_1x1)


barcolor( color_bar ? getColor(sign_bar): na)

/// COLOR WORKS 




// Zodiac and Longitude Relationship Table 
var table aspectTable = table.new(position.bottom_right, 12, 13, border_width=1)

// === Header Labels ===
headerLabels = array.from("","","","Start &\n☌", "90°\n□", "120°\n△", "180°\n☍", "240°\n△", "270°\n□", "        ", "60°\n✶", "300°\n✶")
zodiacGlyphs = array.from("♈︎", "♉︎", "♊︎", "♋︎", "♌︎", "♍︎", "♎︎", "♏︎", "♐︎", "♑︎", "♒︎", "♓︎")


var row11 = array.from("Pisces", "Gemini", "Cancer", "Virgo", "Scorpio", "Sagittarius", "Taurus", "Capricorn")
var row10 = array.from("Aquarius", "Taurus", "Gemini", "Leo", "Libra", "Scorpio", "Aries", "Sagittarius")
var row9 = array.from("Capricorn", "Aries", "Taurus", "Cancer", "Virgo", "Libra", "Pisces", "Scorpio")
var row8 = array.from("Sagittarius", "Pisces", "Aries", "Gemini", "Leo", "Virgo", "Aquarius", "Libra")
var row7 = array.from("Scorpio", "Aquarius", "Pisces", "Taurus", "Cancer", "Leo", "Capricorn", "Virgo")
var row6 = array.from("Libra", "Capricorn", "Aquarius", "Aries", "Gemini", "Cancer", "Sagittarius", "Leo")
var row5 = array.from("Virgo", "Sagittarius", "Capricorn", "Pisces", "Taurus", "Gemini", "Scorpio", "Cancer")
var row4 = array.from("Leo", "Scorpio", "Sagittarius", "Aquarius", "Aries", "Taurus", "Libra", "Gemini")
var row3 = array.from("Cancer", "Libra", "Scorpio", "Capricorn", "Pisces", "Aries", "Virgo", "Taurus")
var row2 = array.from("Gemini", "Virgo", "Libra", "Sagittarius", "Aquarius", "Pisces", "Leo", "Aries")
var row1 = array.from("Taurus", "Leo", "Virgo", "Scorpio", "Capricorn", "Aquarius", "Cancer", "Pisces")
var row0 = array.from("Aries", "Cancer", "Leo", "Libra", "Sagittarius", "Capricorn", "Gemini", "Aquarius")


if barstate.islast and show_clr_tab
    // Header row
    for col = 0 to 11
        label = array.get(headerLabels, col)
        table.cell(aspectTable, col, 0, label, text_color=tab_text_clr, text_size= label_text_size, text_formatting = text.format_bold)

    // Zodiac rows
    for row = 0 to 11
        currentRow = row == 0  ? row0 :
                     row == 1  ? row1 :
                     row == 2  ? row2 :
                     row == 3  ? row3 :
                     row == 4  ? row4 :
                     row == 5  ? row5 :
                     row == 6  ? row6 :
                     row == 7  ? row7 :
                     row == 8  ? row8 :
                     row == 9  ? row9 :
                     row == 10 ? row10 :
                                 row11

        glyph = array.get(zodiacGlyphs, row)
        deg   = row * 30
        inRange = hue_1x1 >= deg and hue_1x1 < deg + 30
        pointer = inRange ? "▶" : ""
        anchor  = (anchor_hue >= deg and anchor_hue < deg + 30) ? "⚓" : ""

        // Column 0: pointer
        table.cell(aspectTable, 0, row + 1, pointer, text_color= tab_text_clr, text_halign=text.align_center, tooltip = f_degsignf(hue_1x1), text_size = label_text_size)

        // Column 1: anchor
        table.cell(aspectTable, 1, row + 1, anchor, text_halign=text.align_center, tooltip = f_degsignf(anchor_hue), text_size = label_text_size)

        // Column 2: glyph
        table.cell(aspectTable, 2, row + 1, glyph, text_color=#000000, text_size = label_text_size)

        // Columns 2–10: aspects
        for col = 3 to 11
            if col == 3
                trueCol = col < 9 ? col - 3 : col - 4
                sign = array.get(currentRow, trueCol)
                table.cell(aspectTable, col, row + 1, sign, bgcolor=getColor(sign), text_color=#000000, text_formatting = text.format_bold, text_size = label_text_size)
            else if col == 9
                table.cell(aspectTable, col, row + 1, "", bgcolor=color.new(color.gray, 85))
            else
                trueCol = col < 9 ? col - 3 : col - 4
                sign = array.get(currentRow, trueCol)
                table.cell(aspectTable, col, row + 1, sign, bgcolor=getColor(sign), text_color=#000000, text_formatting = text.format_italic, text_size = label_text_size)




// Alert conditions
alertcondition(ta.cross(close, p1x1), title="1x1 Cross", message="Price crossed the 1x1 Line")
alertcondition(ta.cross(close, p1x8), title="1x8 Cross", message="Price crossed the 1x8 Line")
alertcondition(ta.cross(close, p1x4), title="1x4 Cross", message="Price crossed the 1x4 Line")
alertcondition(ta.cross(close, p1x3), title="1x3 Cross", message="Price crossed the 1x3 Line")
alertcondition(ta.cross(close, p1x2), title="1x2 Cross", message="Price crossed the 1x2 Line")
alertcondition(ta.cross(close, p2x1), title="2x1 Cross", message="Price crossed the 2x1 Line")
alertcondition(ta.cross(close, p3x1), title="3x1 Cross", message="Price crossed the 3x1 Line")
alertcondition(ta.cross(close, p4x1), title="4x1 Cross", message="Price crossed the 4x1 Line")
alertcondition(ta.cross(close, p8x1), title="8x1 Cross", message="Price crossed the 8x1 Line")

Overview

Price and Longitude Angles supports one or two planets, geocentric or heliocentric positions, and single, average, or synodic calculation modes. The scale sets price per degree of longitude, and toggles show individual fan ratios, an info table, cycle counts, and optional zodiac bar coloring. Future projections extend the lines by a chosen number of bars. It is an overlay indicator and does not generate trade entries or exits.

Details

Published by

blueprintresearch-dev on GitHub

Code credit

BlueprintResearch / Javonnii, as written in the script's header

Type

Indicator, Pine Script v6

Draws

On the price chart

Category

Support & resistance, Screeners & dashboards

Markets

Any symbol

Timeframes

Any timeframe

Risk Notes

No repainting, lookahead or stacked entries were found in its code or README. That says nothing about how well it works: test it first.

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
Select a Significant High or Low Pivottime0high_low_bar_time
Select High/Low of pivotstringLowselectHL1
Primarystring☉︎ Sunplanet1_in
Secondarystring☉︎ Sunplanet2_in
Use Geocentricbooltruegeo
Calculation ModestringSingleline_type
Price per 1° Longitudefloat1.0scale_ratio1
Mirror Fan Directionboolfalsemirror_in
Show 1x8 Fan Linebooltrueshow_1x8
Show 1x4 Fan Linebooltrueshow_1x4
Show 1x3 Fan Linebooltrueshow_1x3
Show 1x2 Fan Linebooltrueshow_1x2
Show all 28 settings
SettingTypeDefaultVariable
Show 1x1 Fan Linebooltrueshow_1x1
Show 2x1 Fan Linebooltrueshow_2x1
Show 3x1 Fan Linebooltrueshow_3x1
Show 4x1 Fan Linebooltrueshow_4x1
Show 8x1 Fan Linebooltrueshow_8x1
Show Info Tablebooltrueshow_info_table
Text Sizestringsize.smalllabel_text_size
Cycle Unit (degrees)float360.0cycleUnit
Show Cycles in Info TablebooltrueshowCycles
Enable Future Projectionsbooltrueshow_projections
Look Ahead Barsint250look_ahead
Projection Transparencyint0proj_transparency
Show Projection End Labelsbooltrueshow_proj_labels
Enable Orbital Bar Coloringboolfalseshow_orbital_clr
Show Zodiac Aspect Tableboolfalseshow_clr_tab
Table Text Colorcolorcolor.whitetab_text_clr
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

575 stars

This overlay indicator scans a pasted list of symbols for ICT liquidity sweep patterns using prior swing sweeps. The screening method looks back one, two, or three swings and can list long and short signals in a results table with adjustable rows, position, an

Indicator v5 SMC / ICT / price actionScreeners & dashboards
Updated 1 year ago · MPL-2.0 GitHub repository of ICT Liquidity Sweep Pattern Scanner
330 stars

This indicator monitors multiple cryptocurrency symbols for trading signals. It calculates and plots entry prices, take profit, and stop loss levels for each symbol including BTCUSDT, ETHUSDT, BNBUSDT and others. Price crossovers trigger buy, sell or close ale

Indicator v5 Support & resistanceScreeners & dashboards
Updated 2 months ago · None stated GitHub repository of Multi Alert Long/Short [Alorse]

BZ-tracker

by Allen1212
303 stars

The BZ-tracker indicator marks ARK Invest buy and sell transactions on stock charts with labels. It plots green labels for purchases and red labels for sales, each annotated with a multiplier such as X1 or X2 to show the number of ARK ETFs trading the stock on

Indicator v4 Support & resistanceScreeners & dashboards
Updated 2 years ago · None stated GitHub repository of BZ-tracker
257 stars

This indicator overlays an intraday SPX and ES chart with a midnight session range, regular-trading-hours opening levels, and a VWAP projection box. It draws the midnight high, mid, and low, flags edge rejections within an absolute tolerance, and plots optiona

Indicator v6 TrendSupport & resistance
Updated 6 weeks ago · None stated GitHub repository of SPX Midnight Box + Snag Logic

Golden Bullet Strategy

by TradersPost
218 stars

The Golden Bullet Strategy is an overlay signal indicator based on Smart Money Concepts and time-based liquidity models. It identifies liquidity sweeps of a designated reference session range during a specified hunt window. It then confirms Market Structure Sh

Indicator v6 SMC / ICT / price actionSessions & time
Updated 3 months ago · None stated GitHub repository of Golden Bullet Strategy

More indicators

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers