SlimRibbonSuperTrend Strategy- Looking for backtesters

E

eddielee394

Member
Warehouse
I've been experimenting with a further modified version of the Slim Ribbon indicator that Markos had originally customized. I've converted it to a strategy along with integrating the SuperTrend ATR, slightly modified the sell signal conditions and a couple other minor tweaks. This is a very early beta version that still requires a good amount of testing and modification to the code, so I'm looking for other members they may be interested in trying it out and offering feedback.

All of the testing I've done so far has been long positions only (no short entries). I've been using this intraday primarily on 5min, 15min & 78 min time frames with pretty decent results (see screenshot below). Generally, I'll use the 78min for a short term swing trend confirmation, 15min to confirm intraday trend reversals & 5min for entries/exits. Buy signals are currently based on the parameters from the original indicator, while sell signals are a combination of the SuperTrendATR crossover and/or the sell signal parameter from the original study. I pretty much only pay attention to the strategy order entries/exit signals & not so much the arrows fired by the study (although I've left them active as early "warning" signals of trend changes in addition to testing/debugging purposes).

Most of the backtesting & performance analysis I've done so far has been on the /ES and haven't done any extensive backtesting on equities, although I have been using it on equities during my live trading as a trend confirmation tool and it's proved to be pretty helpful so far. Would be interested to do further equity backtesting with hard performance analysis.

As far as settings, I've set the default values to the settings that I've seen the best performance on based on the backtests so far (ATR mult: 1.2 & natr: 3). But, I'd be interested to see the performance based on other tweaks to the settings.

Note: this is a VERY, VERY early version of the script - so please keep that in mind when testing.

Early Performance Assessment:

Symbol: /ES
TimeFrame: 30 day/15min
PositionSize: 1 contract



Study link: http://tos.mx/veElNOu

Python:
#################################################################################
# SlimRibbonSuperTrend                                                          
# SlimRibbon study with SuperTrend ATR                                          
# Author: Eddielee394                                                           
# Version: 0.2                                                                  
# inspired by: SlimRibbon by Slim Miller and further customized by Markos       
#################################################################################

SetChartType(ChartType.CANDLE_TREND);

input price = close;

input superfast_length = 8;

input fast_length = 13;

input slow_length = 21;

input displace = 0;

input atrMult = 1.2;
#hint atrMult: increases the sensitivity of the ATR line

input nATR = 3;
#hint nATR:

input avgType = AverageType.HULL;
#hint avgType: sets the average type used to calculate the SuperTrendATR

input enableAlerts = no;
#hint enableAlerts: disables all alerts

input hideMovingAverages = yes;
#hint hideMovingAverages: hides all the moving average lines from the slimRibbon

input hideSuperTrendAtr = no;
#hint hideSuperTrendAtr: hides the SuperTrend ATR line

input tradesize = 1;

def ATR = MovingAverage(avgType, TrueRange(high, close, low), nATR);

def UP = HL2 + (atrMult * ATR);

def DN = HL2 + (-atrMult * ATR);

def ST = if close < ST[1] then UP else DN;

plot SuperTrend = ST;
SuperTrend.AssignValueColor(if close < ST then Color.RED else Color.GREEN);
SuperTrend.SetHiding(hideSuperTrendAtr);

def SuperTrendUP = if ST crosses below close[-1] then 1 else 0;
def isSuperTrendUP = SuperTrend > close;
def SuperTrendDN = if ST crosses above close[-1] then 1 else 0;
def isSuperTrendDN = SuperTrend < close;

#moving averages
def mov_avg8 = ExpAverage(price[-displace], superfast_length);

def mov_avg13 = ExpAverage(price[-displace], fast_length);

def mov_avg21 = ExpAverage(price[-displace], slow_length);

plot Superfast = mov_avg8;
Superfast.SetHiding(hideMovingAverages);

plot Fast = mov_avg13;
Fast.SetHiding(hideMovingAverages);

plot Slow = mov_avg21;
Slow.SetHiding(hideMovingAverages);


def buy = mov_avg8 > mov_avg13 and mov_avg13 > mov_avg21 and low > mov_avg8;

def stopbuy = mov_avg8 <= mov_avg13;

def buynow = !buy[1] and buy;

def buysignal = CompoundValue(1, if buynow and !stopbuy then 1 else if buysignal[1] == 1 and stopbuy then 0 else buysignal[1], 0);

plot Buy_Signal = buysignal[1] == 0 and buysignal == 1;

Buy_Signal.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);

Buy_Signal.SetDefaultColor(Color.GREEN);

Buy_Signal.HideTitle();

plot Momentum_Down = buysignal[1] == 1 and buysignal == 0;

Momentum_Down.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);

Momentum_Down.SetDefaultColor(Color.YELLOW);

Momentum_Down.HideTitle();

def sell = mov_avg8 < mov_avg13 and mov_avg13 < mov_avg21 and high < mov_avg8;

def stopsell = mov_avg8 >= mov_avg13;

def sellnow = !sell[1] and sell;

def sellsignal = CompoundValue(1, if sellnow and !stopsell then 1 else if sellsignal[1] == 1 and stopsell then 0 else sellsignal[1], 0);


plot Sell_Signal = sellsignal[1] == 0 and sellsignal;

Sell_Signal.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);

Sell_Signal.SetDefaultColor(Color.RED);

Sell_Signal.HideTitle();

plot Momentum_Up = sellsignal[1] == 1 and sellsignal == 0;

Momentum_Up.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);

Momentum_Up.SetDefaultColor(Color.YELLOW);

Momentum_Up.HideTitle();

plot Colorbars = if buysignal == 1 then 1 else if sellsignal == 1 then 2 else if buysignal == 0 or sellsignal == 0 then 3 else 0;

Colorbars.Hide();

Colorbars.DefineColor("Buy_Signal_Bars", Color.GREEN);

Colorbars.DefineColor("Sell_Signal_Bars", Color.RED);

Colorbars.DefineColor("Neutral", Color.YELLOW);

AssignPriceColor(if Colorbars == 1 then Colorbars.Color("buy_signal_bars") else if Colorbars == 2 then Colorbars.Color("Sell_Signal_bars") else  Colorbars.Color("neutral"));

#Orders
def sellTrigger = if SuperTrendDN or Sell_Signal then 1 else 0;
def buyTrigger = Buy_Signal;

AddOrder(OrderType.BUY_TO_OPEN, condition = buyTrigger, price = close, tradeSize = tradesize, tickcolor = Color.CYAN, arrowcolor = Color.CYAN, name = "Buy");

AddOrder(OrderType.SELL_TO_CLOSE, condition = sellTrigger, price = close[-1], tradeSize = tradesize, tickcolor = Color.PINK, arrowcolor = Color.DARK_RED, name = "Sell");


#Alerts

Alert(condition = enableAlerts and buysignal[1] == 0 and buysignal == 1, text = "Buy Signal", sound = Sound.Bell, "alert type" = Alert.BAR);

Alert(condition = enableAlerts and buysignal[1] == 1 and buysignal == 0, text = "Momentum_Down", sound = Sound.Bell, "alert type" = Alert.BAR);

Alert(condition = enableAlerts and sellsignal[1] == 0 and sellsignal == 1, text = "Sell Signal", sound = Sound.Bell, "alert type" = Alert.BAR);

Alert(condition = enableAlerts and sellsignal[1] == 1 and sellsignal == 0, text = "Momentum_Up", sound = Sound.Bell, "alert type" = Alert.BAR);

#end strategy
 
YungTraderFromMontana

YungTraderFromMontana

Active member
It's looking good keep at it. I think the best quick implementation would be sorting out consolidated periods. It losses lots of gains to chop looking at the floating p/l. It would also save you a little on fees/poor fills.
 
T

tradebyday

New member
So I have been testing something similar as far as trend trading, and I think there are a few tools to help with the elimination of chop trades. I will see what I can produce for you testing out the z-score ( https://tos.mx/jXHKqap ) and i will test out ergodic
 
M

maddy

New member
Warehouse
@eddielee394

-- Will this work for intraday?
-- Will this work for SPY?
 
Last edited by a moderator:
E

eddielee394

Member
Warehouse
-- Will this work for intraday?
-- Will this work for SPY?
Please refer to the original post.


All of the testing I've done so far has been long positions only (no short entries). I've been using this intraday primarily on 5min, 15min & 78 min time frames with pretty decent results (see screenshot below).
Most of the backtesting & performance analysis I've done so far has been on the /ES and haven't done any extensive backtesting on equities, although I have been using it on equities during my live trading as a trend confirmation tool and it's proved to be pretty helpful so far. Would be interested to do further equity backtesting with hard performance analysis.
 
E

eddielee394

Member
Warehouse
I think the best quick implementation would be sorting out consolidated periods. It losses lots of gains to chop looking at the floating p/l.
Yeah, most definitely. That's one of the caveats with this strategy, is that performance suffers hard during choppy periods. Lots of churn. Also, I don't have hard data to support this thesis but I feel like there's a lower win probability when a buy signal triggers while price is below the ATR. I was thinking about tweaking the buy conditions a bit to avoid those scenarios, but wanted to compile some more data on this current version of the strategy first.


So I have been testing something similar as far as trend trading, and I think there are a few tools to help with the elimination of chop trades. I will see what I can produce for you testing out the z-score ( https://tos.mx/jXHKqap ) and i will test out ergodic
Cool, sounds good. Whatever you're willing to share would be super helpful.
 
B

Bobbydigital83

New member
I think i just saw it repaint on me. Trying to wait for another signal to show it.. maybe someone else caught one?
 
E

eddielee394

Member
Warehouse
I think i just saw it repaint on me. Trying to wait for another signal to show it.. maybe someone else caught one?
No it doesn't repaint. Signals are set to trigger on the close of the next candle after the buy/sell condition is met. So the buy/sell condition is confirmed first, then the signal fires.
 
B

Bobbydigital83

New member
The "sell" signal repaints. I saw it do it twice so far.
 
E

eddielee394

Member
Warehouse
The "sell" signal repaints. I saw it do it twice so far.
Thanks for the info. So I can debug further, can you confirm if the repaint behavior occurs before or after the signal candle closes please? i.e. does the sell signal disappear/appear while signal candle is still open or is it changing once the signal candle has closed & subsequent candles have already rendered?
 
B

Bobbydigital83

New member
So the bar will signal a "close" only after the bar has already started.

So let's say the current bar is open. If the conditions for a "close" are met, it will retroactively signal as if a "close" signal was fired off from the very beginning.
 
E

eddielee394

Member
Warehouse
So the bar will signal a "close" only after the bar has already started.

So let's say the current bar is open. If the conditions for a "close" are met, it will retroactively signal as if a "close" signal was fired off from the very beginning.
Got it, thanks for the extra info. Would it be possible for you to post a screenshot of the behavior so i can dig into it a bit deeper? If you can make sure to include the ticker symbol, aggregation period & at least the last couple buy/sell orders in the screenshot that'd be great too. Thanks again!
 
B

Bobbydigital83

New member
https://ibb.co/cL78jf0

/es, 30 days & 15 mins


Where i have the circle is where the order "close" used to show up but now its shows up in the bottom. Not sure if you found the repaint issue and fixed it but it seems to be working fine now.
 
B

Bobbydigital83

New member
I changed the code here...

I also added some bits that should eliminate all repaints plus should give more "true to life" method for entries and exits...


#################################################################################
# SlimRibbonSuperTrend #
# SlimRibbon study with SuperTrend ATR #
# Author: Eddielee394 #
# Version: 0.2 #
# inspired by: SlimRibbon by Slim Miller and further customized by Markos #
#################################################################################

SetChartType(ChartType.CANDLE_TREND);

input price = close;

input superfast_length = 8;

input fast_length = 13;

input slow_length = 21;

input displace = 0;

input atrMult = 1.2;
#hint atrMult: increases the sensitivity of the ATR line

input nATR = 3;
#hint nATR:

input avgType = AverageType.HULL;
#hint avgType: sets the average type used to calculate the SuperTrendATR

input enableAlerts = no;
#hint enableAlerts: disables all alerts

input hideMovingAverages = yes;
#hint hideMovingAverages: hides all the moving average lines from the slimRibbon

input hideSuperTrendAtr = no;
#hint hideSuperTrendAtr: hides the SuperTrend ATR line

input tradesize = 1;

def ATR = MovingAverage(avgType, TrueRange(high, close, low), nATR);

def UP = HL2 + (atrMult * ATR);

def DN = HL2 + (-atrMult * ATR);

def ST = if close < ST[1] then UP else DN;

plot SuperTrend = ST;
SuperTrend.AssignValueColor(if close < ST then Color.RED else Color.GREEN);
SuperTrend.SetHiding(hideSuperTrendAtr);

def SuperTrendUP = if ST crosses below close[0] then 1 else 0;
def isSuperTrendUP = SuperTrend > close;
def SuperTrendDN = if ST crosses above close[0] then 1 else 0;
def isSuperTrendDN = SuperTrend < close;

#moving averages
def mov_avg8 = ExpAverage(price[-displace], superfast_length);

def mov_avg13 = ExpAverage(price[-displace], fast_length);

def mov_avg21 = ExpAverage(price[-displace], slow_length);

plot Superfast = mov_avg8;
Superfast.SetHiding(hideMovingAverages);

plot Fast = mov_avg13;
Fast.SetHiding(hideMovingAverages);

plot Slow = mov_avg21;
Slow.SetHiding(hideMovingAverages);


def buy = mov_avg8 > mov_avg13 and mov_avg13 > mov_avg21 and low > mov_avg8;

def stopbuy = mov_avg8 <= mov_avg13;

def buynow = !buy[1] and buy;

def buysignal = CompoundValue(1, if buynow and !stopbuy then 1 else if buysignal[1] == 1 and stopbuy then 0 else buysignal[1], 0);

plot Buy_Signal = buysignal[1] == 0 and buysignal == 1;

Buy_Signal.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);

Buy_Signal.SetDefaultColor(Color.GREEN);

Buy_Signal.HideTitle();

plot Momentum_Down = buysignal[1] == 1 and buysignal == 0;

Momentum_Down.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);

Momentum_Down.SetDefaultColor(Color.YELLOW);

Momentum_Down.HideTitle();

def sell = mov_avg8 < mov_avg13 and mov_avg13 < mov_avg21 and high < mov_avg8;

def stopsell = mov_avg8 >= mov_avg13;

def sellnow = !sell[1] and sell;

def sellsignal = CompoundValue(1, if sellnow and !stopsell then 1 else if sellsignal[1] == 1 and stopsell then 0 else sellsignal[1], 0);


plot Sell_Signal = sellsignal[1] == 0 and sellsignal;

Sell_Signal.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);

Sell_Signal.SetDefaultColor(Color.RED);

Sell_Signal.HideTitle();

plot Momentum_Up = sellsignal[1] == 1 and sellsignal == 0;

Momentum_Up.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);

Momentum_Up.SetDefaultColor(Color.YELLOW);

Momentum_Up.HideTitle();

plot Colorbars = if buysignal == 1 then 1 else if sellsignal == 1 then 2 else if buysignal == 0 or sellsignal == 0 then 3 else 0;

Colorbars.Hide();

Colorbars.DefineColor("Buy_Signal_Bars", Color.GREEN);

Colorbars.DefineColor("Sell_Signal_Bars", Color.RED);

Colorbars.DefineColor("Neutral", Color.YELLOW);

AssignPriceColor(if Colorbars == 1 then Colorbars.Color("buy_signal_bars") else if Colorbars == 2 then Colorbars.Color("Sell_Signal_bars") else Colorbars.Color("neutral"));

#Orders
def sellTrigger = if SuperTrendDN or Sell_Signal then 1 else 0;
def buyTrigger = Buy_Signal;

AddOrder(OrderType.BUY_TO_OPEN, condition = buyTrigger, price = open + ticksize(), tradeSize = tradesize, tickcolor = Color.CYAN, arrowcolor = Color.CYAN, name = "Buy");

AddOrder(OrderType.SELL_TO_CLOSE, condition = sellTrigger, price = close, tradeSize = tradesize, tickcolor = Color.PINK, arrowcolor = Color.DARK_RED, name = "Sell");


#Alerts

Alert(condition = enableAlerts and buysignal[1] == 0 and buysignal == 1, text = "Buy Signal", sound = Sound.Bell, "alert type" = Alert.BAR);

Alert(condition = enableAlerts and buysignal[1] == 1 and buysignal == 0, text = "Momentum_Down", sound = Sound.Bell, "alert type" = Alert.BAR);

Alert(condition = enableAlerts and sellsignal[1] == 0 and sellsignal == 1, text = "Sell Signal", sound = Sound.Bell, "alert type" = Alert.BAR);

Alert(condition = enableAlerts and sellsignal[1] == 1 and sellsignal == 0, text = "Momentum_Up", sound = Sound.Bell, "alert type" = Alert.BAR);

#end strategy
 
S

stoneyhrm

New member
I've been tooling around with it on the 5m, just downloaded it literally 10 mins ago. I'm really liking it for scalps on the 5 min, seems to not be phased by false moves upward.
 
E

eddielee394

Member
Warehouse
Where i have the circle is where the order "close" used to show up but now its shows up in the bottom. Not sure if you found the repaint issue and fixed it but it seems to be working fine now.
Cool, thanks for the screenshot & additional info. I believe there may be some confusion here regarding what specifically occurs when a study repaints (in addition to some misleading visual quirks with the strategy buy/sell order arrows & ThinkOrSwim). Repainting is when a signal, indicator, etc for a past candle changes based on the value of a future one. For example, a sell signal on the 3rd candle appears after the 5th candle makes a new low. That would be considered a repaint. In a live environment, we can't go back in time to trade that sell signal since it already happened. The ZigZag indicator is a good example, since preceding lows can't be defined until a new subsequent high is established.

In the case of this specific script I haven't been able to identify any cases where repainting occurs. Additionally, the signal logic is based on various moving average crossovers which by their very nature are lagging indicators - hence the buy/sell condition would need to be satisfied PRIOR to the signal being triggered. What I think you may be seeing is an order signal momentarily displaying before the close of the condition candle and then the condition being invalidated before the candle close - causing the signal to disappear. This is expected behavior & wouldn't be considered a repaint as entry/exit signal is only confirmed upon close of the condition candle. It shouldn't affect back testing results either. The caveat to that is TOS has some quirky behavior with the AddOrder() function where the order arrow will display on the bar after the condition is met, even though the actual price for that order is based on the closed condition bar. There's workarounds for it, but i just haven't tried implementing any yet.

Code:
def SuperTrendUP = if ST crosses below close then 1 else 0;

def SuperTrendDN = if ST crosses above close then 1 else 0;
Code:
AddOrder(OrderType.BUY_TO_OPEN, condition = buyTrigger, price = open + ticksize(), tradeSize = tradesize, tickcolor = Color.CYAN, arrowcolor = Color.CYAN, name = "Buy");
The modifications here use historical prices from bars preceding the signals. The SuperTrend* boolean conditions should only return true if the subsequent bar crosses over. So looking at this it's actually the opposite of true to life since the buy prices are prices that occur before the actual signals trigger.

Also when using the open price for the the buy order, the condition would need to be fulfilled before the trigger - hence the candle would need to already be closed. This instead says enter an order for the open price of a candle that's already closed above that value. I'm not sure why tickSize() is being added the to the open price. That'll further skew any p&L analysis. It's even more prevalent when you do a side by side P&L comparsion (see screenshot - original code on left, modified on right). There's a pretty significant increase in P&L for the right chart due to entries being made based on the open price of what would be in real life unconfirmed buy signals.


What I did notice from your screenshot is that when a buy signal triggers while price is already below the StATR, and a subsequent sell is triggered it fires the sell order 1 candle later than it should be, instead of on the confirmation of the SL sell signal. So i'll have to figure out a better way to integrate the atr crossover & the slim ribbon sell signals. That explains your comment in the screenshot about the sell signal placement. I appreciate you bringing that to my attention :)
 
E

eddielee394

Member
Warehouse
I've been tooling around with it on the 5m, just downloaded it literally 10 mins ago. I'm really liking it for scalps on the 5 min, seems to not be phased by false moves upward.
Be cautious on smaller time frames (<15min) particularly during choppy/consolidation periods. It fires ALOT of false signals. On my 5min charts I like it more as a reference than a dependency, if that makes any sense.
 

Top