Question: I'm working with the Indie library to create a candlestick pattern indicator, and I'm facing an issue with how labels are displayed on the chart. As you can see in the attached screenshot, the candlesticks are currently rendered on top of my pattern labels.  What I need help with:  Label Order: Is there a way to make the labels appear in front of the candlesticks instead of behind them? Currently, the candlesticks are obscuring the labels, making some of them difficult to see.  Label Transparency: Is it possible to make these labels semi-transparent so that even when they're in front of candlesticks, the underlying price action is still somewhat visible?   Here's the relevant part of my code for the label markers: @plot.marker(color=color.GREEN, text='BE', style=plot.marker_style.LABEL, position=plot.marker_position.BELOW) # Bullish Engulfing @plot.marker(color=color.TEAL, text='MORN', style=plot.marker_style.LABEL, position=plot.marker_position.BELOW) # Morning Star  I've searched through the TakeProfit/Indie documentation but couldn't find options for:  Controlling the z-index/order of elements on the chart  Setting transparency for markers/labels  Any alternative ways to highlight patterns that would solve this visibility issue   Full code: # indie:lang_version = 5 from indie import indicator, param, plot, color, MainContext from indie.algorithms import Ema, Atr import math # Define helper functions at global scope def is_downtrend(fast_ema: float, slow_ema: float) -> bool: return fast_ema < slow_ema def is_uptrend(fast_ema: float, slow_ema: float) -> bool: return fast_ema > slow_ema def is_significant_body(body: float, atr_value: float, minimum_body_atr: float) -> bool: return body > minimum_body_atr * atr_value def is_doji(ctx: MainContext, i: int, atr_value: float, doji_ratio: float) -> bool: high, low = ctx.high[i], ctx.low[i] open_, close = ctx.open[i], ctx.close[i] body = abs(close - open_) range_ = high - low # Check if range is significant compared to market volatility is_significant = range_ > 0.3 * atr_value # Traditional doji has very small body relative to range return is_significant and body <= doji_ratio * range_ def is_small_body(ctx: MainContext, i: int) -> bool: open_, close = ctx.open[i], ctx.close[i] high, low = ctx.high[i], ctx.low[i] body = abs(close - open_) range_ = high - low return body < 0.3 * range_ def is_bullish_engulfing(ctx: MainContext, atr_value: float, minimum_body_atr: float, volume_ratio: float) -> bool: body_prev = abs(ctx.close[1] - ctx.open[1]) body_curr = abs(ctx.close[0] - ctx.open[0]) # Check for significant body size is_significant = is_significant_body(body_curr, atr_value, minimum_body_atr) return ( ctx.close[1] < ctx.open[1] and # Previous candle is bearish ctx.close[0] > ctx.open[0] and # Current candle is bullish ctx.open[0] <= ctx.close[1] and ctx.close[0] >= ctx.open[1] and # Body engulfing is_significant and ctx.volume[0] > ctx.volume[1] * volume_ratio # Volume confirmation ) def is_bearish_engulfing(ctx: MainContext, atr_value: float, minimum_body_atr: float, volume_ratio: float) -> bool: body_prev = abs(ctx.close[1] - ctx.open[1]) body_curr = abs(ctx.close[0] - ctx.open[0]) # Check for significant body size is_significant = is_significant_body(body_curr, atr_value, minimum_body_atr) return ( ctx.close[1] > ctx.open[1] and # Previous candle is bullish ctx.close[0] < ctx.open[0] and # Current candle is bearish ctx.open[0] >= ctx.close[1] and ctx.close[0] <= ctx.open[1] and # Body engulfing is_significant and ctx.volume[0] > ctx.volume[1] * volume_ratio # Volume confirmation ) def is_morning_star(ctx: MainContext, atr_value: float, minimum_body_atr: float, gap_atr_ratio: float) -> bool: open1, close1 = ctx.open[2], ctx.close[2] open2, close2 = ctx.open[1], ctx.close[1] open3, close3 = ctx.open[0], ctx.close[0] body1 = abs(close1 - open1) body3 = abs(close3 - open3) # First candle should be bearish and significant is_bearish1 = close1 < open1 is_significant1 = is_significant_body(body1, atr_value, minimum_body_atr) # Second candle should have a small body is_small_body2 = is_small_body(ctx, 1) # Third candle should be bullish and significant is_bullish3 = close3 > open3 is_significant3 = is_significant_body(body3, atr_value, minimum_body_atr) # Check for gaps or near-gaps using ATR gap_down = min(open1, close1) - max(open2, close2) > gap_atr_ratio * atr_value gap_up = min(open3, close3) - max(open2, close2) > gap_atr_ratio * atr_value # Check if third candle closes above midpoint of first closes_above_midpoint = close3 > (open1 + close1) / 2 return ( is_bearish1 and is_significant1 and is_small_body2 and is_bullish3 and is_significant3 and closes_above_midpoint and (gap_down or gap_up) # At least one gap should be present ) def is_evening_star(ctx: MainContext, atr_value: float, minimum_body_atr: float, gap_atr_ratio: float) -> bool: open1, close1 = ctx.open[2], ctx.close[2] open2, close2 = ctx.open[1], ctx.close[1] open3, close3 = ctx.open[0], ctx.close[0] body1 = abs(close1 - open1) body3 = abs(close3 - open3) # First candle should be bullish and significant is_bullish1 = close1 > open1 is_significant1 = is_significant_body(body1, atr_value, minimum_body_atr) # Second candle should have a small body is_small_body2 = is_small_body(ctx, 1) # Third candle should be bearish and significant is_bearish3 = close3 < open3 is_significant3 = is_significant_body(body3, atr_value, minimum_body_atr) # Check for gaps or near-gaps using ATR gap_up = min(open2, close2) - max(open1, close1) > gap_atr_ratio * atr_value gap_down = min(open2, close2) - max(open3, close3) > gap_atr_ratio * atr_value # Check if third candle closes below midpoint of first closes_below_midpoint = close3 < (open1 + close1) / 2 return ( is_bullish1 and is_significant1 and is_small_body2 and is_bearish3 and is_significant3 and closes_below_midpoint and (gap_up or gap_down) # At least one gap should be present ) def is_hammer(ctx: MainContext, in_downtrend: bool, atr_value: float, shadow_body_ratio: float) -> bool: open_, close = ctx.open[0], ctx.close[0] high, low = ctx.high[0], ctx.low[0] body = abs(close - open_) range_ = high - low upper_shadow = high - max(open_, close) lower_shadow = min(open_, close) - low is_significant = range_ > 0.5 * atr_value return ( in_downtrend and is_significant and body > 0 and # Ensure there is a body lower_shadow > shadow_body_ratio * body and # Long lower shadow upper_shadow < 0.5 * body and # Small upper shadow close >= open_ # Preferably bullish (close >= open) ) def is_shooting_star(ctx: MainContext, in_uptrend: bool, atr_value: float, shadow_body_ratio: float) -> bool: open_, close = ctx.open[0], ctx.close[0] high, low = ctx.high[0], ctx.low[0] body = abs(close - open_) range_ = high - low upper_shadow = high - max(open_, close) lower_shadow = min(open_, close) - low is_significant = range_ > 0.5 * atr_value return ( in_uptrend and is_significant and body > 0 and # Ensure there is a body upper_shadow > shadow_body_ratio * body and # Long upper shadow lower_shadow < 0.5 * body and # Small lower shadow close <= open_ # Preferably bearish (close <= open) ) def is_dark_cloud_cover(ctx: MainContext, in_uptrend: bool, atr_value: float, minimum_body_atr: float) -> bool: open1, close1 = ctx.open[1], ctx.close[1] open2, close2 = ctx.open[0], ctx.close[0] body1 = abs(close1 - open1) body2 = abs(close2 - open2) body_mid = (open1 + close1) / 2 is_significant1 = is_significant_body(body1, atr_value, minimum_body_atr) is_significant2 = is_significant_body(body2, atr_value, minimum_body_atr) return ( in_uptrend and close1 > open1 and # First candle is bullish close2 < open2 and # Second candle is bearish open2 > close1 and # Second candle opens above first candle's close close2 < body_mid and # Second candle closes below midpoint of first close2 > open1 and # Second candle doesn't close below first candle's open is_significant1 and is_significant2 ) def is_piercing(ctx: MainContext, in_downtrend: bool, atr_value: float, minimum_body_atr: float) -> bool: open1, close1 = ctx.open[1], ctx.close[1] open2, close2 = ctx.open[0], ctx.close[0] body1 = abs(close1 - open1) body2 = abs(close2 - open2) body_mid = (open1 + close1) / 2 is_significant1 = is_significant_body(body1, atr_value, minimum_body_atr) is_significant2 = is_significant_body(body2, atr_value, minimum_body_atr) return ( in_downtrend and close1 < open1 and # First candle is bearish close2 > open2 and # Second candle is bullish open2 < close1 and # Second candle opens below first candle's close close2 > body_mid and # Second candle closes above midpoint of first close2 < open1 and # Second candle doesn't close above first candle's open is_significant1 and is_significant2 ) def is_three_black_crows(ctx: MainContext, in_uptrend: bool, atr_value: float, minimum_body_atr: float) -> bool: # Calculate bearish body sizes b1 = ctx.open[2] - ctx.close[2] b2 = ctx.open[1] - ctx.close[1] b3 = ctx.open[0] - ctx.close[0] # Calculate shadows upper_shadow1 = ctx.high[2] - ctx.open[2] upper_shadow2 = ctx.high[1] - ctx.open[1] upper_shadow3 = ctx.high[0] - ctx.open[0] lower_shadow1 = ctx.close[2] - ctx.low[2] lower_shadow2 = ctx.close[1] - ctx.low[1] lower_shadow3 = ctx.close[0] - ctx.low[0] # Significant bodies and small shadows relative to bodies are_significant = ( b1 > minimum_body_atr * atr_value and b2 > minimum_body_atr * atr_value and b3 > minimum_body_atr * atr_value ) small_shadows = ( upper_shadow1 < 0.3 * b1 and upper_shadow2 < 0.3 * b2 and upper_shadow3 < 0.3 * b3 and lower_shadow1 < 0.3 * b1 and lower_shadow2 < 0.3 * b2 and lower_shadow3 < 0.3 * b3 ) # Each opens within the previous candle's body (traditional definition) proper_opens = ( ctx.open[1] <= ctx.open[2] and ctx.open[1] >= ctx.close[2] and ctx.open[0] <= ctx.open[1] and ctx.open[0] >= ctx.close[1] ) return ( in_uptrend and # All three candles are bearish ctx.close[2] < ctx.open[2] and ctx.close[1] < ctx.open[1] and ctx.close[0] < ctx.open[0] and # Each closes lower than the previous ctx.close[1] < ctx.close[2] and ctx.close[0] < ctx.close[1] and are_significant and small_shadows and proper_opens ) def is_three_white_soldiers(ctx: MainContext, in_downtrend: bool, atr_value: float, minimum_body_atr: float) -> bool: # Calculate bullish body sizes b1 = ctx.close[2] - ctx.open[2] b2 = ctx.close[1] - ctx.open[1] b3 = ctx.close[0] - ctx.open[0] # Calculate shadows upper_shadow1 = ctx.high[2] - ctx.close[2] upper_shadow2 = ctx.high[1] - ctx.close[1] upper_shadow3 = ctx.high[0] - ctx.close[0] lower_shadow1 = ctx.open[2] - ctx.low[2] lower_shadow2 = ctx.open[1] - ctx.low[1] lower_shadow3 = ctx.open[0] - ctx.low[0] # Significant bodies and small shadows relative to bodies are_significant = ( b1 > minimum_body_atr * atr_value and b2 > minimum_body_atr * atr_value and b3 > minimum_body_atr * atr_value ) small_shadows = ( upper_shadow1 < 0.3 * b1 and upper_shadow2 < 0.3 * b2 and upper_shadow3 < 0.3 * b3 and lower_shadow1 < 0.3 * b1 and lower_shadow2 < 0.3 * b2 and lower_shadow3 < 0.3 * b3 ) # Each opens within the previous candle's body (traditional definition) proper_opens = ( ctx.open[1] >= ctx.open[2] and ctx.open[1] <= ctx.close[2] and ctx.open[0] >= ctx.open[1] and ctx.open[0] <= ctx.close[1] ) return ( in_downtrend and # All three candles are bullish ctx.close[2] > ctx.open[2] and ctx.close[1] > ctx.open[1] and ctx.close[0] > ctx.open[0] and # Each closes higher than the previous ctx.close[1] > ctx.close[2] and ctx.close[0] > ctx.close[1] and are_significant and small_shadows and proper_opens ) @indicator('Enhanced Candlestick Patterns v3', overlay_main_pane=True) @param.float('doji_ratio', default=0.05, min=0.01, max=0.2, title='Doji Body/Range Ratio') @param.float('shadow_body_ratio', default=2.0, min=1.0, max=5.0, title='Shadow/Body Ratio') @param.float('minimum_body_atr', default=0.3, min=0.1, max=1.0, title='Minimum Body/ATR Ratio') @param.float('gap_atr_ratio', default=0.1, min=0.0, max=0.5, title='Gap/ATR Ratio') @param.float('volume_ratio', default=1.2, min=1.0, max=3.0, title='Volume Increase Ratio') @param.int('fast_ema', default=20, min=5, max=50, title='Fast EMA Length') @param.int('slow_ema', default=50, min=20, max=200, title='Slow EMA Length') # Bullish Patterns (varying shades of green/teal) @plot.marker(color=color.GREEN, text='BE', style=plot.marker_style.LABEL, position=plot.marker_position.BELOW) # Bullish Engulfing @plot.marker(color=color.TEAL, text='MORN', style=plot.marker_style.LABEL, position=plot.marker_position.BELOW) # Morning Star @plot.marker(color=color.LIME, text='H', style=plot.marker_style.LABEL, position=plot.marker_position.BELOW) # Hammer @plot.marker(color=color.rgba(0, 180, 80, 1), text='P', style=plot.marker_style.LABEL, position=plot.marker_position.BELOW) # Piercing @plot.marker(color=color.rgba(0, 128, 64, 1), text='TWS', style=plot.marker_style.LABEL, position=plot.marker_position.BELOW) # Three White Soldiers # Bearish Patterns (varying shades of red/purple) @plot.marker(color=color.RED, text='SE', style=plot.marker_style.LABEL, position=plot.marker_position.ABOVE) # Bearish Engulfing @plot.marker(color=color.PURPLE, text='EVE', style=plot.marker_style.LABEL, position=plot.marker_position.ABOVE) # Evening Star @plot.marker(color=color.FUCHSIA, text='SS', style=plot.marker_style.LABEL, position=plot.marker_position.ABOVE) # Shooting Star @plot.marker(color=color.rgba(180, 0, 80, 1), text='DCC', style=plot.marker_style.LABEL, position=plot.marker_position.ABOVE) # Dark Cloud Cover @plot.marker(color=color.MAROON, text='TBC', style=plot.marker_style.LABEL, position=plot.marker_position.ABOVE) # Three Black Crows def Main(self, doji_ratio, shadow_body_ratio, minimum_body_atr, gap_atr_ratio, volume_ratio, fast_ema, slow_ema): # Main indicator logic h = self.high[0] - self.low[0] # Compute indicators fast_ema_series = Ema.new(self.close, fast_ema) slow_ema_series = Ema.new(self.close, slow_ema) atr14 = Atr.new(14) in_downtrend = is_downtrend(fast_ema_series[0], slow_ema_series[0]) in_uptrend = is_uptrend(fast_ema_series[0], slow_ema_series[0]) return ( self.low[0] - h * 0.05 if is_bullish_engulfing(self, atr14[0], minimum_body_atr, volume_ratio) else math.nan, self.high[0] + h * 0.05 if is_bearish_engulfing(self, atr14[0], minimum_body_atr, volume_ratio) else math.nan, self.low[0] - h * 0.1 if is_morning_star(self, atr14[0], minimum_body_atr, gap_atr_ratio) else math.nan, self.high[0] + h * 0.1 if is_evening_star(self, atr14[0], minimum_body_atr, gap_atr_ratio) else math.nan, self.low[0] - h * 0.15 if is_hammer(self, in_downtrend, atr14[0], shadow_body_ratio) else math.nan, self.high[0] + h * 0.15 if is_shooting_star(self, in_uptrend, atr14[0], shadow_body_ratio) else math.nan, self.high[0] + h * 0.2 if is_dark_cloud_cover(self, in_uptrend, atr14[0], minimum_body_atr) else math.nan, self.low[0] - h * 0.2 if is_piercing(self, in_downtrend, atr14[0], minimum_body_atr) else math.nan, self.high[0] + h * 0.25 if is_three_black_crows(self, in_uptrend, atr14[0], minimum_body_atr) else math.nan, self.low[0] - h * 0.25 if is_three_white_soldiers(self, in_downtrend, atr14[0], minimum_body_atr) else math.nan ) 

Answer:  Now you can not change the z-index from the indicator code, but you can do it through the UI:   Yes, you can adjust the transparency of labels in the same way as any other elements: color=indie.color.TEAL(alpha), for example:   @plot.marker(color=color.GREEN(0.5), text='BE', style=plot.marker_style.LABEL, position=plot.marker_position.BELOW) # Bullish Engulfing @plot.marker(color=color.TEAL(0.5), text='MORN', style=plot.marker_style.LABEL, position=plot.marker_position.BELOW) # Morning Star    You can also adjust the value and position of your markers so that they don't cover anything. If the marker is tied to the upper point of the candle, then it is more convenient to use position=plot.marker_position.ABOVE. And vice versa.   So your full code will be: # indie:lang_version = 5 from indie import indicator, param, plot, color, MainContext from indie.algorithms import Ema, Atr import math # Define helper functions at global scope def is_downtrend(fast_ema: float, slow_ema: float) -> bool: return fast_ema < slow_ema def is_uptrend(fast_ema: float, slow_ema: float) -> bool: return fast_ema > slow_ema def is_significant_body(body: float, atr_value: float, minimum_body_atr: float) -> bool: return body > minimum_body_atr * atr_value def is_doji(ctx: MainContext, i: int, atr_value: float, doji_ratio: float) -> bool: high, low = ctx.high[i], ctx.low[i] open_, close = ctx.open[i], ctx.close[i] body = abs(close - open_) range_ = high - low # Check if range is significant compared to market volatility is_significant = range_ > 0.3 * atr_value # Traditional doji has very small body relative to range return is_significant and body <= doji_ratio * range_ def is_small_body(ctx: MainContext, i: int) -> bool: open_, close = ctx.open[i], ctx.close[i] high, low = ctx.high[i], ctx.low[i] body = abs(close - open_) range_ = high - low return body < 0.3 * range_ def is_bullish_engulfing(ctx: MainContext, atr_value: float, minimum_body_atr: float, volume_ratio: float) -> bool: body_prev = abs(ctx.close[1] - ctx.open[1]) body_curr = abs(ctx.close[0] - ctx.open[0]) # Check for significant body size is_significant = is_significant_body(body_curr, atr_value, minimum_body_atr) return ( ctx.close[1] < ctx.open[1] and # Previous candle is bearish ctx.close[0] > ctx.open[0] and # Current candle is bullish ctx.open[0] <= ctx.close[1] and ctx.close[0] >= ctx.open[1] and # Body engulfing is_significant and ctx.volume[0] > ctx.volume[1] * volume_ratio # Volume confirmation ) def is_bearish_engulfing(ctx: MainContext, atr_value: float, minimum_body_atr: float, volume_ratio: float) -> bool: body_prev = abs(ctx.close[1] - ctx.open[1]) body_curr = abs(ctx.close[0] - ctx.open[0]) # Check for significant body size is_significant = is_significant_body(body_curr, atr_value, minimum_body_atr) return ( ctx.close[1] > ctx.open[1] and # Previous candle is bullish ctx.close[0] < ctx.open[0] and # Current candle is bearish ctx.open[0] >= ctx.close[1] and ctx.close[0] <= ctx.open[1] and # Body engulfing is_significant and ctx.volume[0] > ctx.volume[1] * volume_ratio # Volume confirmation ) def is_morning_star(ctx: MainContext, atr_value: float, minimum_body_atr: float, gap_atr_ratio: float) -> bool: open1, close1 = ctx.open[2], ctx.close[2] open2, close2 = ctx.open[1], ctx.close[1] open3, close3 = ctx.open[0], ctx.close[0] body1 = abs(close1 - open1) body3 = abs(close3 - open3) # First candle should be bearish and significant is_bearish1 = close1 < open1 is_significant1 = is_significant_body(body1, atr_value, minimum_body_atr) # Second candle should have a small body is_small_body2 = is_small_body(ctx, 1) # Third candle should be bullish and significant is_bullish3 = close3 > open3 is_significant3 = is_significant_body(body3, atr_value, minimum_body_atr) # Check for gaps or near-gaps using ATR gap_down = min(open1, close1) - max(open2, close2) > gap_atr_ratio * atr_value gap_up = min(open3, close3) - max(open2, close2) > gap_atr_ratio * atr_value # Check if third candle closes above midpoint of first closes_above_midpoint = close3 > (open1 + close1) / 2 return ( is_bearish1 and is_significant1 and is_small_body2 and is_bullish3 and is_significant3 and closes_above_midpoint and (gap_down or gap_up) # At least one gap should be present ) def is_evening_star(ctx: MainContext, atr_value: float, minimum_body_atr: float, gap_atr_ratio: float) -> bool: open1, close1 = ctx.open[2], ctx.close[2] open2, close2 = ctx.open[1], ctx.close[1] open3, close3 = ctx.open[0], ctx.close[0] body1 = abs(close1 - open1) body3 = abs(close3 - open3) # First candle should be bullish and significant is_bullish1 = close1 > open1 is_significant1 = is_significant_body(body1, atr_value, minimum_body_atr) # Second candle should have a small body is_small_body2 = is_small_body(ctx, 1) # Third candle should be bearish and significant is_bearish3 = close3 < open3 is_significant3 = is_significant_body(body3, atr_value, minimum_body_atr) # Check for gaps or near-gaps using ATR gap_up = min(open2, close2) - max(open1, close1) > gap_atr_ratio * atr_value gap_down = min(open2, close2) - max(open3, close3) > gap_atr_ratio * atr_value # Check if third candle closes below midpoint of first closes_below_midpoint = close3 < (open1 + close1) / 2 return ( is_bullish1 and is_significant1 and is_small_body2 and is_bearish3 and is_significant3 and closes_below_midpoint and (gap_up or gap_down) # At least one gap should be present ) def is_hammer(ctx: MainContext, in_downtrend: bool, atr_value: float, shadow_body_ratio: float) -> bool: open_, close = ctx.open[0], ctx.close[0] high, low = ctx.high[0], ctx.low[0] body = abs(close - open_) range_ = high - low upper_shadow = high - max(open_, close) lower_shadow = min(open_, close) - low is_significant = range_ > 0.5 * atr_value return ( in_downtrend and is_significant and body > 0 and # Ensure there is a body lower_shadow > shadow_body_ratio * body and # Long lower shadow upper_shadow < 0.5 * body and # Small upper shadow close >= open_ # Preferably bullish (close >= open) ) def is_shooting_star(ctx: MainContext, in_uptrend: bool, atr_value: float, shadow_body_ratio: float) -> bool: open_, close = ctx.open[0], ctx.close[0] high, low = ctx.high[0], ctx.low[0] body = abs(close - open_) range_ = high - low upper_shadow = high - max(open_, close) lower_shadow = min(open_, close) - low is_significant = range_ > 0.5 * atr_value return ( in_uptrend and is_significant and body > 0 and # Ensure there is a body upper_shadow > shadow_body_ratio * body and # Long upper shadow lower_shadow < 0.5 * body and # Small lower shadow close <= open_ # Preferably bearish (close <= open) ) def is_dark_cloud_cover(ctx: MainContext, in_uptrend: bool, atr_value: float, minimum_body_atr: float) -> bool: open1, close1 = ctx.open[1], ctx.close[1] open2, close2 = ctx.open[0], ctx.close[0] body1 = abs(close1 - open1) body2 = abs(close2 - open2) body_mid = (open1 + close1) / 2 is_significant1 = is_significant_body(body1, atr_value, minimum_body_atr) is_significant2 = is_significant_body(body2, atr_value, minimum_body_atr) return ( in_uptrend and close1 > open1 and # First candle is bullish close2 < open2 and # Second candle is bearish open2 > close1 and # Second candle opens above first candle's close close2 < body_mid and # Second candle closes below midpoint of first close2 > open1 and # Second candle doesn't close below first candle's open is_significant1 and is_significant2 ) def is_piercing(ctx: MainContext, in_downtrend: bool, atr_value: float, minimum_body_atr: float) -> bool: open1, close1 = ctx.open[1], ctx.close[1] open2, close2 = ctx.open[0], ctx.close[0] body1 = abs(close1 - open1) body2 = abs(close2 - open2) body_mid = (open1 + close1) / 2 is_significant1 = is_significant_body(body1, atr_value, minimum_body_atr) is_significant2 = is_significant_body(body2, atr_value, minimum_body_atr) return ( in_downtrend and close1 < open1 and # First candle is bearish close2 > open2 and # Second candle is bullish open2 < close1 and # Second candle opens below first candle's close close2 > body_mid and # Second candle closes above midpoint of first close2 < open1 and # Second candle doesn't close above first candle's open is_significant1 and is_significant2 ) def is_three_black_crows(ctx: MainContext, in_uptrend: bool, atr_value: float, minimum_body_atr: float) -> bool: # Calculate bearish body sizes b1 = ctx.open[2] - ctx.close[2] b2 = ctx.open[1] - ctx.close[1] b3 = ctx.open[0] - ctx.close[0] # Calculate shadows upper_shadow1 = ctx.high[2] - ctx.open[2] upper_shadow2 = ctx.high[1] - ctx.open[1] upper_shadow3 = ctx.high[0] - ctx.open[0] lower_shadow1 = ctx.close[2] - ctx.low[2] lower_shadow2 = ctx.close[1] - ctx.low[1] lower_shadow3 = ctx.close[0] - ctx.low[0] # Significant bodies and small shadows relative to bodies are_significant = ( b1 > minimum_body_atr * atr_value and b2 > minimum_body_atr * atr_value and b3 > minimum_body_atr * atr_value ) small_shadows = ( upper_shadow1 < 0.3 * b1 and upper_shadow2 < 0.3 * b2 and upper_shadow3 < 0.3 * b3 and lower_shadow1 < 0.3 * b1 and lower_shadow2 < 0.3 * b2 and lower_shadow3 < 0.3 * b3 ) # Each opens within the previous candle's body (traditional definition) proper_opens = ( ctx.open[1] <= ctx.open[2] and ctx.open[1] >= ctx.close[2] and ctx.open[0] <= ctx.open[1] and ctx.open[0] >= ctx.close[1] ) return ( in_uptrend and # All three candles are bearish ctx.close[2] < ctx.open[2] and ctx.close[1] < ctx.open[1] and ctx.close[0] < ctx.open[0] and # Each closes lower than the previous ctx.close[1] < ctx.close[2] and ctx.close[0] < ctx.close[1] and are_significant and small_shadows and proper_opens ) def is_three_white_soldiers(ctx: MainContext, in_downtrend: bool, atr_value: float, minimum_body_atr: float) -> bool: # Calculate bullish body sizes b1 = ctx.close[2] - ctx.open[2] b2 = ctx.close[1] - ctx.open[1] b3 = ctx.close[0] - ctx.open[0] # Calculate shadows upper_shadow1 = ctx.high[2] - ctx.close[2] upper_shadow2 = ctx.high[1] - ctx.close[1] upper_shadow3 = ctx.high[0] - ctx.close[0] lower_shadow1 = ctx.open[2] - ctx.low[2] lower_shadow2 = ctx.open[1] - ctx.low[1] lower_shadow3 = ctx.open[0] - ctx.low[0] # Significant bodies and small shadows relative to bodies are_significant = ( b1 > minimum_body_atr * atr_value and b2 > minimum_body_atr * atr_value and b3 > minimum_body_atr * atr_value ) small_shadows = ( upper_shadow1 < 0.3 * b1 and upper_shadow2 < 0.3 * b2 and upper_shadow3 < 0.3 * b3 and lower_shadow1 < 0.3 * b1 and lower_shadow2 < 0.3 * b2 and lower_shadow3 < 0.3 * b3 ) # Each opens within the previous candle's body (traditional definition) proper_opens = ( ctx.open[1] >= ctx.open[2] and ctx.open[1] <= ctx.close[2] and ctx.open[0] >= ctx.open[1] and ctx.open[0] <= ctx.close[1] ) return ( in_downtrend and # All three candles are bullish ctx.close[2] > ctx.open[2] and ctx.close[1] > ctx.open[1] and ctx.close[0] > ctx.open[0] and # Each closes higher than the previous ctx.close[1] > ctx.close[2] and ctx.close[0] > ctx.close[1] and are_significant and small_shadows and proper_opens ) @indicator('Enhanced Candlestick Patterns v3', overlay_main_pane=True) @param.float('doji_ratio', default=0.05, min=0.01, max=0.2, title='Doji Body/Range Ratio') @param.float('shadow_body_ratio', default=2.0, min=1.0, max=5.0, title='Shadow/Body Ratio') @param.float('minimum_body_atr', default=0.3, min=0.1, max=1.0, title='Minimum Body/ATR Ratio') @param.float('gap_atr_ratio', default=0.1, min=0.0, max=0.5, title='Gap/ATR Ratio') @param.float('volume_ratio', default=1.2, min=1.0, max=3.0, title='Volume Increase Ratio') @param.int('fast_ema', default=20, min=5, max=50, title='Fast EMA Length') @param.int('slow_ema', default=50, min=20, max=200, title='Slow EMA Length') # Bullish Patterns (varying shades of green/teal) @plot.marker(color=color.GREEN(0.5), text='BE', style=plot.marker_style.LABEL, position=plot.marker_position.BELOW) # Bullish Engulfing @plot.marker(color=color.TEAL(0.5), text='MORN', style=plot.marker_style.LABEL, position=plot.marker_position.ABOVE) # Morning Star @plot.marker(color=color.LIME(0.5), text='H', style=plot.marker_style.LABEL, position=plot.marker_position.BELOW) # Hammer @plot.marker(color=color.rgba(0, 180, 80, 0.5), text='P', style=plot.marker_style.LABEL, position=plot.marker_position.ABOVE) # Piercing @plot.marker(color=color.rgba(0, 128, 64, 0.5), text='TWS', style=plot.marker_style.LABEL, position=plot.marker_position.BELOW) # Three White Soldiers # Bearish Patterns (varying shades of red/purple) @plot.marker(color=color.RED(0.5), text='SE', style=plot.marker_style.LABEL, position=plot.marker_position.ABOVE) # Bearish Engulfing @plot.marker(color=color.PURPLE(0.5), text='EVE', style=plot.marker_style.LABEL, position=plot.marker_position.ABOVE) # Evening Star @plot.marker(color=color.FUCHSIA(0.5), text='SS', style=plot.marker_style.LABEL, position=plot.marker_position.BELOW) # Shooting Star @plot.marker(color=color.rgba(180, 0, 80, 0.5), text='DCC', style=plot.marker_style.LABEL, position=plot.marker_position.ABOVE) # Dark Cloud Cover @plot.marker(color=color.MAROON(0.5), text='TBC', style=plot.marker_style.LABEL, position=plot.marker_position.BELOW) # Three Black Crows def Main(self, doji_ratio, shadow_body_ratio, minimum_body_atr, gap_atr_ratio, volume_ratio, fast_ema, slow_ema): # Main indicator logic h = self.high[0] - self.low[0] # Compute indicators fast_ema_series = Ema.new(self.close, fast_ema) slow_ema_series = Ema.new(self.close, slow_ema) atr14 = Atr.new(14) in_downtrend = is_downtrend(fast_ema_series[0], slow_ema_series[0]) in_uptrend = is_uptrend(fast_ema_series[0], slow_ema_series[0]) return ( self.low[0] - h * 0.05 if is_bullish_engulfing(self, atr14[0], minimum_body_atr, volume_ratio) else math.nan, self.high[0] + h * 0.05 if is_bearish_engulfing(self, atr14[0], minimum_body_atr, volume_ratio) else math.nan, self.low[0] - h * 0.1 if is_morning_star(self, atr14[0], minimum_body_atr, gap_atr_ratio) else math.nan, self.high[0] + h * 0.1 if is_evening_star(self, atr14[0], minimum_body_atr, gap_atr_ratio) else math.nan, self.low[0] - h * 0.15 if is_hammer(self, in_downtrend, atr14[0], shadow_body_ratio) else math.nan, self.high[0] + h * 0.15 if is_shooting_star(self, in_uptrend, atr14[0], shadow_body_ratio) else math.nan, self.high[0] + h * 0.2 if is_dark_cloud_cover(self, in_uptrend, atr14[0], minimum_body_atr) else math.nan, self.low[0] - h * 0.2 if is_piercing(self, in_downtrend, atr14[0], minimum_body_atr) else math.nan, self.high[0] + h * 0.25 if is_three_black_crows(self, in_uptrend, atr14[0], minimum_body_atr) else math.nan, self.low[0] - h * 0.25 if is_three_white_soldiers(self, in_downtrend, atr14[0], minimum_body_atr) else math.nan )