SPX Trading Strategy for ThinkorSwim

Status
Not open for further replies.

@barbaros

I've been experimenting with the study a bit. I set the bb cross distance to .35 instead of 1. It gives me about 20-25 more trades per 30 days (between 9:30-1600), and the winrate does not seem to be affected by this in a statistically significant way.

One thing I don't understand: if I set exitat profitdeltaormacd, the winrate is lower than macd alone, which has an astronomically high winrate by itself. For example, with bbcrossdistance .35, 930-1600, 30d 10m, allowing cross before 930, exitat macdonly gives 57 trades with 89.47% winrate. But same setup with profitdeltaormacd (and profit delta of 5pts) gives 57 trades with 61.4% winrate.

Shouldn't the option of profit delta or macd provide the same or higher winrate than macd alone? Since it is considered a win if it hits 5pts or macd for a profit (which alone has a 89% winrate), then it should be 89% winrate even if it never hits the 5pt target (and equal or possibly higher than 89% if it sometimes hits 5pts), or am I missing something? [EDIT: I'm starting to think the winrate for profitdeltaormacd considers a win to be only when it hits the profit delta and not if it stops with macd earlier, even if at a profit. Is that correct?]

Btw, what is considered a win for the winrate calculation? anything above 0 or does it have a minimum. I could not understand it from the code.
 
Last edited:

Ben's Swing Trading Strategy + Indicator

I wouldn't call this a course. My goal is zero fluff. I will jump right into my current watchlist, tell you the ThinkorSwim indicator that I'm using, and past trade setups to help you understand my swing trading strategy.

I'm Interested

Join useThinkScript to post your question to a community of 21,000+ developers and traders.

It’s very easy....all you need to do is open the settings for each of the indicators and start changing the numbers around. I’d start with one of the days this week where it was a bad or very short lived entry and make it to where it does NOT get you in the trade. I always tweak to safety first. Then see what it does in backtesting. You’ll never find perfection....but def get it to where it keeps the trade safe first. At least that’s always been my approach.
Thanks. I'll take a look over the weekend and see what I come up with.
 

@barbaros

I've been experimenting with the study a bit. I set the bb cross distance to .35 instead of 1. It gives me about 20-25 more trades per 30 days (between 9:30-1600), and the winrate does not seem to be affected by this in a statistically significant way.

One thing I don't understand: if I set exitat profitdeltaormacd, the winrate is lower than macd alone, which has an astronomically high winrate by itself. For example, with bbcrossdistance .35, 930-1600, 30d 10m, allowing cross before 930, exitat macdonly gives 57 trades with 89.47% winrate. But same setup with profitdeltaormacd (and profit delta of 5pts) gives 57 trades with 61.4% winrate.

Shouldn't the option of profit delta or macd provide the same or higher winrate than macd alone? Since it is considered a win if it hits 5pts or macd for a profit (which alone has a 89% winrate), then it should be 89% winrate even if it never hits the 5pt target (and equal or possibly higher than 89% if it sometimes hits 5pts), or am I missing something? [EDIT: I'm starting to think the winrate for profitdeltaormacd considers a win to be only when it hits the profit delta and not if it stops with macd earlier, even if at a profit. Is that correct?]

Btw, what is considered a win for the winrate calculation? anything above 0 or does it have a minimum. I could not understand it from the code.
Well, you found another bug. I need to hire you to test my other code. I’ll debug and fix this weekend.
 
Well, you found another bug. I need to hire you to test my other code. I’ll debug and fix this weekend.
Couldn't let it go. Here is a potential fix. Let me know how it works out.

Python:
# SPX Strategy
# Strategy designed by Hypoluxa
# barbaros - v1  - 2021/02/25
# barbaros - v2  - 2021/02/25 - added warning arrows, alerts, statistics and non SPX trading option
# barbaros - v3  - 2021/02/26 - added SMA plots, chart type, and price plot
# barbaros - v4  - 2021/03/02 - added trade time limit and do not cross before start time
# barbaros - v5  - 2021/03/02 - added direction label
# barbaros - v6  - 2021/03/03 - added alerts, exit at profit delta, arrows for exiting position, and changed defaults to show all
# barbaros - v7  - 2021/03/03 - removed inputs for disabling arrows - this can be done in ToS already,
#                              added profit target line, ExitAt selection and ATR target
# barbaros - v8  - 2021/03/05 - added profit target label, changed the exit strategy to high/low instead of close
# barbaros - v9  - 2021/03/06 - added entry price label
# barbaros - v10 - 2021/03/11 - trying to fix before 9:40am EST crossing filter
# barbaros - v11 - 2021/03/11 - removed chart type overwriting
# barbaros - v12 - 2021/03/11 - fixed issue with disabling DoNotCrossBeforeStart
# barbaros - v13 - 2021/03/12 - fixed issue with macdonly win counting

# Setup
# 10 Min chart
# Heikin Ashi candles
# SMA 3/9
# MACD Bollinger Bands (the one Ben has posted on here a while back) 8,16,36 and uncheck "show plot" on the BB upper, BB lower and BB mid line. You will only be using the zero line, MACD dots and MACD Line for entry purposes. The inputs of "b length" and "bb num dev" are irrelevant here since you will remove the the plots that I mentioned.
# ErgodicOsc 2,10,36 (changed the default negative to red)

# Option call logic:
# 1. The SMA's have to cross first.
# 2. For a call opportunity, you will then wait to see if the MACD dots cross above the MACD zero line. Its critical here to wait until one dot has cleared the zero line...you MUST see a gap....never enter with the dot on the line. Dots must be consistent as well...if its going up...then they must all be white...if a red dot populates between the time a white dot hits the zero line and the time one crosses clear....don't enter. I have a screenshot below showing this example - 13:00 a red dot appears.
# 3. The ErgodicOsc HAS to be green when the MACD dot crosses above the zero line. You can hold the trade most of the time until this turns from green to red....but I always set a sell limit just in case it whips back in the opposite direction.

# Option put logic:
# Obviously its the complete opposite of what I've described above for a call. BUT - the SMA's still have to cross first.

# Note:
# Historical price action testing shows that if the MACDBB crosses zerobase before 9:40 EST, do not take the trade. Also, there might be too much choppiness after 15:00 EST.

declare upper;

input AllowNonSPX = no;
input ShowLabels = yes;
input ShowStatistics = yes;
input ExitAt = {default "profitDeltaOrMACD", "MACDOnly", "ATRorMACD"};
input ProfitDelta = 1.0;
input ATRLength = 7.0;
input ATRMult = 0.5;
input LimitTime = yes;
input StartTime = 940;
input StopTime = 1500;
input DoNotCrossBeforeStart = yes;

input price = close;
input SMAFastLength = 3;
input SMASlowLength = 9;
input BBlength = 30;
input BBNum_Dev = 0.8;
input BBCrossInBars = 3;
input BBCrossDistance = 1.0;
input MACDfastLength = 8;
input MACDslowLength = 16;
input MACDLength = 36;
input ERGODICLongLength = 2;
input ERGODICShortLength = 10;
input ERGODICSignalLength = 36;
input ERGODICAverageType = {"SIMPLE", default "EXPONENTIAL", "WEIGHTED", "WILDERS", "HULL"};

# Check for 10min chart
Assert(AllowNonSPX or GetAggregationPeriod() == AggregationPeriod.TEN_MIN, "Incorrect Chart Time, use 10m");
Assert(AllowNonSPX or GetSymbol() == "SPX", "Incorrect Chart Time, use 10m");

# MACD
def MACD_Data = MACD(fastLength = MACDfastLength, slowLength = MACDslowLength, MACDLength = MACDLength);
def MACD_Direction = if MACD_Data > MACD_Data[1] then 1 else -1;
def MACD_CrossBar = if MACD_Data crosses above 0 within 1 bar or MACD_Data crosses below 0 within 1 bar then BarNumber() else MACD_CrossBar[1];
def MACD_CrossBarAgo = BarNumber() - MACD_CrossBar;

# Ergodic
def Ergodic_Data = ErgodicOsc("long length" = ERGODICLongLength, "short length" = ERGODICShortLength, "signal length" = ERGODICSignalLength, "average type" = ERGODICAverageType).ErgodicOsc;

# SMAs
def SMA_Fast = SimpleMovingAvg(price, SMAFastLength);
def SMA_Slow = SimpleMovingAvg(price, SMASlowLength);

# ATR for progit target
def atrDelta = ATR(ATRLength) * ATRMult;

# Time Limit
def isTradeTime = if LimitTime then SecondsFromTime(StartTime) >= 0 and SecondsTillTime(StopTime) >= 0 else yes;
def isNotCrossBeforeStart = if !DoNotCrossBeforeStart then yes
                            else if DoNotCrossBeforeStart and SecondsFromTime(StartTime) == 0 and MACD_CrossBarAgo == 1 then no
                            else if DoNotCrossBeforeStart and SecondsFromTime(StartTime) != 0 and MACD_CrossBar[1] != MACD_CrossBar then yes
                            else isNotCrossBeforeStart[1];

# Signals
def buySignal = isTradeTime and isNotCrossBeforeStart and SMA_Fast > SMA_Slow and Ergodic_Data > 0 and MACD_Direction == 1 and MACD_Data >= BBCrossDistance and MACD_Data crosses above 0 within BBCrossInBars bars;
def buyWarnSignal = isTradeTime and isNotCrossBeforeStart and SMA_Fast > SMA_Slow and Ergodic_Data > 0 and MACD_Direction == 1 and MACD_CrossBar[1] != MACD_CrossBar;

def sellSignal = isTradeTime and isNotCrossBeforeStart and SMA_Fast < SMA_Slow and Ergodic_Data < 0 and MACD_Direction == -1 and MACD_Data <= -BBCrossDistance and MACD_Data crosses below 0 within BBCrossInBars bars;
def sellWarnSignal = isTradeTime and isNotCrossBeforeStart and SMA_Fast < SMA_Slow and Ergodic_Data < 0 and MACD_Direction == -1 and MACD_CrossBar[1] != MACD_CrossBar;

# Plots
plot buy = buySignal and !buySignal[1];
buy.setPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
buy.setDefaultColor(Color.GREEN);
buy.setLineWeight(3);

plot buyWarn = buyWarnSignal and !buyWarnSignal[1];
buyWarn.setPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
buyWarn.setDefaultColor(Color.CYAN);
buyWarn.setLineWeight(1);

plot sell = sellSignal and !sellSignal[1];
sell.setPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
sell.setDefaultColor(Color.RED);
sell.setLineWeight(3);

plot sellWarn = sellWarnSignal and !sellWarnSignal[1];
sellWarn.setPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
sellWarn.setDefaultColor(Color.MAGENTA);
sellWarn.setLineWeight(1);

plot lastPrice = price;
lastPrice.setPaintingStrategy(PaintingStrategy.POINTS);
lastPrice.setDefaultColor(Color.GREEN);

plot fast = SMA_Fast;
fast.setPaintingStrategy(PaintingStrategy.LINE);
fast.setDefaultColor(Color.CYAN);
fast.setLineWeight(1);

plot slow = SMA_Slow;
slow.setPaintingStrategy(PaintingStrategy.LINE);
slow.setDefaultColor(Color.MAGENTA);
slow.setLineWeight(1);

# PnL
def entryPrice = if (buySignal and !buySignal[1]) or (sellSignal and !sellSignal[1]) then open[-1] else entryPrice[1];
def profitDeltaTarget = if ExitAt == ExitAt.profitDeltaOrMACD then ProfitDelta else if ExitAt == ExitAt.ATRorMACD then atrDelta else 0;
def profitTarget = if buySignal and !buySignal[1] then entryPrice + profitDeltaTarget
                   else if sellSignal and !sellSignal[1] then entryPrice - profitDeltaTarget
                   else profitTarget[1];
def orderDir = if BarNumber() == 1 then 0
               else if orderDir[1] == 0 and buySignal and !buySignal[1] then 1
               else if orderDir[1] == 1 and (MACD_Direction != 1 or (ExitAt != ExitAt.MACDOnly and high >= profitTarget)) then 0
               else if orderDir[1] == 0 and sellSignal and !sellSignal[1] then -1
               else if orderDir[1] == -1 and (MACD_Direction != -1 or (ExitAt != ExitAt.MACDOnly and low <= profitTarget)) then 0
               else orderDir[1];

def isOrder = if IsNaN(orderDir) then no else orderDir crosses 0;
def orderCount = CompoundValue(1, if IsNaN(isOrder) then 0 else if isOrder then orderCount[1] + 1 else orderCount[1], 0);

def orderWinners = if BarNumber() == 1 then 0
                    else if orderDir[1] == 1 and orderDir == 0 then
                        if (ExitAt != ExitAt.MACDOnly and high >= profitTarget[1]) or (close > entryPrice) then orderWinners[1] + 1 else orderWinners[1]
                    else if orderDir[1] == -1 and orderDir == 0 then
                        if (ExitAt != ExitAt.MACDOnly and low <= profitTarget[1]) or (close < entryPrice) then orderWinners[1] + 1 else orderWinners[1]
            else orderWinners[1];
def winRate = orderWinners / orderCount;

# Plot Exit Positions
plot buyExit = orderDir[1] == 1 and orderDir != 1;
buyExit.setPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
buyExit.setDefaultColor(Color.GREEN);
buyExit.setLineWeight(3);

plot sellExit = orderDir[1] == -1 and orderDir != -1;
sellExit.setPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
sellExit.setDefaultColor(Color.RED);
sellExit.setLineWeight(3);

plot profitTargetLevel = if orderDir != 0 or orderDir[1] != 0 then profitTarget else Double.NaN;
profitTargetLevel.setPaintingStrategy(PaintingStrategy.LINE);
profitTargetLevel.setDefaultColor(Color.GREEN);
profitTargetLevel.setLineWeight(3);

# Labels
AddLabel(ShowLabels, "SPX Strategy", Color.WHITE);
AddLabel(ShowLabels, if buy then "Buy"
                     else if sell then "Sell"
                     else if buyWarn or sellWarn then "Warn"
                     else if orderDir == 1 then "Long"
                     else if orderDir == -1 then "Short"
                     else "Neutral",
                     if buy then Color.GREEN
                     else if sell then Color.RED
                     else if buyWarn then Color.YELLOW
                     else if sellWarn then Color.LIGHT_RED
                     else if orderDir == 1 then Color.YELLOW
                     else if orderDir == -1 then Color.LIGHT_RED
                     else Color.GRAY
);
AddLabel(ShowLabels and orderDir != 0 and !isNaN(entryPrice), "Entry " + AsDollars(entryPrice), Color.GRAY);
AddLabel(ShowLabels and orderDir != 0 and !isNaN(profitTarget), "Target " + AsDollars(profitTarget), Color.GRAY);
AddLabel(ShowLabels and ShowStatistics, "" + orderCount + " Trades | " + AsPercent(winRate), if winRate > 0.5 then Color.GREEN else Color.RED);

# Alerts
Alert(buy, "Buy", Alert.BAR, Sound.Ring);
Alert(sell, "Sell", Alert.BAR, Sound.Ring);
Alert(buyWarn, "Buy Warning", Alert.BAR, Sound.Ding);
Alert(sellWarn, "Sell Warning", Alert.BAR, Sound.Ding);
Alert(buyExit, "Buy Exit", Alert.BAR, Sound.Ring);
Alert(sellExit, "Sell Exit", Alert.BAR, Sound.Ring);
 
It helps as a gauge as to what SPX is going to do and possibly for how long. If VIX is in a downfall...then SPX will be going up and vice versa. It’s just an additional confirmation.
kind of like sports...one team is winning and one is losing.

SPX setup screenshot:

n7fh4h9.png
Hi Hypoluxa, thank you for sharing this strategy. For the MACDBollinger, what is the price setting you use? Thank you.
 
Couldn't let it go. Here is a potential fix. Let me know how it works out.

Python:
# SPX Strategy
# Strategy designed by Hypoluxa
# barbaros - v1  - 2021/02/25
# barbaros - v2  - 2021/02/25 - added warning arrows, alerts, statistics and non SPX trading option
# barbaros - v3  - 2021/02/26 - added SMA plots, chart type, and price plot
# barbaros - v4  - 2021/03/02 - added trade time limit and do not cross before start time
# barbaros - v5  - 2021/03/02 - added direction label
# barbaros - v6  - 2021/03/03 - added alerts, exit at profit delta, arrows for exiting position, and changed defaults to show all
# barbaros - v7  - 2021/03/03 - removed inputs for disabling arrows - this can be done in ToS already,
#                              added profit target line, ExitAt selection and ATR target
# barbaros - v8  - 2021/03/05 - added profit target label, changed the exit strategy to high/low instead of close
# barbaros - v9  - 2021/03/06 - added entry price label
# barbaros - v10 - 2021/03/11 - trying to fix before 9:40am EST crossing filter
# barbaros - v11 - 2021/03/11 - removed chart type overwriting
# barbaros - v12 - 2021/03/11 - fixed issue with disabling DoNotCrossBeforeStart
# barbaros - v13 - 2021/03/12 - fixed issue with macdonly win counting

# Setup
# 10 Min chart
# Heikin Ashi candles
# SMA 3/9
# MACD Bollinger Bands (the one Ben has posted on here a while back) 8,16,36 and uncheck "show plot" on the BB upper, BB lower and BB mid line. You will only be using the zero line, MACD dots and MACD Line for entry purposes. The inputs of "b length" and "bb num dev" are irrelevant here since you will remove the the plots that I mentioned.
# ErgodicOsc 2,10,36 (changed the default negative to red)

# Option call logic:
# 1. The SMA's have to cross first.
# 2. For a call opportunity, you will then wait to see if the MACD dots cross above the MACD zero line. Its critical here to wait until one dot has cleared the zero line...you MUST see a gap....never enter with the dot on the line. Dots must be consistent as well...if its going up...then they must all be white...if a red dot populates between the time a white dot hits the zero line and the time one crosses clear....don't enter. I have a screenshot below showing this example - 13:00 a red dot appears.
# 3. The ErgodicOsc HAS to be green when the MACD dot crosses above the zero line. You can hold the trade most of the time until this turns from green to red....but I always set a sell limit just in case it whips back in the opposite direction.

# Option put logic:
# Obviously its the complete opposite of what I've described above for a call. BUT - the SMA's still have to cross first.

# Note:
# Historical price action testing shows that if the MACDBB crosses zerobase before 9:40 EST, do not take the trade. Also, there might be too much choppiness after 15:00 EST.

declare upper;

input AllowNonSPX = no;
input ShowLabels = yes;
input ShowStatistics = yes;
input ExitAt = {default "profitDeltaOrMACD", "MACDOnly", "ATRorMACD"};
input ProfitDelta = 1.0;
input ATRLength = 7.0;
input ATRMult = 0.5;
input LimitTime = yes;
input StartTime = 940;
input StopTime = 1500;
input DoNotCrossBeforeStart = yes;

input price = close;
input SMAFastLength = 3;
input SMASlowLength = 9;
input BBlength = 30;
input BBNum_Dev = 0.8;
input BBCrossInBars = 3;
input BBCrossDistance = 1.0;
input MACDfastLength = 8;
input MACDslowLength = 16;
input MACDLength = 36;
input ERGODICLongLength = 2;
input ERGODICShortLength = 10;
input ERGODICSignalLength = 36;
input ERGODICAverageType = {"SIMPLE", default "EXPONENTIAL", "WEIGHTED", "WILDERS", "HULL"};

# Check for 10min chart
Assert(AllowNonSPX or GetAggregationPeriod() == AggregationPeriod.TEN_MIN, "Incorrect Chart Time, use 10m");
Assert(AllowNonSPX or GetSymbol() == "SPX", "Incorrect Chart Time, use 10m");

# MACD
def MACD_Data = MACD(fastLength = MACDfastLength, slowLength = MACDslowLength, MACDLength = MACDLength);
def MACD_Direction = if MACD_Data > MACD_Data[1] then 1 else -1;
def MACD_CrossBar = if MACD_Data crosses above 0 within 1 bar or MACD_Data crosses below 0 within 1 bar then BarNumber() else MACD_CrossBar[1];
def MACD_CrossBarAgo = BarNumber() - MACD_CrossBar;

# Ergodic
def Ergodic_Data = ErgodicOsc("long length" = ERGODICLongLength, "short length" = ERGODICShortLength, "signal length" = ERGODICSignalLength, "average type" = ERGODICAverageType).ErgodicOsc;

# SMAs
def SMA_Fast = SimpleMovingAvg(price, SMAFastLength);
def SMA_Slow = SimpleMovingAvg(price, SMASlowLength);

# ATR for progit target
def atrDelta = ATR(ATRLength) * ATRMult;

# Time Limit
def isTradeTime = if LimitTime then SecondsFromTime(StartTime) >= 0 and SecondsTillTime(StopTime) >= 0 else yes;
def isNotCrossBeforeStart = if !DoNotCrossBeforeStart then yes
                            else if DoNotCrossBeforeStart and SecondsFromTime(StartTime) == 0 and MACD_CrossBarAgo == 1 then no
                            else if DoNotCrossBeforeStart and SecondsFromTime(StartTime) != 0 and MACD_CrossBar[1] != MACD_CrossBar then yes
                            else isNotCrossBeforeStart[1];

# Signals
def buySignal = isTradeTime and isNotCrossBeforeStart and SMA_Fast > SMA_Slow and Ergodic_Data > 0 and MACD_Direction == 1 and MACD_Data >= BBCrossDistance and MACD_Data crosses above 0 within BBCrossInBars bars;
def buyWarnSignal = isTradeTime and isNotCrossBeforeStart and SMA_Fast > SMA_Slow and Ergodic_Data > 0 and MACD_Direction == 1 and MACD_CrossBar[1] != MACD_CrossBar;

def sellSignal = isTradeTime and isNotCrossBeforeStart and SMA_Fast < SMA_Slow and Ergodic_Data < 0 and MACD_Direction == -1 and MACD_Data <= -BBCrossDistance and MACD_Data crosses below 0 within BBCrossInBars bars;
def sellWarnSignal = isTradeTime and isNotCrossBeforeStart and SMA_Fast < SMA_Slow and Ergodic_Data < 0 and MACD_Direction == -1 and MACD_CrossBar[1] != MACD_CrossBar;

# Plots
plot buy = buySignal and !buySignal[1];
buy.setPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
buy.setDefaultColor(Color.GREEN);
buy.setLineWeight(3);

plot buyWarn = buyWarnSignal and !buyWarnSignal[1];
buyWarn.setPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
buyWarn.setDefaultColor(Color.CYAN);
buyWarn.setLineWeight(1);

plot sell = sellSignal and !sellSignal[1];
sell.setPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
sell.setDefaultColor(Color.RED);
sell.setLineWeight(3);

plot sellWarn = sellWarnSignal and !sellWarnSignal[1];
sellWarn.setPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
sellWarn.setDefaultColor(Color.MAGENTA);
sellWarn.setLineWeight(1);

plot lastPrice = price;
lastPrice.setPaintingStrategy(PaintingStrategy.POINTS);
lastPrice.setDefaultColor(Color.GREEN);

plot fast = SMA_Fast;
fast.setPaintingStrategy(PaintingStrategy.LINE);
fast.setDefaultColor(Color.CYAN);
fast.setLineWeight(1);

plot slow = SMA_Slow;
slow.setPaintingStrategy(PaintingStrategy.LINE);
slow.setDefaultColor(Color.MAGENTA);
slow.setLineWeight(1);

# PnL
def entryPrice = if (buySignal and !buySignal[1]) or (sellSignal and !sellSignal[1]) then open[-1] else entryPrice[1];
def profitDeltaTarget = if ExitAt == ExitAt.profitDeltaOrMACD then ProfitDelta else if ExitAt == ExitAt.ATRorMACD then atrDelta else 0;
def profitTarget = if buySignal and !buySignal[1] then entryPrice + profitDeltaTarget
                   else if sellSignal and !sellSignal[1] then entryPrice - profitDeltaTarget
                   else profitTarget[1];
def orderDir = if BarNumber() == 1 then 0
               else if orderDir[1] == 0 and buySignal and !buySignal[1] then 1
               else if orderDir[1] == 1 and (MACD_Direction != 1 or (ExitAt != ExitAt.MACDOnly and high >= profitTarget)) then 0
               else if orderDir[1] == 0 and sellSignal and !sellSignal[1] then -1
               else if orderDir[1] == -1 and (MACD_Direction != -1 or (ExitAt != ExitAt.MACDOnly and low <= profitTarget)) then 0
               else orderDir[1];

def isOrder = if IsNaN(orderDir) then no else orderDir crosses 0;
def orderCount = CompoundValue(1, if IsNaN(isOrder) then 0 else if isOrder then orderCount[1] + 1 else orderCount[1], 0);

def orderWinners = if BarNumber() == 1 then 0
                    else if orderDir[1] == 1 and orderDir == 0 then
                        if (ExitAt != ExitAt.MACDOnly and high >= profitTarget[1]) or (close > entryPrice) then orderWinners[1] + 1 else orderWinners[1]
                    else if orderDir[1] == -1 and orderDir == 0 then
                        if (ExitAt != ExitAt.MACDOnly and low <= profitTarget[1]) or (close < entryPrice) then orderWinners[1] + 1 else orderWinners[1]
            else orderWinners[1];
def winRate = orderWinners / orderCount;

# Plot Exit Positions
plot buyExit = orderDir[1] == 1 and orderDir != 1;
buyExit.setPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
buyExit.setDefaultColor(Color.GREEN);
buyExit.setLineWeight(3);

plot sellExit = orderDir[1] == -1 and orderDir != -1;
sellExit.setPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
sellExit.setDefaultColor(Color.RED);
sellExit.setLineWeight(3);

plot profitTargetLevel = if orderDir != 0 or orderDir[1] != 0 then profitTarget else Double.NaN;
profitTargetLevel.setPaintingStrategy(PaintingStrategy.LINE);
profitTargetLevel.setDefaultColor(Color.GREEN);
profitTargetLevel.setLineWeight(3);

# Labels
AddLabel(ShowLabels, "SPX Strategy", Color.WHITE);
AddLabel(ShowLabels, if buy then "Buy"
                     else if sell then "Sell"
                     else if buyWarn or sellWarn then "Warn"
                     else if orderDir == 1 then "Long"
                     else if orderDir == -1 then "Short"
                     else "Neutral",
                     if buy then Color.GREEN
                     else if sell then Color.RED
                     else if buyWarn then Color.YELLOW
                     else if sellWarn then Color.LIGHT_RED
                     else if orderDir == 1 then Color.YELLOW
                     else if orderDir == -1 then Color.LIGHT_RED
                     else Color.GRAY
);
AddLabel(ShowLabels and orderDir != 0 and !isNaN(entryPrice), "Entry " + AsDollars(entryPrice), Color.GRAY);
AddLabel(ShowLabels and orderDir != 0 and !isNaN(profitTarget), "Target " + AsDollars(profitTarget), Color.GRAY);
AddLabel(ShowLabels and ShowStatistics, "" + orderCount + " Trades | " + AsPercent(winRate), if winRate > 0.5 then Color.GREEN else Color.RED);

# Alerts
Alert(buy, "Buy", Alert.BAR, Sound.Ring);
Alert(sell, "Sell", Alert.BAR, Sound.Ring);
Alert(buyWarn, "Buy Warning", Alert.BAR, Sound.Ding);
Alert(sellWarn, "Sell Warning", Alert.BAR, Sound.Ding);
Alert(buyExit, "Buy Exit", Alert.BAR, Sound.Ring);
Alert(sellExit, "Sell Exit", Alert.BAR, Sound.Ring);
@barbaros Thank you for the great work! I have a quick question. In your code you have input BBNum_Dev = 0.8; whereas @Hypoluxa has bb num dev = 0.5. Is your code still correct or should this be changed? Cheers!
 
Hey everyone, I’ve been working on a script to help determine optimal MACD settings (for any ticker, but SPX for the case at hand) and have got it functioning as of now. Basically it is set up in a nested loop structure to loop through all possible MACD settings.

In my script entry is defined as a cross of the MACD line over the zero line (note SMA cross and Ergodic are not checked - they always seem to be in line with the MACD cross anyways).

Exit is defined as when the MACD closes lower than the previous close (in the case of an entry being triggered by MACD crossing above the zeroline). Vice versa for when an entry is triggered due to MACD crossing below the zeroline.

The script is set up to only trigger an entry if a cross occurs within a single day (to avoid gap up/down triggering an entry upon open). Exits are set up likewise to ensure they are done same day and not held overnight.

Input capabilities:
  • Any timeframe intraday prices (would have to make some adjustments for daily/weekly timeframes).
  • Can be used for any ticker.

Results Output for each combination of MACD inputs:
  • Number of MACD crosses (sample size).
  • Average number of consecutive increasing/decreasing MACD closes (depending if cross was above/below zeroline) - help to reduce combinations that whipsaw.
  • Avg/Max/Min (+/-) price movement after a cross above the zeroline - determine if long positions are significantly better than shorts.
  • Avg/Max/Min (+/-) price movement after a cross below the zeroline - vice versa from above.
  • Cumulative avg/max/min price movement regardless of cross direction.
  • Win % defined as a minimum price movement of $3 for SPX (min. movement can be altered for SPX and any other ticker).

I’ve currently been choosing the “optimal” settings based upon the highest win % for cases with over 125 crosses. Data I’ve been using for testing is 10min timeframe back to 01 JUN.

I still want to check out the “optimal” settings on a stock chart to verify everything seems good and will post a follow-up with “optimal” results I found if anyone is interested. I can post the complete raw data results if there is interest as well.
 
Hey everyone, I’ve been working on a script to help determine optimal MACD settings (for any ticker, but SPX for the case at hand) and have got it functioning as of now. Basically it is set up in a nested loop structure to loop through all possible MACD settings.

In my script entry is defined as a cross of the MACD line over the zero line (note SMA cross and Ergodic are not checked - they always seem to be in line with the MACD cross anyways).

Exit is defined as when the MACD closes lower than the previous close (in the case of an entry being triggered by MACD crossing above the zeroline). Vice versa for when an entry is triggered due to MACD crossing below the zeroline.

The script is set up to only trigger an entry if a cross occurs within a single day (to avoid gap up/down triggering an entry upon open). Exits are set up likewise to ensure they are done same day and not held overnight.

Input capabilities:
  • Any timeframe intraday prices (would have to make some adjustments for daily/weekly timeframes).
  • Can be used for any ticker.

Results Output for each combination of MACD inputs:
  • Number of MACD crosses (sample size).
  • Average number of consecutive increasing/decreasing MACD closes (depending if cross was above/below zeroline) - help to reduce combinations that whipsaw.
  • Avg/Max/Min (+/-) price movement after a cross above the zeroline - determine if long positions are significantly better than shorts.
  • Avg/Max/Min (+/-) price movement after a cross below the zeroline - vice versa from above.
  • Cumulative avg/max/min price movement regardless of cross direction.
  • Win % defined as a minimum price movement of $3 for SPX (min. movement can be altered for SPX and any other ticker).

I’ve currently been choosing the “optimal” settings based upon the highest win % for cases with over 125 crosses. Data I’ve been using for testing is 10min timeframe back to 01 JUN.

I still want to check out the “optimal” settings on a stock chart to verify everything seems good and will post a follow-up with “optimal” results I found if anyone is interested. I can post the complete raw data results if there is interest as well.
Very cool. Can't wait to see the results.
 
Couldn't let it go. Here is a potential fix. Let me know how it works out.

Python:
# SPX Strategy
# Strategy designed by Hypoluxa
# barbaros - v1  - 2021/02/25
# barbaros - v2  - 2021/02/25 - added warning arrows, alerts, statistics and non SPX trading option
# barbaros - v3  - 2021/02/26 - added SMA plots, chart type, and price plot
# barbaros - v4  - 2021/03/02 - added trade time limit and do not cross before start time
# barbaros - v5  - 2021/03/02 - added direction label
# barbaros - v6  - 2021/03/03 - added alerts, exit at profit delta, arrows for exiting position, and changed defaults to show all
# barbaros - v7  - 2021/03/03 - removed inputs for disabling arrows - this can be done in ToS already,
#                              added profit target line, ExitAt selection and ATR target
# barbaros - v8  - 2021/03/05 - added profit target label, changed the exit strategy to high/low instead of close
# barbaros - v9  - 2021/03/06 - added entry price label
# barbaros - v10 - 2021/03/11 - trying to fix before 9:40am EST crossing filter
# barbaros - v11 - 2021/03/11 - removed chart type overwriting
# barbaros - v12 - 2021/03/11 - fixed issue with disabling DoNotCrossBeforeStart
# barbaros - v13 - 2021/03/12 - fixed issue with macdonly win counting

# Setup
# 10 Min chart
# Heikin Ashi candles
# SMA 3/9
# MACD Bollinger Bands (the one Ben has posted on here a while back) 8,16,36 and uncheck "show plot" on the BB upper, BB lower and BB mid line. You will only be using the zero line, MACD dots and MACD Line for entry purposes. The inputs of "b length" and "bb num dev" are irrelevant here since you will remove the the plots that I mentioned.
# ErgodicOsc 2,10,36 (changed the default negative to red)

# Option call logic:
# 1. The SMA's have to cross first.
# 2. For a call opportunity, you will then wait to see if the MACD dots cross above the MACD zero line. Its critical here to wait until one dot has cleared the zero line...you MUST see a gap....never enter with the dot on the line. Dots must be consistent as well...if its going up...then they must all be white...if a red dot populates between the time a white dot hits the zero line and the time one crosses clear....don't enter. I have a screenshot below showing this example - 13:00 a red dot appears.
# 3. The ErgodicOsc HAS to be green when the MACD dot crosses above the zero line. You can hold the trade most of the time until this turns from green to red....but I always set a sell limit just in case it whips back in the opposite direction.

# Option put logic:
# Obviously its the complete opposite of what I've described above for a call. BUT - the SMA's still have to cross first.

# Note:
# Historical price action testing shows that if the MACDBB crosses zerobase before 9:40 EST, do not take the trade. Also, there might be too much choppiness after 15:00 EST.

declare upper;

input AllowNonSPX = no;
input ShowLabels = yes;
input ShowStatistics = yes;
input ExitAt = {default "profitDeltaOrMACD", "MACDOnly", "ATRorMACD"};
input ProfitDelta = 1.0;
input ATRLength = 7.0;
input ATRMult = 0.5;
input LimitTime = yes;
input StartTime = 940;
input StopTime = 1500;
input DoNotCrossBeforeStart = yes;

input price = close;
input SMAFastLength = 3;
input SMASlowLength = 9;
input BBlength = 30;
input BBNum_Dev = 0.8;
input BBCrossInBars = 3;
input BBCrossDistance = 1.0;
input MACDfastLength = 8;
input MACDslowLength = 16;
input MACDLength = 36;
input ERGODICLongLength = 2;
input ERGODICShortLength = 10;
input ERGODICSignalLength = 36;
input ERGODICAverageType = {"SIMPLE", default "EXPONENTIAL", "WEIGHTED", "WILDERS", "HULL"};

# Check for 10min chart
Assert(AllowNonSPX or GetAggregationPeriod() == AggregationPeriod.TEN_MIN, "Incorrect Chart Time, use 10m");
Assert(AllowNonSPX or GetSymbol() == "SPX", "Incorrect Chart Time, use 10m");

# MACD
def MACD_Data = MACD(fastLength = MACDfastLength, slowLength = MACDslowLength, MACDLength = MACDLength);
def MACD_Direction = if MACD_Data > MACD_Data[1] then 1 else -1;
def MACD_CrossBar = if MACD_Data crosses above 0 within 1 bar or MACD_Data crosses below 0 within 1 bar then BarNumber() else MACD_CrossBar[1];
def MACD_CrossBarAgo = BarNumber() - MACD_CrossBar;

# Ergodic
def Ergodic_Data = ErgodicOsc("long length" = ERGODICLongLength, "short length" = ERGODICShortLength, "signal length" = ERGODICSignalLength, "average type" = ERGODICAverageType).ErgodicOsc;

# SMAs
def SMA_Fast = SimpleMovingAvg(price, SMAFastLength);
def SMA_Slow = SimpleMovingAvg(price, SMASlowLength);

# ATR for progit target
def atrDelta = ATR(ATRLength) * ATRMult;

# Time Limit
def isTradeTime = if LimitTime then SecondsFromTime(StartTime) >= 0 and SecondsTillTime(StopTime) >= 0 else yes;
def isNotCrossBeforeStart = if !DoNotCrossBeforeStart then yes
                            else if DoNotCrossBeforeStart and SecondsFromTime(StartTime) == 0 and MACD_CrossBarAgo == 1 then no
                            else if DoNotCrossBeforeStart and SecondsFromTime(StartTime) != 0 and MACD_CrossBar[1] != MACD_CrossBar then yes
                            else isNotCrossBeforeStart[1];

# Signals
def buySignal = isTradeTime and isNotCrossBeforeStart and SMA_Fast > SMA_Slow and Ergodic_Data > 0 and MACD_Direction == 1 and MACD_Data >= BBCrossDistance and MACD_Data crosses above 0 within BBCrossInBars bars;
def buyWarnSignal = isTradeTime and isNotCrossBeforeStart and SMA_Fast > SMA_Slow and Ergodic_Data > 0 and MACD_Direction == 1 and MACD_CrossBar[1] != MACD_CrossBar;

def sellSignal = isTradeTime and isNotCrossBeforeStart and SMA_Fast < SMA_Slow and Ergodic_Data < 0 and MACD_Direction == -1 and MACD_Data <= -BBCrossDistance and MACD_Data crosses below 0 within BBCrossInBars bars;
def sellWarnSignal = isTradeTime and isNotCrossBeforeStart and SMA_Fast < SMA_Slow and Ergodic_Data < 0 and MACD_Direction == -1 and MACD_CrossBar[1] != MACD_CrossBar;

# Plots
plot buy = buySignal and !buySignal[1];
buy.setPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
buy.setDefaultColor(Color.GREEN);
buy.setLineWeight(3);

plot buyWarn = buyWarnSignal and !buyWarnSignal[1];
buyWarn.setPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
buyWarn.setDefaultColor(Color.CYAN);
buyWarn.setLineWeight(1);

plot sell = sellSignal and !sellSignal[1];
sell.setPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
sell.setDefaultColor(Color.RED);
sell.setLineWeight(3);

plot sellWarn = sellWarnSignal and !sellWarnSignal[1];
sellWarn.setPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
sellWarn.setDefaultColor(Color.MAGENTA);
sellWarn.setLineWeight(1);

plot lastPrice = price;
lastPrice.setPaintingStrategy(PaintingStrategy.POINTS);
lastPrice.setDefaultColor(Color.GREEN);

plot fast = SMA_Fast;
fast.setPaintingStrategy(PaintingStrategy.LINE);
fast.setDefaultColor(Color.CYAN);
fast.setLineWeight(1);

plot slow = SMA_Slow;
slow.setPaintingStrategy(PaintingStrategy.LINE);
slow.setDefaultColor(Color.MAGENTA);
slow.setLineWeight(1);

# PnL
def entryPrice = if (buySignal and !buySignal[1]) or (sellSignal and !sellSignal[1]) then open[-1] else entryPrice[1];
def profitDeltaTarget = if ExitAt == ExitAt.profitDeltaOrMACD then ProfitDelta else if ExitAt == ExitAt.ATRorMACD then atrDelta else 0;
def profitTarget = if buySignal and !buySignal[1] then entryPrice + profitDeltaTarget
                   else if sellSignal and !sellSignal[1] then entryPrice - profitDeltaTarget
                   else profitTarget[1];
def orderDir = if BarNumber() == 1 then 0
               else if orderDir[1] == 0 and buySignal and !buySignal[1] then 1
               else if orderDir[1] == 1 and (MACD_Direction != 1 or (ExitAt != ExitAt.MACDOnly and high >= profitTarget)) then 0
               else if orderDir[1] == 0 and sellSignal and !sellSignal[1] then -1
               else if orderDir[1] == -1 and (MACD_Direction != -1 or (ExitAt != ExitAt.MACDOnly and low <= profitTarget)) then 0
               else orderDir[1];

def isOrder = if IsNaN(orderDir) then no else orderDir crosses 0;
def orderCount = CompoundValue(1, if IsNaN(isOrder) then 0 else if isOrder then orderCount[1] + 1 else orderCount[1], 0);

def orderWinners = if BarNumber() == 1 then 0
                    else if orderDir[1] == 1 and orderDir == 0 then
                        if (ExitAt != ExitAt.MACDOnly and high >= profitTarget[1]) or (close > entryPrice) then orderWinners[1] + 1 else orderWinners[1]
                    else if orderDir[1] == -1 and orderDir == 0 then
                        if (ExitAt != ExitAt.MACDOnly and low <= profitTarget[1]) or (close < entryPrice) then orderWinners[1] + 1 else orderWinners[1]
            else orderWinners[1];
def winRate = orderWinners / orderCount;

# Plot Exit Positions
plot buyExit = orderDir[1] == 1 and orderDir != 1;
buyExit.setPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
buyExit.setDefaultColor(Color.GREEN);
buyExit.setLineWeight(3);

plot sellExit = orderDir[1] == -1 and orderDir != -1;
sellExit.setPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
sellExit.setDefaultColor(Color.RED);
sellExit.setLineWeight(3);

plot profitTargetLevel = if orderDir != 0 or orderDir[1] != 0 then profitTarget else Double.NaN;
profitTargetLevel.setPaintingStrategy(PaintingStrategy.LINE);
profitTargetLevel.setDefaultColor(Color.GREEN);
profitTargetLevel.setLineWeight(3);

# Labels
AddLabel(ShowLabels, "SPX Strategy", Color.WHITE);
AddLabel(ShowLabels, if buy then "Buy"
                     else if sell then "Sell"
                     else if buyWarn or sellWarn then "Warn"
                     else if orderDir == 1 then "Long"
                     else if orderDir == -1 then "Short"
                     else "Neutral",
                     if buy then Color.GREEN
                     else if sell then Color.RED
                     else if buyWarn then Color.YELLOW
                     else if sellWarn then Color.LIGHT_RED
                     else if orderDir == 1 then Color.YELLOW
                     else if orderDir == -1 then Color.LIGHT_RED
                     else Color.GRAY
);
AddLabel(ShowLabels and orderDir != 0 and !isNaN(entryPrice), "Entry " + AsDollars(entryPrice), Color.GRAY);
AddLabel(ShowLabels and orderDir != 0 and !isNaN(profitTarget), "Target " + AsDollars(profitTarget), Color.GRAY);
AddLabel(ShowLabels and ShowStatistics, "" + orderCount + " Trades | " + AsPercent(winRate), if winRate > 0.5 then Color.GREEN else Color.RED);

# Alerts
Alert(buy, "Buy", Alert.BAR, Sound.Ring);
Alert(sell, "Sell", Alert.BAR, Sound.Ring);
Alert(buyWarn, "Buy Warning", Alert.BAR, Sound.Ding);
Alert(sellWarn, "Sell Warning", Alert.BAR, Sound.Ding);
Alert(buyExit, "Buy Exit", Alert.BAR, Sound.Ring);
Alert(sellExit, "Sell Exit", Alert.BAR, Sound.Ring);
Hi i was out this week so i did not follow the changes... what did you do changes and what changed for a buy signal and sell signal... thanks
 
Wheres the latest study? I need to re import but when i import the script @python posted nothing is coming up on my charts.
 
Last edited:
Hey everyone, I’ve been working on a script to help determine optimal MACD settings (for any ticker, but SPX for the case at hand) and have got it functioning as of now. Basically it is set up in a nested loop structure to loop through all possible MACD settings.

In my script entry is defined as a cross of the MACD line over the zero line (note SMA cross and Ergodic are not checked - they always seem to be in line with the MACD cross anyways).

Exit is defined as when the MACD closes lower than the previous close (in the case of an entry being triggered by MACD crossing above the zeroline). Vice versa for when an entry is triggered due to MACD crossing below the zeroline.

The script is set up to only trigger an entry if a cross occurs within a single day (to avoid gap up/down triggering an entry upon open). Exits are set up likewise to ensure they are done same day and not held overnight.

Input capabilities:
  • Any timeframe intraday prices (would have to make some adjustments for daily/weekly timeframes).
  • Can be used for any ticker.

Results Output for each combination of MACD inputs:
  • Number of MACD crosses (sample size).
  • Average number of consecutive increasing/decreasing MACD closes (depending if cross was above/below zeroline) - help to reduce combinations that whipsaw.
  • Avg/Max/Min (+/-) price movement after a cross above the zeroline - determine if long positions are significantly better than shorts.
  • Avg/Max/Min (+/-) price movement after a cross below the zeroline - vice versa from above.
  • Cumulative avg/max/min price movement regardless of cross direction.
  • Win % defined as a minimum price movement of $3 for SPX (min. movement can be altered for SPX and any other ticker).

I’ve currently been choosing the “optimal” settings based upon the highest win % for cases with over 125 crosses. Data I’ve been using for testing is 10min timeframe back to 01 JUN.

I still want to check out the “optimal” settings on a stock chart to verify everything seems good and will post a follow-up with “optimal” results I found if anyone is interested. I can post the complete raw data results if there is interest as well.
Just a few quick preliminary findings if anyone wanted to check them out:

- Exiting a trade based upon MACD changing direction after a cross doesn't seem to be a great exit strategy. Trades can be high percentage winners, however it appears a lot of the time the trade will move against you and can do so for some time before reversing. Personally, for scalping, I don't see that as being an optimal strategy.

- I visually explored the use of price crossing SMA's as a possible exit strategy, but wasn't liking results based upon a MACD entry so I quickly ditched that idea.

- The current exit strategy I implemented that seems promising is staying in a trade as long as subsequent closing prices stay above the entry closing price and until a price delta is met (i.e. in this case @Hypoluxa aims for an underlying price movement of $3). Using this method, the highest win rate is 71.66% for MACD inputs of 12 and 32 (note: MACD length does not impact results, only the fast and slow length do). As a reference the current recommended inputs of 8 and 16 return a win rate of ~59%.

If anyone has any other recommended exit strategies to test out feel free to share and I'll try to implement if they aren't overly difficult.

Side note: it has become very obvious you should not get greedy with any of these methods as moves > 10-15 points prior to an adverse turn are rare and without risk management aiming for these levels of gains can be costly. I'd also recommend using this method in conjunction with support/resistance levels to help time entry/exit locations.
 
@PhillyEaglesS Your research and findings are much appreciated.

@barbaros Thanks for the updated code and fixing those bugs. Questions and requests:

1. How is winrate calculated in general? Are all gains, even if small considered wins? I assume yes
2. With profitdeltaormacd, is winrate determined by whichever of the two exit options hits first? I assume yes
3. Request: total P/L dollar amount (addition to stats), which you've already mentioned you're thinking of. This would be highly valuable to determining the best r/r ratio and strategy.
4. Request: option for exitat option of profitdelta alone. This would allow determining best r/r ratio and strat for profit targets. As is, profitdeltaormacd will not provide a full picture because it could hit macd first. Profitdelta alone would allow us to see what % of time it would hit a profit target, say 3 points or 5points, if we let it run. I suppose there would have to be a stop loss setting for stat calculations and I'm not sure what the best approach is for that. I would assume an option within study for the user to set a sl, by dollar amount or points. Or perhaps there's better approach?

 
Just a few quick preliminary findings if anyone wanted to check them out:

- Exiting a trade based upon MACD changing direction after a cross doesn't seem to be a great exit strategy. Trades can be high percentage winners, however it appears a lot of the time the trade will move against you and can do so for some time before reversing. Personally, for scalping, I don't see that as being an optimal strategy.

- I visually explored the use of price crossing SMA's as a possible exit strategy, but wasn't liking results based upon a MACD entry so I quickly ditched that idea.

- The current exit strategy I implemented that seems promising is staying in a trade as long as subsequent closing prices stay above the entry closing price and until a price delta is met (i.e. in this case @Hypoluxa aims for an underlying price movement of $3). Using this method, the highest win rate is 71.66% for MACD inputs of 12 and 32 (note: MACD length does not impact results, only the fast and slow length do). As a reference the current recommended inputs of 8 and 16 return a win rate of ~59%.

If anyone has any other recommended exit strategies to test out feel free to share and I'll try to implement if they aren't overly difficult.

Side note: it has become very obvious you should not get greedy with any of these methods as moves > 10-15 points prior to an adverse turn are rare and without risk management aiming for these levels of gains can be costly. I'd also recommend using this method in conjunction with support/resistance levels to help time entry/exit locations.
Interesting. I've experimented a bit and here's what I found with just a few minutes. My settings may vary a bit from yours (time limite, crossing before 9:30), but the important thing is the effect of your macdd settings. The following numbers are just using the built in stats for winrate, using past 180 days, on /es.

For profitdeltaormacdd (exitat) with profitdelta of 3, and mac cross distance of 1.0, your macd numbers provide a better winrate (73.25% vs. 70.75%). However, it also provides less trades, 157 vs. 212.

For the same comparison, using .65 cross distance, your macd numbers don't provide a benefit (70.14% with original macdd numbers vs. 69.9% with your numbers). And your numbers allow far fewer trades, 278 vs. 196.

So, imo, it's unclear whether your number do provide an advantage, are neutral, or possibly a disadvantage due to less trades (with equal winrate). I understand you're using a different exit strategy and that may change things altogether. But if we're discussing this study and strat, as is, then further testing and confirmation is necessary.

[EDIT: after I wrote this, I realized that you were discussing SPX, not /es, and that's my bad. But perhaps my points should be considered for SPX as well.]
 
Last edited:
@PhillyEaglesS Your research and findings are much appreciated.

@barbaros Thanks for the updated code and fixing those bugs. Questions and requests:

1. How is winrate calculated in general? Are all gains, even if small considered wins? I assume yes
2. With profitdeltaormacd, is winrate determined by whichever of the two exit options hits first? I assume yes
3. Request: total P/L dollar amount (addition to stats), which you've already mentioned you're thinking of. This would be highly valuable to determining the best r/r ratio and strategy.
4. Request: option for exitat option of profitdelta alone. This would allow determining best r/r ratio and strat for profit targets. As is, profitdeltaormacd will not provide a full picture because it could hit macd first. Profitdelta alone would allow us to see what % of time it would hit a profit target, say 3 points or 5points, if we let it run. I suppose there would have to be a stop loss setting for stat calculations and I'm not sure what the best approach is for that. I would assume an option within study for the user to set a sl, by dollar amount or points. Or perhaps there's better approach?

1 and 2, your assumptions are correct, yes.

3 and 4, on the roadmap.

thank you for all your thoughts, testing and suggestions. All of these will make this indicator much better.
 
Interesting. I've experimented a bit and here's what I found with just a few minutes. My settings may vary a bit from yours (time limite, crossing before 9:30), but the important thing is the effect of your macdd settings. The following numbers are just using the built in stats for winrate, using past 180 days, on /es.

For profitdeltaormacdd (exitat) with profitdelta of 3, and mac cross distance of 1.0, your macd numbers provide a better winrate (73.25% vs. 70.75%). However, it also provides less trades, 157 vs. 212.

For the same comparison, using .65 cross distance, your macd numbers don't provide a benefit (70.14% with original macdd numbers vs. 69.9% with your numbers). And your numbers allow far fewer trades, 278 vs. 196.

So, imo, it's unclear whether your number do provide an advantage, are neutral, or possibly a disadvantage due to less trades (with equal winrate). I understand you're using a different exit strategy and that may change things altogether. But if we're discussing this study and strat, as is, then further testing and confirmation is necessary.

[EDIT: after I wrote this, I realized that you were discussing SPX, not /es, and that's my bad. But perhaps my points should be considered for SPX as well.]
For the comparison I mentioned, the results were obtained using the script I created - this isn't a ToS script, I'm using data that @Arkitekt has helped provide me with. That being said there are a few key differences:
  • The exit strategy I implemented is staying in a trade as long as subsequent closing prices stay above the entry closing price and until a price delta is met; exit has nothing to do with MACD settings. I'd have to dive into the ToS script you're using some more to find the exit strategy discrepancies to quantify just how significantly they alter the results I'm getting.
  • Different data sets will provide different results as mine extends further than 180 days.

Worth noting - this is the first time I've begun to look into @barbaros code, however in the brief time I have, it appears buy triggers are overlapping days (i.e. buy triggers end of day and sell trigger is displaying on the following). I'm not sure if this is purely a visual bug or if this is being taken into account for winrate. I'm using default settings with the exception of:
  • MACD Fast = 12
  • MACD Slow = 32
  • Time = 930-1550
One example of this can be seen 8/4-8/5, another 8/7-8/10.

As you said more testing and confirmation is necessary, which is the plan!
 
Update...If you are using this with futures other than /ES, we need to add margin requirements for each future. Profit value is calculated with tick value. It will auto adjust on each futures type, but the ROI percentage depends on the margin requirement. If you are trading options, PnL and ROI is probably not relevant to you.

Python:
# SPX Strategy
# Strategy designed by Hypoluxa
# barbaros - v1  - 2021/02/25
# barbaros - v2  - 2021/02/25 - added warning arrows, alerts, statistics and non SPX trading option
# barbaros - v3  - 2021/02/26 - added SMA plots, chart type, and price plot
# barbaros - v4  - 2021/03/02 - added trade time limit and do not cross before start time
# barbaros - v5  - 2021/03/02 - added direction label
# barbaros - v6  - 2021/03/03 - added alerts, exit at profit delta, arrows for exiting position, and changed defaults to show all
# barbaros - v7  - 2021/03/03 - removed inputs for disabling arrows - this can be done in ToS already,
#                              added profit target line, ExitAt selection and ATR target
# barbaros - v8  - 2021/03/05 - added profit target label, changed the exit strategy to high/low instead of close
# barbaros - v9  - 2021/03/06 - added entry price label
# barbaros - v10 - 2021/03/11 - trying to fix before 9:40am EST crossing filter
# barbaros - v11 - 2021/03/11 - removed chart type overwriting
# barbaros - v12 - 2021/03/11 - fixed issue with disabling DoNotCrossBeforeStart
# barbaros - v13 - 2021/03/12 - fixed issue with macdonly win counting
# barbaros - v14 - 2021/03/13 - added PnL and ROI - note: only /ES margin is available

# Setup
# 10 Min chart
# Heikin Ashi candles
# SMA 3/9
# MACD Bollinger Bands (the one Ben has posted on here a while back) 8,16,36 and uncheck "show plot" on the BB upper, BB lower and BB mid line. You will only be using the zero line, MACD dots and MACD Line for entry purposes. The inputs of "b length" and "bb num dev" are irrelevant here since you will remove the the plots that I mentioned.
# ErgodicOsc 2,10,36 (changed the default negative to red)

# Option call logic:
# 1. The SMA's have to cross first.
# 2. For a call opportunity, you will then wait to see if the MACD dots cross above the MACD zero line. Its critical here to wait until one dot has cleared the zero line...you MUST see a gap....never enter with the dot on the line. Dots must be consistent as well...if its going up...then they must all be white...if a red dot populates between the time a white dot hits the zero line and the time one crosses clear....don't enter. I have a screenshot below showing this example - 13:00 a red dot appears.
# 3. The ErgodicOsc HAS to be green when the MACD dot crosses above the zero line. You can hold the trade most of the time until this turns from green to red....but I always set a sell limit just in case it whips back in the opposite direction.

# Option put logic:
# Obviously its the complete opposite of what I've described above for a call. BUT - the SMA's still have to cross first.

# Note:
# Historical price action testing shows that if the MACDBB crosses zerobase before 9:40 EST, do not take the trade. Also, there might be too much choppiness after 15:00 EST.

declare upper;

input AllowNonSPX = no;
input ShowLabels = yes;
input ShowStatistics = yes;
input ExitAt = {default "profitDeltaOrMACD", "MACDOnly", "ATRorMACD"};
input ProfitDelta = 1.0;
input ATRLength = 7.0;
input ATRMult = 0.5;
input LimitTime = yes;
input StartTime = 940;
input StopTime = 1500;
input DoNotCrossBeforeStart = yes;

input price = close;
input SMAFastLength = 3;
input SMASlowLength = 9;
input BBlength = 30;
input BBNum_Dev = 0.8;
input BBCrossInBars = 3;
input BBCrossDistance = 1.0;
input MACDfastLength = 8;
input MACDslowLength = 16;
input MACDLength = 36;
input ERGODICLongLength = 2;
input ERGODICShortLength = 10;
input ERGODICSignalLength = 36;
input ERGODICAverageType = {"SIMPLE", default "EXPONENTIAL", "WEIGHTED", "WILDERS", "HULL"};

# Check for 10min chart
Assert(AllowNonSPX or GetAggregationPeriod() == AggregationPeriod.TEN_MIN, "Incorrect Chart Time, use 10m");
Assert(AllowNonSPX or GetSymbol() == "SPX", "Incorrect Chart Time, use 10m");

# MACD
def MACD_Data = MACD(fastLength = MACDfastLength, slowLength = MACDslowLength, MACDLength = MACDLength);
def MACD_Direction = if MACD_Data > MACD_Data[1] then 1 else -1;
def MACD_CrossBar = if MACD_Data crosses above 0 within 1 bar or MACD_Data crosses below 0 within 1 bar then BarNumber() else MACD_CrossBar[1];
def MACD_CrossBarAgo = BarNumber() - MACD_CrossBar;

# Ergodic
def Ergodic_Data = ErgodicOsc("long length" = ERGODICLongLength, "short length" = ERGODICShortLength, "signal length" = ERGODICSignalLength, "average type" = ERGODICAverageType).ErgodicOsc;

# SMAs
def SMA_Fast = SimpleMovingAvg(price, SMAFastLength);
def SMA_Slow = SimpleMovingAvg(price, SMASlowLength);

# ATR for progit target
def atrDelta = ATR(ATRLength) * ATRMult;

# Time Limit
def isTradeTime = if LimitTime then SecondsFromTime(StartTime) >= 0 and SecondsTillTime(StopTime) >= 0 else yes;
def isNotCrossBeforeStart = if !DoNotCrossBeforeStart then yes
                            else if DoNotCrossBeforeStart and SecondsFromTime(StartTime) == 0 and MACD_CrossBarAgo == 1 then no
                            else if DoNotCrossBeforeStart and SecondsFromTime(StartTime) != 0 and MACD_CrossBar[1] != MACD_CrossBar then yes
                            else isNotCrossBeforeStart[1];

# Signals
def buySignal = isTradeTime and isNotCrossBeforeStart and SMA_Fast > SMA_Slow and Ergodic_Data > 0 and MACD_Direction == 1 and MACD_Data >= BBCrossDistance and MACD_Data crosses above 0 within BBCrossInBars bars;
def buyWarnSignal = isTradeTime and isNotCrossBeforeStart and SMA_Fast > SMA_Slow and Ergodic_Data > 0 and MACD_Direction == 1 and MACD_CrossBar[1] != MACD_CrossBar;

def sellSignal = isTradeTime and isNotCrossBeforeStart and SMA_Fast < SMA_Slow and Ergodic_Data < 0 and MACD_Direction == -1 and MACD_Data <= -BBCrossDistance and MACD_Data crosses below 0 within BBCrossInBars bars;
def sellWarnSignal = isTradeTime and isNotCrossBeforeStart and SMA_Fast < SMA_Slow and Ergodic_Data < 0 and MACD_Direction == -1 and MACD_CrossBar[1] != MACD_CrossBar;

# Plots
plot buy = buySignal and !buySignal[1];
buy.setPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
buy.setDefaultColor(Color.GREEN);
buy.setLineWeight(3);

plot buyWarn = buyWarnSignal and !buyWarnSignal[1];
buyWarn.setPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
buyWarn.setDefaultColor(Color.CYAN);
buyWarn.setLineWeight(1);

plot sell = sellSignal and !sellSignal[1];
sell.setPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
sell.setDefaultColor(Color.RED);
sell.setLineWeight(3);

plot sellWarn = sellWarnSignal and !sellWarnSignal[1];
sellWarn.setPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
sellWarn.setDefaultColor(Color.MAGENTA);
sellWarn.setLineWeight(1);

plot lastPrice = price;
lastPrice.setPaintingStrategy(PaintingStrategy.POINTS);
lastPrice.setDefaultColor(Color.GREEN);

plot fast = SMA_Fast;
fast.setPaintingStrategy(PaintingStrategy.LINE);
fast.setDefaultColor(Color.CYAN);
fast.setLineWeight(1);

plot slow = SMA_Slow;
slow.setPaintingStrategy(PaintingStrategy.LINE);
slow.setDefaultColor(Color.MAGENTA);
slow.setLineWeight(1);

# PnL
def entryPrice = if (buySignal and !buySignal[1]) or (sellSignal and !sellSignal[1]) then open[-1] else entryPrice[1];
def profitDeltaTarget = if ExitAt == ExitAt.profitDeltaOrMACD then ProfitDelta else if ExitAt == ExitAt.ATRorMACD then atrDelta else 0;
def profitTarget = if buySignal and !buySignal[1] then entryPrice + profitDeltaTarget
                   else if sellSignal and !sellSignal[1] then entryPrice - profitDeltaTarget
                   else profitTarget[1];
def orderDir = if BarNumber() == 1 then 0
               else if orderDir[1] == 0 and buySignal and !buySignal[1] then 1
               else if orderDir[1] == 1 and (MACD_Direction != 1 or (ExitAt != ExitAt.MACDOnly and high >= profitTarget)) then 0
               else if orderDir[1] == 0 and sellSignal and !sellSignal[1] then -1
               else if orderDir[1] == -1 and (MACD_Direction != -1 or (ExitAt != ExitAt.MACDOnly and low <= profitTarget)) then 0
               else orderDir[1];

def isOrder = if IsNaN(orderDir) then no else orderDir crosses 0;
def orderCount = CompoundValue(1, if IsNaN(isOrder) then 0 else if isOrder then orderCount[1] + 1 else orderCount[1], 0);

def orderWinners = if BarNumber() == 1 then 0
                    else if orderDir[1] == 1 and orderDir == 0 then
                        if (ExitAt != ExitAt.MACDOnly and high >= profitTarget[1]) or (close > entryPrice) then orderWinners[1] + 1 else orderWinners[1]
                    else if orderDir[1] == -1 and orderDir == 0 then
                        if (ExitAt != ExitAt.MACDOnly and low <= profitTarget[1]) or (close < entryPrice) then orderWinners[1] + 1 else orderWinners[1]
                    else orderWinners[1];
def winRate = orderWinners / orderCount;

def PnL = if BarNumber() == 1 then 0
           else if orderDir[1] == 1 and orderDir == 0 then
                 PnL[1] + ((close - entryPrice) * TickValue())
           else if orderDir[1] == -1 and orderDir == 0 then
               PnL[1] + ((entryPrice - close) * TickValue())
           else PnL[1];

def costPrice = if GetSymbol() == "/ES:XCME" then 13000
                else entryPrice;
def ROI = if BarNumber() == 1 then 0
           else if orderDir[1] == 1 and orderDir == 0 then
                 ROI[1] + (((close - entryPrice) * TickValue())/costPrice)
           else if orderDir[1] == -1 and orderDir == 0 then
               ROI[1] + (((entryPrice - close) * TickValue())/costPrice)
           else ROI[1];

# Plot Exit Positions
plot buyExit = orderDir[1] == 1 and orderDir != 1;
buyExit.setPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
buyExit.setDefaultColor(Color.GREEN);
buyExit.setLineWeight(3);

plot sellExit = orderDir[1] == -1 and orderDir != -1;
sellExit.setPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
sellExit.setDefaultColor(Color.RED);
sellExit.setLineWeight(3);

plot profitTargetLevel = if orderDir != 0 or orderDir[1] != 0 then profitTarget else Double.NaN;
profitTargetLevel.setPaintingStrategy(PaintingStrategy.LINE);
profitTargetLevel.setDefaultColor(Color.GREEN);
profitTargetLevel.setLineWeight(3);

# Labels
AddLabel(ShowLabels, "SPX Strategy", Color.WHITE);
AddLabel(ShowLabels, if buy then "Buy"
                     else if sell then "Sell"
                     else if buyWarn or sellWarn then "Warn"
                     else if orderDir == 1 then "Long"
                     else if orderDir == -1 then "Short"
                     else "Neutral",
                     if buy then Color.GREEN
                     else if sell then Color.RED
                     else if buyWarn then Color.YELLOW
                     else if sellWarn then Color.LIGHT_RED
                     else if orderDir == 1 then Color.YELLOW
                     else if orderDir == -1 then Color.LIGHT_RED
                     else Color.GRAY
);
AddLabel(ShowLabels and orderDir != 0 and !isNaN(entryPrice), "Entry " + AsDollars(entryPrice), Color.GRAY);
AddLabel(ShowLabels and orderDir != 0 and !isNaN(profitTarget), "Target " + AsDollars(profitTarget), Color.GRAY);
AddLabel(ShowLabels and ShowStatistics, "" + orderCount + " Trades | " + AsPercent(winRate), if winRate > 0.5 then Color.GREEN else Color.RED);
AddLabel(ShowLabels and ShowStatistics, "" + AsDollars(PnL) + " | " + AsPercent(ROI), if ROI > 0 then Color.GREEN else Color.RED);

# Alerts
Alert(buy, "Buy", Alert.BAR, Sound.Ring);
Alert(sell, "Sell", Alert.BAR, Sound.Ring);
Alert(buyWarn, "Buy Warning", Alert.BAR, Sound.Ding);
Alert(sellWarn, "Sell Warning", Alert.BAR, Sound.Ding);
Alert(buyExit, "Buy Exit", Alert.BAR, Sound.Ring);
Alert(sellExit, "Sell Exit", Alert.BAR, Sound.Ring);
 
1 and 2, your assumptions are correct, yes.

3 and 4, on the roadmap.

thank you for all your thoughts, testing and suggestions. All of these will make this indicator much better.
No, dude, thank you. This developing study is quite amazing. I don't have too much coding knowledge, but I'm happy to contribute by testing/experimenting.

1. Another request, if you think they're useful: exitat ergodicosc alone and profitdeltaorergodicosc. @Hypoluxa mentioned in the op that you could potentially ride it out till ergodic turns on you. My observations are that it appears better for longer runs than macd alone.

2. Not sure what's happened with your latest update and if it's accurate, but the winrate for macd alone has dropped a ton. I'm showing 38% winrate on my settings where it was previously 89%. Are you sure it's accurate?

3. When I try to set cross distance to 0 (experimenting), I'm not sure it's showing accurate winrate because it shows the same winrate if I change it to -.5, -1, etc. Is this working properly or is there something wrong?

4. What does bb cross in bars do exactly? I don't understand from the code. I knw you mentioned that it will allow less or more trades depending on its value change, but can you please explain what it changes exactly? Is it something I can see on the macd indicator?
 
Status
Not open for further replies.

New Indicator: Buy the Dip

Check out our Buy the Dip indicator and see how it can help you find profitable swing trading ideas. Scanner, watchlist columns, and add-ons are included.

Download the indicator

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

87k+ Posts
325 Online
Create Post

The Market Trading Game Changer

Join 2,500+ subscribers inside the useThinkScript VIP Membership Club
  • Exclusive indicators
  • Proven strategies & setups
  • Private Discord community
  • ‘Buy The Dip’ signal alerts
  • Exclusive members-only content
  • Add-ons and resources
  • 1 full year of unlimited support

Frequently Asked Questions

What is useThinkScript?

useThinkScript is the #1 community of stock market investors using indicators and other tools to power their trading strategies. Traders of all skill levels use our forums to learn about scripting and indicators, help each other, and discover new ways to gain an edge in the markets.

How do I get started?

We get it. Our forum can be intimidating, if not overwhelming. With thousands of topics, tens of thousands of posts, our community has created an incredibly deep knowledge base for stock traders. No one can ever exhaust every resource provided on our site.

If you are new, or just looking for guidance, here are some helpful links to get you started.

What are the benefits of VIP Membership?
VIP members get exclusive access to these proven and tested premium indicators: Buy the Dip, Advanced Market Moves 2.0, Take Profit, and Volatility Trading Range. In addition, VIP members get access to over 50 VIP-only custom indicators, add-ons, and strategies, private VIP-only forums, private Discord channel to discuss trades and strategies in real-time, customer support, trade alerts, and much more. Learn all about VIP membership here.
How can I access the premium indicators?
To access the premium indicators, which are plug and play ready, sign up for VIP membership here.
Back
Top