Strategy Backtester

T

tomsk

Well-known member
VIP
How do you guys backtest a strategy on ThinkorSwim? I'd like to take a look at the results for the Sequence Counter found in TOS.
@Miket You can backtest any studies by using the Strategy functionality on TOS.
Here is a very simple example of a strategy
Bullish when price closes above the 34 EMA and bearish when prices closes below the 34 EMA

Code:
input length = 34;
input price = close;

plot EMA = ExpAverage(price, length);
EMA.AssignValueColor(if EMA > EMA[1]
                     then Color.Green
                     else Color.Red);
AssignPriceColor(if EMA > EMA[1]
                 then Color.Green
                 else Color.Red);
AddOrder(OrderType.BUY_AUTO, EMA crosses above EMA[1]);
AddOrder(OrderType.Sell_AUTO, EMA crosses below EMA[1]);
To see the results of your strategy, position your mouse on any of the strategy points on your chart
Right click the mouse and select "Show report"
You will then see a table of your buy/sell strategy with date of trade, price as well as P/L from that strategy
 
J

jhatch

New member
Hi all, I was wondering if I could get some help creating a strategy to backtest with this indicator I found on here. The indicator is an altered version of the RSI laguere and features entry and exit targets. I was wondering if its possible to somehow test the accuracy of these entry/exit signals.

Thanks for your time.

Code:

Code:
# RSI in Laguerre Time MTF Option_v3
# Modification of Mobius's script of Elher's code
# Mobius
# V02.07.2014
# translation of J Elher's code
#DISCLAIMER: Mobius mentioned that it was not his habit to give trading advice or even advice on how to use indicators. However, there's been such a proliferation of bad, inaccurate, false info regarding so many of them recently he felt compelled to give some accurate information.

#Mobius©: I use a very simple method – RSI Laguerre and Fractal Energy on a list of very liquid stocks. I look for polarity change and trade when both RSI and FE are in “confluence”. If volatility is high enough I sell spreads if not I buy them. Other than hedging (which I do a lot of) that's it. I like it simple.

#The typical base setting I like to use for the FE is a length of 8. If I'm trading options I like to look at it about the length of time I'm buying or selling the option for. I want to know if it's reverting and where the energy is so I'll use a longer length for reversion and a shorter length to see if energy is building or waning.

#If RSI Laguerre is descending and FE over .6, that tells me something is changing and I'm already looking at an equity I've determined is about to make a polarity change. So the worse case that happens is that the security grinds sideways for a few days.

#A reading of the FE over .6 is an indication that energy has been built up. If the FE is high (over .6) and RSI LaGuerre is breaking lower FE will follow suit. If RSI reverses and goes above .8 I'm outa there, with the assumption I have a short position.

#FE is a gauge of both mean reverting and linearity. Descending readings indicate a trend is on. A reading below .3 indicates exhaustion in trend or near exhaustion. A reading above .6 indicates moving sideways with rapid reversion and energy building for a move again.

#Above .6 - Think price compression or squeeze
#Below .3 - Think running out of gas

#Here's an example:

#FE at 60 periods is oscillating around .5 tightly while FE at 8 periods is over .6. Zscore is over 2 and is starting to roll over. That is a good short to the mean.

#Short trade setup I look for with RSI Laguerre adjusted with FE.

#1) Polarity Change - Equity has gone from making higher highs and higher lows to making a lower high and lower low and is now putting in another lower high

#2) RSI Laguerrer is above .8 and descending from 1

#3) Fractal Energy is below .38 and nose down or above .6 and rolling over. In the first case, below .38, FE is indicating trend exahustion and RSI is likely showing as a peak and not running across pegged at 1. In the second case price has risen to a lower resistance and has been rolling slowly over building energy.


# 20160708-blt added new MTF Option; added Target Code (similar to other versions)
#              added rsi value coloring and clouds
# 20160712-modified the target tracking to include entry and target bubbles. The entry bubbles will appear where there is a change in color of the rsi line
# 20160714-changed atr from atr() to average(truerange(h,c,l),atrlength) to allow atr to be that of the higher timeframe when it's selected, rather than always the current timeframe for targets
#20160715-v2a-added inputs to turn bubbles on/off
#20160716-v3-added candle (normal open, high, low, close) vs candle_hybrid candle type in Mobius' original version. The candle option was included to use when this is combined with another indicator, such as the Fractal Energy indicator. The candle_hybrid seems smoother. A label is added to show what type of candle is being used....Bubble Movers are better defined...Shortened Target Bubble...Added input outputformat to handle bonds, forex, etc format

declare lower;
#Inputs:
input gamma              = .5;
input usecandletype      = {candle, default candle_hybrid};
input usehigheraggperiod = {default "Current", "Higher"};
input agg                = AggregationPeriod.TWO_MIN;
#Current uses the current chart agg. So if you are on a 1min chart the indicator is based on 1min. If you select higher, then whatever agg you enter at the input will be what is used for the RSI. So if you are on a 1min chart and higher is set to 2min, then a 2min RSI and the entries and targets are 2min based.
input outputformat       = {default Rounded, "Not Rounded"};
;#Hint outputformat: 'Not Rounded' is used for notes, bonds (eg: 109'110), forex, etc type format.
input atrlength          = 14;
input atrfactor          = 3;
input overbought         = .8;
input oversold           = .2;
# Variables:
def o;
def h;
def l;
def c;
def CU1;
def CU2;
def CU;
def CD1;
def CD2;
def CD;
def L0;
def L1;
def L2;
def L3;
plot RSI;
plot OS;
plot OB;
def error = usehigheraggperiod == usehigheraggperiod."Higher" and GetAggregationPeriod() > agg;
# Calculations
switch (usehigheraggperiod) {
case Current:
    if usecandletype == usecandletype.candle_hybrid {
        o = (open + close[1]) / 2;
        h = Max(high, close[1]);
        l = Min(low, close[1]);
        c = (o + h + l + close) / 4;
    } else {
        o = open;
        h = high;
        l = low;
        c = close;
    }
case Higher:
    if error {
        o = Double.NaN;
        h = Double.NaN;
        l = Double.NaN;
        c = Double.NaN;
    } else {
        if usecandletype == usecandletype.candle_hybrid {
            o = (open(period = agg)     + close(period = agg)[1]) / 2;
            h = Max(high(period = agg)  , close(period = agg)[1]);
            l = Min(low(period = agg)   , close(period = agg)[1]);
            c = ((open(period = agg)    + close(period = agg)[1]) / 2
            + Max(high(period = agg), close(period = agg)[1])
            + Min(low(period = agg) , close(period = agg)[1])
            + close(period = agg)) / 4;
        } else {
            o = open(period = agg);
            h = high(period = agg);
            l = low(period = agg);
            c = close(period = agg);
        }
    }
}
AddLabel(yes, if usecandletype == usecandletype.candle
              then "Candle"
              else "Candle_Hybrid",
              Color.LIGHT_GRAY);
AddLabel(error, "Chart Aggregation Period is greater than RSI aggregation period. Need to Change input to a higher agg than current chart aggregation or choose 'Current' at input usehigheraggperiod", Color.WHITE);
L0 = (1 – gamma) * c + gamma * L0[1];
L1 = -gamma * L0 + L0[1] + gamma * L1[1];
L2 = -gamma * L1 + L1[1] + gamma * L2[1];
L3 = -gamma * L2 + L2[1] + gamma * L3[1];
if L0 >= L1
then {
    CU1 = L0 - L1;
    CD1 = 0;
} else {
    CD1 = L1 - L0;
    CU1 = 0;
}
if L1 >= L2
then {
    CU2 = CU1 + L1 - L2;
    CD2 = CD1;
} else {
    CD2 = CD1 + L2 - L1;
    CU2 = CU1;
}
if L2 >= L3
then {
    CU = CU2 + L2 - L3;
    CD = CD2;
} else {
    CU = CU2;
    CD = CD2 + L3 - L2;
}

RSI = if IsNaN(close) then Double.NaN
      else if CU + CD <> 0
      then    CU / (CU + CD)
      else 0;
OS  = if IsNaN(close)
      then Double.NaN else oversold;
OB  = if IsNaN(close)
      then Double.NaN
      else overbought;
plot mid = if IsNaN(close) then Double.NaN else 0.5;
plot lineh = 1.35;
plot linel = -.35;
lineh.SetDefaultColor(Color.GRAY);
linel.SetDefaultColor(Color.GRAY);
mid.HideBubble();
lineh.HideBubble();
linel.HideBubble();
def rsiu = if RSI crosses above OB or
              RSI crosses above OS
           then 1
           else if rsiu[1] == 1 and !(RSI crosses below OB) and RSI > OS
           then 1
           else 0;
RSI.AssignValueColor(if rsiu
                     then Color.GREEN
                     else Color.RED);

input usealerts = no;
Alert(usealerts and RSI crosses below overbought, "", Alert.BAR, Sound.Bell);
Alert(usealerts and RSI crosses above oversold, "", Alert.BAR, Sound.Bell);

AddCloud(if RSI >= overbought
         then RSI
         else Double.NaN,
         overbought,
         Color.GREEN, Color.GREEN);
AddCloud(if RSI <= oversold
         then oversold
         else Double.NaN,
         RSI,
         Color.RED, Color.RED);

AddLabel(yes, "RSI (" + (if usehigheraggperiod == usehigheraggperiod.Higher then agg / 60000 else GetAggregationPeriod() / 60000) + "min): " + Round(RSI, 2),
               if RSI > 0
               then if RSI[1] > RSI
                    then Color.DARK_GREEN
                    else Color.GREEN
               else if RSI[1] < RSI
               then Color.DARK_RED
               else  Color.RED);


def atr    = Average(TrueRange(h, c, l), atrlength);
def entry  = if RSI crosses above oversold or rsiu[1] == 0 and RSI crosses above overbought
             then close
             else if RSI crosses below overbought or rsiu[1] and RSI crosses below oversold
             then close
             else entry[1];
def ebar   = if RSI crosses above oversold or rsiu[1] == 0 and RSI crosses above overbought
             then BarNumber()
             else if RSI crosses below overbought or rsiu[1] and RSI crosses below oversold
             then BarNumber()
             else Double.NaN;
def target = if RSI crosses above oversold or rsiu[1] == 0 and RSI crosses above overbought
             then Round((close + (atrfactor * atr)) / TickSize(), 0) * TickSize()
             else if RSI crosses below overbought or rsiu[1] and RSI crosses below oversold
             then Round((close - (atrfactor * atr)) / TickSize(), 0) * TickSize()
             else target[1];
def u_d    = if RSI crosses above oversold or rsiu[1] == 0 and RSI crosses above overbought
             then 1
             else if RSI crosses below overbought or rsiu[1] and RSI crosses below oversold
             then -1
             else u_d[1];
def goalu  = if u_d > 0  and  high >= target
             then 1
             else if goalu[1] == 1 and u_d > 0
             then 1
             else 0;
def goald  = if u_d < 0 and  low <= target
             then 1
             else if goald[1] == 1 and u_d < 0
             then 1
             else 0;

AddLabel(1,  if error then " "
              else (if u_d > 0 then "Up " else "Down ") +
                    "Target "      + (if outputformat == outputformat."Not Rounded"
                                     then AsPrice(target)
                                     else AsText(target)) +   
                    (if u_d < 0
                     then if  goald == 0
                     then " Need " + (if outputformat == outputformat."Not Rounded"
                                     then AsPrice(target - close)
                                     else AsText(Round((target - close), 2)))
                     else " Met "
                     else "") +
                     (if u_d > 0
                      then if goalu == 0
                     then " Need " + (if outputformat == outputformat."Not Rounded"
                                     then AsPrice(target - close)
                                     else AsText(Round((target - close), 2)))
                     else " Met "
                     else ""),
              if u_d < 0 and !goald or u_d > 0 and !goalu
              then Color.WHITE
              else Color.GREEN);

input showentrybubble = yes;
input bubblemover_entry_stop_targets = 2;#Hint bubblemover_entry_stop_targets: = number of spaces to move Entry, Stop and Target bubbles sideways
def m   = bubblemover_entry_stop_targets;
def  m1 = m + 1;
AddChartBubble(showentrybubble and BarNumber() == HighestAll(ebar),
               if RSI crosses above oversold or rsiu[1] and RSI crosses below oversold
               then oversold
               else overbought,
               "Entry: " + if outputformat == outputformat."Not Rounded"
                                     then AsPrice(entry)
                                     else AsText(Round(entry, 2)),
               if u_d > 0
               then Color.GREEN   
               else Color.RED,
               if u_d > 0
               then no
               else yes);

input showtargetbubble = yes;
AddChartBubble(showtargetbubble and IsNaN(close[m]) and !IsNaN(close[m1]),
               if Between(RSI[m1], oversold, overbought)
               then RSI[m1]
               else if u_d[m1] > 0
               then overbought
               else oversold,
               if error[m1]
               then " "
               else (if u_d[m1] > 0 then "U-" else "D-") +
                    "Tgt "   +   (if outputformat == outputformat."Not Rounded"
                                 then AsPrice(target[m1])
                                 else AsText(target[m1])) +   
                    (if u_d[m1] < 0 and !goald[m1]
                     then "\nDiff     " + (if outputformat == outputformat."Not Rounded"
                                          then AsPrice(target[m1] - close[m1])
                                          else if outputformat == outputformat."Not Rounded"
                                          then AsPrice(target[m1] - close[m1])
                                          else AsText(Round((target[m1] - close[m1]), 2)))
                     else if u_d[m1] > 0 and !goalu[m1]
                     then "\nDiff      " + (if outputformat == outputformat."Not Rounded"
                                           then AsPrice(target[m1] - close[m1])
                                           else AsText(Round((target[m1] - close[m1]), 2)))
                     else ""),
              if u_d[m1] < 0 and !goald[m1] or u_d[m1] > 0 and !goalu[m1]
              then Color.WHITE
              else Color.GREEN,
              if u_d[m1] < 0
              then no
              else yes);
#RSI Laguerre Bubble
input showRSIbubble = no;
input bubblemover_RSI = 20;#Hint bubblemover_RSI: = number of spaces to move RSI Laguerre indicator bubble sideways
def   z  = bubblemover_RSI;
def   z1 = z + 1;
AddChartBubble(showRSIbubble and IsNaN(close[z]) and !IsNaN(close[z1]),
               mid[z1],
               "R",
               if rsiu[z1]
               then Color.GREEN
               else Color.RED);
# End Code Basic RSI Laguerre
 
M

Miket

Member


Please let me know what you guys think when looking at these numbers. This is my first time looking at the strategy report. This is a macd fast 5 slow 10 ema. Length of time is 1 year daily. Any help is greatly appreciated. I'm trying to learn
 
K

King12x

New member
Greetings everyone,

First of all let me start of by saying: I am new here and what a real pleasure and delight to have found this forum. The knowledge and insights that everyone sheds here is truly refreshing.

No, to the real question. I have been trying to moving my trading strategy from scalp and day trade (profitable) to swing trading, I have look really hard and finally found something that I think it make sense. now don't laugh at this but my means of backtesting have been looking back data and accumulating over 500 trade through simulator and keeping STRICK and MATICULUSE data if it past my profitability test then its adopted.

Now, I have this new strategy that definitely need coding due to the scanner aspect of it. surprisingly the code is not even in TOS or any other platform that I have explored. however, I found it on Trade view (partial), but unfortunately its not transferable over to TOS. if anyone is willing to help, I don't mind sharing the criteria with everyone.

Thank you kindly,
 
BenTen

BenTen

Administrative
Staff
VIP
@King12x We don't know until we try. That being said, without seeing any source code we won't be able to know whether we can help or not.
 
K

King12x

New member
@BenTen, Thank you kindly for you replay. By me being new and all, I didn't want to sound presumptuous. First of all this strategy is not initiated by me and I don't know the original creator of it. However, I saw a lot of chatter online about, but I believe it might have first been originated at one point or another by a guy called PivaotBoss which I think he took the concepts from a guy called Mark Fischer book Logical Trader. I think I gave credit to were it due. That being said, so my parameters for it is follow: The strategy core concept it relay on support and resistance basted on Central Pivot Range (CPR) the equation for calculating CPR is:

TC = (Pivot – BC) + Pivot
Pivot = (High + Low + Close)/3
BC = (High + Low)/2

step 1:
I look for stocks from my watchlist after close of the market (regular trading hours) which didn't make contact with CPR and traded the entire day outside of the zone. if a scanner can be created for this it will be awesome since it opens a while new universe of new opportunities.

Step 2:
I monitor those stocks for the next 5 to 7 days looking for them to use the CPR from that day as support of resistance point (consider it like filling the gap)

Step3:
if they make contact by maximum 1PM EST then I use that the 1st level middle level as an opportunity to go long or short and stop loss is just bellow the 3 pivot level.

I am doing all this manual by openning alot of mini grid charts on TOS and drowing those pivot lines and monitioring them by eye/ alerts for contact

this is the source code from tradeview:

Code:
//Created by CristianD
study(title="Pivot Range", shorttitle="CD_PivotR", overlay=true)
sd = input(true, title="Show Daily Pivots?")
//Pivot Range Calculations - Mark Fisher
pivot = (high + low + close ) / 3.0
bc = (high + low ) / 2.0
tc = (pivot - bc) + pivot
//Daily Pivot Range
dtime_pivot = security(tickerid, 'D', pivot[1])
dtime_bc = security(tickerid, 'D', bc[1])
dtime_tc = security(tickerid, 'D', tc[1])
offs_daily = 0
plot(sd and dtime_pivot ? dtime_pivot : na, title="Daily Pivot",style=circles, color=fuchsia,linewidth=3)
plot(sd and dtime_bc ? dtime_bc : na, title="Daily BC",style=circles, color=blue,linewidth=3)
plot(sd and dtime_tc ? dtime_tc : na, title="Daily TC",style=circles, color=blue,linewidth=3)

attached: please see a screen shot illustrating the setup as it happened on IMMU on 12/4 through 12/10.

I tried to post an image but I think I don't have the privilege to do so.
so here is a link https://ibb.co/fDttPDG

Thank you for taking the time to look at this idea and let start the conversation
 
K

King12x

New member
Greetings Trader,

I have found this code by Robert Pyan and its fantastic it shows the opening range for the 5 minutes.

Code:
# 5 min opening range
# Robert Payne

def OpenRangeMinutes = 5;
def MarketOpenTime = 0930;
input ShowTodayOnly = yes;

def Today = if GetDay() == GetLastDay() then 1 else 0;
def FirstMinute = if SecondsFromTime(MarketOpenTime) < 60 then 1 else 0;
def OpenRangeTime = if SecondsFromTime(MarketOpenTime) < 60 * OpenRangeMinutes then 1 else 0;

def ORHigh =  if FirstMinute then high else if OpenRangeTime and high > ORHigh[1] then high else ORHigh[1];
def ORLow = if FirstMinute then low else if OpenRangeTime and low < ORLow[1] then low else ORLow[1];

plot OpenRangeHigh = if ShowTodayOnly and !Today then Double.NaN else if !OpenRangeTime then ORHigh else Double.NaN;
plot OpenRangeLow = if ShowTodayOnly and !Today then Double.NaN else if !OpenRangeTime then ORLow else Double.NaN;

OpenRangeHigh.SetStyle(Curve.SHORT_DASH);
OpenRangeHigh.SetDefaultColor(Color.YELLOW);
OpenRangeHigh.SetLineWeight(1);
OpenRangeLow.SetStyle(Curve.SHORT_DASH);
OpenRangeLow.SetDefaultColor(Color.YELLOW);
OpenRangeLow.SetLineWeight(1);

# Plot 5 min entry line
def first30 = if SecondsFromTime(0930) >= 0 and SecondsTillTime(1000) > 0 then 1 else 0;
def dailyRange = high(period="day" )[1] - low(period="day" )[1];
def range = average(dailyrange,10);

plot entryLine1 = if first30 then openrangehigh + (range * 0.03) else double.nan;
entryline1.setdefaultcolor(color.green);
entryline1.setpaintingStrategy(paintingStrategy.HORIZONTAL);

plot entryLine2 = if first30 then openrangelow - (range * 0.03) else double.nan;
entryline2.setdefaultcolor(color.green);
entryline2.setpaintingStrategy(paintingStrategy.HORIZONTAL);

def SoundAlert = high crosses above entryLine1 or low crosses below entryLine2;
Alert(SoundAlert, "Something exciting just happened with " + GetSymbol(), Alert.BAR, Sound.Ring);
I have heard of a strategy of buying or shorting the opening 5 minutes rang of the day and would like to test it. I have tried to create orders for back test but some how I have killed the code. any assistance please?
 
K

King12x

New member
@BenTen, You guys rock big time... I have just downloaded the strategy and played around with it and it looks great. I even went to the thread and replay all the comments so I don't as any dumb or repeated questions. once again. Thank you!

P.s. were you able to look at CPR strategy I spoke of above? does it make sense coding wise?
 
BenTen

BenTen

Administrative
Staff
VIP
@King12x I looked at the CPR indicator you talked about. I think someone has shared the indicator for ThinkorSwim on here. If not, I plan on releasing it to our Warehouse members sometimes next week. As far as the strategy backtesting go, I will have to look at it once again and see if we can do anything about it.
 
K

King12x

New member
@BenTen, I have searched all of your site and unfortunately its not available anymore. please when you create it let me know. And also, how do I get that warehouse designation that I see you have? I hope it doesn't involve coding, lol!
 
BenTen

BenTen

Administrative
Staff
VIP
@King12x The Warehouse is our subscription service. You can read more about it here.
 
N

naskove

New member
VIP
Hi, need some help with this strategy to backtest, trying verify it is correct, it is based on this info from Bernstein:
Code:
input over_bought = 80;
input over_sold = 20;
input sell = 70;
input KPeriod = 14;
input DPeriod = 14;
input priceH = high;
input priceL = low;
input priceC = close;
input slowing_period = 3;
input averageType = AverageType.SIMPLE;
input showBreakoutSignals = {default "No", "On FullK", "On FullD", "On FullK & FullD"};

def lowest_k = Lowest(priceL, KPeriod);
def c1 = priceC - lowest_k;
def c2 = Highest(priceH, KPeriod) - lowest_k;
def FastK = if c2 != 0 then c1 / c2 * 100 else 0;

def FullK = MovingAverage(averageType, FastK, slowing_period);
def FullD = MovingAverage(averageType, FullK, DPeriod);

def OverBought = over_bought;
def OverSold = over_sold;

def upK = FullK crosses above OverSold;
def upD = FullD crosses above OverSold;
def downK = FullK crosses below OverBought;
def downD = FullD crosses below OverBought;
def buysignal = FullK crosses above overBought;
def sellsignal = Fullk crosses below sell;
input length = 14;
input averageType1 = AverageType.WILDERS;
def ADX = DMI(length, averageType1).ADX;
addorder(orderType.BUY_AUTO, buysignal and fullK > 50 and adx < 20);
addorder(ordertype.SELL_AUTO, sellsignal);
 

Top