OrderBlox is an advanced indicator for the TradingView platform, specifically designed for traders looking to leverage the benefits of KNN (k-Nearest Neighbors) learning to detect areas of accumulation, distribution, and liquidity. With this innovative approach, traders can take advantage of the enhanced “backward-forward” concept to identify key market levels with increased accuracy.
Orderblox in action on Dow Jones Index.

Here is the complete list of features present in this indicator :
- Detection of accumulation and distribution areas using configurable order blocks and KNN algorithms to analyze historical and current signals.
- Option to choose between closing prices or high/low prices as the signal source.
- Inversion of signals for a different perspective.
Display of price labels and boxes. - Automatic settings based on different time period categories (short, medium, long term).
- Identification of liquidity areas by analyzing the gaps between high and low prices of candles.
Customization of the liquidity gap length. - Display of liquidity bands.
- Calculation of probable price levels for retests using historical data and accumulation and distribution trends.
- Accumulation and distribution bands: these bands allow you to visualize the levels of accumulation and distribution to facilitate market analysis and identify areas of interest.
Choice of the average type for accumulation and distribution bands (SMA, EMA, WMA, VWMA, HMA). - Adjustment of the confidence level and the number of iterations for the cumulative distribution function, used to calculate the confidence intervals of the accumulation and distribution bands.
- Dynamic parameters available to adapt the indicator settings according to changing market conditions.
- Choice of the minimum and maximum length of KNN history.
- Selection of the number of neighbors in the KNN model.
- Filtering of Doji signals with customizable threshold and lookback period.
- Customization of colors for accumulation, distribution, A/D average, and liquidity gap.
- Possibility to highlight bars based on the signal.
- Choice of the information table position (top left, bottom left, top right, bottom right).
OrderBlox is a powerful tool for traders looking to improve their analysis of financial markets by detecting key areas of interest. By combining the advantages of KNN learning with advanced features such as the detection of accumulation, distribution, and liquidity areas, as well as dynamic and customizable settings, this indicator allows traders to better understand market movements and anticipate price levels likely to be retested.
All options have their original default settings. You need to be able to readjust them depending on the market instrument you want to analyze. You have the possibility to put alerts which will notify you when an indicator gives a new signal. For this, we have united all the alerts in one thanks to the “Any Alert Function Call” option, simply activate or deactivate the options you want to have in your alerts before placing the alert.
Or copy / paste the source code into your pine editor :
// This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs License. License : https://creativecommons.org/licenses/by-nc-nd/4.0/ // © RickSimpson //@version=5 indicator('OrderBlox', 'OrderBlox', true, max_bars_back=500, max_labels_count=500, max_boxes_count=500, max_lines_count=500) import RicardoSantos/MathSpecialFunctionsGamma/1 // Select the preferred timeframe category for automatic settings i_auto_settings = input.bool(false, "Enable Automatic Settings", group="Automatic Settings") i_timeframe_options = input.string("Medium Timeframe", "Select Timeframe", options=["Short Timeframe", "Medium Timeframe", "Long Timeframe"], group="Automatic Settings", tooltip="Short Timeframe = ('M15 - M30') - Medium Timeframe = ('H1 - H4') - Long Timeframe = ('H4 - Daily')") // Define values based on the selected timeframe acc_len = i_timeframe_options == "Short Timeframe" ? 8 : i_timeframe_options == "Medium Timeframe" ? 12 : 20 dist_len = i_timeframe_options == "Short Timeframe" ? 8 : i_timeframe_options == "Medium Timeframe" ? 12 : 20 liquidity_len = i_timeframe_options == "Short Timeframe" ? 20 : i_timeframe_options == "Medium Timeframe" ? 25 : 50 knn_min = i_timeframe_options == "Short Timeframe" ? 50 : i_timeframe_options == "Medium Timeframe" ? 100 : 150 knn_max = i_timeframe_options == "Short Timeframe" ? 10 : i_timeframe_options == "Medium Timeframe" ? 25 : 50 k_neighbors = i_timeframe_options == "Short Timeframe" ? 5 : i_timeframe_options == "Medium Timeframe" ? 7 : 13 // Input variables for user customization i_use_close_as_source = input.bool(true, "Use Close Price as Signal Source", group="Signal Source settings", tooltip="If unchecked, then 'High/Low' will be set as the source") i_invertsignals = input.bool(false, "Inverse (Bearish <-> Bullish) Signals", group="Signal Source settings", tooltip="Invert the calculation to have a different perspective on the signals") i_show_labels = input.bool(true, "Show Price Labels", group="Accumulation and Distribution settings") i_show_boxes = input.bool(true, "Display Boxes", group="Accumulation and Distribution settings") i_index_period = input.int(5, "OrderBlocks Period", minval=1, maxval=500, group="Accumulation and Distribution settings", tooltip="There is no 'ideal' value for defining the length of OrderBlocks. However, a shorter value should be preferred for lower and medium timeframes (e.g., 2-4) and a longer length for higher timeframes (e.g., 3-6 candles). Keep in mind that these values are merely examples, and the user should adjust the value according to their preference due to the varying volatilities among financial markets, timeframes, etc") i_accum_length = i_auto_settings ? acc_len : input.int(12, "Accumulation Length", minval=1, maxval=100, group="Accumulation and Distribution settings") i_dist_length = i_auto_settings ? dist_len : input.int(12, "Distribution Length", minval=1, maxval=100, group="Accumulation and Distribution settings") i_liquidity_gap_len = i_auto_settings ? liquidity_len : input.int(25, "Liquidity Gap Length", minval=10, group="Accumulation and Distribution settings") i_show_bands = input.bool(true, "Display bands", group="Accumulation and Distribution settings", tooltip="Length of the liquidity gap for the Accumulation and Distribution settings. Determines the number of bars considered when calculating the liquidity gap. A longer length may provide a smoother representation of liquidity trends, while a shorter length may be more sensitive to recent changes. Adjust the value based on your preference and the specific market conditions") i_confidence = input.float(0.999, "Confidence", minval=0.01, maxval=1, group="Accumulation and Distribution settings", tooltip="the 'Tolerance' refers to the desired level of accuracy when searching for the t-value using binary search. In our case, the tolerance is set at 0.000001. This means that the binary search loop will continue to search for the t-value until the difference between the t-distribution cumulative function (t_dist_cdf) and the p-value (probability) is less than or equal to 0.000001. This tolerance ensures that the found t-value is accurate enough to be used in the calculation of the confidence intervals") i_average_type = input.string("EMA", "Average Type", options=["SMA", "EMA", "WMA", "VWMA", "HMA"], group="Accumulation and Distribution settings") i_cdf_iters = input.int(2, "Cumulative Distribution Function Iterations", minval=1, group="Accumulation and Distribution settings", tooltip="The greater the accuracy of the t distribution cumulative function will be. However, this will also increase the calculation time. Choosing a value for 'Cumulative Distribution Function Iterations' thus depends on the trade-off between desired accuracy and acceptable calculation time for the indicator") i_backward = i_auto_settings ? knn_min : input.int(100, "KNN History Length Min", minval=1, maxval=500, group="KNN Backward-Forward settings", tooltip="Minimum history length for the KNN model. Determines how far back the model will look to find similar patterns. A shorter length may result in more noise, while a longer length may be less sensitive to recent changes") i_forward = i_auto_settings ? knn_max : input.int(25, "KNN History Length Max", minval=1, maxval=500, group="KNN Backward-Forward settings", tooltip="Maximum history length for the KNN model. Determines the range of history lengths the model will consider. A larger value may capture more patterns, but can also increase the chance of overfitting and reduce responsiveness") i_k = i_auto_settings ? k_neighbors : input.int(5, "Number of Neighbors in KNN", minval=1, maxval=500, group="KNN Model settings", tooltip="Number of neighbors to consider in the KNN model. A larger number may provide more stable predictions, but can also result in a loss of sensitivity to smaller patterns. Choose a value that balances stability and sensitivity based on your trading strategy") i_lookback = input.int(14, "lookback Period", minval=1, group="Doji Signals Filter", tooltip="Lookback period for filtering Doji signals. The lookback period determines how many bars the filter will consider when identifying Doji patterns. A longer lookback period may result in fewer false signals, but can also reduce sensitivity to recent changes") i_doji_threshold = input.float(0.001, "Doji Threshold", group="Doji Signals Filter", tooltip="Doji threshold for filtering signals. The threshold determines how close the open and close prices must be for a bar to be considered a Doji pattern. A smaller threshold will result in stricter filtering, while a larger threshold may capture more potential Doji patterns") i_accum_color = input.color(color.new(#0863b5, 0), "Accumulation Color", group="Customization settings", inline="customcolor1") i_dist_color = input.color(color.new(#e3001f, 0), "Distribution Color", group="Customization settings", inline="customcolor1") i_avg_color = input.color(color.new(#808080, 0), "A/D Average Color", group="Customization settings", inline="customcolor2") i_colliquity = input.color(color.new(#FFA500, 0), "Liquidity Gap Color", group="Customization settings", inline="customcolor2") i_ad_transp = input.int(90, "Box Transparency", minval=1, maxval=100, group="Customization settings") i_box_count = input.int(20, "Maximum Displayed Zones", minval=1, maxval=500, group="Customization settings") i_use_barcolor = input.bool(true, "Highlight Bars Based on Signal", group="Customization settings") i_table_pos = input.string("Top Right", "Table Position", options=["Top Left", "Bottom Left", "Top Right", "Bottom Right"], group="Customization settings") // Declare price-related variables index = int(i_index_period + 1) close_ = close[index] low_ = low[index] high_ = high[index] open_ = open[index] bar_index_ = bar_index[index] // Initialize bearish and bullish candle count arrays bearCandleArray = array.new_int(0) bullCandleArray = array.new_int(0) // Bearish and bullish candle count for i = 1 to index by i_index_period // Increment bearish candle count if close[i] is less than open[i] (or greater if i_invertsignals is true) if (i_invertsignals and close[i] > open[i]) or (not i_invertsignals and close[i] < open[i]) array.push(bearCandleArray, 1) else array.push(bearCandleArray, 0) // Increment bullish candle count if close[i] is greater than open[i] (or less if i_invertsignals is true) if (i_invertsignals and close[i] < open[i]) or (not i_invertsignals and close[i] > open[i]) array.push(bullCandleArray, 1) else array.push(bullCandleArray, 0) // Calculate the bearish and bullish candle counts bearcandle = array.sum(bearCandleArray) bullcandle = array.sum(bullCandleArray) // Calculate normalized distribution src = close abs = math.abs(close - close[1]) / close * 100 accumulation_c = math.sum(close == high and close == low or high == low ? 0 : (close - open) / (high - low) * volume, i_accum_length) distribution_c = math.sum(close == high and close == low or high == low ? 0 : (close - open) / (high - low) * volume, i_dist_length) // Function to calculate the moving average based on the selected indicator type avg(src) => switch i_average_type "SMA" => ta.sma(src, i_accum_length), ta.sma(src, i_dist_length) "EMA" => ta.ema(src, i_accum_length), ta.ema(src, i_dist_length) "WMA" => ta.wma(src, i_accum_length), ta.wma(src, i_dist_length) "VWMA" => ta.vwma(src, i_accum_length), ta.vwma(src, i_dist_length) "HMA" => ta.hma(src, i_accum_length), ta.hma(src, i_dist_length) // Function to calculate the gamma function gamma(float x) => MathSpecialFunctionsGamma.Gamma(x) // Function to calculate the probability density function (PDF) of the Student's t-distribution t_dist_pdf(float x, int v) => gamma((v + 1) / 2) / (math.sqrt(v * math.pi) * gamma(v / 2)) * math.pow(1 + x * x / v, -(v + 1) / 2) // Function to calculate the rising factorial rising_factorial(float c, int n) => float product = 1.0 if n > 0 for i = 0 to n - 1 product *= (c + i) product // Function to calculate the hypergeometric function hypergeometric(float a, float b, float c, float z) => float sum = 0.0 for i = 0 to i_cdf_iters sum += rising_factorial(a, i) * rising_factorial(b, i) * math.pow(z, i) / (rising_factorial(c, i) * rising_factorial(i, i)) sum // Function to calculate the cumulative distribution function (CDF) of the Student's t-distribution t_dist_cdf(float x, int v) => 1.0 / 2 + x * gamma((v + 1) / 2) * (hypergeometric(1.0 / 2, (v + 1) / 2, 3.0 / 2, x * x / v) / (math.sqrt(v * math.pi) * gamma(v / 2))) // Calculate Student-t distribution for accumulation and distribution accum_std = ta.stdev(accumulation_c, i_accum_length) dist_std = ta.stdev(distribution_c, i_dist_length) accum_t = t_dist_pdf(accumulation_c / accum_std, i_accum_length - 1) dist_t = t_dist_pdf(distribution_c / dist_std, i_dist_length - 1) // Normalize the Student-t distributions norm_accum = 100 * accum_t / math.sum(volume, i_accum_length) norm_dist = 100 * dist_t / math.sum(volume, i_dist_length) // Find the index of the minimum value in an array find_min_index(arr) => float min_val = array.get(arr, 0) int min_index = 0 for i = 1 to array.size(arr) - 1 val = array.get(arr, i) if val < min_val min_val := val min_index := i min_index // KNN model function with time-based weighting knn_weighted(norm_accum, norm_dist, k) => float prediction = na distances = array.new_float(0) // Calculate the time-based weight weighting_factor = 1 max_age = bar_index - math.min(bar_index[1], bar_index[k]) for i = 1 to k // Calculate the time-based weight for the current historical signal age = bar_index - bar_index[i] weight = (1 - (age / max_age)) * weighting_factor // Calculate Euclidean distance between the current point and the previous k points, adjusted by the weight distance = math.sqrt(math.pow((norm_accum - norm_accum[i]) * weight, 2) + math.pow((norm_dist - norm_dist[i]) * weight, 2)) array.push(distances, distance) // Find the index with the minimum distance min_index = find_min_index(distances) // Use the close price corresponding to the minimum distance index as the prediction prediction := close[min_index] prediction // KNN model function knn(norm_accum, norm_dist, k) => float prediction = na distances = array.new_float(0) // Calculate Euclidean distance between the current point and the previous k points for i = 1 to k distance = math.sqrt(math.pow(norm_accum - norm_accum[i], 2) + math.pow(norm_dist - norm_dist[i], 2)) array.push(distances, distance) // Find the index with the minimum distance min_index = find_min_index(distances) // Use the close price corresponding to the minimum distance index as the prediction prediction := close[min_index] prediction // This function performs an adaptive binary search to find the optimal length // for k-NN regression within the range [i_backward, i_forward], minimizing prediction error. adaptive_len() => int left = i_backward int right = i_forward int best_len = i_backward float min_error = 10e10 while right - left > 1 int mid = int((left + right) / 2) float error_left = 0.0 float error_mid = 0.0 float error_right = 0.0 float prev_error_mid = 0.0 for j = 1 to mid - 1 pred_left = knn(norm_accum[j], norm_dist[j], i_k) error_left := error_left + math.abs(close[j] - pred_left) for j = mid - 1 to mid + 1 if j == mid prev_error_mid := error_mid pred_mid = knn(norm_accum[j], norm_dist[j], i_k) error_mid := error_mid + math.abs(close[j] - pred_mid) error_right := error_mid - prev_error_mid if error_left < error_mid and error_left < error_right right := mid - 1 else left := mid best_len := int((left + right) / 2) best_len // Precompute the adaptive length and store it in a variable using the 'var' keyword var int dynamic_len = adaptive_len() // Predict the next close price using the KNN model knn_prediction = (knn_weighted(norm_accum, norm_dist, i_k) + knn(norm_accum, norm_dist, i_k)) / 2 // isDoji() function checks if the current bar is a doji by comparing the size of its body with the size of the entire candle. isDoji() => dojiSize = math.abs(close - open) bodySize = high - low dojiSize / bodySize < i_doji_threshold // Add detected dojis to an array doji_array = array.new_bool(i_lookback, na) for i = 0 to i_lookback - 1 array.set(doji_array, i, isDoji()[i]) // Check if a doji is detected at a given index isDojiAtIndex(_index) => array.get(doji_array, _index) // Calculate p-value given the confidence p = (1 - i_confidence) / 2 // Binary search function to find t_value t_value_binary_search(int length) => float lower = -100 float upper = 100 float mid = (lower + upper) / 2 while math.abs(t_dist_cdf(mid, length - 1) - p) > 0.000001 if t_dist_cdf(mid, length - 1) > p upper := mid else lower := mid mid := (lower + upper) / 2 float t_value = mid if t_value < 0 t_value := -t_value t_value // Calculate t_value for accumulation and distribution lengths float t_value_accum = t_value_binary_search(i_accum_length) float t_value_dist = t_value_binary_search(i_dist_length) // Calculate the midline x_bar = avg(src) // Calculate the squared differences squared_diff = math.pow(src - x_bar, 2) // Calculate the summed squares for accumulation and distribution lengths separately summed_squares_accum = math.sum(squared_diff, i_accum_length) summed_squares_dist = math.sum(squared_diff, i_dist_length) // Calculate mean squared errors for both lengths mean_squared_error_accum = summed_squares_accum / (i_accum_length - 1) mean_squared_error_dist = summed_squares_dist / (i_dist_length - 1) // Calculate RMSE for both lengths rmse_accum = math.sqrt(mean_squared_error_accum) rmse_dist = math.sqrt(mean_squared_error_dist) // Calculate root standard error for both lengths root_standard_error_accum = math.sqrt(1 + 1 / i_accum_length + squared_diff / summed_squares_accum) root_standard_error_dist = math.sqrt(1 + 1 / i_dist_length + squared_diff / summed_squares_dist) // Calculate the offsets using the t_values offset_accum = t_value_accum * rmse_accum * root_standard_error_accum offset_dist = t_value_dist * rmse_dist * root_standard_error_dist // Calculate the upper and lower bounds upper_bound = x_bar + offset_dist lower_bound = x_bar - offset_accum // Draw the Bollinger bands p1 = plot(i_show_bands ? lower_bound : na, color=i_accum_color, title="Lower Bound") p2 = plot(i_show_bands ? upper_bound : na, color=i_dist_color, title="Upper Bound") // Plot the average line plot(i_show_bands ? x_bar : na, color=i_avg_color, title="Average") // Fill the area between the bands fill(p1, p2, color=color.rgb(0, 50, 255, 90), title="Confidence Interval") // Alerts for crossing the lines if (ta.cross(close, upper_bound) and barstate.isconfirmed) alert("Price crossed the Distribution line.", alert.freq_once_per_bar_close) if (ta.cross(close, lower_bound) and barstate.isconfirmed) alert("Price crossed the Accumulation line.", alert.freq_once_per_bar_close) if (ta.cross(close, x_bar) and barstate.isconfirmed) alert("Price crossed the Average line.", alert.freq_once_per_bar_close) // Calculate bullish and bearish order blocks with KNN model prediction bear_cond = (bearcandle == (0)) and knn_prediction < close and abs bull_cond = (bullcandle == (0)) and knn_prediction > close and abs // The bear and bull conditions check if the respective bearish (bear_cond) and bullish (bull_cond) conditions are met // and the current bar is not a doji. bear = bear_cond and not isDojiAtIndex(0) bull = bull_cond and not isDojiAtIndex(0) // Converting boolean variables to float var float bearish_signal = na if bear bearish_signal := i_use_close_as_source ? close : high bearish_signal var float bullish_signal = na if bull bullish_signal := i_use_close_as_source ? close : low bullish_signal // Declare box-related variables var box[] distributionbox = array.new_box() var box[] accumulationbox = array.new_box() var line[] bearmedianlinearray = array.new_line() var line[] bullmedianlinearray = array.new_line() var color distributionboxcolor = color.new(i_dist_color, i_ad_transp) var color accumulationboxcolor = color.new(i_accum_color, i_ad_transp) var color distributionborderboxcolor = color.new(i_dist_color, 0) var color accumulationborderboxcolor = color.new(i_accum_color, 0) // Calculate distribution box f_chopped_off_bear(distributionbox) => if array.size(distributionbox) > 0 for i = array.size(distributionbox) -1 to 0 by 1 cutbox = array.get(distributionbox, i) boxlowzone = box.get_bottom(cutbox) boxhighzone = box.get_top(cutbox) boxrightzone = box.get_right(cutbox) if na or bar_index - 1 == boxrightzone and not(high > boxlowzone and low < boxlowzone or high > boxhighzone and low < boxhighzone) box.set_right(array.get(distributionbox, i), bar_index) // Plot distribution box if not na(bearish_signal) and i_show_boxes and bearish_signal != bearish_signal[1] boxhighzone = high_ boxlowzone = close_ < open_ ? close_ : open_ bearbox = box.new(bar_index_, boxhighzone, bar_index, boxlowzone, border_color=distributionborderboxcolor, border_style=line.style_dashed, bgcolor=distributionboxcolor) medianprice = (boxhighzone + boxlowzone) / 2 bearmedianline = line.new(bar_index_, medianprice, bar_index, medianprice, width=1, color=distributionborderboxcolor) if array.size(distributionbox) > i_box_count box.delete(array.shift(distributionbox)) line.delete(array.shift(bearmedianlinearray)) array.push(distributionbox, bearbox) array.push(bearmedianlinearray, bearmedianline) // Calculate accumulation box f_chopped_off_bull(accumulationbox) => if array.size(accumulationbox) > 0 for i = array.size(accumulationbox) -1 to 0 by 1 cutbox = array.get(accumulationbox, i) boxlowzone = box.get_bottom(cutbox) boxhighzone = box.get_top(cutbox) boxrightzone = box.get_right(cutbox) if na or bar_index - 1 == boxrightzone and not(high > boxlowzone and low < boxlowzone or high > boxhighzone and low < boxhighzone) box.set_right(array.get(accumulationbox, i), bar_index) // Plot accumulation box if not na(bullish_signal) and i_show_boxes and bullish_signal != bullish_signal[1] boxlowzone = low_ boxhighzone = close_ < open_ ? open_ : close_ bullbox = box.new(bar_index_, boxhighzone, bar_index, boxlowzone, border_color=accumulationborderboxcolor, border_style=line.style_dashed, bgcolor=accumulationboxcolor) medianprice = (boxhighzone + boxlowzone) / 2 bullmedianline = line.new(bar_index_, medianprice, bar_index, medianprice, width=1, color=accumulationborderboxcolor) if array.size(accumulationbox) > i_box_count box.delete(array.shift(accumulationbox)) line.delete(array.shift(bullmedianlinearray)) array.push(accumulationbox, bullbox) array.push(bullmedianlinearray, bullmedianline) // Define function to extend median lines f_extend_bear_median_lines(distributionbox, bearmedianlinearray) => if array.size(distributionbox) > 0 and array.size(bearmedianlinearray) > 0 for i = array.size(distributionbox) -1 to 0 by 1 cutbox = array.get(distributionbox, i) cutline = array.get(bearmedianlinearray, i) boxrightzone = box.get_right(cutbox) boxlowzone = box.get_bottom(cutbox) boxhighzone = box.get_top(cutbox) medianprice = (boxhighzone + boxlowzone) / 2 line.set_xy2(cutline, boxrightzone, medianprice) f_extend_bull_median_lines(accumulationbox, bullmedianlinearray) => if array.size(accumulationbox) > 0 and array.size(bullmedianlinearray) > 0 for i = array.size(accumulationbox) -1 to 0 by 1 cutbox = array.get(accumulationbox, i) cutline = array.get(bullmedianlinearray, i) boxrightzone = box.get_right(cutbox) boxlowzone = box.get_bottom(cutbox) boxhighzone = box.get_top(cutbox) medianprice = (boxhighzone + boxlowzone) / 2 line.set_xy2(cutline, boxrightzone, medianprice) f_chopped_off_bear(distributionbox) f_extend_bear_median_lines(distributionbox, bearmedianlinearray) f_chopped_off_bear(accumulationbox) f_extend_bull_median_lines(accumulationbox, bullmedianlinearray) // Get the last bear and bull boxes and median lines last_distributionbox = array.size(distributionbox) > 0 ? array.get(distributionbox, array.size(distributionbox) - 1) : na last_accumulationbox = array.size(accumulationbox) > 0 ? array.get(accumulationbox, array.size(accumulationbox) - 1) : na last_bearmedianline = array.size(bearmedianlinearray) > 0 ? array.get(bearmedianlinearray, array.size(bearmedianlinearray) - 1) : na last_bullmedianline = array.size(bullmedianlinearray) > 0 ? array.get(bullmedianlinearray, array.size(bullmedianlinearray) - 1) : na last_distributionbox_top = box.get_top(last_distributionbox) last_distributionbox_bot = box.get_bottom(last_distributionbox) last_distributionbox_right = box.get_right(last_distributionbox) last_accumulationbox_top = box.get_top(last_accumulationbox) last_accumulationbox_bot = box.get_bottom(last_accumulationbox) last_accumulationbox_right = box.get_right(last_accumulationbox) last_bearmedianline_price = line.get_y1(last_bearmedianline) last_bullmedianline_price = line.get_y1(last_bullmedianline) // Alert conditions for the last bear box if (ta.cross(close, last_distributionbox_top) and bar_index[1] > last_distributionbox_right) alert("Price broke the Top of the last Distribution Box.", alert.freq_once_per_bar_close) if (ta.cross(close, last_distributionbox_bot) and bar_index[1] > last_distributionbox_right) alert("Price broke the Bottom of the Last Distribution Box.", alert.freq_once_per_bar_close) // Alert conditions for the last bull box if (ta.cross(close, last_accumulationbox_top) and bar_index[1] > last_accumulationbox_right) alert("Price broke the Top of the last Accumulation Box.", alert.freq_once_per_bar_close) if (ta.cross(close, last_accumulationbox_bot) and bar_index[1] > last_accumulationbox_right) alert("Price broke the Bottom of the last Accumulation Box.", alert.freq_once_per_bar_close) // Alert conditions for the last bear median line if (ta.cross(close, last_bearmedianline_price) and bar_index[1] > last_distributionbox_right) alert("Price crossed the Median Line of the last Distribution Box.", alert.freq_once_per_bar_close) // Alert conditions for the last bull median line if (ta.cross(close, last_bullmedianline_price) and bar_index[1] > last_accumulationbox_right) alert("Price crossed the Median Line of the last Accumulation Box.", alert.freq_once_per_bar_close) // Declare related variables to find the nearest historical price to the last detected signal historical_prices = float(na) bullish_distance = float(na) bearish_distance = float(na) min_diff = float(na) closest_distance = float(na) last_signal_index = int(na) last_bearish_signal_index = int(0) last_bullish_signal_index = int(0) // Calculate the distance between the historical price closest to the last detected bullish or bearish signal if not na(bullish_signal) bullish_distance := math.abs(historical_prices - bullish_signal) if not na(bearish_signal) bearish_distance := math.abs(historical_prices - bearish_signal) // Compare the distances to determine the closest one to the current price if not na(bullish_distance) and not na(bearish_distance) closest_distance := math.min(bullish_distance, bearish_distance) else closest_distance := na(bullish_distance) ? bearish_distance : bullish_distance // Update the historical_prices variable based on the closest bullish or bearish signal if not na(closest_distance) if closest_distance == bullish_distance historical_prices := bullish_signal else if closest_distance == bearish_distance historical_prices := bearish_signal // Iterate through the range of i_lookback periods and store the last bearish and bullish signal indices for i = 0 to dynamic_len-1 if not na(bearish_signal[i]) last_bearish_signal_index := i if not na(bullish_signal[i]) last_bullish_signal_index := i // Determine the last signal index and find the nearest historical price to the current price if last_bearish_signal_index > last_bullish_signal_index last_signal_index := last_bearish_signal_index else last_signal_index := last_bullish_signal_index // Iterate from the last signal index to the end of the i_lookback period to find the nearest historical price to the current price while considering the last detected signal for i = last_signal_index to dynamic_len-1 diff = math.abs(close - (na(bearish_signal[i]) ? bullish_signal[i] : bearish_signal[i])) if (na(min_diff) or diff < min_diff) and (bearish_signal[i] != na(bearish_signal[i]) or bullish_signal[i] != na(bullish_signal[i])) historical_prices := bearish_signal[i] != na(bearish_signal[i]) ? bearish_signal[i] : bullish_signal[i] // Initialize the liquidity gap array and define the gap_exists() function var liquidity_zone_array = array.new_float(i_liquidity_gap_len, na) // This function is used to check if there is a gap between two bars. gap_exists_ad(higherBarHigh, lowerBarLow, ad_threshold) => gap_exists = higherBarHigh < lowerBarLow and math.abs(accumulation_c[1] - distribution_c[2]) > ad_threshold // Find liquidity gaps and store them in the liquidity_gap_array for i = 1 to i_liquidity_gap_len if gap_exists_ad(high[i], low[i+1], 0.02) // Check if there's a gap between bars i and i+1 array.set(liquidity_zone_array, i - 1, high[i]) // Use the highest price of the bar with the gap as the liquidity zone // Get the latest liquidity gap value liquidity_zone = array.get(liquidity_zone_array, 0) // Plot the liquidity gap line and alert when the price crosses the line if not na(liquidity_zone) line.new(x1=bar_index-i_liquidity_gap_len, y1=liquidity_zone, x2=bar_index, y2=liquidity_zone, width=1, color=color.new(i_colliquity, 0)) // Price crossing the liquidity gap line if (ta.cross(close, liquidity_zone) or ta.cross(liquidity_zone, close)) and not na(liquidity_zone) and not (ta.cross(close[1], liquidity_zone[1]) or ta.cross(liquidity_zone[1], close[1])) alert('Price crossed "Liquidity Gap" line! at: ' + str.tostring(liquidity_zone), alert.freq_once_per_bar_close) // Initialize and update the table to display information about signals var infoTable = table.new(position=i_table_pos == 'Top Left' ? position.top_left : i_table_pos == 'Bottom Left' ? position.bottom_left : i_table_pos == 'Top Right' ? position.top_right : position.bottom_right, columns=2, rows=5, bgcolor=color.new(color.black, 0), border_color=color.new(color.black, 0), border_width=1) // Update the table with the latest signal information if not na(bearish_signal) and bearish_signal != bearish_signal[1] table.cell(infoTable, 0, 0, "Last Distribution", text_color=color.white, bgcolor=color.new(i_dist_color, 70)) table.cell(infoTable, 1, 0, str.tostring(bearish_signal, "#.########"), text_color=color.white, bgcolor=color.new(i_dist_color, 70)) // Check if the "Last Bearish Block" price has changed alert('"Last Distribution" price has changed! at: ' + str.tostring(math.round_to_mintick(bearish_signal)), alert.freq_once_per_bar_close) if not na(bullish_signal) and bullish_signal != bullish_signal[1] table.cell(infoTable, 0, 1, "Last Accumulation", text_color=color.white, bgcolor=color.new(i_accum_color, 70)) table.cell(infoTable, 1, 1, str.tostring(bullish_signal, "#.########"), text_color=color.white, bgcolor=color.new(i_accum_color, 70)) // Check if the "Last Bullish Block" price has changed alert('"Last Accumulation" price has changed! at: ' + str.tostring(math.round_to_mintick(bullish_signal)), alert.freq_once_per_bar_close) // Check if historical_prices is available, and if the price is equal to or surpasses the last detected bearish or bullish order block, depending on the trend, or if the current price crosses the probable retest price if not na(historical_prices) and ((i_invertsignals and historical_prices <= bearish_signal and close <= bearish_signal) or (not i_invertsignals and historical_prices >= bearish_signal and close >= bearish_signal) or (i_invertsignals and historical_prices >= bullish_signal and close >= bullish_signal) or (not i_invertsignals and historical_prices <= bullish_signal and close <= bullish_signal)) or (close >= historical_prices or close <= historical_prices) price_color = historical_prices >= bearish_signal ? color.new(i_accum_color, 70) : color.new(i_dist_color, 70) price_bgcolor = historical_prices >= bearish_signal ? color.new(i_accum_color, 70) : color.new(i_dist_color, 70) table.cell(infoTable, 0, 3, "Probable Retest", text_color=color.white, bgcolor=price_bgcolor) table.cell(infoTable, 1, 3, str.tostring(historical_prices, "#.########"), text_color=color.white, bgcolor=price_color) // Plot the probable retest line line.new(x1=bar_index - 1, y1=historical_prices, x2=bar_index, y2=historical_prices, width=1, color=color.new(price_color, 0)) // Check if the "Probable Retest" price has changed if (ta.cross(close, historical_prices) or ta.cross(historical_prices, close)) alert('"Probable Retest" price has changed! at: ' + str.tostring(math.round_to_mintick(historical_prices)), alert.freq_once_per_bar_close) // Alert when the price crosses the probable retest line and update the table with the liquidity gap information if (ta.cross(close, historical_prices) or ta.cross(historical_prices, close)) and not na(historical_prices) and not (ta.cross(close[1], historical_prices[1]) or ta.cross(historical_prices[1], close[1])) alert('Price crossed "Probable Retest" line! at: ' + str.tostring(historical_prices)) // Update the table with the liquidity gap information if not na(liquidity_zone) table.cell(infoTable, 0, 4, "Liquidity Gap", text_color=color.white, bgcolor=color.new(i_colliquity, 70)) table.cell(infoTable, 1, 4, str.tostring(liquidity_zone, "#.########"), text_color=color.white, bgcolor=color.new(i_colliquity, 70)) // Check if the "Liquidity Gap" price has changed if not na(liquidity_zone) and liquidity_zone != array.get(liquidity_zone_array, 1) alert('"Liquidity Gap" price has changed! at: ' + str.tostring(math.round_to_mintick(liquidity_zone)), alert.freq_once_per_bar_close) // Display labels for new bearish and bullish signals if the 'i_show_labels' input is enabled if not na(bearish_signal) and bearish_signal != bearish_signal[1] and i_show_labels label.new(bar_index, bearish_signal, text=str.tostring(bearish_signal, "#.########"), yloc=yloc.abovebar, style=label.style_label_down, textcolor=color.new(color.white, 0), color=color.new(i_dist_color, 0), size=size.tiny) if not na(bullish_signal) and bullish_signal != bullish_signal[1] and i_show_labels label.new(bar_index, bullish_signal, text=str.tostring(bullish_signal, "#.########"), yloc=yloc.belowbar, style=label.style_label_up, textcolor=color.new(color.white, 0), color=color.new(i_accum_color, 0), size=size.tiny) // Customize bar colors based on the trend bearish_color = color.new(i_dist_color, 20) bullish_color = color.new(i_accum_color, 20) neutral_color = color.new(#808080, 90) // Use bearish_signal and bullish_signal for bar coloring barcolor(i_use_barcolor ? (not na(bearish_signal) and bearish_signal != bearish_signal[1]) ? bearish_color : (not na(bullish_signal) and bullish_signal != bullish_signal[1]) ? bullish_color : neutral_color : na
The content covered on this website is NOT investment advice and I am not a financial advisor.