Everyone, here is the Balanced BB Breakout v2.1.
Release notes:
Release notes:
- Added RSI IFT
- Added option to select between Balance of Power and RSI IFT
- PnL is hidden by default now
- Added BB Crossing options, crossing from above or below. You may want to alert on crossing below the upper BB band but alert when crossing above the lower BB band. You can also set this to zero or middle line for both if you like.
- Changed how the Balance of Power order direction is calculated. @Chuck, I have good evidence that it was buying the BOP signal instead of selling and it was selling the BOP signal instead of buying. The vertical lines were correct, but the PnL calculation was reversed. Let me know if I am incorrect and we can revert it back.
- Added an option to enter number of shares/contracts for PnL
Python:
# Balanced BB Breakout Indicator
# Free for use. Header credits must be included when any form of the code included in this package is used.
# User assumes all risk. Author not responsible for errors or use of tool.
# v1.2 - Assembled by Chuck Edwards
# v2.0 - Barbaros - cleanup
# v2.1 - Barbaros - added RSI IFT, NumberOfShares, BB crossing options
declare lower;
# Market Forecast
def pIntermediate = MarketForecast().Intermediate;
AddLabel(yes,
"Market Forecast " +
if pIntermediate >= 80 then "Bullish" else
if pIntermediate <= 20 then "Bearish" else
if pIntermediate > pIntermediate[1] then "Rising" else
if pIntermediate < pIntermediate[1] then "Falling" else " ",
if pIntermediate >= 80 then CreateColor(204, 255, 204) else # Pre_Cyan
if pIntermediate <= 20 then Color.RED else
if pIntermediate > pIntermediate[1] then CreateColor(0, 165, 0) else # LabelGreen
if pIntermediate < pIntermediate[1] then Color.RED else Color.LIGHT_GRAY
);
# MACD_BB
input BBlength = 15;
input BBNum_Dev = 1.0;
input MACDfastLength = 12;
input MACDslowLength = 26;
input fastLengthMACD = 12;
input slowLengthMACD = 26;
input MACDLength = 25;
input averageTypeMACD = AverageType.WEIGHTED;
input showBreakoutSignals = no;
def MACD_Data = MACD(fastLength = MACDfastLength, slowLength = MACDslowLength, MACDLength = MACDLength);
plot MACD_Dots = MACD_Data;
plot MACD_Line = MACD_Data;
plot BB_Upper = reference BollingerBands(price = MACD_Line, length = BBlength, Num_Dev_Dn = -BBNum_Dev, Num_Dev_Up = BBNum_Dev).UpperBand;
plot BB_Lower = reference BollingerBands(price = MACD_Line, length = BBlength, Num_Dev_Dn = -BBNum_Dev, Num_Dev_Up = BBNum_Dev).Lowerband;
plot BB_Midline = reference BollingerBands(price = MACD_Line, length = BBlength, Num_Dev_Dn = -BBNum_Dev, Num_Dev_Up = BBNum_Dev).MidLine;
BB_Upper.SetDefaultColor(Color.RED);
BB_Lower.SetDefaultColor(Color.GREEN);
BB_Midline.SetDefaultColor(Color.LIGHT_RED);
BB_Midline.SetStyle(Curve.FIRM);
MACD_Line.AssignValueColor(
if MACD_Line > MACD_Line[1] and MACD_Line >= BB_Upper then Color.GREEN
else if MACD_Line < MACD_Line[1] and MACD_Line >= BB_Upper then Color.DARK_GREEN
else if MACD_Line < MACD_Line[1] and MACD_Line <= BB_Lower then Color.RED else
if MACD_Line > MACD_Line[1] and MACD_Line <= BB_Lower then Color.DARK_RED
else Color.GRAY
);
MACD_Line.SetLineWeight(1);
MACD_Dots.AssignValueColor(
if MACD_Line > MACD_Line[1] and MACD_Line > BB_Upper then Color.GREEN
else if MACD_Line < MACD_Line[1] and MACD_Line > BB_Upper then Color.DARK_GREEN
else if MACD_Line < MACD_Line[1] and MACD_Line < BB_Lower then Color.RED
else if MACD_Line > MACD_Line[1] and MACD_Line < BB_Lower then Color.DARK_RED
else Color.GRAY
);
MACD_Dots.SetPaintingStrategy(PaintingStrategy.LINE_VS_POINTS);
MACD_Dots.SetLineWeight(1);
AddLabel(yes,
"Trend " +
if MACD_Line > MACD_Line[1] and MACD_Line > BB_Upper then "Bullish"
else if MACD_Line < MACD_Line[1] and MACD_Line > BB_Upper then "Falling"
else if MACD_Line < MACD_Line[1] and MACD_Line < BB_Lower then "Bearish"
else if MACD_Line > MACD_Line[1] and MACD_Line < BB_Lower then "Rising"
else "Neutral",
if MACD_Line > MACD_Line[1] and MACD_Line > BB_Upper then Color.GREEN
else if MACD_Line < MACD_Line[1] and MACD_Line > BB_Upper then Color.DARK_GREEN
else if MACD_Line < MACD_Line[1] and MACD_Line < BB_Lower then Color.RED
else if MACD_Line > MACD_Line[1] and MACD_Line < BB_Lower then Color.DARK_RED
else Color.GRAY
);
# Keltner Channels
input factor = 1.5;
input length = 20;
input averageType = AverageType.EXPONENTIAL;
input trueRangeAverageType = AverageType.EXPONENTIAL;
def shift = factor * MovingAverage(trueRangeAverageType, TrueRange(high, close, low), length);
def average = MovingAverage(averageType, close, length);
def Upper_Band = average + shift;
def Lower_Band = average - shift;
# Bollinger Bands
input BBLength2 = 20;
input Num_Dev_Dn = -2.0;
input Num_Dev_up = 2.0;
input bb_averageType = AverageType.SIMPLE;
def sDev = StDev(data = close, length = BBLength2);
def MidLine = MovingAverage(bb_averageType, data = close, length = BBLength2);
def LowerBand = MidLine + Num_Dev_Dn * sDev;
def UpperBand = MidLine + Num_Dev_up * sDev;
# BB Cloud
AddCloud(if UpperBand <= Upper_Band and LowerBand >= Lower_Band then BB_Upper else BB_Lower, BB_Lower, Color.YELLOW);
AddCloud(BB_Upper, BB_Lower, Color.LIGHT_RED);
# BB Alert
input BBCrossFromAboveAlert = {default "Zero", "Lower", "Middle", "Upper"};
input BBCrossFromBelowAlert = {default "Zero", "Lower", "Middle", "Upper"};
def BBCrossFromAboveVal = if BBCrossFromAboveAlert == BBCrossFromAboveAlert.Lower then BB_Lower
else if BBCrossFromAboveAlert == BBCrossFromAboveAlert.Upper then BB_Upper
else 0;
def BBCrossFromBelowVal = if BBCrossFromBelowAlert == BBCrossFromBelowAlert.Lower then BB_Lower
else if BBCrossFromBelowAlert == BBCrossFromBelowAlert.Upper then BB_Upper
else 0;
Alert(MACD_Line crosses above BBCrossFromBelowVal, "Time to Buy", Alert.BAR, Sound.Chimes);
Alert(MACD_Line crosses below BBCrossFromBelowVal, "Time to Sell", Alert.BAR, Sound.Bell);
# RSI/STOCASTIC/MACD CONFLUENCE COMBO
def Value = MovingAverage(averageTypeMACD, close, fastLengthMACD) - MovingAverage(averageTypeMACD, close, slowLengthMACD);
def Avg = MovingAverage(averageTypeMACD, Value, MACDLength);
plot Diff = Value - Avg;
Diff.SetDefaultColor(GetColor(5));
Diff.SetPaintingStrategy(PaintingStrategy.HISTOGRAM);
Diff.SetLineWeight(4);
Diff.DefineColor("Positive and Up", Color.GREEN);
Diff.DefineColor("Positive and Down", Color.DARK_GREEN);
Diff.DefineColor("Negative and Down", Color.RED);
Diff.DefineColor("Negative and Up", Color.DARK_RED);
Diff.AssignValueColor(
if Diff >= 0 then
if Diff > Diff[1] then Diff.Color("Positive and Up") else Diff.Color("Positive and Down")
else
if Diff < Diff[1] then Diff.Color("Negative and Down") else Diff.Color("Negative and Up")
);
plot ZeroLine = 0;
ZeroLine.SetDefaultColor(Color.RED);
ZeroLine.HideTitle();
ZeroLine.HideBubble();
plot UpSignalMACD = if Diff crosses above ZeroLine then ZeroLine else Double.NaN;
UpSignalMACD.SetDefaultColor(Color.UPTICK);
UpSignalMACD.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
UpSignalMACD.SetHiding(!showBreakoutSignals);
plot DownSignalMACD = if Diff crosses below ZeroLine then ZeroLine else Double.NaN;
DownSignalMACD.SetDefaultColor(Color.DOWNTICK);
DownSignalMACD.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN);
DownSignalMACD.SetHiding(!showBreakoutSignals);
input lengthRSI = 7;
input averageTypeRSI = AverageType.EXPONENTIAL;
def NetChgAvg = MovingAverage(averageTypeRSI, close - close[1], lengthRSI);
def TotChgAvg = MovingAverage(averageTypeRSI, AbsValue(close - close[1]), lengthRSI);
def ChgRatio = if TotChgAvg != 0 then NetChgAvg / TotChgAvg else 0;
def RSI = 50 * (ChgRatio + 1);
input KPeriod = 5;
input DPeriod = 3;
input averageTypeStoch = AverageType.WILDERS;
def SlowK = reference StochasticFull(80, 20, KPeriod, DPeriod, high, low, close, 3, averageTypeStoch).FullK;
def SlowD = reference StochasticFull(80, 20, KPeriod, DPeriod, high, low, close, 3, averageTypeStoch).FullD;
def rsiGreen = if RSI >= 50 then 1 else Double.NaN;
def rsiRed = if RSI < 50 then 1 else Double.NaN;
def stochGreen = if SlowK >= 50 then 1 else Double.NaN;
def stochRed = if SlowK < 50 then 1 else Double.NaN;
def macdGreen = if Value > Avg then 1 else Double.NaN;
def macdRed = if Value < Avg then 1 else Double.NaN;
input paintBars = yes;
AssignPriceColor(
if paintBars and RSI >= 50 and SlowK >= 50 and Value > Avg then Color.GREEN
else if paintBars and RSI < 50 and SlowK < 50 and Value < Avg then Color.RED
else if paintBars then Color.DARK_GRAY
else Color.CURRENT
);
# Shade areas based on criteria; adjust as needed
AddCloud(
if rsiGreen and stochGreen and macdGreen then Double.POSITIVE_INFINITY else Double.NaN,
if rsiGreen and stochGreen then Double.NEGATIVE_INFINITY else Double.NaN, Color.LIGHT_GREEN
);
AddCloud(
if rsiRed and stochRed and macdRed then Double.POSITIVE_INFINITY else Double.NaN,
if rsiRed and stochRed then Double.NEGATIVE_INFINITY else Double.NaN, Color.LIGHT_RED
);
# Balance of Power
input EMA = 20;
input TEMA = 20;
def THL = If(high != low, high - low, 0.01);
def BullOpen = (high - open) / THL;
def BearOpen = (open - low) / THL;
def BullClose = (close - low) / THL;
def BearClose = (high - close) / THL;
def BullOC = If(close > open, (close - open) / THL, 0);
def BearOC = If(open > close, (open - close) / THL, 0);
def BullReward = (BullOpen + BullClose + BullOC) / 1;
def BearReward = (BearOpen + BearClose + BearOC) / 1;
def BOP = BullReward - BearReward;
def SmoothBOP = ExpAverage(BOP, EMA);
def xPrice = SmoothBOP;
def xEMA1 = ExpAverage(SmoothBOP, TEMA);
def xEMA2 = ExpAverage(xEMA1, TEMA);
def xEMA3 = ExpAverage(xEMA2, TEMA);
def nRes = 3 * xEMA1 - 3 * xEMA2 + xEMA3;
def SmootherBOP = nRes;
def s1 = SmoothBOP;
def s2 = SmootherBOP;
def s3 = SmootherBOP[2];
def BOPshort = s2 < s3 and s2[1] > s3[1];
def BOPlong = s2 > s3 and s2[1] < s3[1];
# RSI IFT
def over_Bought = .5;
def over_Sold = -.5;
def R = reference RSI(5, close) - 50;
def AvgRSI = MovingAverage(AverageType.Exponential,R,9);
def inverseRSI = (Power(Double.E, 2 * AvgRSI) - 1) / (Power(Double.E, 2 * AvgRSI) + 1);
def RSI_IFT_Buy = (inverseRSI > 0) and (inverseRSI[1] < 0);
def RSI_IFT_Sell = (inverseRSI[1] > 0) and (inverseRSI < 0);
# Vertical Buy/Sell Signals
input buySellStrategy = { "BalanceOfPower", default "RSI_IFT" };
AddVerticalLine(if buySellStrategy == buySellStrategy.BalanceOfPower then BOPshort else RSI_IFT_Sell,
close, Color.RED, Curve.SHORT_DASH);
AddVerticalLine(if buySellStrategy == buySellStrategy.BalanceOfPower then BOPlong else RSI_IFT_Buy,
close, Color.GREEN, Curve.SHORT_DASH);
# P/L Statement
input PandL_Label = No;
input NumOfShares = 1;
input IntraDayOnly = Yes;
Assert(NumOfShares >= 1, "Number of Shares needs to be equal or grater than 1");
def orderDir = if IntraDayOnly and (SecondsTillTime(930) > 0 or SecondsFromTime(1600) > 0) then 0
else CompoundValue(1,
if buySellStrategy == buySellStrategy.BalanceOfPower then
if BOPlong then 1 else if BOPShort then -1 else orderDir[1]
else
if RSI_IFT_Buy then 1 else if RSI_IFT_Sell then -1 else orderDir[1]
, 0);
def isOrder = orderDir crosses 0;
def orderCount = CompoundValue(1, if IsNaN(isOrder) then 0 else if isOrder then orderCount[1] + 1 else orderCount[1], 0);
def noBar = IsNaN(open[-1]);
def orderPrice = if isOrder then if noBar then close else open[-1] else orderPrice[1];
def profitLoss = if !isOrder or orderCount == 1 then 0
else if orderDir < 0 then orderPrice[1] - orderPrice
else if orderDir > 0 then orderPrice - orderPrice[1] else 0;
def profitLossSum = CompoundValue(1, if IsNaN(isOrder) then 0 else if isOrder then profitLossSum[1] + profitLoss else profitLossSum[1], 0);
def orderWinners = CompoundValue(1, if IsNaN(isOrder) then orderWinners[1] else if orderCount > 1 and profitLoss > 0 then orderWinners[1] + 1 else orderWinners[1], 0);
AddLabel(PandL_Label,
orderCount + " orders (" + AsPercent(orderWinners / orderCount) + ") | P/L " + AsDollars ((profitLossSum / TickSize()) * TickValue() * NumOfShares),
if profitLossSum > 0 then Color.GREEN else if profitLossSum < 0 then Color.RED else Color.GRAY
);