Skip to content

Instantly share code, notes, and snippets.

@jjo
Last active July 18, 2025 17:50
Show Gist options
  • Save jjo/c9b8b4dcf9c21db8276471c6e9a3b33f to your computer and use it in GitHub Desktop.
Save jjo/c9b8b4dcf9c21db8276471c6e9a3b33f to your computer and use it in GitHub Desktop.
jjo's TBO Trend Breakout Oscillator (aiming at something alike CBT's private one -- without having used it :o)
//@version=5
indicator("jjo's TBO inChart (similar to CBT's)", shorttitle="jjo-TBO-inChart", overlay=false)
// === INDICATOR INPUTS ===
// Core TBO Settings
tbo_fast = input.int(9, "TBO Fast Period", minval=5, maxval=20, group="TBO Core")
tbo_medium = input.int(21, "TBO Medium Period", minval=15, maxval=35, group="TBO Core")
tbo_slow = input.int(50, "TBO Slow Period", minval=30, maxval=100, group="TBO Core")
tbo_smoothing = input.int(3, "TBO Smoothing", minval=1, maxval=10, group="TBO Core")
// Signal Sensitivity
signal_sensitivity = input.string("Medium", "Signal Sensitivity", options=["High", "Medium", "Low"], group="Signals")
show_divergence = input.bool(true, "Show Divergence Signals", group="Signals")
show_volume_signals = input.bool(true, "Show Volume Signals", group="Signals")
// Visual Settings
show_table = input.bool(true, "Show Info Table", group="Visual")
color_bars = input.bool(false, "Color Bars", group="Visual")
// === SENSITIVITY MAPPING ===
// Define thresholds based on sensitivity setting
buy_threshold = signal_sensitivity == "High" ? -0.15 : signal_sensitivity == "Medium" ? -0.25 : -0.35
sell_threshold = signal_sensitivity == "High" ? 0.15 : signal_sensitivity == "Medium" ? 0.25 : 0.35
extreme_buy = signal_sensitivity == "High" ? -0.85 : signal_sensitivity == "Medium" ? -0.75 : -0.65
extreme_sell = signal_sensitivity == "High" ? 0.85 : signal_sensitivity == "Medium" ? 0.75 : 0.65
volume_mult = signal_sensitivity == "High" ? 2.0 : signal_sensitivity == "Medium" ? 1.5 : 1.2
// === CORE CALCULATIONS ===
// Enhanced TBO calculation combining multiple approaches
ema_fast = ta.ema(close, tbo_fast)
ema_medium = ta.ema(close, tbo_medium)
ema_slow = ta.ema(close, tbo_slow)
// RSI calculation
rsi = ta.rsi(close, 14)
// Price momentum component
price_momentum = (close - close[tbo_fast]) / close * 100
volume_momentum = ta.change(volume, 1) / ta.sma(volume, 20)
// EMA relationship scoring
ema_score = 0.0
ema_score := ema_fast > ema_medium ? ema_score + 0.33 : ema_score - 0.33
ema_score := ema_medium > ema_slow ? ema_score + 0.33 : ema_score - 0.33
ema_score := close > ema_fast ? ema_score + 0.34 : ema_score - 0.34
// Volatility adjustment
atr = ta.atr(14)
atr_norm = atr / ta.sma(atr, 50)
// Combined TBO oscillator
tbo_raw = (ema_score * 0.5) + (ta.rsi(close, 14) - 50) / 50 * 0.3 + (price_momentum * 0.2)
tbo_adjusted = tbo_raw * math.min(atr_norm, 2.0) // Volatility adjustment
tbo = ta.ema(tbo_adjusted, tbo_smoothing)
// === SIGNAL GENERATION ===
// Volume analysis
volume_ma = ta.sma(volume, 20)
high_volume = volume > volume_ma * volume_mult
// Basic signals
buy_signal = ta.crossover(tbo, buy_threshold) and tbo < 0.5
sell_signal = ta.crossunder(tbo, sell_threshold) and tbo > -0.5
// Strong signals (extreme levels)
strong_buy = ta.crossover(tbo, extreme_buy) and tbo[1] < extreme_buy
strong_sell = ta.crossunder(tbo, extreme_sell) and tbo[1] > extreme_sell
// Volume-confirmed signals
vol_buy = buy_signal and high_volume
vol_sell = sell_signal and high_volume
// === DIVERGENCE DETECTION ===
// Simplified but effective divergence detection
lookback = 5
price_high = ta.highest(high, lookback)
price_low = ta.lowest(low, lookback)
tbo_high = ta.highest(tbo, lookback)
tbo_low = ta.lowest(tbo, lookback)
// Bullish divergence: Price makes lower low, TBO makes higher low
bull_div_condition = low == price_low and low < low[lookback] and
tbo == tbo_low and tbo > tbo[lookback] and tbo < -0.2
// Bearish divergence: Price makes higher high, TBO makes lower high
bear_div_condition = high == price_high and high > high[lookback] and
tbo == tbo_high and tbo < tbo[lookback] and tbo > 0.2
// Divergence signals
bullish_divergence = bull_div_condition and show_divergence
bearish_divergence = bear_div_condition and show_divergence
// === TREND STRENGTH ===
trend_strength = math.abs(tbo)
trend_direction = tbo > 0 ? "Bullish" : tbo < 0 ? "Bearish" : "Neutral"
// Market state
market_state = tbo > extreme_sell ? "Overbought" :
tbo < extreme_buy ? "Oversold" :
tbo > 0 ? "Bullish" :
tbo < 0 ? "Bearish" : "Neutral"
// === SIGNAL PLOTS ===
// Basic signals (reduced to tiny size)
plotshape(buy_signal, "Buy Signal", shape.labelup, location.belowbar,
color.green, text="BUY", textcolor=color.white, size=size.tiny)
plotshape(sell_signal, "Sell Signal", shape.labeldown, location.abovebar,
color.red, text="SELL", textcolor=color.white, size=size.tiny)
// Strong signals (reduced to small size)
plotshape(strong_buy, "Strong Buy", shape.triangleup, location.belowbar,
color.lime, text="STRONG", textcolor=color.black, size=size.small)
plotshape(strong_sell, "Strong Sell", shape.triangledown, location.abovebar,
color.maroon, text="STRONG", textcolor=color.white, size=size.small)
// Volume-confirmed signals (tiny size, fixed for overlay)
plotshape(vol_buy and show_volume_signals, "Volume Buy", shape.circle, location.belowbar,
color.blue, text="VOL", textcolor=color.white, size=size.tiny)
plotshape(vol_sell and show_volume_signals, "Volume Sell", shape.circle, location.abovebar,
color.purple, text="VOL", textcolor=color.white, size=size.tiny)
// Divergence signals on price chart
plotshape(bullish_divergence, "Bullish Divergence", shape.diamond, location.belowbar,
color.yellow, text="DIV+", textcolor=color.black, size=size.tiny)
plotshape(bearish_divergence, "Bearish Divergence", shape.diamond, location.abovebar,
color.orange, text="DIV-", textcolor=color.white, size=size.tiny)
// === ALERTS ===
alertcondition(buy_signal, "TBO Buy Signal", "TBO: Buy Signal Generated")
alertcondition(sell_signal, "TBO Sell Signal", "TBO: Sell Signal Generated")
alertcondition(strong_buy, "TBO Strong Buy", "TBO: Strong Buy Signal")
alertcondition(strong_sell, "TBO Strong Sell", "TBO: Strong Sell Signal")
alertcondition(vol_buy, "TBO Volume Buy", "TBO: Volume-Confirmed Buy")
alertcondition(vol_sell, "TBO Volume Sell", "TBO: Volume-Confirmed Sell")
alertcondition(bullish_divergence, "TBO Bullish Divergence", "TBO: Bullish Divergence Detected")
alertcondition(bearish_divergence, "TBO Bearish Divergence", "TBO: Bearish Divergence Detected")
// === BAR COLORING ===
// Bar coloring (optional for overlay mode)
barcolor(color_bars ? (strong_buy or buy_signal ? color.new(color.green, 80) :
strong_sell or sell_signal ? color.new(color.red, 80) :
bullish_divergence ? color.new(color.yellow, 80) :
bearish_divergence ? color.new(color.orange, 80) : na) : na, title="Signal Bars")
// === PERFORMANCE TABLE ===
if barstate.islast and show_table
var table info_table = table.new(position.top_right, 2, 13, bgcolor=color.white, border_width=1)
// Header
table.cell(info_table, 0, 0, "TBO Pro Dashboard", bgcolor=color.navy, text_color=color.white, text_size=size.small)
table.cell(info_table, 1, 0, "🎯", bgcolor=color.navy, text_color=color.white, text_size=size.small)
// Current readings
table.cell(info_table, 0, 1, "TBO Value", text_color=color.black, text_size=size.small)
table.cell(info_table, 1, 1, str.tostring(tbo, "#.###"),
text_color=tbo > 0 ? color.green : color.red, text_size=size.small)
table.cell(info_table, 0, 2, "Market State", text_color=color.black, text_size=size.small)
state_color = market_state == "Overbought" ? color.red :
market_state == "Oversold" ? color.green :
market_state == "Bullish" ? color.blue :
market_state == "Bearish" ? color.orange : color.gray
table.cell(info_table, 1, 2, market_state, text_color=state_color, text_size=size.small)
table.cell(info_table, 0, 3, "Trend Strength", text_color=color.black, text_size=size.small)
strength_text = trend_strength > 0.5 ? "Strong" : trend_strength > 0.25 ? "Moderate" : "Weak"
table.cell(info_table, 1, 3, strength_text, text_color=color.black, text_size=size.small)
table.cell(info_table, 0, 4, "Volume Status", text_color=color.black, text_size=size.small)
vol_status = high_volume ? "HIGH" : "NORMAL"
vol_color = high_volume ? color.green : color.gray
table.cell(info_table, 1, 4, vol_status, text_color=vol_color, text_size=size.small)
table.cell(info_table, 0, 5, "RSI", text_color=color.black, text_size=size.small)
rsi_color = rsi > 70 ? color.red : rsi < 30 ? color.green : color.black
table.cell(info_table, 1, 5, str.tostring(rsi, "#.1"), text_color=rsi_color, text_size=size.small)
table.cell(info_table, 0, 6, "Fast EMA", text_color=color.black, text_size=size.small)
table.cell(info_table, 1, 6, str.tostring(ema_fast, "#.##"), text_color=color.blue, text_size=size.small)
table.cell(info_table, 0, 7, "Medium EMA", text_color=color.black, text_size=size.small)
table.cell(info_table, 1, 7, str.tostring(ema_medium, "#.##"), text_color=color.orange, text_size=size.small)
table.cell(info_table, 0, 8, "Slow EMA", text_color=color.black, text_size=size.small)
table.cell(info_table, 1, 8, str.tostring(ema_slow, "#.##"), text_color=color.red, text_size=size.small)
table.cell(info_table, 0, 9, "EMA Alignment", text_color=color.black, text_size=size.small)
alignment = ema_fast > ema_medium and ema_medium > ema_slow ? "🟢 BULL" :
ema_fast < ema_medium and ema_medium < ema_slow ? "🔴 BEAR" : "🟡 MIXED"
alignment_color = ema_fast > ema_medium and ema_medium > ema_slow ? color.green :
ema_fast < ema_medium and ema_medium < ema_slow ? color.red : color.orange
table.cell(info_table, 1, 9, alignment, text_color=alignment_color, text_size=size.small)
table.cell(info_table, 0, 10, "Sensitivity", text_color=color.black, text_size=size.small)
table.cell(info_table, 1, 10, signal_sensitivity, text_color=color.black, text_size=size.small)
table.cell(info_table, 0, 11, "Last Signal", text_color=color.black, text_size=size.small)
last_signal = strong_buy ? "STRONG BUY" : strong_sell ? "STRONG SELL" :
buy_signal ? "BUY" : sell_signal ? "SELL" : "NONE"
signal_color = strong_buy or buy_signal ? color.green : strong_sell or sell_signal ? color.red : color.gray
table.cell(info_table, 1, 11, last_signal, text_color=signal_color, text_size=size.small)
table.cell(info_table, 0, 12, "Next Level", text_color=color.black, text_size=size.small)
next_level = tbo > 0 ? str.tostring(sell_threshold, "#.##") : str.tostring(buy_threshold, "#.##")
table.cell(info_table, 1, 12, next_level, text_color=color.black, text_size=size.small)
//@version=5
indicator("jjo's TBO outChart (similar to CBT's)", shorttitle="jjo-TBO-outChart", overlay=false)
// === INPUT PARAMETERS ===
// Trend Detection
trend_length = input.int(21, "Trend Length", minval=5, maxval=100, group="Trend Detection")
fast_ma = input.int(8, "Fast MA Period", minval=3, maxval=50, group="Trend Detection")
slow_ma = input.int(21, "Slow MA Period", minval=10, maxval=100, group="Trend Detection")
// Momentum & RSI
rsi_length = input.int(14, "RSI Length", minval=5, maxval=50, group="Momentum")
rsi_overbought = input.float(70, "RSI Overbought", minval=50, maxval=90, group="Momentum")
rsi_oversold = input.float(30, "RSI Oversold", minval=10, maxval=50, group="Momentum")
// Volume Analysis
volume_ma_length = input.int(20, "Volume MA Length", minval=5, maxval=100, group="Volume")
volume_multiplier = input.float(1.5, "Volume Breakout Multiplier", minval=1.0, maxval=5.0, group="Volume")
// Breakout Detection
breakout_threshold = input.float(0.02, "Breakout Threshold (%)", minval=0.005, maxval=0.1, group="Breakout")
atr_length = input.int(14, "ATR Length", minval=5, maxval=50, group="Breakout")
// Signal Filtering
enable_rsi_filter = input.bool(true, "Enable RSI Filter", group="Signal Filtering")
enable_volume_filter = input.bool(true, "Enable Volume Filter", group="Signal Filtering")
// === CALCULATIONS ===
// Basic price data
src = close
high_price = high
low_price = low
// Moving Averages
ema_fast = ta.ema(src, fast_ma)
ema_slow = ta.ema(src, slow_ma)
sma_trend = ta.sma(src, trend_length)
// RSI
rsi = ta.rsi(src, rsi_length)
// Volume Analysis
vol_ma = ta.sma(volume, volume_ma_length)
vol_breakout = volume > (vol_ma * volume_multiplier)
// ATR for volatility
atr = ta.atr(atr_length)
// === TREND CALCULATIONS ===
// Trend Direction
trend_up = ema_fast > ema_slow and ema_slow > sma_trend
trend_down = ema_fast < ema_slow and ema_slow < sma_trend
trend_neutral = not trend_up and not trend_down
// Trend Strength (normalized oscillator)
price_position = (src - ta.lowest(low_price, trend_length)) / (ta.highest(high_price, trend_length) - ta.lowest(low_price, trend_length))
trend_strength = (price_position - 0.5) * 2 // Normalize to -1 to +1
// === MOMENTUM OSCILLATOR ===
// Price momentum
price_change = ta.change(src, 1)
momentum = ta.sma(price_change, fast_ma)
momentum_normalized = momentum / atr
// Combined momentum (price + volume)
volume_momentum = ta.change(volume, 1) / ta.sma(volume, volume_ma_length)
combined_momentum = momentum_normalized + (volume_momentum * 0.3)
// === TBO MAIN OSCILLATOR ===
// Combine trend strength with momentum
tbo_raw = (trend_strength * 0.6) + (combined_momentum * 0.4)
// Smooth the oscillator
tbo = ta.ema(tbo_raw, 5)
// === BREAKOUT DETECTION ===
// Price breakout conditions
price_range = ta.highest(high_price, trend_length) - ta.lowest(low_price, trend_length)
breakout_up = (high_price - ta.highest(high_price[1], trend_length-1)) / price_range > breakout_threshold
breakout_down = (ta.lowest(low_price[1], trend_length-1) - low_price) / price_range > breakout_threshold
// === SUPPORT & RESISTANCE LEVELS ===
// Dynamic S/R based on recent highs/lows
pivot_high = ta.pivothigh(high_price, 5, 5)
pivot_low = ta.pivotlow(low_price, 5, 5)
resistance_level = ta.valuewhen(not na(pivot_high), pivot_high, 0)
support_level = ta.valuewhen(not na(pivot_low), pivot_low, 0)
// Convert S/R to oscillator space
resistance_osc = 0.8
support_osc = -0.8
// === SIGNAL GENERATION ===
// RSI filter conditions
rsi_bullish = not enable_rsi_filter or rsi < rsi_overbought
rsi_bearish = not enable_rsi_filter or rsi > rsi_oversold
// Volume filter conditions
vol_condition = not enable_volume_filter or vol_breakout
// Long signals
long_condition = trend_up and tbo > 0 and ta.crossover(tbo, 0.2) and rsi_bullish and vol_condition
long_breakout = breakout_up and trend_up and rsi_bullish and vol_condition
// Short signals
short_condition = trend_down and tbo < 0 and ta.crossunder(tbo, -0.2) and rsi_bearish and vol_condition
short_breakout = breakout_down and trend_down and rsi_bearish and vol_condition
// Close signals
close_long = ta.crossunder(tbo, 0.6) or (rsi > rsi_overbought and enable_rsi_filter)
close_short = ta.crossover(tbo, -0.6) or (rsi < rsi_oversold and enable_rsi_filter)
// === VISUALIZATION ===
// Main TBO line
plot(tbo, "TBO", color=color.blue, linewidth=2)
// Zero line
hline(0, "Zero Line", color=color.gray, linestyle=hline.style_dashed)
// Overbought/Oversold levels
hline(0.8, "Overbought", color=color.red, linestyle=hline.style_dotted)
hline(-0.8, "Oversold", color=color.green, linestyle=hline.style_dotted)
// Signal levels
hline(0.2, "Bull Signal", color=color.lime, linestyle=hline.style_dotted, display=display.none)
hline(-0.2, "Bear Signal", color=color.orange, linestyle=hline.style_dotted, display=display.none)
// Background coloring for trend
bgcolor(trend_up ? color.new(color.green, 95) : trend_down ? color.new(color.red, 95) : color.new(color.gray, 98))
// TBO histogram coloring
tbo_color = tbo > tbo[1] ? (tbo > 0 ? color.lime : color.green) : (tbo > 0 ? color.red : color.maroon)
plot(tbo, "TBO Histogram", color=tbo_color, style=plot.style_histogram, histbase=0, transp=70)
// === SIGNAL PLOTS ===
// Long signals
plotshape(long_condition, "Long Signal", shape.labelup, location.bottom, color.lime, text="LONG", textcolor=color.white, size=size.small)
plotshape(long_breakout, "Long Breakout", shape.triangleup, location.bottom, color.green, text="B↑", textcolor=color.white, size=size.tiny)
// Short signals
plotshape(short_condition, "Short Signal", shape.labeldown, location.top, color.red, text="SHORT", textcolor=color.white, size=size.small)
plotshape(short_breakout, "Short Breakout", shape.triangledown, location.top, color.maroon, text="B↓", textcolor=color.white, size=size.tiny)
// Close signals
plotshape(close_long, "Close Long", shape.xcross, location.top, color.orange, text="CL", textcolor=color.white, size=size.tiny)
plotshape(close_short, "Close Short", shape.xcross, location.bottom, color.yellow, text="CS", textcolor=color.black, size=size.tiny)
// === ALERTS ===
alertcondition(long_condition, "TBO Long Signal", "TBO Long Signal Generated")
alertcondition(short_condition, "TBO Short Signal", "TBO Short Signal Generated")
alertcondition(long_breakout, "TBO Long Breakout", "TBO Long Breakout Detected")
alertcondition(short_breakout, "TBO Short Breakout", "TBO Short Breakout Detected")
alertcondition(close_long, "TBO Close Long", "TBO Close Long Signal")
alertcondition(close_short, "TBO Close Short", "TBO Close Short Signal")
// === TABLE INFO ===
if barstate.islast
var table info_table = table.new(position.top_right, 2, 6, bgcolor=color.white, border_width=1)
table.cell(info_table, 0, 0, "TBO", text_color=color.black, text_size=size.small)
table.cell(info_table, 1, 0, str.tostring(math.round(tbo, 3)), text_color=color.black, text_size=size.small)
table.cell(info_table, 0, 1, "Trend", text_color=color.black, text_size=size.small)
table.cell(info_table, 1, 1, trend_up ? "UP" : trend_down ? "DOWN" : "NEUTRAL",
text_color=trend_up ? color.green : trend_down ? color.red : color.gray, text_size=size.small)
table.cell(info_table, 0, 2, "RSI", text_color=color.black, text_size=size.small)
table.cell(info_table, 1, 2, str.tostring(math.round(rsi, 1)), text_color=color.black, text_size=size.small)
table.cell(info_table, 0, 3, "Volume", text_color=color.black, text_size=size.small)
table.cell(info_table, 1, 3, vol_breakout ? "HIGH" : "NORMAL",
text_color=vol_breakout ? color.orange : color.gray, text_size=size.small)
table.cell(info_table, 0, 4, "Strength", text_color=color.black, text_size=size.small)
table.cell(info_table, 1, 4, str.tostring(math.round(trend_strength, 3)), text_color=color.black, text_size=size.small)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment