Central Bank Dark Energy Tracer
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © kstrat2001

//@version=5
// Central Bank Dark Energy Tracer
indicator(title="CBDE Tracer", shorttitle = "CBDET", overlay = true)

string[] symbol_preset_arr = array.from("QQQ", "SPX", "BTC", "ES1", "NQ1")
var factor_idx = 0
for i = 0 to (array.size(symbol_preset_arr) == 0 ? na : array.size(symbol_preset_arr) - 1)
    if str.contains(syminfo.ticker, array.get(symbol_preset_arr, i))
        factor_idx := i

float[] scale_factors_cbs = array.from(1.805, 18.0, 100.0, 18.0, 60)
float[] price_offsets_cbs = array.from(-289.0, -1950.0, 100.0, -1920.0, -7000)
float[] scale_factors_fred = array.from(0.0152, 0.14, 100.0, 0.14, 0.6)
float[] price_offsets_fred = array.from(-160, -215, 100.0, -185, -6000)
float[] scale_factors_rrp = array.from(0.705, 6.2, 100.0, 6.2, 30)
float[] price_offsets_rrp = array.from(92.0, 2245.0, 100.0, 2275.0, 3700)

// Scale factors without input settings
scale_factor_cbs = array.get(scale_factors_cbs, factor_idx)
price_offset_cbs = array.get(price_offsets_cbs, factor_idx)

scale_factor_fred = array.get(scale_factors_fred, factor_idx)
price_offset_fred = array.get(price_offsets_fred, factor_idx)

scale_factor_rrp = array.get(scale_factors_rrp, factor_idx)
price_offset_rrp = array.get(price_offsets_rrp, factor_idx)

//Comment the above definitions, and use these input defs instead to tweak the offset using input settings, rather than having to code them
// scale_factor_cbs = input.float(18, "Scale Factor", -10000.0, 10000.0, 0.001, inline = "cbs", group = "Non-US Central Bank Settings")
// price_offset_cbs = input.float(-1950, "Price Offset", -10000.0, 10000.0, 0.01, inline = "cbs", group = "Non-US Central Bank Settings")

// scale_factor_fred = input.float(0.11,"Scale Factor", -10000.0, 10000.0, 0.001, inline = "fed", group = "US Central Bank Settings")
// price_offset_fred = input.float(2080, "Pricce Offset", -10000.0, 10000.0, 5, inline = "fed", group = "US Central Bank Settings")

// scale_factor_rrp = input.float(6.2,"Scale Factor", -10000.0, 10000.0, 0.001, inline = "rrp", group = "RRP")
// price_offset_rrp = input.float(2245, "Pricce Offset", -10000.0, 10000.0, 5, inline = "rrp", group = "RRP")

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

scale_factor_avg = input.float(1.0, "Scale Factor", -10000.0, 10000.0, 0.1, inline = "avg", group = "CBDET Signal Settings")
price_offset_avg = input.float(0, "Price Offset", -100000, 100000, 5, inline = "avg", group = "CBDET Signal Settings")

cbdet_avg_use_ema = input.bool(false, "Use EMA", "Draw CBDET signal with EMA to smooth out price signal", inline = "EMA Line", group = "CBDET Signal Settings")
cbdet_avg_ema_length = input.int(12, " with length:", 1, 1000, inline = "EMA Line", group = "CBDET Signal Settings")

cbdet_sma_length = input.int(200, " SMA length:", tooltip = "simple moving average using current chart time frame", minval = 1, maxval = 1000, inline = "SMA Line", group = "CBDET Signal Settings")

chart_settings_group_name = "Chart Settings"
show_divergence_bg_color_indicator = input.bool(false, "Price Divergence Indicator", group = chart_settings_group_name)
price_higher_bg_color = input.color(color.red, "Price Higher BG Color", group = chart_settings_group_name)
price_lower_bg_color = input.color(color.green, "Price Lower BG Color", group = chart_settings_group_name)

fed_bal_sheet_group_name = "Include Fed Balance Sheet Items"
include_wshoicl = input.bool(true, "Inflation Compensation (WSHOICL)", "Include Fed 'Inflation Compensation' (WSHOICL) from assets portion of balance sheet", group = fed_bal_sheet_group_name)
include_wupsho  = input.bool(true, "Unamortized Premiums (WUPSHO)", "Include Fed 'Unamortized Premiums' (WUPSHO) from assets portion of balance sheet", group = fed_bal_sheet_group_name)
include_wlcfll  = input.bool(true, "Loans (WLCFLL)", "Include Fed 'Loans' (WLCFLL) from assets portion of balance sheet", group = fed_bal_sheet_group_name)
include_wlad    = input.bool(true, "Other/Remittances (WLAD)", "Include Fed remittances (WLAD) from liabilities portion of balance sheet", group = fed_bal_sheet_group_name)

tga_override    = input.bool(false, "TGA Balance Override (Billions)", "Overrides the current balance of the Treasury General Accout.  This allows use of the daily balance data rather than waiting for the weekly data to update.", inline = "tga_override", group = fed_bal_sheet_group_name)
tga_override_value = input.float(0, "", 0, 10000000000, 1, inline = "tga_override", group = fed_bal_sheet_group_name)

table_settings_str = "Table Settings"
show_info_table = input.bool(false, "Show Liquidity Table", group = table_settings_str)
show_fred_table = input.bool(false, "Show FRED Table", group = table_settings_str)
show_assets_table = input.bool(false, "Show CB Assets Table", group = table_settings_str)

// FOREIGN CENTRAL BANK ASSETS
currency_japan       = request.security("FRED:JPNASSETS", "D", close)
currency_china       = request.security("CNCBBS         ", "D", close)
currency_uk          = request.security("GBCBBS        ", "W", close)
currency_ecb         = request.security("ECBASSETSW    ", "D", close)
currency_snb         = request.security("CHCBBS        ", "D", close)
currency_krw         = request.security("ECONOMICS:KRCBBS", "D", close)
currency_cad         = request.security("CACBBS", "D", close)

// FOREIGN CENTRAL BANK ASSETS
line_japan       = request.security("FRED:JPNASSETS * FX_IDC:JPYUSD", "D", close, currency = currency.USD)
line_china       = request.security("CNCBBS          * FX_IDC:CNYUSD", "D", close, currency = currency.USD)
line_uk          = request.security("GBCBBS         * FX:GBPUSD",     "D", close, currency = currency.USD)
line_ecb         = request.security("ECBASSETSW     * FX:EURUSD",     "D", close, currency = currency.USD)
line_snb         = request.security("CHCBBS",                         "D", close, currency = currency.USD)
line_krc         = request.security("ECONOMICS:KRCBBS*FX_IDC:KRWUSD", "D", close, currency = currency.USD)
line_cad         = request.security("CACBBS         * CADUSD",        "D", close, currency = currency.USD)

line_fed_walcl   = request.security("FRED:WALCL",                     "D", close, currency = currency.USD)
line_fed_tga     = request.security("WDTGAL",                        "D", close, currency = currency.USD)
line_wresbal     = request.security("FRED:WRESBAL",                   "D", close, currency = currency.USD)
line_rrp         = request.security("RRPONTSYD",                      "D", close, currency = currency.USD)

// Optional factors on Fed balance sheet
line_fed_wshoicl   = request.security("WSHOICL",                        "D", close, currency = currency.USD)
line_fed_wupsho    = request.security("WUPSHO",                         "D", close, currency = currency.USD)
line_fed_wlcfll    = request.security("WLCFLL",                         "D", close, currency = currency.USD)
line_fed_wlad      = request.security("WLAD",                           "D", close, currency = currency.USD)

// HELPER METHODS ------------------------------------------------------------------------
billions_str(value, currency_symbol = '$') =>
    str.format("{0}{1, number, #,###.#} B",currency_symbol, value/1000000000)

oscillator_gradient_color(signal, high_color, low_color) =>
    signal_max = ta.max(signal)
    signal_min = ta.min(signal)
    bull_color = color.from_gradient(signal, 0, signal_max, color.new(high_color, 90), color.new(high_color, 20))
    bear_color = color.from_gradient(signal, signal_min, 0, color.new(low_color,   20), color.new(low_color, 90))
    osc_color  = signal < 0 ? bear_color : bull_color

// Currencies
cb_asstes = line_japan + line_china + line_uk + line_ecb + line_snb + line_krc + line_cad
cb_scaled = ((cb_asstes / 70000000000) * scale_factor_cbs) + price_offset_cbs

// Plot the raw signal for all non-US CB assets in USD
plot(cb_asstes/(1000*1000*1000), "Non-US Liquidity in Blns USD", color.olive, 1, trackprice = true, display = display.none)

// Plot the scaled version of non-UDS assets
plot(cb_scaled, "Non-US Liquidity (scaled)", color.white, 1, trackprice = true, display = display.none)

// FRED
// FED optional assets lines
wshoicl_final = (include_wshoicl ? 0 : line_fed_wshoicl)
wupsho_final  = (include_wupsho ? 0 : line_fed_wupsho)
wlcfll_final  = (include_wlcfll ? 0 : line_fed_wlcfll)
assets_subtractions = wlcfll_final + wupsho_final + wshoicl_final
assets_total = line_fed_walcl - assets_subtractions

// FED liabilities
tga_final = barstate.islast and tga_override ? tga_override_value * 1000000000 : line_fed_tga
wlad_final = (include_wlad ? line_fed_wlad : 0)
liabilities_total = tga_final + line_rrp + wlad_final

mid_wb_fred = assets_total - liabilities_total
fred_scaled = (mid_wb_fred / 200000000) * scale_factor_fred + price_offset_fred
plot(fred_scaled, "US Fed Liquidity (scaled)", color.lime, 1, trackprice = true, display = display.none)

rrp_scaled = (1/line_rrp * 2000000000000 * 307) * scale_factor_rrp + price_offset_rrp
plot(rrp_scaled, "Reverse Repo Liquidity (scaled)", color.orange, 1, trackprice = true, display = display.none)

aggregate_signal = ((fred_scaled + cb_scaled) * 0.5 * scale_factor_avg) + price_offset_avg
aggregate_signal_ema = ta.ema(aggregate_signal, cbdet_avg_ema_length)

plot(cbdet_avg_use_ema ? aggregate_signal_ema : aggregate_signal, "Combined CBDET Formula", color.purple,  linewidth = 2, trackprice = true)

// DMA signal
aggregate_signal_sma = ta.sma(aggregate_signal, cbdet_sma_length)
plot(aggregate_signal_sma, "CBDET SMA", color.rgb(5, 203, 243),  linewidth = 1, trackprice = false, display = display.none)

bg_color = chart.bg_color
divergence_signal = close - (cbdet_avg_use_ema ? aggregate_signal_ema : aggregate_signal)
if(show_divergence_bg_color_indicator)
    //bg_color := (aggregate_signal < close) ? price_higher_bg_color : price_lower_bg_color
    bg_color := oscillator_gradient_color(divergence_signal, price_higher_bg_color, price_lower_bg_color)
    
bgcolor(bg_color, display = show_divergence_bg_color_indicator ? display.all : display.none)

create_info_column(table, col, usd_signal, color_positive, color_negative, label_str, inverted) =>
    delta = (usd_signal[0] - usd_signal[1]) * (inverted ? -1.0 : 1.0)
    table.cell(table, col, 0, label_str,                    bgcolor = color.white)
    table.cell(table, col, 1, billions_str(usd_signal[0]),  bgcolor = color.white)
    table.cell(table, col, 2, billions_str(delta),          bgcolor = delta > 0 ? color.green : color.orange)

// Draw info table
if show_info_table
    num_cols = 10
    num_rows = 3

    var table info_table = table.new(position.bottom_right, num_cols, num_rows, frame_color = color.gray, bgcolor = color.gray, border_width = 1, frame_width = 1, border_color = color.black)
    
    // Header
    table.cell(info_table, 0, 1, "Total",           bgcolor = color.white)
    table.cell(info_table, 0, 2, "1 bar delta",        bgcolor = color.white)

    create_info_column(info_table, 1, line_japan, color.green, color.orange, "JPY Assets", false)
    create_info_column(info_table, 2, line_china, color.green, color.orange, "CNY Assets", false)
    create_info_column(info_table, 3, line_uk, color.green, color.orange, "UK Assets", false)
    create_info_column(info_table, 4, line_ecb, color.green, color.orange, "ECB Assets", false)
    create_info_column(info_table, 5, line_snb, color.green, color.orange, "SNB Assets", false)
    create_info_column(info_table, 6, line_krc, color.green, color.orange, "KRC Assets", false)
    create_info_column(info_table, 7, line_cad, color.green, color.orange, "CAD Assets", false)
    create_info_column(info_table, 8, cb_asstes, color.green, color.orange, "Non-US Total", false)
    create_info_column(info_table, 9, mid_wb_fred, color.green, color.orange, "FRED Formula", false)

// Draw info table
if show_fred_table
    num_cols = 5
    num_rows = 3

    var table fred_table = table.new(position.bottom_left, num_cols, num_rows, frame_color = color.gray, bgcolor = color.gray, border_width = 1, frame_width = 1, border_color = color.black)
    
    // Header
    table.cell(fred_table, 0, 1, "Total",           bgcolor = color.white)
    table.cell(fred_table, 0, 2, "1 bar delta",        bgcolor = color.white)
    create_info_column(fred_table, 1, line_rrp, color.green, color.orange, "FRED RRP", true)
    create_info_column(fred_table, 2, line_wresbal, color.green, color.orange, "WRESBAL", false)
    create_info_column(fred_table, 3, line_fed_tga, color.green, color.orange, "FRED TGA", true)
    create_info_column(fred_table, 4, line_fed_walcl, color.green, color.orange, "FRED WALCL", false)

create_currency_info_column(table, col, currency_signal, color_positive, color_negative, label_str, currency_symbol = '$') =>
    delta = (currency_signal[0] - currency_signal[1])
    table.cell(table, col, 0, label_str,                         bgcolor = color.white)
    table.cell(table, col, 1, billions_str(currency_signal[0], currency_symbol),  bgcolor = color.white)
    table.cell(table, col, 2, billions_str(delta, currency_symbol),               bgcolor = delta > 0 ? color.green : color.orange)
    
// Render currency table
if show_assets_table
    num_cols = 8
    num_rows = 3

    var table info_table = table.new(position.top_right, num_cols, num_rows, frame_color = color.gray, bgcolor = color.gray, border_width = 1, frame_width = 1, border_color = color.black)
    
    // Header
    table.cell(info_table, 0, 1, "Total",           bgcolor = color.white)
    table.cell(info_table, 0, 2, "1 bar chng",        bgcolor = color.white)

    create_currency_info_column(info_table, 1, currency_japan, color.green, color.orange, "JPY", currency_symbol = "¥")
    create_currency_info_column(info_table, 2, currency_china, color.green, color.orange, "CNY", currency_symbol = "¥")
    create_currency_info_column(info_table, 3, currency_uk, color.green, color.orange, "GBP", currency_symbol = "£")
    create_currency_info_column(info_table, 4, currency_ecb, color.green, color.orange, "Euro", currency_symbol = "€")
    create_currency_info_column(info_table, 5, currency_snb, color.green, color.orange, "SNB", currency_symbol = "$")
    create_currency_info_column(info_table, 6, currency_krw, color.green, color.orange, "KRW", currency_symbol = "₩")
    create_currency_info_column(info_table, 7, currency_cad, color.green, color.orange, "CAD", currency_symbol = "$")

