OPEN-SOURCE SCRIPT
تم تحديثه

Universal Ratio Trend Matrix [InvestorUnknown]

The Universal Ratio Trend Matrix is designed for trend analysis on asset/asset ratios, supporting up to 40 different assets. Its primary purpose is to help identify which assets are outperforming others within a selection, providing a broad overview of market trends through a matrix of ratios. The indicator automatically expands the matrix based on the number of assets chosen, simplifying the process of comparing multiple assets in terms of performance.

Key features include the ability to choose from a narrow selection of indicators to perform the ratio trend analysis, allowing users to apply well-defined metrics to their comparison.

Drawback: Due to the computational intensity involved in calculating ratios across many assets, the indicator has a limitation related to loading speed. TradingView has time limits for calculations, and for users on the basic (free) plan, this could result in frequent errors due to exceeded time limits. To use the indicator effectively, users with any paid plans should run it on timeframes higher than 8h (the lowest timeframe on which it managed to load with 40 assets), as lower timeframes may not reliably load.

Indicators:
  • RSI_raw: Simple function to calculate the Relative Strength Index (RSI) of a source (asset price).
  • RSI_sma: Calculates RSI followed by a Simple Moving Average (SMA).
  • RSI_ema: Calculates RSI followed by an Exponential Moving Average (EMA).
  • CCI: Calculates the Commodity Channel Index (CCI).
  • Fisher: Implements the Fisher Transform to normalize prices.


Utility Functions:
  • f_remove_exchange_name: Strips the exchange name from asset tickers (e.g., "INDEX:BTCUSD" to "BTCUSD").


f_remove_exchange_name(simple string name) =>
    string[] parts      = str.split(name, ":")
    string result       = array.size(parts) > 1 ? array.get(parts, 1) : name
    result


  • f_get_price: Retrieves the closing price of a given asset ticker using request.security().
  • f_constant_src: Checks if the source data is constant by comparing multiple consecutive values.


لقطة

Inputs:
  • General settings allow users to select the number of tickers for analysis (used_assets) and choose the trend indicator (RSI, CCI, Fisher, etc.).
  • Table settings customize how trend scores are displayed in terms of text size, header visibility, highlighting options, and top-performing asset identification.
  • The script includes inputs for up to 40 assets, allowing the user to select various cryptocurrencies (e.g., BTCUSD, ETHUSD, SOLUSD) or other assets for trend analysis.


Price Arrays:
  • Price values for each asset are stored in variables (price_a1 to price_a40) initialized as na. These prices are updated only for the number of assets specified by the user (used_assets).
  • Trend scores for each asset are stored in separate arrays


// declare price variables as "na"
var float price_a1  = na,   var float price_a2  = na,   var float price_a3  = na,   var float price_a4  = na,   var float price_a5  = na
var float price_a6  = na,   var float price_a7  = na,   var float price_a8  = na,   var float price_a9  = na,   var float price_a10 = na
var float price_a11 = na,   var float price_a12 = na,   var float price_a13 = na,   var float price_a14 = na,   var float price_a15 = na
var float price_a16 = na,   var float price_a17 = na,   var float price_a18 = na,   var float price_a19 = na,   var float price_a20 = na
var float price_a21 = na,   var float price_a22 = na,   var float price_a23 = na,   var float price_a24 = na,   var float price_a25 = na
var float price_a26 = na,   var float price_a27 = na,   var float price_a28 = na,   var float price_a29 = na,   var float price_a30 = na
var float price_a31 = na,   var float price_a32 = na,   var float price_a33 = na,   var float price_a34 = na,   var float price_a35 = na
var float price_a36 = na,   var float price_a37 = na,   var float price_a38 = na,   var float price_a39 = na,   var float price_a40 = na

// create "empty" arrays to store trend scores
var a1_array  = array.new_int(40, 0),      var a2_array  = array.new_int(40, 0),    var a3_array  = array.new_int(40, 0),      var a4_array  = array.new_int(40, 0)
var a5_array  = array.new_int(40, 0),      var a6_array  = array.new_int(40, 0),    var a7_array  = array.new_int(40, 0),      var a8_array  = array.new_int(40, 0)
var a9_array  = array.new_int(40, 0),      var a10_array = array.new_int(40, 0),    var a11_array = array.new_int(40, 0),      var a12_array = array.new_int(40, 0)
var a13_array = array.new_int(40, 0),      var a14_array = array.new_int(40, 0),    var a15_array = array.new_int(40, 0),      var a16_array = array.new_int(40, 0)
var a17_array = array.new_int(40, 0),      var a18_array = array.new_int(40, 0),    var a19_array = array.new_int(40, 0),      var a20_array = array.new_int(40, 0)
var a21_array = array.new_int(40, 0),      var a22_array = array.new_int(40, 0),    var a23_array = array.new_int(40, 0),      var a24_array = array.new_int(40, 0)
var a25_array = array.new_int(40, 0),      var a26_array = array.new_int(40, 0),    var a27_array = array.new_int(40, 0),      var a28_array = array.new_int(40, 0)
var a29_array = array.new_int(40, 0),      var a30_array = array.new_int(40, 0),    var a31_array = array.new_int(40, 0),      var a32_array = array.new_int(40, 0)
var a33_array = array.new_int(40, 0),      var a34_array = array.new_int(40, 0),    var a35_array = array.new_int(40, 0),      var a36_array = array.new_int(40, 0)
var a37_array = array.new_int(40, 0),      var a38_array = array.new_int(40, 0),    var a39_array = array.new_int(40, 0),      var a40_array = array.new_int(40, 0)

f_get_price(simple string ticker) =>
    request.security(ticker, "", close)

// Prices for each USED asset
f_get_asset_price(asset_number, ticker) =>
    if (used_assets >= asset_number)
        f_get_price(ticker)
    else
        na

// overwrite empty variables with the prices if "used_assets" is greater or equal to the asset number
if barstate.isconfirmed // use barstate.isconfirmed to avoid "na prices" and calculation errors that result in empty cells in the table
    price_a1  := f_get_asset_price(1,  asset1),   price_a2  := f_get_asset_price(2,  asset2),   price_a3  := f_get_asset_price(3,  asset3),   price_a4  := f_get_asset_price(4,  asset4)
    price_a5  := f_get_asset_price(5,  asset5),   price_a6  := f_get_asset_price(6,  asset6),   price_a7  := f_get_asset_price(7,  asset7),   price_a8  := f_get_asset_price(8,  asset8)
    price_a9  := f_get_asset_price(9,  asset9),   price_a10 := f_get_asset_price(10, asset10),  price_a11 := f_get_asset_price(11, asset11),  price_a12 := f_get_asset_price(12, asset12)
    price_a13 := f_get_asset_price(13, asset13),  price_a14 := f_get_asset_price(14, asset14),  price_a15 := f_get_asset_price(15, asset15),  price_a16 := f_get_asset_price(16, asset16)
    price_a17 := f_get_asset_price(17, asset17),  price_a18 := f_get_asset_price(18, asset18),  price_a19 := f_get_asset_price(19, asset19),  price_a20 := f_get_asset_price(20, asset20)
    price_a21 := f_get_asset_price(21, asset21),  price_a22 := f_get_asset_price(22, asset22),  price_a23 := f_get_asset_price(23, asset23),  price_a24 := f_get_asset_price(24, asset24)
    price_a25 := f_get_asset_price(25, asset25),  price_a26 := f_get_asset_price(26, asset26),  price_a27 := f_get_asset_price(27, asset27),  price_a28 := f_get_asset_price(28, asset28)
    price_a29 := f_get_asset_price(29, asset29),  price_a30 := f_get_asset_price(30, asset30),  price_a31 := f_get_asset_price(31, asset31),  price_a32 := f_get_asset_price(32, asset32)
    price_a33 := f_get_asset_price(33, asset33),  price_a34 := f_get_asset_price(34, asset34),  price_a35 := f_get_asset_price(35, asset35),  price_a36 := f_get_asset_price(36, asset36)
    price_a37 := f_get_asset_price(37, asset37),  price_a38 := f_get_asset_price(38, asset38),  price_a39 := f_get_asset_price(39, asset39),  price_a40 := f_get_asset_price(40, asset40)


Universal Indicator Calculation (f_calc_score):
  • This function allows switching between different trend indicators (RSI, CCI, Fisher) for flexibility.
  • It uses a switch-case structure to calculate the indicator score, where a positive trend is denoted by 1 and a negative trend by 0. Each indicator has its own logic to determine whether the asset is trending up or down.


// use switch to allow "universality" in indicator selection
f_calc_score(source, trend_indicator, int_1, int_2) =>
    int score = na

    if (not f_constant_src(source)) and source > 0.0 // Skip if you are using the same assets for ratio (for example BTC/BTC)
        x = switch trend_indicator
            "RSI (Raw)" => RSI_raw(source, int_1)
            "RSI (SMA)" => RSI_sma(source, int_1, int_2)
            "RSI (EMA)" => RSI_ema(source, int_1, int_2)
            "CCI"       => CCI(source, int_1)
            "Fisher"    => Fisher(source, int_1)

        y = switch trend_indicator
            "RSI (Raw)" => x > 50 ? 1 : 0
            "RSI (SMA)" => x > 50 ? 1 : 0
            "RSI (EMA)" => x > 50 ? 1 : 0
            "CCI"       => x > 0 ? 1 : 0
            "Fisher"    => x > x[1] ? 1 : 0

        score := y
    else
        score := 0

    score


Array Setting Function (f_array_set):
  • This function populates an array with scores calculated for each asset based on a base price (p_base) divided by the prices of the individual assets.
  • It processes multiple assets (up to 40), calling the f_calc_score function for each.


// function to set values into the arrays
f_array_set(a_array, p_base) =>

    array.set(a_array,  0, f_calc_score(p_base / price_a1,  trend_indicator, int_1, int_2))
    array.set(a_array,  1, f_calc_score(p_base / price_a2,  trend_indicator, int_1, int_2))
    array.set(a_array,  2, f_calc_score(p_base / price_a3,  trend_indicator, int_1, int_2))
    array.set(a_array,  3, f_calc_score(p_base / price_a4,  trend_indicator, int_1, int_2))
    array.set(a_array,  4, f_calc_score(p_base / price_a5,  trend_indicator, int_1, int_2))
    array.set(a_array,  5, f_calc_score(p_base / price_a6,  trend_indicator, int_1, int_2))
    array.set(a_array,  6, f_calc_score(p_base / price_a7,  trend_indicator, int_1, int_2))
    array.set(a_array,  7, f_calc_score(p_base / price_a8,  trend_indicator, int_1, int_2))
    array.set(a_array,  8, f_calc_score(p_base / price_a9,  trend_indicator, int_1, int_2))
    array.set(a_array,  9, f_calc_score(p_base / price_a10, trend_indicator, int_1, int_2))
    array.set(a_array, 10, f_calc_score(p_base / price_a11, trend_indicator, int_1, int_2))
    array.set(a_array, 11, f_calc_score(p_base / price_a12, trend_indicator, int_1, int_2))
    array.set(a_array, 12, f_calc_score(p_base / price_a13, trend_indicator, int_1, int_2))
    array.set(a_array, 13, f_calc_score(p_base / price_a14, trend_indicator, int_1, int_2))
    array.set(a_array, 14, f_calc_score(p_base / price_a15, trend_indicator, int_1, int_2))
    array.set(a_array, 15, f_calc_score(p_base / price_a16, trend_indicator, int_1, int_2))
    array.set(a_array, 16, f_calc_score(p_base / price_a17, trend_indicator, int_1, int_2))
    array.set(a_array, 17, f_calc_score(p_base / price_a18, trend_indicator, int_1, int_2))
    array.set(a_array, 18, f_calc_score(p_base / price_a19, trend_indicator, int_1, int_2))
    array.set(a_array, 19, f_calc_score(p_base / price_a20, trend_indicator, int_1, int_2))
    array.set(a_array, 20, f_calc_score(p_base / price_a21, trend_indicator, int_1, int_2))
    array.set(a_array, 21, f_calc_score(p_base / price_a22, trend_indicator, int_1, int_2))
    array.set(a_array, 22, f_calc_score(p_base / price_a23, trend_indicator, int_1, int_2))
    array.set(a_array, 23, f_calc_score(p_base / price_a24, trend_indicator, int_1, int_2))
    array.set(a_array, 24, f_calc_score(p_base / price_a25, trend_indicator, int_1, int_2))
    array.set(a_array, 25, f_calc_score(p_base / price_a26, trend_indicator, int_1, int_2))
    array.set(a_array, 26, f_calc_score(p_base / price_a27, trend_indicator, int_1, int_2))
    array.set(a_array, 27, f_calc_score(p_base / price_a28, trend_indicator, int_1, int_2))
    array.set(a_array, 28, f_calc_score(p_base / price_a29, trend_indicator, int_1, int_2))
    array.set(a_array, 29, f_calc_score(p_base / price_a30, trend_indicator, int_1, int_2))
    array.set(a_array, 30, f_calc_score(p_base / price_a31, trend_indicator, int_1, int_2))
    array.set(a_array, 31, f_calc_score(p_base / price_a32, trend_indicator, int_1, int_2))
    array.set(a_array, 32, f_calc_score(p_base / price_a33, trend_indicator, int_1, int_2))
    array.set(a_array, 33, f_calc_score(p_base / price_a34, trend_indicator, int_1, int_2))
    array.set(a_array, 34, f_calc_score(p_base / price_a35, trend_indicator, int_1, int_2))
    array.set(a_array, 35, f_calc_score(p_base / price_a36, trend_indicator, int_1, int_2))
    array.set(a_array, 36, f_calc_score(p_base / price_a37, trend_indicator, int_1, int_2))
    array.set(a_array, 37, f_calc_score(p_base / price_a38, trend_indicator, int_1, int_2))
    array.set(a_array, 38, f_calc_score(p_base / price_a39, trend_indicator, int_1, int_2))
    array.set(a_array, 39, f_calc_score(p_base / price_a40, trend_indicator, int_1, int_2))

    a_array


Conditional Array Setting (f_arrayset):
  • This function checks if the number of used assets is greater than or equal to a specified number before populating the arrays.


// only set values into arrays for USED assets
f_arrayset(asset_number, a_array, p_base) =>
    if (used_assets >= asset_number)
        f_array_set(a_array, p_base)
    else
        na


Main Logic
  • The main logic initializes arrays to store scores for each asset. Each array corresponds to one asset's performance score.
  • Setting Trend Values: The code calls f_arrayset for each asset, populating the respective arrays with calculated scores based on the asset prices.
  • Combining Arrays: A combined_array is created to hold all the scores from individual asset arrays. This array facilitates further analysis, allowing for an overview of the performance scores of all assets at once.


// create a combined array (work-around since pinescript doesn't support having array of arrays)
var combined_array = array.new_int(40 * 40, 0)

if barstate.islast
    for i = 0 to 39
        array.set(combined_array, i,             array.get(a1_array,  i))
        array.set(combined_array, i + (40 * 1),  array.get(a2_array,  i))
        array.set(combined_array, i + (40 * 2),  array.get(a3_array,  i))
        array.set(combined_array, i + (40 * 3),  array.get(a4_array,  i))
        array.set(combined_array, i + (40 * 4),  array.get(a5_array,  i))
        array.set(combined_array, i + (40 * 5),  array.get(a6_array,  i))
        array.set(combined_array, i + (40 * 6),  array.get(a7_array,  i))
        array.set(combined_array, i + (40 * 7),  array.get(a8_array,  i))
        array.set(combined_array, i + (40 * 8),  array.get(a9_array,  i))
        array.set(combined_array, i + (40 * 9),  array.get(a10_array, i))
        array.set(combined_array, i + (40 * 10), array.get(a11_array, i))
        array.set(combined_array, i + (40 * 11), array.get(a12_array, i))
        array.set(combined_array, i + (40 * 12), array.get(a13_array, i))
        array.set(combined_array, i + (40 * 13), array.get(a14_array, i))
        array.set(combined_array, i + (40 * 14), array.get(a15_array, i))
        array.set(combined_array, i + (40 * 15), array.get(a16_array, i))
        array.set(combined_array, i + (40 * 16), array.get(a17_array, i))
        array.set(combined_array, i + (40 * 17), array.get(a18_array, i))
        array.set(combined_array, i + (40 * 18), array.get(a19_array, i))
        array.set(combined_array, i + (40 * 19), array.get(a20_array, i))
        array.set(combined_array, i + (40 * 20), array.get(a21_array, i))
        array.set(combined_array, i + (40 * 21), array.get(a22_array, i))
        array.set(combined_array, i + (40 * 22), array.get(a23_array, i))
        array.set(combined_array, i + (40 * 23), array.get(a24_array, i))
        array.set(combined_array, i + (40 * 24), array.get(a25_array, i))
        array.set(combined_array, i + (40 * 25), array.get(a26_array, i))
        array.set(combined_array, i + (40 * 26), array.get(a27_array, i))
        array.set(combined_array, i + (40 * 27), array.get(a28_array, i))
        array.set(combined_array, i + (40 * 28), array.get(a29_array, i))
        array.set(combined_array, i + (40 * 29), array.get(a30_array, i))
        array.set(combined_array, i + (40 * 30), array.get(a31_array, i))
        array.set(combined_array, i + (40 * 31), array.get(a32_array, i))
        array.set(combined_array, i + (40 * 32), array.get(a33_array, i))
        array.set(combined_array, i + (40 * 33), array.get(a34_array, i))
        array.set(combined_array, i + (40 * 34), array.get(a35_array, i))
        array.set(combined_array, i + (40 * 35), array.get(a36_array, i))
        array.set(combined_array, i + (40 * 36), array.get(a37_array, i))
        array.set(combined_array, i + (40 * 37), array.get(a38_array, i))
        array.set(combined_array, i + (40 * 38), array.get(a39_array, i))
        array.set(combined_array, i + (40 * 39), array.get(a40_array, i))



  • Calculating Sums: A separate array_sums is created to store the total score for each asset by summing the values of their respective score arrays. This allows for easy comparison of overall performance.
  • Ranking Assets: The final part of the code ranks the assets based on their total scores stored in array_sums. It assigns a rank to each asset, where the asset with the highest score receives the highest rank.


// create array for asset RANK based on array.sum
var ranks = array.new_int(used_assets, 0)

// for loop that calculates the rank of each asset
if barstate.islast
    for i = 0 to (used_assets - 1)
        int rank = 1
        for x = 0 to (used_assets - 1)
            if i != x
                if array.get(array_sums, i) < array.get(array_sums, x)
                    rank := rank + 1
        array.set(ranks, i, rank)


Dynamic Table Creation
  • Initialization: The table is initialized with a base structure that includes headers for asset names, scores, and ranks. The headers are set to remain constant, ensuring clarity for users as they interpret the displayed data.
  • Data Population: As scores are calculated for each asset, the corresponding values are dynamically inserted into the table. This is achieved through a loop that iterates over the scores and ranks stored in the combined_array and array_sums, respectively.


Automatic Extending Mechanism
  • Variable Asset Count: The code checks the number of assets defined by the user. Instead of hardcoding the number of rows in the table, it uses a variable to determine the extent of the data that needs to be displayed. This allows the table to expand or contract based on the number of assets being analyzed.
  • Dynamic Row Generation: Within the loop that populates the table, the code appends new rows for each asset based on the current asset count. The structure of each row includes the asset name, its score, and its rank, ensuring that the table remains consistent regardless of how many assets are involved.


// Automatically extending table based on the number of used assets
var table table = table.new(position.bottom_center, 50, 50, color.new(color.black, 100), color.white, 3, color.white, 1)

if barstate.islast
    if not hide_head
        table.cell(table, 0, 0, "Universal Ratio Trend Matrix", text_color = color.white, bgcolor = #010c3b, text_size = fontSize)
        table.merge_cells(table, 0, 0, used_assets + 3, 0)

    if not hide_inps
        table.cell(table, 0, 1,
         text = "Inputs: You are using " + str.tostring(trend_indicator) + ", which takes: " + str.tostring(f_get_input(trend_indicator)),
         text_color = color.white, text_size = fontSize), table.merge_cells(table, 0, 1, used_assets + 3, 1)

    table.cell(table, 0, 2, "Assets", text_color = color.white, text_size = fontSize, bgcolor = #010c3b)

    for x = 0 to (used_assets - 1)
        table.cell(table, x + 1, 2, text = str.tostring(array.get(assets, x)), text_color = color.white, bgcolor = #010c3b, text_size = fontSize)
        table.cell(table, 0, x + 3, text = str.tostring(array.get(assets, x)), text_color = color.white, bgcolor = f_asset_col(array.get(ranks, x)), text_size = fontSize)

    for r = 0 to (used_assets - 1)
        for c = 0 to (used_assets - 1)
            table.cell(table, c + 1, r + 3, text = str.tostring(array.get(combined_array, c + (r * 40))),
             text_color = hl_type == "Text" ? f_get_col(array.get(combined_array, c + (r * 40))) : color.white, text_size = fontSize,
             bgcolor = hl_type == "Background" ? f_get_col(array.get(combined_array, c + (r * 40))) : na)

    for x = 0 to (used_assets - 1)
        table.cell(table, x + 1, x + 3, "", bgcolor = #010c3b)

    table.cell(table, used_assets + 1, 2, "", bgcolor = #010c3b)

    for x = 0 to (used_assets - 1)
        table.cell(table, used_assets + 1, x + 3, "==>", text_color = color.white)

    table.cell(table, used_assets + 2, 2, "SUM", text_color = color.white, text_size = fontSize, bgcolor = #010c3b)
    table.cell(table, used_assets + 3, 2, "RANK", text_color = color.white, text_size = fontSize, bgcolor = #010c3b)
    for x = 0 to (used_assets - 1)
        table.cell(table, used_assets + 2, x + 3,
         text = str.tostring(array.get(array_sums, x)),
         text_color = color.white, text_size = fontSize,
         bgcolor = f_highlight_sum(array.get(array_sums, x), array.get(ranks, x)))
        table.cell(table, used_assets + 3, x + 3,
         text = str.tostring(array.get(ranks, x)),
         text_color = color.white, text_size = fontSize,
         bgcolor = f_highlight_rank(array.get(ranks, x)))
ملاحظات الأخبار
Add option to Display Summary of the Ratio Trend Matrix.

simple string table_mode        = input.string("FULL", "Table Mode", options = ["FULL", "Summary"], group = G2)


لقطة
Summary of original Table on the left, Summary of top ranked assets on the right (manipulated with "Highlight Top Assets" input.

إخلاء المسؤولية

لا يُقصد بالمعلومات والمنشورات أن تكون، أو تشكل، أي نصيحة مالية أو استثمارية أو تجارية أو أنواع أخرى من النصائح أو التوصيات المقدمة أو المعتمدة من TradingView. اقرأ المزيد في شروط الاستخدام.