BTC Performance Table / BTC Seasonality Visualization
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Nuggeler
// v1 12-02-2023
//@version=5

// What it does:
// This script visualizes Bitcoins "seasonality", in form of a coloured table (based on the idea from "BigBangTheory")
// The table shows which months do statistically perform better/worse in comparison to other months.

// How to use the script:
// Choose ticker "BLX" ("BraveNewCoin Liquid Index for Bitcoin"). It shows a complete BTC chart history.
// Set the chart time frame to weekly or daily. Table position on the screen and its colors are configurable. 

// Table explanation:
// Cells show gain (green) or loss (red) for each month, in comparison to the previous month. 
// The year column shows total gain (green) or loss (red) for that particular year. 
// Each value is presented as a rounded, single digit, percentage number.

// How it works:
// The script calculates the difference between each monthly and yearly open & close price, storing them inside arrays. 
// Then it populates the table, using those numbers and doing the cell coloring (yellow cell, if "no change" happened).  

// German Short-Description
// Prozentuale Übersicht in Tabellenform der monatlichen, sowie der jährlichen, Performance des Bitcoin (basierend auf der Idee von "BigBangTheory"). 
// Zwecks vollständiger Darstellung muss der Ticker "BLX" ("BraveNewCoin Liquid Index for Bitcoin") im weekly oder daily timeframe aktiv sein.

indicator('BTC performance table / seasonality', "BTC-PT", overlay=true)

// check time frame
var correct_TF = (timeframe.isweekly and timeframe.multiplier == 1) or (timeframe.isdaily and timeframe.multiplier == 1)

// show info label, when time frame check failed
if not correct_TF and barstate.islastconfirmedhistory
    labelText = "\n Use ticker BLX and set your time frame lower than monthly" + "\n or else you will not see full history and no results for " + str.tostring(year(time)) + "\n\nChart time frame is: " + timeframe.period   
    label.new(x=bar_index, y=close, style=label.style_label_left, color=color.new(#ff4800, 0), textcolor=color.rgb(0, 0, 0), size=size.large, text=labelText)

// table position options
table_pos=input.string(position.bottom_right, "Table Position", [position.top_left, position.top_center, position.top_right, position.middle_left, position.middle_center, position.middle_right, position.bottom_left, position.bottom_center, position.bottom_right])

// get monthly and yearly open and close prices
mo = request.security(syminfo.tickerid, 'M', open, gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_on)
mc = request.security(syminfo.tickerid, 'M', close, gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_on)
yo = request.security(syminfo.tickerid, '12M', open, gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_on)
yc = request.security(syminfo.tickerid, '12M', close, gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_on) 

// check for new month or year 
new_month = month(time) != month(time[1])
new_year = year(time) != year(time[1])

// calculate difference between open and close prices
mbar_pc = math.round(((mc - mo) * 100) / mo, precision = 2)
ybar_pc = math.round(((yc - yo) * 100) / yo, precision = 2)

// variables
cur_month_pc = 0.0
cur_year_pc = 0.0

cur_month_pc := barstate.islastconfirmedhistory ? 0.0 : +mbar_pc
cur_year_pc := barstate.islastconfirmedhistory ? 0.0 : +ybar_pc

var month_time = array.new_int(0)
var month_bh = array.new_float(0)
var year_time = array.new_int(0)
var year_bh = array.new_float(0)

// helper
last_computed = false

// populate arrays
if not na(cur_month_pc[1]) and (new_month or barstate.islastconfirmedhistory)
    if last_computed[1]
        array.pop(month_time)

    array.push(month_bh, cur_month_pc[1])
    array.push(month_time, time[1])

if not na(cur_year_pc[1]) and (new_year or barstate.islastconfirmedhistory)
    if last_computed[1]
        array.pop(year_time)

    array.push(year_bh, cur_year_pc[1])
    array.push(year_time, time[1])

last_computed := barstate.islast ? true : nz(last_computed[1])

// function for cell coloring
getCellColor(pc) =>
    if pc > 0
        input.color(color.rgb(75, 175, 75), "Positive Performance")
    else if pc < 0
        input.color(color.rgb(255, 100, 100), "Negative Performance")
    else
        color.rgb(175, 175, 75)

// initialize table    
var monthly_table = table(na)

// table layout 
if barstate.islastconfirmedhistory
    monthly_table := table.new(table_pos, columns=14,rows = array.size(year_time) + 1, bgcolor = input.color(color.rgb(221, 221, 221), "Background Color"), frame_color = input.color(color.rgb(0, 100, 0),"Frame color") , frame_width = 4, border_color = input.color( color.rgb(255, 255, 255), "Border Color"), border_width = 2)

    table.cell(monthly_table, 0, 0, '', bgcolor=#bbbbbb)
    table.cell(monthly_table, 1, 0, 'Jan', bgcolor=#bbbbbb)
    table.cell(monthly_table, 2, 0, 'Feb', bgcolor=#bbbbbb)
    table.cell(monthly_table, 3, 0, 'Mar', bgcolor=#bbbbbb)
    table.cell(monthly_table, 4, 0, 'Apr', bgcolor=#bbbbbb)
    table.cell(monthly_table, 5, 0, 'May', bgcolor=#bbbbbb)
    table.cell(monthly_table, 6, 0, 'Jun', bgcolor=#bbbbbb)
    table.cell(monthly_table, 7, 0, 'Jul', bgcolor=#bbbbbb)
    table.cell(monthly_table, 8, 0, 'Aug', bgcolor=#bbbbbb)
    table.cell(monthly_table, 9, 0, 'Sep', bgcolor=#bbbbbb)
    table.cell(monthly_table, 10, 0, 'Oct', bgcolor=#bbbbbb)
    table.cell(monthly_table, 11, 0, 'Nov', bgcolor=#bbbbbb)
    table.cell(monthly_table, 12, 0, 'Dec', bgcolor=#bbbbbb)
    table.cell(monthly_table, 13, 0, 'Year', bgcolor=#8f8f8f)

// read arrays and populate table
    for yi = 0 to array.size(year_bh) - 1
        table.cell(monthly_table, 0, yi + 1, str.tostring(year(array.get(year_time, yi))), bgcolor=#adadad)

        y_color = getCellColor(array.get(year_bh, yi))
        table.cell(monthly_table, 13, yi + 1, str.tostring(math.round(array.get(year_bh, yi), precision = 1)) + "%", bgcolor=y_color)

    for mi = 0 to array.size(month_time) - 1
        m_row = year(array.get(month_time, mi)) - year(array.get(year_time, 0)) + 1
        m_col = month(array.get(month_time, mi))
        m_color = getCellColor(array.get(month_bh, mi))

        table.cell(monthly_table, m_col, m_row, str.tostring(math.round(array.get(month_bh, mi), precision = 1)) + "%", bgcolor=m_color) 