Close Combination Lock Style - Visual Appeal
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © processingclouds

//@version=5
indicator("Close Prices - Combination Lock Style", "Stylish Price Change", overlay=true)

//  ----- Variable {
varip float up_lvl1 = na
varip float up_lvl2 = na
varip float up_lvl3 = na
varip float dn_lvl1 = na
varip float dn_lvl2 = na
varip float dn_lvl3 = na
varip float current = na
var int integral = na
var int fractional = na
var int isDecimal = na
var table combinations = na

//  Get Integral and Fractional part of close price
value = close
isDecimal := str.pos(str.tostring(value), ".")
integral := na(isDecimal)?str.length(str.tostring(value)):str.pos(str.tostring(value), ".")
fractional := na(isDecimal)?0:str.length(str.substring(str.tostring(value), isDecimal+1))

//  Inputs
theme = input.string(defval="Black Dials", title="Choose Theme", options=["Black Dials","Silver Dials"])
//  ----- }


//  ----- Table Creation Functions {
fillTable(tableId, col, row, value, lvl) =>
    backColor = color.new(theme == "Black Dials" ? color.black : color.white, lvl==0?0:lvl==1?40:60)
    foreColor = color.new(theme == "Black Dials" ? color.white : color.black, 0)
    table.cell(tableId, col, row, value,  0,3, foreColor, text.align_center, text.align_center, lvl==0?size.large:lvl==1?size.normal:size.small, backColor)

fillCell(value, row) =>
    if not na(value)
        valueAsString = str.tostring(value)
        for i = 0 to integral - 1
            fillTable(combinations, i, row, str.substring(valueAsString,i,i+1), row==2?0:(row==1 or row==3)?1:2)
        if isDecimal 
            fillTable(combinations, integral, row, ".", row==2?0:(row==1 or row==3)?1:2)
            if (str.length(valueAsString)-integral>0)
                for i = 0 to fractional - 1
                    fillTable(combinations, i+integral+1, row, str.substring(valueAsString,i+integral+1,i+integral+2), row==2?0:(row==1 or row==3)?1:2)
//  ----- }

//  ----- Main Logic {

//  Create New Table
if (barstate.isfirst)    
    combinations := table.new(position.middle_right, fractional + integral + 1 + 1, 5, border_color=theme=="Black Dials"?color.white:color.black, border_width =1)

//  Update all combination values
if (barstate.isrealtime or barstate.islast)
    value = close
    if (current != value)
        if value < current
            up_lvl3 := up_lvl2
            up_lvl2 := up_lvl1
            up_lvl1 := current
            if (dn_lvl1 == value)
                dn_lvl1 := dn_lvl2    
                dn_lvl2 := dn_lvl3
                dn_lvl3 := low
            table.cell(combinations, fractional + integral + 1, 2, "▼",  0, 3, color.red, text.align_center, text.align_center, size.large, color.new(theme == "Black Dials" ? color.black : color.silver, 0))
        if value > current
            dn_lvl3 := dn_lvl2
            dn_lvl2 := dn_lvl1
            dn_lvl1 := current
            if (up_lvl1 == value)
                up_lvl1 := up_lvl2
                up_lvl2 := up_lvl3
                up_lvl3 := high
            table.cell(combinations, fractional + integral + 1, 2, "▲",  0, 3, color.green, text.align_center, text.align_center, size.large, color.new(theme == "Black Dials" ? color.black : color.silver, 0))
    else
        table.cell(combinations, fractional + integral + 1, 2, "=",  0, 3, color.yellow, text.align_center, text.align_center, size.large, color.new(theme == "Black Dials" ? color.black : color.silver, 0))
    current := value
    
    //  Call function to fill table with values
    fillCell(up_lvl1, 0)
    fillCell(up_lvl2, 1)
    fillCell(current, 2)
    fillCell(dn_lvl1, 3)
    fillCell(dn_lvl2, 4)

//  ----- }


//  ----- Versions
//  02 19 22 - First Version published