REIT ETF Trading System For ThinkOrSwim

█ OVERVIEW

This strategy script demonstrates the application of the Real Estate Investment Trust (REIT) ETF trading system presented in the article by Markos Katsanos titled "Is The Price REIT?" from TASC's June 2024 edition of Traders' Tips.


█ CONCEPTS

REIT stocks and ETFs offer a simplified, diversified approach to real estate investment. They exhibit sensitivity to interest rates, often moving inversely to interest rate and treasury yield changes. Markos Katsanos explores this relationship and the correlation of prices with the broader market to develop a trading strategy for REIT ETFs.

The script employs Bollinger Bands and Donchian channel indicators to identify oversold conditions and trends in REIT ETFs. It incorporates the 10-year treasury yield index (TNX) as a proxy for interest rates and the S&P 500 ETF (SPY) as a benchmark for the overall market. The system filters trade entries based on their behavior and correlation with the REIT ETF price.


█ CALCULATIONS

The strategy initiates long entries (buy signals) under two conditions:
1. Oversold condition
  • The weekly ETF low price dips below the 15-week Bollinger Band bottom, the closing price is above the value by at least 0.2 * ATR (Average True Range), and the price exceeds the week's median.
  • Either of the following:
    – The TNX index is down over 15% from its 25-week high, and its correlation with the ETF price is less than 0.3.
    – The yield is below 2%.

2. Uptrend
  • The weekly ETF price crosses above the previous week's 30-week Donchian channel high.
  • The SPY ETF is above its 20-week moving average.
  • Either of the following:
    – Over ten weeks have passed since the TNX index was at its 30-week high.
    – The correlation between the TNX value and the ETF price exceeds 0.3.
    – The yield is below 2%.

The strategy also includes three exit (sell) rules:
1. Trailing (Chandelier) stop
  • The weekly close drops below the highest close over the last five weeks by over 1.5 * ATR.
  • The TNX value rises over the latest 25 weeks, with a yield exceeding 4%, or its value surges over 15% above the 25-week low.

2. Stop-loss
  • The ETF's price declines by at least 8% of the previous week's close and falls below the 30-week moving average.
  • The SPY price is down by at least 8%, or its correlation with the ETF's price is negative.

3. Overbought condition
  • The ETF's value rises above the 100-week low by over 50%.
  • The ETF's price falls over 1.5 * ATR below the 3-week high.
  • The ETF's 10-week Stochastic indicator exceeds 90 within the last three weeks.

Find the ToS script in the post below.
jPctLkj.png
 
Last edited by a moderator:
check the below:

CSS:
# // Indicator for TOS
#// © PineCodersTASC
#//  TASC Issue: June 2024 - Vol. 42, Issue 7
#//     Article: An Approach To Real Estate Investment Trusts
#//              Is The Price REIT?
#//  Article By: Markos Katsanos
#//    Language: TradingView's Pine Script™ v5
#// Provided By: PineCoders, for tradingview.com
#string it  = 'TASC 2024.06 REIT ETF Trading System'
# -- Converte by Sam4Cok@Samer800 - Request from useThinkScript.com member
input bbLength  = 15; #, 'Bollinger Bands Length'
input bbMulti  = 2.0; #, 'Bollinger Bands Multiplier'
input tnxLength = 25; #, 'Lookback Period for TNX Index'
input minTnxChangePercentage = 15; #, 'Min TNX Change, %'
input DonchianLength  = 30; #,  'Donchian Channel Length'
input MaxCorrelationForBuySignal = 0.3; #, 'Max Correlation for Buy Signal'
input MinYield = 20.0; #, 'Min Yield (TNX/10)'
input atrMulti = 1.5; #, 'ATR Stop'
input stoplossPercentage = 8.0; #, 'Stop Loss, %'

def na = Double.NaN;
def bar_index = BarNumber();
#/ --- Helper Functions ---
# stoch(source, high, low, length) =>
script stoch {
    input src = close;
    input h = high;
    input l = low;
    input len = 14;
    def hh = Highest(h, len);
    def ll = Lowest(l, len);
    def c1 = src - ll;
    def c2 = hh - ll;
    def stoch = if c2 != 0 then c1 / c2 * 100 else 0;
    plot return = stoch;
}
script NotAtLoss {
    input src = close;
    input limit = high;
    input iMAXlos = 8;
    def nom = (1.0 - iMAXlos / 100.0);
    def NotAtLoss = src < (nom * limit);
    plot out = NotAtLoss;
}
#// Reference indices (SPY and TNX)
def spy1 = close(Symbol = "SPY");
def tnx1 = close(Symbol = "TNX:CGI");
def spy = if isNaN(spy1) then spy[1] else spy1;
def tnx = if isNaN(tnx1) then tnx[1] else tnx1;
#// Treasury Yield, Stochastic, ATR
def yld     = tnx;
def sto     = stoch(close, high, low, 10);
def stoch   = Average(sto, 3);
def atr15   = ATR(Length = 15);

#// HH & LL of TNX
def TNXll   = Lowest(tnx, tnxLength);
def TNXhh   = Highest(tnx, tnxLength);
def TNXup   = ((tnx - TNXll) / (TNXll + 0.001)) * 100.0;
def TNXdn   = ((tnx - TNXhh) / (TNXhh + 0.001)) * 100.0;

#// HH & LL of ETF
def LLbars  = 100; #if bar_index >= 100 then 100 else bar_index + 1;
def ll      = Lowest(low, LLbars);
def PERCup  = (close - ll) / (ll + 0.001) * 100.0;
def DCupper = Highest(close, DonchianLength)[1] ;

#// Correlations
def CORt    = Correlation(close, tnx, 20);
def CORs    = Correlation(close, spy, 20);
def BBlower = Average(close, bbLength) - bbMulti * StDev(close, bbLength);
def  isStop  = close < Average(close, 30) and
     NotAtLoss(close, Highest(close, 2), stoplossPercentage) and
     (NotAtLoss(spy, spy[1], stoplossPercentage) or CORs < 0);

#// Trade Signals
def Sell1; def Stop0; def SellOB;
def BuyBB    = (low < BBlower and
     close > BBlower + 0.2 * atr15) and
     ((TNXdn < -minTnxChangePercentage and CORt < MaxCorrelationForBuySignal) or
     yld < MinYield) and close > hl2 and !isStop;
def BuyTrend = close > DCupper and
     spy > Average(spy, 20) and
     (GetMaxValueOffset(tnx, DonchianLength) > 10 or
     CORt > MaxCorrelationForBuySignal or yld < MinYield);

#// Stop Loss
    Sell1  = (TNXup > minTnxChangePercentage or
     (yld > 4.0 and TNXup > 0.0)) and
     close < Highest(close, 5) - atrMulti * atr15 and !BuyBB; #  // Chandelier Exit
    Stop0  = close < Average(close, 30) and
     NotAtLoss(close, close[1], stoplossPercentage) and
     (NotAtLoss(spy, spy[1], stoplossPercentage) or CORs < 0);
    SellOB = PERCup > 50.0 and
     close < Highest(high, 3) - atrMulti * atr15 and Highest(stoch, 3) > 90;

def stopAll = (Sell1 + Stop0 + SellOB) > 0;
def buyCondition = (BuyBB + BuyTrend) > 0;
def buyCond;
def closeBuy;
def inTrade;
def buyType;

if buyCondition and !inTrade[1] {
    inTrade = yes;
    buyCond = yes;
    buyType = if BuyTrend then 1 else -1;
    closeBuy = no;
} else if stopAll and inTrade[1] {
    inTrade = no;
    buyCond = no;
    buyType = no;
    closeBuy = yes;
} else {
    inTrade = inTrade[1];
    buyCond = no;
    buyType = buyType[1];
    closeBuy = no;
}
AddChartBubble(buyCond, low, if buyType>0 then "Buy Trend" else "Buy BB", color.CYAN, no);
AddChartBubble(closeBuy, high, if buyType[1]>0 then "Close Trend" else "Close BB", color.MAGENTA);

#-- END of CODE
 
check the below:

CSS:
# // Indicator for TOS
#// © PineCodersTASC
#//  TASC Issue: June 2024 - Vol. 42, Issue 7
#//     Article: An Approach To Real Estate Investment Trusts
#//              Is The Price REIT?
#//  Article By: Markos Katsanos
#//    Language: TradingView's Pine Script™ v5
#// Provided By: PineCoders, for tradingview.com
#string it  = 'TASC 2024.06 REIT ETF Trading System'
# -- Converte by Sam4Cok@Samer800 - Request from useThinkScript.com member
input bbLength  = 15; #, 'Bollinger Bands Length'
input bbMulti  = 2.0; #, 'Bollinger Bands Multiplier'
input tnxLength = 25; #, 'Lookback Period for TNX Index'
input minTnxChangePercentage = 15; #, 'Min TNX Change, %'
input DonchianLength  = 30; #,  'Donchian Channel Length'
input MaxCorrelationForBuySignal = 0.3; #, 'Max Correlation for Buy Signal'
input MinYield = 20.0; #, 'Min Yield (TNX/10)'
input atrMulti = 1.5; #, 'ATR Stop'
input stoplossPercentage = 8.0; #, 'Stop Loss, %'

def na = Double.NaN;
def bar_index = BarNumber();
#/ --- Helper Functions ---
# stoch(source, high, low, length) =>
script stoch {
    input src = close;
    input h = high;
    input l = low;
    input len = 14;
    def hh = Highest(h, len);
    def ll = Lowest(l, len);
    def c1 = src - ll;
    def c2 = hh - ll;
    def stoch = if c2 != 0 then c1 / c2 * 100 else 0;
    plot return = stoch;
}
script NotAtLoss {
    input src = close;
    input limit = high;
    input iMAXlos = 8;
    def nom = (1.0 - iMAXlos / 100.0);
    def NotAtLoss = src < (nom * limit);
    plot out = NotAtLoss;
}
#// Reference indices (SPY and TNX)
def spy1 = close(Symbol = "SPY");
def tnx1 = close(Symbol = "TNX:CGI");
def spy = if isNaN(spy1) then spy[1] else spy1;
def tnx = if isNaN(tnx1) then tnx[1] else tnx1;
#// Treasury Yield, Stochastic, ATR
def yld     = tnx;
def sto     = stoch(close, high, low, 10);
def stoch   = Average(sto, 3);
def atr15   = ATR(Length = 15);

#// HH & LL of TNX
def TNXll   = Lowest(tnx, tnxLength);
def TNXhh   = Highest(tnx, tnxLength);
def TNXup   = ((tnx - TNXll) / (TNXll + 0.001)) * 100.0;
def TNXdn   = ((tnx - TNXhh) / (TNXhh + 0.001)) * 100.0;

#// HH & LL of ETF
def LLbars  = 100; #if bar_index >= 100 then 100 else bar_index + 1;
def ll      = Lowest(low, LLbars);
def PERCup  = (close - ll) / (ll + 0.001) * 100.0;
def DCupper = Highest(close, DonchianLength)[1] ;

#// Correlations
def CORt    = Correlation(close, tnx, 20);
def CORs    = Correlation(close, spy, 20);
def BBlower = Average(close, bbLength) - bbMulti * StDev(close, bbLength);
def  isStop  = close < Average(close, 30) and
     NotAtLoss(close, Highest(close, 2), stoplossPercentage) and
     (NotAtLoss(spy, spy[1], stoplossPercentage) or CORs < 0);

#// Trade Signals
def Sell1; def Stop0; def SellOB;
def BuyBB    = (low < BBlower and
     close > BBlower + 0.2 * atr15) and
     ((TNXdn < -minTnxChangePercentage and CORt < MaxCorrelationForBuySignal) or
     yld < MinYield) and close > hl2 and !isStop;
def BuyTrend = close > DCupper and
     spy > Average(spy, 20) and
     (GetMaxValueOffset(tnx, DonchianLength) > 10 or
     CORt > MaxCorrelationForBuySignal or yld < MinYield);

#// Stop Loss
    Sell1  = (TNXup > minTnxChangePercentage or
     (yld > 4.0 and TNXup > 0.0)) and
     close < Highest(close, 5) - atrMulti * atr15 and !BuyBB; #  // Chandelier Exit
    Stop0  = close < Average(close, 30) and
     NotAtLoss(close, close[1], stoplossPercentage) and
     (NotAtLoss(spy, spy[1], stoplossPercentage) or CORs < 0);
    SellOB = PERCup > 50.0 and
     close < Highest(high, 3) - atrMulti * atr15 and Highest(stoch, 3) > 90;

def stopAll = (Sell1 + Stop0 + SellOB) > 0;
def buyCondition = (BuyBB + BuyTrend) > 0;
def buyCond;
def closeBuy;
def inTrade;
def buyType;

if buyCondition and !inTrade[1] {
    inTrade = yes;
    buyCond = yes;
    buyType = if BuyTrend then 1 else -1;
    closeBuy = no;
} else if stopAll and inTrade[1] {
    inTrade = no;
    buyCond = no;
    buyType = no;
    closeBuy = yes;
} else {
    inTrade = inTrade[1];
    buyCond = no;
    buyType = buyType[1];
    closeBuy = no;
}
AddChartBubble(buyCond, low, if buyType>0 then "Buy Trend" else "Buy BB", color.CYAN, no);
AddChartBubble(closeBuy, high, if buyType[1]>0 then "Close Trend" else "Close BB", color.MAGENTA);

#-- END of CODE
@samer800 great job like always, quick question, do you think is possible to add a arrow with the buy trend and end trend? also color candles, with a buy trend candle color cyan and stay that color until the next close signal, and magenta color with close trend and stay that color until next signal. thank you in advance
 
@samer800 great job like always, quick question, do you think is possible to add a arrow with the buy trend and end trend? also color candles, with a buy trend candle color cyan and stay that color until the next close signal, and magenta color with close trend and stay that color until next signal. thank you in advance
check the below:

CSS:
# // Indicator for TOS
#// © PineCodersTASC
#//  TASC Issue: June 2024 - Vol. 42, Issue 7
#//     Article: An Approach To Real Estate Investment Trusts
#//              Is The Price REIT?
#//  Article By: Markos Katsanos
#//    Language: TradingView's Pine Script™ v5
#// Provided By: PineCoders, for tradingview.com
#string it  = 'TASC 2024.06 REIT ETF Trading System'
# -- Converte by Sam4Cok@Samer800 - Request from useThinkScript.com member

input colorBars = yes;
input signalStyle = {Default "Bubbles", "Arrows", "Don't Show Signals"};
input bbLength  = 15; #, 'Bollinger Bands Length'
input bbMulti  = 2.0; #, 'Bollinger Bands Multiplier'
input tnxLength = 25; #, 'Lookback Period for TNX Index'
input minTnxChangePercentage = 15; #, 'Min TNX Change, %'
input DonchianLength  = 30; #,  'Donchian Channel Length'
input MaxCorrelationForBuySignal = 0.3; #, 'Max Correlation for Buy Signal'
input MinYield = 20.0; #, 'Min Yield (TNX/10)'
input atrMulti = 1.5; #, 'ATR Stop'
input stoplossPercentage = 8.0; #, 'Stop Loss, %'


def na = Double.NaN;
def bar_index = BarNumber();

def bubble = signalStyle==signalStyle."Bubbles";
def arrow = signalStyle==signalStyle."Arrows";
#/ --- Helper Functions ---
# stoch(source, high, low, length) =>
script stoch {
    input src = close;
    input h = high;
    input l = low;
    input len = 14;
    def hh = Highest(h, len);
    def ll = Lowest(l, len);
    def c1 = src - ll;
    def c2 = hh - ll;
    def stoch = if c2 != 0 then c1 / c2 * 100 else 0;
    plot return = stoch;
}
script NotAtLoss {
    input src = close;
    input limit = high;
    input iMAXlos = 8;
    def nom = (1.0 - iMAXlos / 100.0);
    def NotAtLoss = src < (nom * limit);
    plot out = NotAtLoss;
}
#// Reference indices (SPY and TNX)
def spy1 = close(Symbol = "SPY");
def tnx1 = close(Symbol = "TNX:CGI");
def spy = if isNaN(spy1) then spy[1] else spy1;
def tnx = if isNaN(tnx1) then tnx[1] else tnx1;
#// Treasury Yield, Stochastic, ATR
def yld     = tnx;
def sto     = stoch(close, high, low, 10);
def stoch   = Average(sto, 3);
def atr15   = ATR(Length = 15);

#// HH & LL of TNX
def TNXll   = Lowest(tnx, tnxLength);
def TNXhh   = Highest(tnx, tnxLength);
def TNXup   = ((tnx - TNXll) / (TNXll + 0.001)) * 100.0;
def TNXdn   = ((tnx - TNXhh) / (TNXhh + 0.001)) * 100.0;

#// HH & LL of ETF
def LLbars  = 100; #if bar_index >= 100 then 100 else bar_index + 1;
def ll      = Lowest(low, LLbars);
def PERCup  = (close - ll) / (ll + 0.001) * 100.0;
def DCupper = Highest(close, DonchianLength)[1] ;

#// Correlations
def CORt    = Correlation(close, tnx, 20);
def CORs    = Correlation(close, spy, 20);
def BBlower = Average(close, bbLength) - bbMulti * StDev(close, bbLength);
def  isStop  = close < Average(close, 30) and
     NotAtLoss(close, Highest(close, 2), stoplossPercentage) and
     (NotAtLoss(spy, spy[1], stoplossPercentage) or CORs < 0);

#// Trade Signals
def Sell1; def Stop0; def SellOB;
def BuyBB    = (low < BBlower and
     close > BBlower + 0.2 * atr15) and
     ((TNXdn < -minTnxChangePercentage and CORt < MaxCorrelationForBuySignal) or
     yld < MinYield) and close > hl2 and !isStop;
def BuyTrend = close > DCupper and
     spy > Average(spy, 20) and
     (GetMaxValueOffset(tnx, DonchianLength) > 10 or
     CORt > MaxCorrelationForBuySignal or yld < MinYield);

#// Stop Loss
    Sell1  = (TNXup > minTnxChangePercentage or
     (yld > 4.0 and TNXup > 0.0)) and
     close < Highest(close, 5) - atrMulti * atr15 and !BuyBB; #  // Chandelier Exit
    Stop0  = close < Average(close, 30) and
     NotAtLoss(close, close[1], stoplossPercentage) and
     (NotAtLoss(spy, spy[1], stoplossPercentage) or CORs < 0);
    SellOB = PERCup > 50.0 and
     close < Highest(high, 3) - atrMulti * atr15 and Highest(stoch, 3) > 90;

def stopAll = (Sell1 + Stop0 + SellOB) > 0;
def buyCondition = (BuyBB + BuyTrend) > 0;
def buyCond;
def closeBuy;
def inTrade;
def buyType;

if buyCondition and !inTrade[1] {
    inTrade = yes;
    buyCond = yes;
    buyType = if BuyTrend then 1 else -1;
    closeBuy = no;
} else if stopAll and inTrade[1] {
    inTrade = no;
    buyCond = no;
    buyType = no;
    closeBuy = yes;
} else {
    inTrade = inTrade[1];
    buyCond = no;
    buyType = buyType[1];
    closeBuy = no;
}

def col = if buyCond then 1 else if closeBuy then -1 else col[1];

#-- signals
plot sigUp = if Arrow and buyCond then low else na;
plot sigDn = if Arrow and closeBuy then high else na;
sigUp.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
sigDn.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
sigUp.SetDefaultColor(Color.CYAN);
sigDn.SetDefaultColor(Color.MAGENTA);

AddChartBubble(bubble and buyCond, low, if buyType>0 then "Buy Trend" else "Buy BB", color.CYAN, no);
AddChartBubble(bubble and closeBuy, high, if buyType[1]>0 then "Close Trend" else "Close BB", color.MAGENTA);

#-- bar color

AssignPriceColor(if !colorBars then Color.CURRENT else
                 if col>0 then Color.DARK_GREEN else if col < 0 then Color.DARK_RED else Color.GRAY);

#-- END of CODE
 
Great work @samer800, as always! Is it possible to create a scan that would show ETFs that are just starting the "Buy Trend" and the "Close Trend"?
 
Last edited by a moderator:
Great work @samer800, as always! Is it possible to create a scan that would show ETFs that are just starting the "Buy Trend" and the "Close Trend"?

Unfortunately no.
There is no way to identify and scan for only ETFs.

However, if you have a manual watchlist of the ETFs that you are trading; you can use this study in the Scan Hacker.

1. copy and save @samer800 latest version as a study.
https://usethinkscript.com/threads/reit-etf-trading-system-for-thinkorswim.19288/#post-145415

2. You scan filters are:
sigUp is true
or
sigDn is true

3, To learn more about setting up the scan filters in the Scan Hacker
https://usethinkscript.com/threads/how-to-use-thinkorswim-stock-hacker-scans.284/
 

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
348 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