OPEN-SOURCE SCRIPT
Telah dikemas kini

Universal Ratio Trend Matrix [InvestorUnknown]

9 766
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").


Pine Script®
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.


syot kilat

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


Pine Script®
// 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.


Pine Script®
// 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.


Pine Script®
// 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.


Pine Script®
// 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.


Pine Script®
// 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.


Pine Script®
// 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.


Pine Script®
// 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)))
Nota Keluaran
Add option to Display Summary of the Ratio Trend Matrix.

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


syot kilat
Summary of original Table on the left, Summary of top ranked assets on the right (manipulated with "Highlight Top Assets" input.

Penafian

Maklumat dan penerbitan adalah tidak dimaksudkan untuk menjadi, dan tidak membentuk, nasihat untuk kewangan, pelaburan, perdagangan dan jenis-jenis lain atau cadangan yang dibekalkan atau disahkan oleh TradingView. Baca dengan lebih lanjut di Terma Penggunaan.