SuperTrend Backtesting Strategy for ThinkorSwim

I did more backtesting of the strategy on Boeing to see if the last 30 days were an aberration and to a certain extent it was. I went back almost a full year, to April 26, 2019 using the 2 minute time period. Through the end of trading today the strategy would have resulted in 2,117 trades netting a profit of $12,721. From the start of that time period through Feb 25, the cumulative profit was +$3,253. Since then it has added +$9,468. Overall the strategy netted 128 profitable days to 120 unprofitable days. The average profit day was +$252 and the average loss day was -$163. Across the entire time period the number of long and short trades were nearly equal, but long trades made 50% more per trade than short trades. Also the strategy was most profitable when trades began between 11am and 1:58pm. The strategy was profitable each day of the week except Thursdays. The most profitable day was Wednesday, followed by Tuesday. The first half hour on Mondays was not profitable but Fridays after 2pm were very profitable.
 
@Midtown Mo Wow - what are you using to get these stats? pulling the backtesting data for each 30day section, uploading it into a dataset for analysis in PowerBI or something? When you did you analysis on the BETA were you using the running intraday beta value or just using the beta posted at the beginning of the trading day?

I'm seeing a similar correlation that you were able to uncover. When beta is higher than like 1.2 gains are more realized. sub 1... nearly nothing but losses.

Did you use anything to come up with this correlation or just trial and error?
 
@RConner7 - Yes, I pulled day for each 30day section and created a database in Excel after running a macro to clean it up. On the Beta, I found a site that published the beta for leading stocks. I thought about Beta after watching how the strategy worked during the trading day today. Since orders are triggered after a number of consecutive up or down candles, it seemed to me that the more variation in the stock price, the more successful the strategy could play out. I thought about intraday Beta but haven't run it yet. Also, since your strategy essentially stops trades after 3pm, it would be better to use an intraday beta that matches that time period. But right now that would take me a lot of effort and I'm not sure the juice is worth the squeeze. What I did though was to look at a proxy for beta over the last year, 30 trading days and 10 trading days by determining the standard deviation of the % change of the daily closing prices across each of the 30 stocks in the DJIA. For the past 30 days, there is very strong correlation between higher standard deviation (i.e. beta) and strategy profitability. However when I just looked at the last 10 days, it broke down a bit (two of the five stocks with the highest standard deviation had a loss). I want to expand this to the S&P 500 to see how this works on a larger number of stocks.
 
I am kind of new to using ToS and familiar with loading scripts in the studies, but when I enter your code in and save it, it is not loading when I add it to my studies for that screen. I copied all of the text you had entered. Can you please advise if I did something incorrect? Thank you
 
I am kind of new to using ToS and familiar with loading scripts in the studies, but when I enter your code in and save it, it is not loading when I add it to my studies for that screen. I copied all of the text you had entered. Can you please advise if I did something incorrect? Thank you

welcome aboard.

When you add the code, make sure you add it to a strategy and not as a study. The strategy tab is on the same window as the edit studies but it's the 2nd tab over. Strategies have AddOrder code lines that will not function as just a study. Hope this helps.
 
Just ran the strategy for each of the stocks in the S&P 500 based on the 2 minute time period for today's trading, April 21. Only 28% of the stocks had a profit using the strategy (but that was alot better than the market overall since only 6% of the S&P 500 stocks closed higher on the day). I was able to get today's high and low prices for each stock over the full day of regular trading. By dividing the high into the low you get the percentage range for each, which I am using as a proxy for beta. The 100 stocks that had the highest "beta" were more likely to be profitable. Here is how each group of 100 stocks fared ranked on the "beta": 1-100 (highest beta) = 46% profitable, 101-200 = 39%, 201-300 = 21%, 301-400 = 20%, 401-500 (lowest beta) = 14%. The correlation of profits to "beta" was positive, but only +0.17, which is not that strong. I actually used the strategy myself today paper trading six stocks. I was able to post a profit and actually did a little better than the strategy by locking in some profits earlier than the strategy allows.
 
@Midtown Mo This is beyond impressive work. Based on your paper trading... did you trade stocks that were in that 1-100 beta range? Were all of those green? Any recommendations that you could see based on your paper trading?

Which 6 did you trade? what was your over win rate?

Sorry for all the questions but sounds like you are on to something here.
 
Last edited by a moderator:
@RConner7 On Tuesday I traded MPC, CFG, PVH, BA, CMA and HES. Those had a higher 'beta' over a recent time period. I also paper-traded six today but the results weren't so good. Today I traded HCA, AMD, HP, CMA, PXD and FTNT because they were the six highest 'beta's during Tuesday's session. Only 2 were profitable per the strategy today and not enough to overcome the losses on the other four. But I ran the strategy on all the S&P 500 again after today's trading. Only 27% were profitable per the strategy, compared to 82% that closed higher today vs. yesterday). But, like yesterday, today's highest-beta stocks were more likely to be profitable. Here is how each group of 100 stocks fared ranked on the "beta": 1-100 (highest beta) = 51% profitable, 101-200 = 27%, 201-300 = 26%, 301-400 = 22%, 401-500 (lowest beta) = 8%. In fact, each of the nine highest-beta stocks on the day were profitable (and 16 of the top 20). So the trick may be to find stocks with the largest change in the first 30-60 minutes and try to use the strategy against them the rest of the day. I may backtest that and see how it turns out.
 
thanks @Midtown Mo, very cool; can you write a post/tutorial on how you are approaching this backtest, I am sure the members of this forum will appreciate it and will spark a discussion on backtesting and system testing approaches. thanks again!
 
Right now OnDemand is updated through Monday, April 20. I backtested the strategy for that day. 57% of the S&P 500 stocks that day were profitable using the strategy. We've seen that stocks with higher betas performed better using the strategy. I have yet to see a strong correlation between the stocks with the highest variation yesterday carry over to strong performance using the strategy the next trading day. So I tested if you could evaluate the performance of the strategy on stocks that had the biggest move in the first hour of the trading day, to see how the strategy performs for the remainder of the day. So far I've only done this for the S&P 500 for April 20. The good news is there is evidence that this approach helps to find profitable stocks. Remember 57% of the S&P 500 stocks were profitable using the strategy on April 20. But that number rises to 80% if you used the strategy on the 20 stocks that had the greatest absolute percentage change from that day's open to the close at 10:30am. Of the 16 profitable stocks in that group, only four had a profitable trade using the strategy before 10:30am. Now this is just for one day and I would want to do more research but if you can increase your odds of success from a 57% probability to 80% using this technique, that should help you increase your overall profits. To answer @diazlaz, my methodology is a bit of a grind. I literally update a single-day 2 minute chart for each of the S&P 500 stocks one at a time and record the daily Total Profit figure and save it in an Excel file. I'm sure there are others who can figure out to write code to do this more quickly. I am good at Excel macros but have no experience with Python, R, SPSS or SAS (and have limited thinkscript skills). I then import each stock's price downloaded from thinkorswim (for intraday data) or http://finance.jasonstrimpel.com/bulk-stock-download/ for end of day data (also good for open, high and low). Tomorrow I will try paper trading six stocks that have the greatest change from the open until 10:30am and see how I do and will share the results.
 
For today, Thursday, April 23, the strategy was profitable on 27% of the S&P 500 stocks (54% of the stocks were up in price on the day). Again, those with the largest percentage difference between their intraday highs and lows performed better: 1-100 (highest variance) = 50% profitable, 101-200 = 36%, 201-300 = 21%, 301-400 = 16%, 401-500 (lowest beta) = 13%. Today I paper traded six stocks that were among the most variable in the first hour of the session. Half were profitable. Unfortunately the losses outweighed the gains. I am finding it very difficult to rely on this as a profitable strategy. As some have mentioned in this thread, some tinkering of the settings may be needed to find more success. Even with fictious money the wins just aren't adding up. Any suggestions?
 
Hi,
First of all thanks a TONNNN for this lovely script.
I loaded script against the futures market. This showed very promising numbers.

I am new to TOS. I was using Tradestatio in the past and switched to TOS as I felt this TOS is more userfriendly.
Few questions.. appricaite if you can help..
Indicator shows like "Buy 100(200)" "Sell 0(0)" and "Sell 100(200)" "Buy 0(0)" What does this mean ?
if it is 100 stocks.. Is it possible to set number of contracts to trade.
Also the $values caliculated is based on Futures ticks or only Stocks value ?


Thanks
 
For today, Thursday, April 23, the strategy was profitable on 27% of the S&P 500 stocks (54% of the stocks were up in price on the day). Again, those with the largest percentage difference between their intraday highs and lows performed better: 1-100 (highest variance) = 50% profitable, 101-200 = 36%, 201-300 = 21%, 301-400 = 16%, 401-500 (lowest beta) = 13%. Today I paper traded six stocks that were among the most variable in the first hour of the session. Half were profitable. Unfortunately the losses outweighed the gains. I am finding it very difficult to rely on this as a profitable strategy. As some have mentioned in this thread, some tinkering of the settings may be needed to find more success. Even with fictious money the wins just aren't adding up. Any suggestions?
How are you getting all of that cool data about percent of s & p stocks as well highest variance?
 
How are you getting all of that cool data about percent of s & p stocks as well highest variance?
This SuperTrend strategy appears to have higher profit correlation with stocks that have wider trading ranges during the day and on days when the volume is higher than average. To prove this I download each day the high and low for each of the S&P 500 stocks from http://finance.jasonstrimpel.com/bulk-stock-download/. Next for each stock divide the high by the low and sort on this column. This gives you the stocks with the highest intraday variance. Next I run a single-day chart for each of the 500 stocks in the S&P 500 and record that day's total profit from the SuperTrend strategy. I have backtested this for each trading day since January 27. I wanted to see if the strategy was profitable before the virus sell off, during it, and since we came off the lows. Happy to report it was profitable in all three time periods, but the highest profit was during the crash as volatility and volume increased. It's not perfect but when analyzing trades for a group of 10 stocks that are in the group to be that day's most volatile, over the past 63 trading days, the strategy was profitable on 76% of the days and the average winner was twice as high as the average loser. This is based on my analysis of more than 11,700 trades across a variety of S&P 500 stocks. These profits were achieved even though only 42% of the trades were profitable. As you know, there can be many false signals but the fact that the average profitable trade is so much more than the average loss on a non-profitable trade makes this strategy a winner. Now this is using the standard parameters for all stocks. I see you've tried to adjust the parameters for one stock but I have yet to try that. Also would be interesting to see the impact on profits by adjusting the parameters and seeing if 1-minute, 2-minute or 3-minute bars are best.
 
@Midtown Mo It would be awesome to see the data of this run on the 2h. I'd expect from looking around to see it being profitable on 80% of stocks with amazing margins on some. I've found some individual stocks with 2/3 win rates and 3/1 win size/loss size ratio.
Code:
#3-SuperTrend & HighLow Strategy by RConner7
#includes Backtesting utility results from eddielee394
#2 min time frame but could 3 ST ATRs and High/Low lookbackPeriod can be adjusted.
#----------------------------
#def atr1 = atr();
#def atr2 = atr() * 2;
#def atr3 = atr() * 3;
 def SDmult = 2.3;
def ATRmult9 = 1.3;
def lengthsqu = 5;
input AlertOn  = yes;
input AtrMult = 1.0;

input nATR = 4;

input AvgType = AverageType.ExpONENTIAL;

input AtrMult_2 = 1.2;

input nATR_2 = 4;

input AvgType_2 = AverageType.WeIGHTED;

input AtrMult_3 = 2.9;

input nATR_3 = 5;

input AvgType_3 = AverageType.ExPONENTIAL;

input PaintBars = yes;

input BubbleOn = yes;

input ShowLabel = yes;
#SMIExtreme
input audioalarm = yes;
input Price = hlc3;
input RsqLength = 5;
input RsqLimit = .5;
input SmiLimit = 30;
input chopband2 = 70;
input smibarbufr = 4;
input showBreakoutSignals = yes;

plot upper = 70;
plot lower = -70;

def overbought = SmiLimit;
def oversold = -SmiLimit;
def percentDLength = 4;
def percentKLength = 5;

#Stochastics

input over_bought = 80;
input over_sold = 20;
input KPeriod = 12;
input DPeriod = 7;
input priceH = high;
input priceL = low;
input priceC = close;
input averageType = AverageType.EXPONENTIAL;

input lengthmo = 12;

def price7 = (open + close)/2;



def K = (Highest(high, lengthmo) + Lowest(low, lengthmo)) /
               2 + ExpAverage(close, lengthmo);
def Momo = Inertia(price7 - K / 2, lengthmo);

def SD = StDev(close, lengthsqu);
def Avg = ExpAverage(close, lengthsqu);
def ATRb = Average(TrueRange(high, close, low), lengthsqu);
def SDup = Avg + (SDmult * SD);
def ATRup = Avg + (ATRmult9 * ATRb);
def Squeeze3 = SDup < ATRup;


def SlowK = reference StochasticFull(over_bought, over_sold, KPeriod, DPeriod, priceH, priceL, priceC, 3, averageType).FullK;
def SlowD = reference StochasticFull(over_bought, over_sold, KPeriod, DPeriod, priceH, priceL, priceC, 3, averageType).FullD;

def badgood = (slowd crosses above slowk);

#ATRBreakout
input BuyEntry = 3;
input SellEntry = 3;

def QB = Highest(high, BuyEntry);
def QS = Lowest(low, SellEntry);
plot trueqb = QB[1];
plot trueqs = QS[1];
plot midline = (QB[1] + QS[1]) / 2;
plot sellline = ((QB[1] * 3) + (QS[1] * 2)) / 5;
plot buyline = ((QS[1] * 3) + (QB[1]) * 2) / 5;


input lengthn = 49;

def lProjection = reference TimeSeriesForecast(price = Price, length = lengthn, "bar plus" = 1);
def convolutionLength = Floor(Sqrt(lengthn));

def LeavittConvolutionSlope = reference LinearRegressionSlope(price = lProjection, length = convolutionLength);
def squeeze = (LeavittConvolutionSlope > .01) or (LeavittConvolutionSlope < -.01);

#def beginDay = if barNumber() < nATR then barNumber() else nATR;

def h = high;

def l = low;

def c = close;

def v = volume;

def bar = BarNumber();

def CloseAllCondition = CLOSE > 600000;

def EOD = close > 600000;

def NotActive = close > 6000000;

def Active1 = close < 60000;

def notrades = close > 60000000;

def ATR = MovingAverage(AvgType, TrueRange(h, c, l), nATR);
def UP_Band_Basic = HL2 + (AtrMult * ATR);

def LW_Band_Basic = HL2 + (-AtrMult * ATR);

def UP_Band = if Active1 and ((UP_Band_Basic < UP_Band[1]) or (close[1] > UP_Band[1])) then UP_Band_Basic else UP_Band[1];

def LW_Band = if Active1 and ((LW_Band_Basic > LW_Band[1]) or (close[1] < LW_Band[1])) then LW_Band_Basic else LW_Band[1];

def ST = if Active1 and ((ST[1] == UP_Band[1]) and (close < UP_Band)) then UP_Band else if ((ST[1] == UP_Band[1]) and (close > UP_Band)) then LW_Band else if ((ST[1] == LW_Band[1]) and (close > LW_Band)) then LW_Band else if ((ST[1] == LW_Band) and (close < LW_Band)) then UP_Band else LW_Band;

plot SuperTrend = ST;

SuperTrend.AssignValueColor(if c < ST then Color.RED else Color.GREEN);

SuperTrend.SetPaintingStrategy(PaintingStrategy.LINE);

AssignPriceColor(if PaintBars and c < ST

                 then Color.RED

                 else if PaintBars and c > ST

                      then Color.GREEN

                      else Color.CURRENT);

#ST2


input PaintBars_2 = no;

input BubbleOn_2 = no;

input ShowLabel_2 = no;

def beginDay_2 = if BarNumber() < nATR_2 then BarNumber() else nATR_2;


def Active_2 = close < 600000;

def ATR_2 = MovingAverage(AvgType_2, TrueRange(h, c, l), nATR_2);
def UP_Band_Basic_2 = HL2 + (AtrMult_2 * ATR_2);

def LW_Band_Basic_2 = HL2 + (-AtrMult_2 * ATR_2);

def UP_Band_2 = if Active_2 and ((UP_Band_Basic_2 < UP_Band_2[1]) or (close[1] > UP_Band_2[1])) then UP_Band_Basic_2 else UP_Band_2[1];

def LW_Band_2 = if Active_2 and ((LW_Band_Basic_2 > LW_Band_2[1]) or (close[1] < LW_Band_2[1])) then LW_Band_Basic_2 else LW_Band_2[1];

def ST_2 = if Active_2 and ((ST_2[1] == UP_Band_2[1]) and (close < UP_Band_2)) then UP_Band_2 else if ((ST_2[1] == UP_Band_2[1]) and (close > UP_Band_2)) then LW_Band_2 else if ((ST_2[1] == LW_Band_2[1]) and (close > LW_Band_2)) then LW_Band_2 else if ((ST_2[1] == LW_Band_2) and (close < LW_Band_2)) then UP_Band_2 else LW_Band_2;


plot SuperTrend_2 = ST_2;

SuperTrend_2.AssignValueColor(if c < ST_2 then Color.RED else Color.GREEN);

SuperTrend_2.SetPaintingStrategy(PaintingStrategy.LINE);

AssignPriceColor(if PaintBars_2 and c < ST_2

                 then Color.RED

                 else if PaintBars_2 and c > ST_2

                      then Color.GREEN

                      else Color.CURRENT);

#ST3



input PaintBars_3 = no;

input BubbleOn_3 = no;

input ShowLabel_3 = no;

#def beginDay_3 = if barNumber() < nATR_3 then barNumber() else nATR_3;


def Active_3 = close < 60000000;

def ATR_3 = MovingAverage(AvgType_3, TrueRange(h, c, l), nATR_3);
def UP_Band_Basic_3 = HL2 + (AtrMult_3 * ATR_3);

def LW_Band_Basic_3 = HL2 + (-AtrMult_3 * ATR_3);

def UP_Band_3 = if Active_3 and ((UP_Band_Basic_3 < UP_Band_3[1]) or (close[1] > UP_Band_3[1])) then UP_Band_Basic_3 else UP_Band_3[1];

def LW_Band_3 = if Active_3 and ((LW_Band_Basic_3 > LW_Band_3[1]) or (close[1] < LW_Band_3[1])) then LW_Band_Basic_3 else LW_Band_3[1];

def ST_3 = if Active_3 and ((ST_3[1] == UP_Band_3[1]) and (close < UP_Band_3)) then UP_Band_3 else if ((ST_3[1] == UP_Band_3[1]) and (close > UP_Band_3)) then LW_Band_3 else if ((ST_3[1] == LW_Band_3[1]) and (close > LW_Band_3)) then LW_Band_3 else if ((ST_3[1] == LW_Band_3) and (close < LW_Band_3)) then UP_Band_3 else LW_Band_3;

plot SuperTrend_3 = ST_3;

SuperTrend_3.AssignValueColor(if c < ST_3 then Color.RED else Color.GREEN);

SuperTrend_3.SetPaintingStrategy(PaintingStrategy.LINE);

AssignPriceColor(if PaintBars_3 and c < ST_3

                 then Color.RED

                 else if PaintBars_3 and c > ST_3

                      then Color.GREEN

                      else Color.CURRENT);

#VWAP
def VWAP = if open > reference VWAP()."VWAP" then 1 else 0;

#Money Flow Index
#input MFI_length = 10;
#input MFI_movingAvgLength = 2;

#def MFI = Average(moneyflow(high, close, low, volume, MFI_length), #MFI_movingAvgLength);


#ADX
#input ADXlength = 14;
#input ADXaverageType = AverageType.WILDERS;

#def ADX = DMI(ADXlength, ADXaverageType).ADX;
#def ADX_Trend = if ADX >= 25 then 1 else 0;

#swing high low
input LookbackPeriod = 2;
input HideCurrentTF = no;

def FirstBar = BarNumber();
def Highest = fold i = 1
             to LookbackPeriod + 1
             with p = 1
             while p
             do high > GetValue(high, -i);
def A = if (FirstBar > LookbackPeriod
            and high == Highest(high, LookbackPeriod)
            and Highest)
            then high
            else Double.NaN;
def Lowest = fold j = 1
            to LookbackPeriod + 1
            with q = 1
            while q
            do low < GetValue(low, -j);
def B = if (FirstBar > LookbackPeriod
            and low == Lowest(low, LookbackPeriod)
            and Lowest)
            then low
            else Double.NaN;

#--------------------------------------------------------------
def _highInPeriod1 = Highest(high, LookbackPeriod);
def _lowInPeriod1 = Lowest(low, LookbackPeriod);
#--------------------------------------------------------------
def marketLow1 = if _lowInPeriod1 < _lowInPeriod1[-LookbackPeriod] then _lowInPeriod1 else _lowInPeriod1[-LookbackPeriod];
def _markedLow1 = low == marketLow1;

rec _lastMarkedLow1 = CompoundValue(1, if IsNaN(_markedLow1) then _lastMarkedLow1[1] else if _markedLow1 then low else _lastMarkedLow1[1], low);
#--------------------------------------------------------------
def marketHigh1 = if _highInPeriod1 > _highInPeriod1[-LookbackPeriod] then _highInPeriod1 else _highInPeriod1[-LookbackPeriod];
def _markedHigh1 = high == marketHigh1;

rec _lastMarkedHigh1 = CompoundValue(1, if IsNaN(_markedHigh1) then _lastMarkedHigh1[1] else if _markedHigh1 then high else _lastMarkedHigh1[1], high);
#--------------------------------------------------------------

plot Resistance1 = _lastMarkedHigh1;
plot Support1 = _lastMarkedLow1;

#---------------------------------------
plot ph = Round(A, 2);
ph.SetPaintingStrategy(PaintingStrategy.VALUES_ABOVE);
plot pl = Round(B, 2);
pl.SetPaintingStrategy(PaintingStrategy.VALUES_BELOW);
#--------------------------------------------------------------
Resistance1.SetPaintingStrategy(PaintingStrategy.DASHES);
Resistance1.SetDefaultColor(Color.GREEN);
Resistance1.SetHiding(HideCurrentTF);
#--------------------------------------------------------------
Support1.SetPaintingStrategy(PaintingStrategy.DASHES);
Support1.SetDefaultColor(Color.RED);
Support1.SetHiding(HideCurrentTF);

#Bull

def ni = (open + close)/2;
def fast_ST_UP = if ni > ST then 1 else 0;
def slow_ST_UP = if ni > ST_2 then 1 else 0;
def slower_ST_UP = if ni > ST_3 then 1 else 0;
def any_UP = fast_ST_UP or slower_ST_UP;
def Trend_UP = if ni > ST then 1 else 0;
def Trend_UP_2 = if ni > ST_2 then 1 else 0;
def Trend_UP_3 = if ni > ST_3 then 1 else 0;
def Trend_UP_ALL = Trend_UP and Trend_UP_3;
def higher_close = if close crosses above Resistance1[1] then 1 else 0;
def candle = ((open + close) / 2);
#Bear
def fast_ST_DN = if HL2 < ST then 1 else 0;
def slow_ST_DN = if HL2 < ST_2 then 1 else 0;
def slower_ST_DN = if HL2 < ST_3 then 1 else 0;
def any_DN = fast_ST_DN or slower_ST_DN;
def Trend_DN = if HL2 < ST then 1 else 0;
def Trend_DN_2 = if HL2 < ST_2 then 1 else 0;
def Trend_DN_3 = if HL2 < ST_3 then 1 else 0;
def Trend_DN_ALL = Trend_DN and Trend_DN_3;
def lower_close = if close crosses below Support1[1] then 1 else 0;
plot middleST = (hl2[1] + ST_3[1])/2;
def fg = (high - close) < ((close - open) * 2);
def fgbear = (close-low) < ((open - close) * 2);
def goodcandle = ((high - close) < (hl2 - low)) or (open > high[1]);
def goodsellcandle = (close - low) < (high - hl2);

AddOrder(OrderType.BUY_AUTO, (any_UP) and Trend_UP_ALL and (higher_close or higher_close[1] or higher_close[2]), price = close, tickcolor = Color.ORANGE, arrowcolor = Color.ORANGE, name = "BUY");

AddOrder(OrderType.SELL_TO_CLOSE, (candle crosses below ST_2), price = open[-1], tickcolor = Color.ORANGE, arrowcolor = Color.ORANGE, name = "Sell To Close");

AddOrder(OrderType.SELL_AUTO, (any_DN) and Trend_DN_ALL and (lower_close or lower_close[1] or lower_close[2])  , price = close, tickcolor = Color.YELLOW, arrowcolor = Color.YELLOW, name = "SELL");

AddOrder(OrderType.BUY_TO_CLOSE, (candle crosses above ST_2 ), price = open[-1], tickcolor = Color.ORANGE, arrowcolor = Color.ORANGE, name = "Buy To Close");



Alert((fast_ST_UP or slow_ST_UP or slower_ST_UP) and Trend_UP and Trend_UP_2 and Trend_UP_3 and Active1 and higher_close and notrades, "BUY BUY BUY", Alert.BAR, Sound.Bell);
Alert((fast_ST_DN or slow_ST_DN or slower_ST_DN) and Trend_DN and Trend_DN_2 and Trend_DN_3  and Active1 and lower_close and notrades, "SELL SELL SELL", Alert.BAR, Sound.Ring);

############################
# FPL Extended
# Extended Floating P&L study.
# Author: Eddielee394
# Version: 1.2
# inspired by FPL Dashboard script developed by Mobius
#
############################

#Hint: An extended floating P&L study to be used alongside TOS strategies for measuring hypothetical strategy performance.\nThis is a work in progress.  And may contain some bugs or other programming issues.

############################
# Instructions
# - Due to limitations with the thinkscript public api, this specific script must be added to a "strategy" study.
#   Generally best practice is to append this script to the end of your custom strategy (ensuring it runs AFTER the
#   `AddOrder()` function is called from the strategy).  A better method would be to use as a lower study but unless
#    a workaround is implemented to handle the `Entry()` function in a lower study, it can only be applied to upper strategies.
#
# - the script uses the `HidePrice()` function which will hide the actual price candles within the upper study,
#   only displaying the FPL histogram.
#
############################


############################
#    Metrics               
#  - Active Trade return %
#  - Entry Count           
#  - Winning Entry Count   
#  - Win rate             
#  - Avg return           
#  - avg win               
#  - avg loss             
#  - peak to valley dd     
#  - largest equity dd     
#  - P&L low               
#  - P&L high             
#  - Highest return     
#  - Lowest return       
############################

############################
#     Todo:               
# - Sharpe Ratio     
# - Sortino Ratio     
# - Calmar Ratio     
# - Avg trade           
#   duration             
# -Buy/hold comparison     
############################


#Globals
def nan = Double.NaN;
def bn = if !IsNaN(close) and !IsNaN(close[1]) and !IsNaN(close[-1]) then BarNumber() else bn[1];

#Inputs
input fplBegin = 0000;
#hint fplBegin: start time: the time in which then dailyHighLow profit should consider the day start.  Recommended value is 0000.

input fplTargetWinLoss = .50;
#hint fplTargetWinLoss: sets the target winlossRatio (in percent) which determines display colors of the W/L label.

input fplTargetWinRate = 1;
#hint fplTargetWinRate: sets the target winRate (float) which determines display colors of the WinRate label;

input fplHidePrice = no;
#hint fplHidePrice: hide's the underlying price graph. \nDefault is no.

input fplHideFPL = yes;
#hint fplHideFPL: hide's the underlying P&L graph.\nDefault is yes.

input fplShowEntryBubbles = no;
#hint fplShowEntryBubbles: display bubbles on the FPL showing the entry and exit P&L values

input fplEnableDebugging = no;
#hint fplEnableDebugging: displays various debugging labels and chart bubbles. \nIt's recommended to hide the price chart & set the fpl hide fpl setting to yes, when enabling.

#temp input var references
def begin = fplBegin;
def targetWinLoss = fplTargetWinLoss;
def targetWinRate = fplTargetWinRate;
def hidePrice = fplHidePrice;
def hideFPL = fplHideFPL;
def showEntryBubbles = fplShowEntryBubbles;
def enableDebugging = fplEnableDebugging;

#hide chart candles
HidePricePlot(hidePrice);

#Plot default Floating P&L
plot FPL = FPL();
FPL.SetPaintingStrategy(PaintingStrategy.SQUARED_HISTOGRAM);
FPL.DefineColor("Positive and Up", Color.GREEN);
FPL.DefineColor("Positive and Down", Color.DARK_GREEN);
FPL.DefineColor("Negative and Down", Color.RED);
FPL.DefineColor("Negative and Up", Color.DARK_RED);
FPL.AssignValueColor(if FPL >= 0
                            then if FPL > FPL[1]
                            then FPL.Color("Positive and Up")
                            else FPL.Color("Positive and Down")
                            else if FPL < FPL[1]
                            then FPL.Color("Negative and Down")
                            else FPL.Color("Negative and Up"));
FPL.SetHiding(hideFPL);

plot ZeroLine = if IsNaN(close)
                then nan
                else 0;
ZeroLine.SetDefaultColor(Color.GRAY);
ZeroLine.SetHiding(hideFPL);

#Global Scripts
script calculateDrawdown {
    input plLow = 1;
    input plHigh = 1;

    def _drawdown = if plHigh == 0
                   then 0 #handles the divide by zero error
                   else (plLow - plHigh) / plHigh;
    plot calculateDrawdown = _drawdown;
}

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;
}
;

script getDurationInMins {
    input gdimBarCount = 1;

    #get the aggregation period (MS per bar)
    def aggPeriod = GetAggregationPeriod();

    #multiply length of bars by aggPeriod to determine total milliseconds then convert to minutes
    def _getDurationInMins = Floor((gdimBarCount * aggPeriod) / 60000);
    plot getDurationInMins = _getDurationInMins;
}

script formatDuration {
    input fdBarCount = 1;
    def _fdDuration = getDurationInMins(fdBarCount);
    def _formatDuration = if _fdDuration >= 60 and _fdDuration < 1440 #hours but not days
                         then 1
                         else if _fdDuration >= 1440 #days
                         then 2
                         else 0;

    plot formatDuration = _formatDuration;
}

script calculateDuration {
    input cdBarCount = 1;
    def _cdDurationFormat = formatDuration(cdBarCount);
    def _cdDuration = getDurationInMins(cdBarCount);
    
    #if minutes > hour convert to hour, else if minutes > day, then convert to days
    def _calculateDuration = if _cdDurationFormat == 1
                             then _cdDuration / 60 #convert to hours if greater than 60min but less than a day (1440)
                             else if  _cdDurationFormat == 2
                             then Ceil(_cdDuration / 1440) #convert to days if greater than 1440mins
                             else _cdDuration; #fallback to minutes

    plot calculateDuration = _calculateDuration;
}

# Entry Calculations.  Note: Only parses on a Strategy Chart
def entry = EntryPrice();

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

def hasEntry = !IsNaN(entry);

def isNewEntry = entryPrice != entryPrice[1];

#is active trade
def Active = if SecondsTillTime(begin) == 0 and
                SecondsFromTime(begin) == 0
             then 1
             else 0;

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 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 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 / entryCount;
def avgReturn = TotalSum(entryFPLAll) / entryCount;
def avgWin = TotalSum(entryFPLWins) / winCount;
def avgLoss = TotalSum(entryFPLLosses) / lossCount;

#Drawdown
def lowestLowBarNumber = HighestAll(if FPL == lowFPL then bn else 0);
def highestHighBarNumber = HighestAll(if FPL == highFPL and FPL != FPL[1] then bn else 0);
def hasPrevLow = lowestLowBarNumber < highestHighBarNumber;

def isPeak = FPL > Highest(FPL[1], 12) and FPL > Highest(FPL[-12], 12);
def isTrough = FPL < Lowest(FPL[1], 12) and FPL < Lowest(FPL[-12], 12);
def _peak = if isPeak then FPL else nan;
def _trough = if isTrough then FPL else nan;
def peak = if !IsNaN(_peak) then FPL else peak[1];
def trough = if !IsNaN(_trough) then FPL else trough[1];
def peakBN = if isPeak then bn else peakBN[1];
def troughBN = if isTrough then bn else troughBN[1];

def ptvDrawdown = if !hasPrevLow then calculateDrawdown(lowFPL, highFPL) else ptvDrawdown[1];
def equityDrawdown = if isTrough and trough < peak then trough - peak else equityDrawdown[1];
def equityDrawdownPercent = if isTrough and trough < peak then calculateDrawdown(trough, peak) else equityDrawdownPercent[1];
def largestEquityDrawdown = LowestAll(equityDrawdown);
def largestEquityDrawdownPercent = LowestAll(equityDrawdownPercent);

def drawdown = if hasPrevLow
               then largestEquityDrawdownPercent
               else ptvDrawdown;

# Drawdown Durations
def equityDrawdownLength = if bn >= peakBN and bn <= troughBN
                           then troughBN - peakBN
                           else equityDrawdownLength[1];

def ptvDrawdownLength = if bn >= highestHighBarNumber and bn <= lowestLowBarNumber
                        then lowestLowBarNumber - highestHighBarNumber
                        else ptvDrawdownLength[1];

def equityDrawdownDuration = calculateDuration(HighestAll(equityDrawdownLength));
def equityDrawdownDurationFormat = formatDuration(HighestAll(equityDrawdownLength));
def ptvDrawdownDuration = calculateDuration(ptvDrawdownLength);

def ptvDrawdownDurationFormat = formatDuration(ptvDrawdownLength);

#Daily profit
def Midnight = if Active then FPL else Midnight[1];
def DaysProfit = FPL - Midnight;

#Plots

AddChartBubble(!hideFPL and showEntryBubbles and isEntryBar, FPL, "Entry: " + entryBarPL + " | " + bn, Color.WHITE);
AddChartBubble(!hideFPL and showEntryBubbles and isExitBar, FPL, "Exit: " + exitBarPL,
               color = if isWin
                       then Color.LIGHT_GREEN
                       else if isLoss
                       then Color.DARK_RED
                       else Color.GRAY,
               up = no
              );

#Labels

AddLabel(yes,
         text = "LastEntry: " + AsPrice(entryPrice)
         );

AddLabel(hasEntry,
         text = "Current Trade % Return:  " + AsPercent(cumsum),
         color = if cumsum > 0
         then Color.GREEN
         else Color.RED
        );

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

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

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

AddLabel(yes,
        text = "HighestReturn: " +  AsDollars(highestReturn),
        color = if highestReturn > 0
                then Color.GREEN
                else Color.RED
        );

AddLabel(yes,
        text = "LowestReturn: " +  AsDollars(lowestReturn),
        color = if lowestReturn > 0
                then Color.GREEN
                else Color.RED
        );

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

AddLabel(yes,
        text = "PeakToValley DD: " +  AsPercent(drawdown) +
               " | Duration: " + ptvDrawdownDuration +
                if ptvDrawdownDurationFormat == 1
                then " hours"
                else if ptvDrawdownDurationFormat == 2
                then " days"
                else " mins" ,
        color = if drawdown > 0
                then Color.GREEN
                else Color.RED
        );


AddLabel(largestEquityDrawdown < 0,
        text = "Largest Equity DD: " +  AsDollars(largestEquityDrawdown) +
               " | Duration: " + equityDrawdownDuration +
                if equityDrawdownDurationFormat == 1
                then " hours"
                else if equityDrawdownDurationFormat == 2
                then " days"
                else " mins",
        color = if largestEquityDrawdown > 0
                then Color.GREEN
                else Color.RED
        );

AddLabel(yes,
         text = "P&L High" +
                (if enableDebugging
                then " at bar " + highBarNumber
                else "") +
                ":  " + AsDollars(highFPL),
        color = Color.GREEN
       );

AddLabel(yes,
         text = "P&L Low" +
                (if enableDebugging
                then " at bar " + lowBarNumber
                else "") +
                ":  " + AsDollars(lowFPL),
        color = Color.RED
       );

AddLabel(yes,
         text = "Days Profit: $" + DaysProfit,
         color = if DaysProfit > 0
                 then Color.GREEN
                 else Color.RED
        );

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

#debugging

#peaks & troughs
AddChartBubble(enableDebugging and isPeak, FPL,
               text = "FPL: " + FPL
                      + " | Peak: " + peak
                      + " | Trough: " + trough[-1]
                      + " | Drawdown: " + AsPercent(calculateDrawdown(trough, peak))
                      + " | PeakBN: " + peakBN
                      + " | BarNumber: " + bn,
               color = Color.LIME
              );

AddChartBubble(enableDebugging and isTrough, FPL,
               text = "FPL: " + FPL
                      + " | Peak: " + peak
                      + " | Trough: " + trough
                      + " | Drawdown: " + AsPercent(calculateDrawdown(trough, peak))
                      + " | TroughBN: " + troughBN
                      + " | BarNumber: " + bn,
              color = Color.LIGHT_RED,
              up = no
             );

AddVerticalLine(enableDebugging and isEntryBar,
                text = "EntryBarNum: " + entryBarNum
                       + " | ExitBarNum: " + exitBarNum[-1]
                       + " | BarNumber: " + bn,
                color = Color.WHITE
);

AddVerticalLine(enableDebugging and isExitBar,
                text =  "EntryBarNum: " + entryBarNum[1]
                        + " | ExitbarNum: " + exitBarNum
                        + " | BarNumber: " + bn
                        + " | EntryReturn: " + entryReturn,
                color = if isWin
                        then Color.LIGHT_GREEN
                        else if isLoss
                        then Color.LIGHT_RED
                        else Color.LIGHT_GREEN
);
 
Why does this script have to be saved as a strategy and not study? trying to learn. I had the same problem as other when installed as study, but after seeing your post and making a strategy it works. Thank you for the information!!
The quick answer is that anytime you see a line of code that says AddOrder, that means the "study" was actually programmed as a strategy. Addorder is what sets up the fake buys and sells. In this case, not only does it have addorder, but the second half of the code is all for tracking the profit/loss of the strategy orders
 
The quick answer is that anytime you see a line of code that says AddOrder, that means the "study" was actually programmed as a strategy. Addorder is what sets up the fake buys and sells. In this case, not only does it have addorder, but the second half of the code is all for tracking the profit/loss of the strategy orders
Thank you so much for the information and education !!
 
Great work with this. Is there a way to get the swing high low (ph/pl) values to paint on the current candle instead of waiting until the following candle opens?
 

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

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

87k+ Posts
149 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