Linear Regression Trading Strategy for ThinkorSwim

I thought it might be interesting to share a very basic Linear Regression Trading Strategy, and then see if we can build on it to improve results.

Here is the basic strategy - Using a 50 period Linear Regression Curve on an hourly chart, we enter / exit trades using the following rules:
  • Long Entry: The Linear Regression Curve is rising AND the close of the previous bar is above the Linear Regression Curve
  • Long Exit: The close of the previous bar is less than the Linear Regression Curve
  • Short Entry: The Linear Regression Curve is falling AND the close of the previous bar is below the Linear Regression Curve
  • Short Exit: The close of the previous bar is greater than the Linear Regression Curve
We enter and exit using one contract only. The strategy can be long, short, or flat.

Code:
#DS_LinRegStrategy
#Basic framework for a Linear Regression based strategy
#@Dublin_Capital
#2020-07-03

input price = close;
input displace = 0;
input LinRegLength = 50;

#Definitions

def LinReg = Inertia(price[-displace], LinRegLength);


#Defining Instrument Fundamentals & P&L for labels
def tickval = TickValue();
def ticksize = TickSize();
def FloatPL = FPL();


#Defining Long/Short Filters (these instructions determine entries / exits)


#Entry / Exit Requirements
def Long1 = LinReg > LinReg[1]
    and close > LinReg;

def ExitLong1 = close < LinReg;


def Short1 = LinReg < LinReg[1]
    and close < LinReg;

def ExitShort1 = close > LinReg[1];



#Order Entry (Set 1)
AddOrder(OrderType.BUY_TO_OPEN, Long1, tickcolor = Color.DARK_GREEN, arrowcolor = Color.DARK_GREEN, name = "LONG1");
AddOrder(OrderType.SELL_TO_CLOSE, ExitLong1, tickcolor = Color.DARK_GREEN, arrowcolor = Color.DARK_GREEN, name = "EXITLONG1");


AddOrder(OrderType.SELL_TO_OPEN, Short1, tickcolor = Color.DARK_RED, arrowcolor = Color.DARK_RED, name = "SHORT1");
AddOrder(OrderType.BUY_TO_CLOSE, ExitShort1, tickcolor = Color.DARK_RED, arrowcolor = Color.DARK_RED, name = "EXITSHORT1");


#Adding Linear Regression Plots
plot LinReg3 = LinReg;
LinReg3.SetDefaultColor(Color.BLUE);


#Adding Alerts
Alert(Long1, "Long", Alert.BAR, Sound.Ding);
Alert(Short1, "Short", Alert.BAR, Sound.Ding);
Alert(ExitLong1, "ExitLong", Alert.BAR, Sound.Ring);
Alert(ExitShort1, "ExitShort", Alert.BAR, Sound.Ring);


#Adding Labels
AddLabel(yes, "$" + tickval);
AddLabel(yes, ticksize);
AddLabel(yes, "$" + FloatPL, if FloatPL > 0 then Color.DARK_GREEN
    else if FloatPL < 0 then Color.DARK_RED
    else Color.GRAY);

#Coloring Bars
AssignPriceColor(if Long1 then Color.DARK_GREEN else if Short1 then Color.DARK_RED else Color.GRAY);

Backtesting for 360 trading days, the results of the strategy are as follows on a portfolio of commonly traded futures instruments. Although profitable, the P&L curve is not ideal, commissions too high, and the win rate could be improved. The majority of the profits come during a small fraction of the overall trading.

Used just as is, this strategy would be a good supplement to a long-only portfolio of stocks, as it seems to keep it's head above water during normal markets, but delivers strong returns during higher volatility (early 2020). That said, I would like to see if we can turn it into a stronger stand-alone strategy.

A linear regression channel is a representation of trend direction and volatility. Buying / selling above and below the middle line doesn't even really make sense, as price is expected to travel up and down within the channel. I guess my main point with all of this is that a profitable trading strategy can be based on almost anything.

I will be taking suggestions and adding / adjusting the strategy in an effort to improve these metrics.

MuAfe2y.png


K0yYFHu.png

BPyFSnK.png


ozuJ10p.png

g8nQGUC.png

HcSPc6x.png
 
Last edited:

tradebyday

Active member
If you wanted to apply a longer period channel but on a shorter timeframe, I suggest adding this indicator to your screen, Colored boxes above the red box= longs only off bottom of regression channel, colored boxes below red box= shorts off top of linear regression channel. Inside the red box you can trade off both sides of the channel. Trading higher timeframes in futures can be rather inefficient for many traders as they do not have access, capital, and/or the stomach to trade positions for that long. But the problem with many shorter timeframe traders is over trading and trying to trade against the trend at times for scalps. Adding this "rainnbow road" I call it will try to assist in the discipline. The boxes are based on % moves for the day and the % can be adjusted. I have it at settings that showed extremes for when to consider scale in for reversals, which I would like to note, may also help out the shorter time frame strategy by helping show areas of extremes when to stop entering trades long or short in areas of over-extension. I will be watching this forum to see what ideas are also thrown out to your original strategy as well.

Code:
#RainbowRoadPercentages by Tradebyday

input Range1 = .0065;
input Range2 = .015;
input Range3 = .03;
input Range4 = .05;

def pc = close(period = "DAY")[1];
plot priorclose = pc;
priorclose.setstyle(curve.short_DASH);
priorclose.setdefaultcolor(color.white);
priorclose.setLineWeight(1);

AddCloud(priorclose + priorclose*Range4, priorclose + priorclose*Range3, COLOR.BLUE);
AddCloud(priorclose + priorclose*Range3, priorclose + priorclose*Range2, COLOR.GREEN);
AddCloud(priorclose + priorclose*Range2, priorclose + priorclose*Range1, COLOR.YELLOW);
AddCloud(priorclose - priorclose*Range1, priorclose + priorclose*Range1, Color.RED);
AddCloud(priorclose - priorclose*Range1, priorclose - priorclose*Range2, Color.YELLOW);
AddCloud(priorclose - priorclose*Range2, priorclose - priorclose*Range3, Color.GREEN);
AddCloud(priorclose - priorclose*Range3, priorclose - priorclose*Range4, COLOR.BLUE);

Linear Regression Slope Column_StanL

Here's a custom column that is simple, easy to understand/use and meaningful in interpretation because it is pure math with no interpretation.

Code:
#hint: Description: A custom column for 'Linear Regression Slope' (LRS) in degrees. The aggregation and length can be changed via 'customize' of the column headings and clicking the scroll of the custom indicator. For example, length = 20 and aggregation = Day, the resulting value is  the slope of the line,in degrees, for the last 20 days. A default agg of 'day' is very useful and the length is your preference.


# Variation of usage: If you are intraday trading, a minute aggregation may be selected and adjusted for the length you desire. If you are accustomed to viewing, for example, a 5-min and 30-min LR, and want to avoid the hazzle of changing these variables, you can make two custom codes name LRS5 and LRS30.


# Interpretation: A 30-degree positive slope means a growth twice that of a 15-degree slope.


####### start ########
input LR_length = 20;# The number of bars used to calculate the LR line.
Plot LRS_Degrees = Round(Atan(LinearRegressionSlope("length" = LR_length)."LRS")  * 180 / Double.Pi, 1);
####### end ########


****************** Built-in TS 'LinearRegressionSlope' ********************************
#The units of the LRS is radians. Multiply LRS by 180/pi = 57.3 to convert to degrees
declare lower;


input price = close;
input length = 14;


plot LRS = 6 * ( wma(price, length) -  Average(price, length) ) / (length - 1);
plot ZeroLine = 0;


LRS.setDefaultColor(getColor(8));
ZeroLine.setDefaultColor(getColor(5));
******************************************************************
 
Before I make any adjustments to the code, I also wanted to look at some different settings. The original test used a 50 period Linear Regression. No other changes, except that I added Bitcoin to the test group.

The longer lengths increase hold time, reduce commissions (less trades), and reduce win rate. Metals and currencies become more profitable with the longer lengths, while indexes become less profitable.

Here are the results from a 100 period Linear Regression:

aqNoKB4.png



And here are the results from a 200 period Linear Regression:

X4sQ5oO.png
 

STAZ012

New member
i think more important than anything is your linear regression blue line . try to create a trendline along up and downs of this reg trend line and i see some interesting patterns. for example check this trendline on daily one year chart for swbi , also look at one year daily chart on CIT , join Reg blue trendline for april 27th , june 3rd and present lows to form a trendline. If that trendline holds it will CIT stock to bounce from here but if it breaks that will be a mjor reversal to downside . Will be interesting to watch ...not sure however how to develop the system on reg trendline trendline pattern even if it holds true .
 
Here is what happens to the strategy if you overlay an Exponential Moving Average on the Linear Regression Curve, and trade using the following settings:

Linear Regression Length: 50
EMA Length: 20

The system enters long when the Linear Regression Curve is rising and is greater than the EMA. It enters short when the Linear Regression Curve is falling and below the EMA.

Compared to the original test, using a 50 period Linear Regression Curve, this improves performance on Energies, Metals, Currencies, and Bonds, but destroys the performance on Indexes. From my experience, this is pretty common - indexes do not trade like other things. I find currencies tend to sustain trends longer and respond better to crossover strategies such as these. Also, long/short strategies have a lot of drag in a bull market (indexes).

Fvwl4Vk.png


EiwThvq.png

S0oeXQQ.png
 
Last edited:
One more adjustment for now:

Linear Regression Length: 100
Hull Moving Average Length: 20

Same entry and exit rules as above. A hull moving average is more sensitive to recent price action, and provides a smoother curve than the Simple Moving Average or EMA. This results in more trades, higher commissions, and a shorter hold time. But it increases the return on the overall portfolio.

As I mentioned before, I don't think it makes sense to trade all instruments with the same settings. But when developing a system, I like to test strategies across a lot of markets using the same settings. I then make adjustments after looking at longer term backtests. This isn't ideal, but is the only way I have found to deal with the limitations of thinkorswim backtesting.

RnzDZVn.png

W1Ye4Pi.png

322guth.png


Here is code for this (it's a bit messy - I'll clean up at some point down the road):
Code:
#DS_LinRegStrategy v 3.0
#Basic framework for a Linear Regression based strategy
#@Dublin_Capital
#2020-07-03

input price = close;
input displace = 0;
input LinRegLength = 100;
input averageLength = 20;
input averageType = averageType.HULL;

#Definitions

def LinReg = Inertia(price[-displace], LinRegLength);
def MA = MovingAverage(averageType, LinReg, averageLength);


#Defining Instrument Fundamentals & P&L for labels
def tickval = TickValue();
def ticksize = TickSize();
def FloatPL = FPL();


#Defining Long/Short Filters (these instructions determine entries / exits)


#Entry / Exit Requirements
def Long1 = LinReg > MA
    and LinReg > LinReg[1];

def ExitLong1 = LinReg < MA;


def Short1 = LinReg < MA
    and LinReg < LinReg[1];

def ExitShort1 = LinReg > MA;



#Order Entry (Set 1)
AddOrder(OrderType.BUY_TO_OPEN, Long1, tickcolor = Color.DARK_GREEN, arrowcolor = Color.DARK_GREEN, name = "LONG1");
AddOrder(OrderType.SELL_TO_CLOSE, ExitLong1, tickcolor = Color.DARK_GREEN, arrowcolor = Color.DARK_GREEN, name = "EXITLONG1");


AddOrder(OrderType.SELL_TO_OPEN, Short1, tickcolor = Color.DARK_RED, arrowcolor = Color.DARK_RED, name = "SHORT1");
AddOrder(OrderType.BUY_TO_CLOSE, ExitShort1, tickcolor = Color.DARK_RED, arrowcolor = Color.DARK_RED, name = "EXITSHORT1");


#Adding Linear Regression Plots
plot LinReg3 = LinReg;
LinReg3.SetDefaultColor(Color.BLUE);

#plotting moving average
plot movingAverage = MA;
movingAverage.SetDefaultColor(Color.DARK_GREEN);



#Adding Alerts
Alert(Long1, "Long", Alert.BAR, Sound.Ding);
Alert(Short1, "Short", Alert.BAR, Sound.Ding);
Alert(ExitLong1, "ExitLong", Alert.BAR, Sound.Ring);
Alert(ExitShort1, "ExitShort", Alert.BAR, Sound.Ring);


#Adding Labels
AddLabel(yes, "$" + tickval);
AddLabel(yes, ticksize);
AddLabel(yes, "$" + FloatPL, if FloatPL > 0 then Color.DARK_GREEN
    else if FloatPL < 0 then Color.DARK_RED
    else Color.GRAY);

#Coloring Bars
AssignPriceColor(if Long1 then Color.DARK_GREEN else if Short1 then Color.DARK_RED else Color.GRAY);
 
Last edited:

madeinnyc

Member
I slightly modified this linear regression slop study to include an angle label. I'm looking to further improve it by adding another angle label that changes the angel value as I hover my pointer over the slope line. Can someone please help:

Code:
declare lower;

input price = close;
input length = 14;
input displace = 0;
def avg = ExpAverage(close, length);
def height = avg - avg[length];
def Angle_deg = ATan(height/length) * 180 / Double.Pi;
addLabel(1, "Angle Deg " + Angle_deg, Color.WHITE);


Assert(length > 1, "'length' must be greater than one: " + length);

plot LRS = 6 * ( wma(price, length) -  Average(price, length) ) / (length - 1);
plot ZeroLine = 0;

LRS.setDefaultColor(getColor(8));
ZeroLine.setDefaultColor(getColor(5));
 
Thank you MerryDay for the tip! Working perfect now!

Dublin_Capital - perfect strategy specially for Swing Trading. The Linear_Regression_3 or Blue Line combining it with the VolumeProfile is a killer strategy! Can someone work on a Scanner for price to be above or below the Blue Regression Line?
 

Englewood85

New member
Regarding this part of your post with the results, did you write a script to generate that? Did the script actually test a strategy, then return the results in Excel?
 

RickK

Active member
This is an interesting thread @Dublin_Capital . Thank you for starting it.

I was intrigued, so instead of patching together a strategy, I just wanted a indicator that would give me a visual representation of suitable longs (and exits) and shorts (and exits).

Pretty much all of the same parameters (conditions/rules) above, but I included a Sideways Chop portion and Bollinger Bands (just to see when the bands are really compressed). First version [for longs] requires price to close (and stay) above a rising Hull(20) and rising LinearRegression(50). Version 2 only requires price to close (and stay) above a rising LinearRegression(50).

Lots of modifications can be made if you edit the "AssignPriceColor" line. i.e..... exit could be when price crosses HMA or crosses LinReg line. Entries could be only if price closes above HMA and LinReg line ONLY WHEN THOSE LINES ARE RISING...... or not. By setting exits to when price crosses back over the Hull(20), losses and drawdowns are kept to a minimum. I wonder what the floating PL looks like on this? .... I don't really know how to code a strategy.

Sideways Chop default setting is 5. It can be adjusted up to 10, making it more restrictive. Or, check out turning off the parameter by setting chop_range to 0.

LinReg_Hull_Chop_Candles https://tos.mx/V79oY5q
LinReg_Hull_Chop_Candles_2 https://tos.mx/Yd7cRYI

Indicator ver 2 on the right.


So I took some time and figured out AddOrder to add to this to make it a strategy.

Well, it makes for a pretty picture, but the 30 day results were dismal. Sorry to waste your time. Not really sure what I did wrong here.
 
Last edited:

coneill17

New member
Hey everyone. This is my first code i've written. Thanks to everyone here i was able to create a strategy i use into a tos strategy to backtest. I use the "TODAY 2m" timeframe and that suits me best. Sometimes it can give false entries but im not automating this so it doesnt matter for me, just dont blindly follow the long entries make sure they make sense to you and your trading style! The code is written so that it takes profit after a .2% increase from entry, for scalps. You can tweak it in the input or the code how you see fit for you. This doesnt repaint to my knowledge.
(If anyone can tweak the code to make it better please do!)

The strategy:
When price CLOSES below the lower boundary of the linear regression channel and crosses back above it, take a position long. Sell based on a % gain increase. I have been using it to scalp.

NOTE: Add the tos indicator LinearRegCh50 to your chart.

Anyone who tries this out, i'd love feedback for how you use it and in any timeframes that work better for you so i can test it out too!

Code:
# By C

# inputs
input percentGainGoal = 0.2;

input pricetype = PriceType.LAST;

#linear reg
input price = close;

def MiddleLR = InertiaAll(price);
def dist = HighestAll(AbsValue(MiddleLR - price)) * 0.5;
def UpperLR = MiddleLR + dist;
def LowerLR = MiddleLR - dist;

#define
def perchange = (100 * (close - EntryPrice())) / EntryPrice();

def exitGood = PerChange > percentGainGoal;

def TradePrice = close(pricetype = pricetype);

def buy = TradePrice crosses above LowerLR;

def sell = exitgood;

AddOrder(OrderType.BUY_TO_OPEN, condition = buy, price = close,100, tickcolor = Color.GREEN, arrowcolor = Color.GREEN, name = “Long”);
AddOrder(OrderType.SELL_TO_CLOSE, condition = sell, price = close,100, tickcolor = Color.Red, arrowcolor = Color.Red, name = “Exit“);
 

Similar threads

Top