Last active
July 18, 2025 17:50
-
-
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//@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) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//@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