$SPY Swing Strategy

WickFlair

New member
First and foremost I wanna thank @barbaros for coding the TW Golden indicator for me... Thank you
$SPY looks like it might be reversing to the upside so along with my intraday strat I wanna see if a swing strat will be profitable...
Author of strat claims an 82% win rate
Can We get TOS convert please?
https://relaxedtrader.com/spy-swing-trading-system/

Let's use the ETF SPY (which tracks the S&P 500) to demonstrate the first line of code in a swing trading strategy.
Looking at closing prices only, let's ask the computer if SPY is above its 66-day moving average:

close > average (close, 66)

If this expression is true, we want to look for swing trades in the long direction.
You only want to buy if price is above the 66-day moving average (this also works on individual stocks and many commodities).
Now, let's move on to the second part of our swing trading strategy, when exactly do you trade SPY?

Only Buy Dips in Up Trending Markets​

Once you establish that a market is moving higher, you only want to buy dips in that uptrend.
You never want to short an up-trending market as you will get your head handed to you. I know this because I used to bet against the market.
I don't know what it is about new traders and wanting to short stocks, but the majority do.
Humans tend to over-estimate their abilities and this is where I think a lot of that psychology comes from.
Check yourself if you are thinking "I'm smarter than the markets".
Nope, you aren't, and neither am I, that's why we test our ideas.
But how do you tell a computer what a dip is?
Use absolute quantifiable terms here; you can't just say "oh the market should find support here." Also keep your definition of a dip super simple.
You don't want to over optimize and run the risk of curve fitting your swing trading strategies.
So, let's look at SPY and ask the computer a simple question, is today's close the lowest in 3 days? You can play around with the number of days to look back at, but I've found 3 works well.

close < lowest (close, 3)

If this expression is true we have ourselves a "dip".
We now have an uptrend established, a dip in the uptrend and it's time to buy!
(This proves to be remarkably profitable as you will see in a minute.)

Sell the Rally for a Profit​

Do you know what the hardest part of trading truly is?
It's not knowing when to buy, it's knowing when to sell.
You might already intuitively know that. How many stocks have you held onto for too long? How many stocks do you currently have in your portfolio that are severely under water?
Anecdotally, my brother once showed me his portfolio and it was full of losers! I asked him why he didn't sell them, and he responded. "I never know when to sell!"
This is why you must have a system in place, especially when swing trading stocks.
Getting in is easy, getting out is hard.
So how do we know when to sell our position in SPY once we bought the dip?
We must wait for a rally. But what is a "rally"? How do we think like a computer to give it exact instructions?
Well, let's use the inverse of how we defined a dip above. Let's ask the computer to tell us when SPY has made a close higher than the previous 19 days.

close >= highest (close, 19)

If this expression is true, it's time to sell SPY and most likely bank a nice profit.
Okay, let's wrap these three simple ideas into a trading strategy for SPY. Then let's plug it into the computer and see what happens.
Computers don't lie to your face like the suits on Wall Street or internet grifters selling doom and gloom and "technical analysis".
Let's recap what we've put together so far in a visual form:

Simple Swing Trading Strategy for SPY Flowchart:​

3 Lines of Code for Swing Trading SPY With 82% Winning Odds

SPY Swing Trading Strategy Flowchart
It's truly that simple, these trading strategy rules are about as basic as they come.
Now, let's look at how unbelievably powerful this little swing trading strategy is over time:
Swing Trade SPY Equity Curve

SPY Swing Trading Strategy Equity Growth
Each of the green dots above signals a new equity high for this trading model.
Incredible isn't it?
This is the power of testing ideas before jumping headfirst into the stock market.
Let's looks at some example trades produced by this simple swing trading system:
How to swing trade SPY

SPY Swing Trading Strategy Trade Examples
You can see when SPY dips it's usually very profitable to be a buyer.
I often get asked if this is the best spy options strategy, but I usually recommend people stay away from options, unless they really know what they are doing.
OK. Let's look at the statistics of this simple swing trade strategy:
SPY Swing Trading System Trade Performance

Trading Statistics of a Simple Swing Trading System for SPY
But can you believe such simple rules can time the stock market with such great odds?
So next time you are about to make a trade, ask yourself if you truly know the odds of making or losing money.
Because if you don't know the exact odds how do you even know how much money to risk?
This is not rocket science. Anyone can do this.
 

barbaros

Administrator
Staff member
Thanks @WickFlair. I already converted this to a strategy for ToS. Here is the script.

Code:
# SPY Daily Trader
# Free for use. Header credits must be included when any form of the code included in this package is used.
# v1.0 - barbaros - converted for b4signals.com

input initialBalance = 1000;
input atrPeriods = 10;
input lowest_close = 3;
input trend_average = 66;
input highest_close = 19;
input stop_var = 7.95;
input targetWinRate = 1;
input targetWinLoss = .50;
input useMoneyManagement = yes;

def FPL = FloatingPL();
def entry = EntryPrice();

def atrA = Average(AbsValue(close - close[1]), atrPeriods);
def atr = if atrA < 1 then 1 else atrA;

def portfolioSize = if !isNaN(FPL) then FPL + initialBalance else initialBalance;
def contractsToTrade = if useMoneyManagement then RoundDown(portfolioSize / (atr * close), 0) else 1;

AddLabel(yes, "Shares To Trade: " + contractsToTrade, color.gray);

def buyCond = close <= Lowest(close, lowest_close) and close > Average(close, trend_average);
def sellCond = close >= Highest(close, highest_close);

def direction = if BarNumber() == 1 then 0
                else if direction[1] == 0 and  buyCond and contractsToTrade > 0 then 1
                else if direction[1] == 1 and sellCond then 0
                else direction[1];

def stop_price = if BarNumber() == 1 then close else if direction crosses 0 then close - (stop_var * atr) else stop_price[1];

AddOrder(OrderType.BUY_TO_OPEN, direction crosses above 0, tradeSize = contractsToTrade, name = "LONG");
AddOrder(OrderType.SELL_TO_CLOSE, direction crosses below 1, name = "EXIT");

#Global Scripts
script incrementValue {
    input condition = yes;
    input increment =  1;
    input startingValue = 0;

    def _value = CompoundValue(1,
                if condition
                then _value[1] + increment
                else _value[1], startingValue);

    plot incrementValue = _value;
}
;

# Entry Calculations.  Note: Only parses on a Strategy Chart
def bn = if !IsNaN(close) and !IsNaN(close[1]) and !IsNaN(close[-1]) then BarNumber() else bn[1];

def entryPrice = if !IsNaN(entry)
                then entry
                else entryPrice[1];

def hasEntry = !IsNaN(entry);

def isNewEntry = entryPrice != entryPrice[1];

#is active trade
def highFPL = HighestAll(FPL);
def lowFPL = LowestAll(FPL);

def fplreturn = (FPL - FPL[1]) / FPL[1];
def cumsum = Sum(fplreturn);

def highBarNumber = CompoundValue(1, if FPL == highFPL
                                    then bn
                                    else highBarNumber[1], 0);

def lowBarNumber = CompoundValue(1, if FPL == lowFPL
                                then bn
                                else lowBarNumber[1], 0);

#Win/Loss ratios
def entryBarsTemp = if hasEntry
                then bn
                else Double.NaN;

def entryBarNum = if hasEntry and isNewEntry
                then bn
                else entryBarNum[1];

def isEntryBar = entryBarNum != entryBarNum[1];

def entryBarPL = if isEntryBar
                then FPL
                else entryBarPL[1];

def exitBarsTemp = if !hasEntry
                and bn > entryBarsTemp[1]
                then bn
                else Double.NaN;

def exitBarNum = if !hasEntry and !IsNaN(exitBarsTemp[1])
                then bn
                else exitBarNum[1];

def isExitBar = exitBarNum != exitBarNum[1];

def exitBarPL = if isExitBar
            then FPL
            else exitBarPL[1];

def entryReturn = if isExitBar then exitBarPL - exitBarPL[1] else entryReturn[1];
def isWin = if isExitBar and entryReturn >= 0 then 1 else 0;
def isLoss = if isExitBar and entryReturn < 0 then 1 else 0;
def entryReturnWin = if isWin then entryReturn else entryReturnWin[1];
def entryReturnLoss = if isLoss then entryReturn else entryReturnLoss[1];
def entryFPLWins = if isWin then entryReturn else 0;
def entryFPLLosses = if isLoss then entryReturn else 0;
def entryFPLAll = if isLoss or isWin then entryReturn else 0;

#Counts
def entryCount = incrementValue(entryFPLAll);
def winCount = incrementValue(isWin);
def lossCount = incrementValue(isLoss);

def highestReturn = if entryReturnWin[1] > highestReturn[1]
                then entryReturnWin[1]
                else highestReturn[1];

def lowestReturn = if entryReturnLoss[1] < lowestReturn[1]
                then entryReturnLoss[1]
                else lowestReturn[1];


def winRate = winCount / lossCount;
def winLossRatio = winCount / (winCount + lossCount); # winCount / entryCount;
def avgReturn = TotalSum(entryFPLAll) / entryCount;
def avgWin = TotalSum(entryFPLWins) / winCount;
def avgLoss = TotalSum(entryFPLLosses) / lossCount;

#Labels

AddLabel(yes,
        text = "Total Trades: " + entryCount + "  ",
        color = Color.WHITE
        );

AddLabel(yes,
        text = "WinCount: " + winCount +
            " | LossCount: " + lossCount +
            " | WinRate: " + Round(winRate, 2) + "  ",
        color = if winRate >= targetWinRate
                then Color.CYAN
                else Color.MAGENTA
        );

AddLabel(yes,
        text = "W/L: " + AsPercent(winLossRatio) + " ",
        color = if winLossRatio > targetWinLoss
                then Color.CYAN
                else Color.MAGENTA
        );

AddLabel(yes,
        text = "AvgReturn: " + AsDollars(avgReturn) +
            " | AvgWin: " + AsDollars(avgWin) +
            " | AvgLoss: " + AsDollars(avgLoss) + "   ",
        color = if avgReturn >= 0
                then Color.CYAN
                else Color.MAGENTA
        );

AddLabel(yes,
        text = "Total Profit: " + AsDollars(FPL) + "  ",
        color = if FPL > 0
                then Color.CYAN
                else Color.MAGENTA
    );

AddLabel(yes,
        text = "Final Portfolio: " + AsDollars(portfolioSize) + " (" + AsPercent(portfolioSize / initialBalance) + ")  ",
        color = if portfolioSize > initialBalance
                then Color.CYAN
                else Color.MAGENTA
    );
 

WickFlair

New member
I believe this beats buy and hold, but haven’t tested much.

Thanks @WickFlair. I already converted this to a strategy for ToS. Here is the script.

Code:
# SPY Daily Trader
# Free for use. Header credits must be included when any form of the code included in this package is used.
# v1.0 - barbaros - converted for b4signals.com

input initialBalance = 1000;
input atrPeriods = 10;
input lowest_close = 3;
input trend_average = 66;
input highest_close = 19;
input stop_var = 7.95;
input targetWinRate = 1;
input targetWinLoss = .50;
input useMoneyManagement = yes;

def FPL = FloatingPL();
def entry = EntryPrice();

def atrA = Average(AbsValue(close - close[1]), atrPeriods);
def atr = if atrA < 1 then 1 else atrA;

def portfolioSize = if !isNaN(FPL) then FPL + initialBalance else initialBalance;
def contractsToTrade = if useMoneyManagement then RoundDown(portfolioSize / (atr * close), 0) else 1;

AddLabel(yes, "Shares To Trade: " + contractsToTrade, color.gray);

def buyCond = close <= Lowest(close, lowest_close) and close > Average(close, trend_average);
def sellCond = close >= Highest(close, highest_close);

def direction = if BarNumber() == 1 then 0
                else if direction[1] == 0 and  buyCond and contractsToTrade > 0 then 1
                else if direction[1] == 1 and sellCond then 0
                else direction[1];

def stop_price = if BarNumber() == 1 then close else if direction crosses 0 then close - (stop_var * atr) else stop_price[1];

AddOrder(OrderType.BUY_TO_OPEN, direction crosses above 0, tradeSize = contractsToTrade, name = "LONG");
AddOrder(OrderType.SELL_TO_CLOSE, direction crosses below 1, name = "EXIT");

#Global Scripts
script incrementValue {
    input condition = yes;
    input increment =  1;
    input startingValue = 0;

    def _value = CompoundValue(1,
                if condition
                then _value[1] + increment
                else _value[1], startingValue);

    plot incrementValue = _value;
}
;

# Entry Calculations.  Note: Only parses on a Strategy Chart
def bn = if !IsNaN(close) and !IsNaN(close[1]) and !IsNaN(close[-1]) then BarNumber() else bn[1];

def entryPrice = if !IsNaN(entry)
                then entry
                else entryPrice[1];

def hasEntry = !IsNaN(entry);

def isNewEntry = entryPrice != entryPrice[1];

#is active trade
def highFPL = HighestAll(FPL);
def lowFPL = LowestAll(FPL);

def fplreturn = (FPL - FPL[1]) / FPL[1];
def cumsum = Sum(fplreturn);

def highBarNumber = CompoundValue(1, if FPL == highFPL
                                    then bn
                                    else highBarNumber[1], 0);

def lowBarNumber = CompoundValue(1, if FPL == lowFPL
                                then bn
                                else lowBarNumber[1], 0);

#Win/Loss ratios
def entryBarsTemp = if hasEntry
                then bn
                else Double.NaN;

def entryBarNum = if hasEntry and isNewEntry
                then bn
                else entryBarNum[1];

def isEntryBar = entryBarNum != entryBarNum[1];

def entryBarPL = if isEntryBar
                then FPL
                else entryBarPL[1];

def exitBarsTemp = if !hasEntry
                and bn > entryBarsTemp[1]
                then bn
                else Double.NaN;

def exitBarNum = if !hasEntry and !IsNaN(exitBarsTemp[1])
                then bn
                else exitBarNum[1];

def isExitBar = exitBarNum != exitBarNum[1];

def exitBarPL = if isExitBar
            then FPL
            else exitBarPL[1];

def entryReturn = if isExitBar then exitBarPL - exitBarPL[1] else entryReturn[1];
def isWin = if isExitBar and entryReturn >= 0 then 1 else 0;
def isLoss = if isExitBar and entryReturn < 0 then 1 else 0;
def entryReturnWin = if isWin then entryReturn else entryReturnWin[1];
def entryReturnLoss = if isLoss then entryReturn else entryReturnLoss[1];
def entryFPLWins = if isWin then entryReturn else 0;
def entryFPLLosses = if isLoss then entryReturn else 0;
def entryFPLAll = if isLoss or isWin then entryReturn else 0;

#Counts
def entryCount = incrementValue(entryFPLAll);
def winCount = incrementValue(isWin);
def lossCount = incrementValue(isLoss);

def highestReturn = if entryReturnWin[1] > highestReturn[1]
                then entryReturnWin[1]
                else highestReturn[1];

def lowestReturn = if entryReturnLoss[1] < lowestReturn[1]
                then entryReturnLoss[1]
                else lowestReturn[1];


def winRate = winCount / lossCount;
def winLossRatio = winCount / (winCount + lossCount); # winCount / entryCount;
def avgReturn = TotalSum(entryFPLAll) / entryCount;
def avgWin = TotalSum(entryFPLWins) / winCount;
def avgLoss = TotalSum(entryFPLLosses) / lossCount;

#Labels

AddLabel(yes,
        text = "Total Trades: " + entryCount + "  ",
        color = Color.WHITE
        );

AddLabel(yes,
        text = "WinCount: " + winCount +
            " | LossCount: " + lossCount +
            " | WinRate: " + Round(winRate, 2) + "  ",
        color = if winRate >= targetWinRate
                then Color.CYAN
                else Color.MAGENTA
        );

AddLabel(yes,
        text = "W/L: " + AsPercent(winLossRatio) + " ",
        color = if winLossRatio > targetWinLoss
                then Color.CYAN
                else Color.MAGENTA
        );

AddLabel(yes,
        text = "AvgReturn: " + AsDollars(avgReturn) +
            " | AvgWin: " + AsDollars(avgWin) +
            " | AvgLoss: " + AsDollars(avgLoss) + "   ",
        color = if avgReturn >= 0
                then Color.CYAN
                else Color.MAGENTA
        );

AddLabel(yes,
        text = "Total Profit: " + AsDollars(FPL) + "  ",
        color = if FPL > 0
                then Color.CYAN
                else Color.MAGENTA
    );

AddLabel(yes,
        text = "Final Portfolio: " + AsDollars(portfolioSize) + " (" + AsPercent(portfolioSize / initialBalance) + ")  ",
        color = if portfolioSize > initialBalance
                then Color.CYAN
                else Color.MAGENTA
    );
Thank you! Wooooo!
 

barbaros

Administrator
Staff member
Thank you @WickFlair and @barbaros ! I'm excited to try this study out! Quick question. What is the study supposed to look like on the chart? I loaded the study but do not see anything on my chart. Thank you!
It should show you the strategy entry/exits and labels on top of the chart with statistics. Make sure you add it as a strategy and not an indicator.
 

SeanC

New member
really good method.. Could this be converted to a watchlist column that shows some type of 'flag' that the end of that day fits the logic? thanks
 

barbaros

Administrator
Staff member
Here is the watchlist column. It will show the following states:

  • BUY: when long entry is triggered
  • EXIT: when long position should be exited
  • LONG: in a long position
  • HOLD: not in a position

bPTXkHN.png


Code:
# SPY Daily Trader Watchlist Column
# Free for use. Header credits must be included when any form of the code included in this package is used.
# v1.0 - barbaros - converted for b4signals.com

input lowest_close = 3;
input trend_average = 66;
input highest_close = 19;

def buyCond = close <= Lowest(close, lowest_close) and close > Average(close, trend_average);
def sellCond = close >= Highest(close, highest_close);

def direction = if BarNumber() == 1 then 0
                else if direction[1] == 0 and  buyCond then 1
                else if direction[1] == 1 and sellCond then 0
                else direction[1];

AssignBackgroundColor(
    if direction crosses above 0 then Color.GREEN
    else if direction crosses below 1 then Color.WHITE
    else Color.Current
);

AddLabel(yes,
    if direction crosses above 0 then "BUY"
    else if direction crosses below 1 then "EXIT"
    else if direction == 1 then "Long"
    else "HOLD",
    if direction crosses above 0 then Color.BLACK
    else if direction crosses below 1 then Color.BLACK
    else Color.LIGHT_GRAY
);
 

barbaros

Administrator
Staff member
And, there can be a need for scanner if this is found useful...

Look for direction to be 0 to 1 for entries and 1 to 0 for exits.

Code:
# SPY Daily Trader Scanner
# Free for use. Header credits must be included when any form of the code included in this package is used.
# v1.0 - barbaros - converted for b4signals.com

input lowest_close = 3;
input trend_average = 66;
input highest_close = 19;

def buyCond = close <= Lowest(close, lowest_close) and close > Average(close, trend_average);
def sellCond = close >= Highest(close, highest_close);

plot direction = if BarNumber() == 1 then 0
                else if direction[1] == 0 and  buyCond then 1
                else if direction[1] == 1 and sellCond then 0
                else direction[1];
 

SeanC

New member
Here is the watchlist column. It will show the following states:

  • BUY: when long entry is triggered
  • EXIT: when long position should be exited
  • LONG: in a long position
  • HOLD: not in a position

bPTXkHN.png


Code:
# SPY Daily Trader Watchlist Column
# Free for use. Header credits must be included when any form of the code included in this package is used.
# v1.0 - barbaros - converted for b4signals.com

input lowest_close = 3;
input trend_average = 66;
input highest_close = 19;

def buyCond = close <= Lowest(close, lowest_close) and close > Average(close, trend_average);
def sellCond = close >= Highest(close, highest_close);

def direction = if BarNumber() == 1 then 0
                else if direction[1] == 0 and  buyCond then 1
                else if direction[1] == 1 and sellCond then 0
                else direction[1];

AssignBackgroundColor(
    if direction crosses above 0 then Color.GREEN
    else if direction crosses below 1 then Color.WHITE
    else Color.Current
);

AddLabel(yes,
    if direction crosses above 0 then "BUY"
    else if direction crosses below 1 then "EXIT"
    else if direction == 1 then "Long"
    else "HOLD",
    if direction crosses above 0 then Color.BLACK
    else if direction crosses below 1 then Color.BLACK
    else Color.LIGHT_GRAY
);
That is great. Thx
 

SeanC

New member
And, there can be a need for scanner if this is found useful...

Look for direction to be 0 to 1 for entries and 1 to 0 for exits.

Code:
# SPY Daily Trader Scanner
# Free for use. Header credits must be included when any form of the code included in this package is used.
# v1.0 - barbaros - converted for b4signals.com

input lowest_close = 3;
input trend_average = 66;
input highest_close = 19;

def buyCond = close <= Lowest(close, lowest_close) and close > Average(close, trend_average);
def sellCond = close >= Highest(close, highest_close);

plot direction = if BarNumber() == 1 then 0
                else if direction[1] == 0 and  buyCond then 1
                else if direction[1] == 1 and sellCond then 0
                else direction[1];
getting an error on the scanner
Too early to access direction at 13:25
Too early to access direction at 14:25
Too early to access direction at 15:22
Too early to access direction at 13:25
Too early to access direction at 14:25
Too early to access direction at 15:22
 

barbaros

Administrator
Staff member
Ah, try this.

Code:
# SPY Daily Trader Scanner
# Free for use. Header credits must be included when any form of the code included in this package is used.
# v1.0 - barbaros - converted for b4signals.com

input lowest_close = 3;
input trend_average = 66;
input highest_close = 19;

def buyCond = close <= Lowest(close, lowest_close) and close > Average(close, trend_average);
def sellCond = close >= Highest(close, highest_close);

def direction = if BarNumber() == 1 then 0
                else if direction[1] == 0 and  buyCond then 1
                else if direction[1] == 1 and sellCond then 0
                else direction[1];
                
plot result = direction;
 

darkelvis

New member
Ok, so I LOVE playing around with TOS strategy back tester.... This strategy looks like it could be decent as a scalp strategy on a 1 minute chart also (on /ES specifically), but I don't want to hold ANYTHING over night. Do you have a code I could add to this to auto close all positions at the end of the regular trading hours?

Follow up question.... is there any code that could be added that would also add a max $$ loss so you could set a stop of whatever $$ you want. Looking at this as a scalp strategy it has a good win rate on the 1 minute chart, but the r:r sucks.
 

darkelvis

New member
Here is what I'm using for current order conditions so it is buying/selling 1 contract at the CLOSE of the bar instead of buying/selling the open of the bar, decreased the win rate by 7% but increased the profit quite a bit:

AddOrder(OrderType.BUY_TO_OPEN, direction crosses above 0, close[-1], 1, name = "LONG");
AddOrder(OrderType.SELL_TO_CLOSE, direction crosses below 1, close[-1], 1, name = "EXIT");
 

barbaros

Administrator
Staff member
Ok, so I LOVE playing around with TOS strategy back tester.... This strategy looks like it could be decent as a scalp strategy on a 1 minute chart also (on /ES specifically), but I don't want to hold ANYTHING over night. Do you have a code I could add to this to auto close all positions at the end of the regular trading hours?

Follow up question.... is there any code that could be added that would also add a max $$ loss so you could set a stop of whatever $$ you want. Looking at this as a scalp strategy it has a good win rate on the 1 minute chart, but the r:r sucks.
Added intraday only option. It is still profitable, but it might benefit from stoploss.

SQ9YhNm.png


Code:
# SPY Daily Trader
# Free for use. Header credits must be included when any form of the code included in this package is used.
# v1.0 - barbaros - converted for b4signals.com
# v1.1 - barbaros - added intraday option

input initialBalance = 1000;
input atrPeriods = 10;
input lowest_close = 3;
input trend_average = 66;
input highest_close = 19;
input stop_var = 7.95;
input targetWinRate = 1;
input targetWinLoss = .50;
input useMoneyManagement = yes;
input intradayOnly = no;

def FPL = FloatingPL();
def entry = EntryPrice();

def isTrade = !intradayOnly or (SecondsTillTime(0930) <= 0 and SecondsTillTime(1600) > 0);

def atrA = Average(AbsValue(close - close[1]), atrPeriods);
def atr = if atrA < 1 then 1 else atrA;

def portfolioSize = if !isNaN(FPL) then FPL + initialBalance else initialBalance;
def contractsToTrade = if useMoneyManagement then RoundDown(portfolioSize / (atr * close), 0) else 1;

AddLabel(yes, "Shares To Trade: " + contractsToTrade, color.gray);

def buyCond = close <= Lowest(close, lowest_close) and close > Average(close, trend_average);
def sellCond = close >= Highest(close, highest_close);

def direction = if BarNumber() == 1 then 0
                else if direction[1] == 0 and  buyCond and contractsToTrade > 0 then 1
                else if direction[1] == 1 and sellCond then 0
                else direction[1];

def stop_price = if BarNumber() == 1 then close else if direction crosses 0 then close - (stop_var * atr) else stop_price[1];

AddOrder(OrderType.BUY_TO_OPEN, isTrade and direction crosses above 0, tradeSize = contractsToTrade, name = "LONG");
AddOrder(OrderType.SELL_TO_CLOSE, !isTrade or direction crosses below 1, name = "EXIT");

#Global Scripts
script incrementValue {
    input condition = yes;
    input increment =  1;
    input startingValue = 0;

    def _value = CompoundValue(1,
                if condition
                then _value[1] + increment
                else _value[1], startingValue);

    plot incrementValue = _value;
}
;

# Entry Calculations.  Note: Only parses on a Strategy Chart
def bn = if !IsNaN(close) and !IsNaN(close[1]) and !IsNaN(close[-1]) then BarNumber() else bn[1];

def entryPrice = if !IsNaN(entry)
                then entry
                else entryPrice[1];

def hasEntry = !IsNaN(entry);

def isNewEntry = entryPrice != entryPrice[1];

#is active trade
def highFPL = HighestAll(FPL);
def lowFPL = LowestAll(FPL);

def fplreturn = (FPL - FPL[1]) / FPL[1];
def cumsum = Sum(fplreturn);

def highBarNumber = CompoundValue(1, if FPL == highFPL
                                    then bn
                                    else highBarNumber[1], 0);

def lowBarNumber = CompoundValue(1, if FPL == lowFPL
                                then bn
                                else lowBarNumber[1], 0);

#Win/Loss ratios
def entryBarsTemp = if hasEntry
                then bn
                else Double.NaN;

def entryBarNum = if hasEntry and isNewEntry
                then bn
                else entryBarNum[1];

def isEntryBar = entryBarNum != entryBarNum[1];

def entryBarPL = if isEntryBar
                then FPL
                else entryBarPL[1];

def exitBarsTemp = if !hasEntry
                and bn > entryBarsTemp[1]
                then bn
                else Double.NaN;

def exitBarNum = if !hasEntry and !IsNaN(exitBarsTemp[1])
                then bn
                else exitBarNum[1];

def isExitBar = exitBarNum != exitBarNum[1];

def exitBarPL = if isExitBar
            then FPL
            else exitBarPL[1];

def entryReturn = if isExitBar then exitBarPL - exitBarPL[1] else entryReturn[1];
def isWin = if isExitBar and entryReturn >= 0 then 1 else 0;
def isLoss = if isExitBar and entryReturn < 0 then 1 else 0;
def entryReturnWin = if isWin then entryReturn else entryReturnWin[1];
def entryReturnLoss = if isLoss then entryReturn else entryReturnLoss[1];
def entryFPLWins = if isWin then entryReturn else 0;
def entryFPLLosses = if isLoss then entryReturn else 0;
def entryFPLAll = if isLoss or isWin then entryReturn else 0;

#Counts
def entryCount = incrementValue(entryFPLAll);
def winCount = incrementValue(isWin);
def lossCount = incrementValue(isLoss);

def highestReturn = if entryReturnWin[1] > highestReturn[1]
                then entryReturnWin[1]
                else highestReturn[1];

def lowestReturn = if entryReturnLoss[1] < lowestReturn[1]
                then entryReturnLoss[1]
                else lowestReturn[1];


def winRate = winCount / lossCount;
def winLossRatio = winCount / (winCount + lossCount); # winCount / entryCount;
def avgReturn = TotalSum(entryFPLAll) / entryCount;
def avgWin = TotalSum(entryFPLWins) / winCount;
def avgLoss = TotalSum(entryFPLLosses) / lossCount;

#Labels

AddLabel(yes,
        text = "Total Trades: " + entryCount + "  ",
        color = Color.WHITE
        );

AddLabel(yes,
        text = "WinCount: " + winCount +
            " | LossCount: " + lossCount +
            " | WinRate: " + Round(winRate, 2) + "  ",
        color = if winRate >= targetWinRate
                then Color.CYAN
                else Color.MAGENTA
        );

AddLabel(yes,
        text = "W/L: " + AsPercent(winLossRatio) + " ",
        color = if winLossRatio > targetWinLoss
                then Color.CYAN
                else Color.MAGENTA
        );

AddLabel(yes,
        text = "AvgReturn: " + AsDollars(avgReturn) +
            " | AvgWin: " + AsDollars(avgWin) +
            " | AvgLoss: " + AsDollars(avgLoss) + "   ",
        color = if avgReturn >= 0
                then Color.CYAN
                else Color.MAGENTA
        );

AddLabel(yes,
        text = "Total Profit: " + AsDollars(FPL) + "  ",
        color = if FPL > 0
                then Color.CYAN
                else Color.MAGENTA
    );

AddLabel(yes,
        text = "Final Portfolio: " + AsDollars(portfolioSize) + " (" + AsPercent(portfolioSize / initialBalance) + ")  ",
        color = if portfolioSize > initialBalance
                then Color.CYAN
                else Color.MAGENTA
    );
 
Top