COMPOSITE MACRO ETF CUMULATIVE RETURN MOMENTUM (08.23.2015)

Here is the updated list of composite ETF components.


# ================================================================== #
# symbol management

cat = {
       'Large Cap'             :['SPY','IVV','VOO','IWB'],
       'Mid Cap'               :['MDY','IJH','VO','IWR'], 
       'Small Cap'             :['IWM','IJR','VB'],
       'Global Equity'         :['VEU','ACWI','VXUS','DGT'],
       'AsiaPac Equity'        :['EWT','EWY','EWA','EWS','AAXJ','FXI','EWH','EWM','EPI'\
       ,'INDA','RSX'],
       'Europe Equity'         :['FEZ','EZU','VGK','HEDJ','EWU','EWI','EWP','EWQ'\
       ,'EWL','EWD'],
       'Emerging | Frontier'   :['EWZ','EWW','ECH','GAF','FM','EEM','VWO'],
       'Real Estate'           :['RWO','RWX','RWR','IYR','VNQ'],
       'Consumer Discretionary':['XLY','XRT','FXD','VCR','RTH','IYC'],
       'Consumer Staples'      :['XLP','FXG','VDC','ECON','IYK'],           
       'Energy'                :['XLE','IPW','XOP','VDE','IYE','XC','OIH'],  
       
       'Financials'            :['XLF','KBE','KIE','IYG','KRE'],
       'Healthcare'            :['XLV','XBI','IBB'],
       'Industrial'            :['XLI','IYT','VIS','IYJ'],
       'Materials'             :['XLB','XHB','XME','IGE','MOO','GDX','GDXJ'],
       'Technology'            :['XLK','SMH','HACK','FDN'],
       'Telecom'               :['IYZ','IXP','VOX'],                        
       'Utilities'             :['IDU','XLU','VPU'],
       'Oil | Gas'             :['UNG','BNO','OIL'],
       'Precious Metals'       :['GLD','SLV','IAU'],
       'Technology'            :['XLK','SMH','HACK','FDN'],  
       'Bonds'                 :['TLT','AGG','JNK','LQD'],
       'T-Bond Yields'         :['^TYX','^TNX','^FVX']
        }  

LAST 1260 TRADING DAYS (5 YEARS)

Source: Yahoo Finance

LAST 504 TRADING DAYS (2 YEARS)

Source: Yahoo Finance

LAST 252 TRADING DAYS (1 YEAR)

Source: Yahoo Finance

LAST 126 TRADING DAYS (6 MONTHS)

Source: Yahoo Finance

LAST 63 TRADING DAYS (3 MONTHS)

Source: Yahoo Finance

LAST 21 TRADING DAYS (1 MONTH)

Source: Yahoo Finance

LAST 10 TRADING DAYS

Source: Yahoo Finance

Get Free Financial Data w/ Python (State street ETF Holdings - SPY)

One issue I frequently encounter during my research is the need to compare an individual stock, or collection of stocks vs its ETF benchmark. To do this I need accurate ETF holdings data. 

Generally this information is located on the ETF provider's website. However,  this information is often inconvenient to access. Most websites including the ETF provider will do something like the following, where they only show the top 10 holdings, when what we really need is accessible only by clicking the highlighted download link.

SPY ETF Holdings Page

This isn't a major issue until you need to access multiple ETF holdings pages. State Street Global Advisors is the ETF provider and this is the structure they use most frequently, therefore I figured it would be a major time saver to write a script to automate this important yet redundant task. 

This code requires the following third-party modules to execute: 

  • Selenium
  • Google Chromedriver (allows Python to open Chrome browser)

Before we get to the code, you must have Chromedriver downloaded and unzipped. Make sure to grab the filepath as we will need it. 


# ----- import modules -----
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import os
import time
from pprint import pprint as pp

Next you will need to grab the correct xpath's from the webpages of interest. I use xpath in this situation because the python script was able to find the correct clickable links every time without issue. 


"""
When you first open the State Street Website you will need to navigate to the 'Holdings' tab and then to the .xls
"""

# ----- webpage xpath -----
holdings_xpath = r"//*[@id='tabs']/a[3]"
xls_xpath = r"//*[@id='FUND_TOP_HOLDINGS']/a"

Next you need to construct a reusable generalized url string which can be used for any of the State Street ETF's. In this example we will be using SPY only.  Additionally I recommend creating a generalized filepath string for the actual downloaded file. This is so we can confirm that the download has completed correctly before exiting the browser in a later step. 


# ----- generalized URL string -----
symbol = 'SPY'
url = r"https://www.spdrs.com/product/fund.seam?ticker={}".format(symbol)
'''
The default naming convention for the holdings file is 'holdings-spy.xls' where the ETF label is lowercase
'''
file_string = my_etf_data_dir + 'holdings-{}.xls'.format(symbol.lower())

Now it's time to setup our chromedriver preferences via the 'ChromeOptions' method. You must define a default directory for this to work properly. During this step I also define my chromedriver filepath for convenience. 


# ----- Chromedriver options/preferences -----
chromeOptions = webdriver.ChromeOptions()
prefs = {'download.default_directory':insert_my_default_dir}
chromeOptions.add_experimental_option('prefs', prefs)
chromedriver_path = insert_my_chromedriver_filepath

Now for the 'money' code. In this step we will instantiate the webdriver (fancy word for automated browser), tell it to navigate to our previously defined URL, tell it to wait until the 'Holdings' tab is visible, click the tab link, then wait again until the 'Download All Holdings .xls' link is visible, click it, confirm the file has downloaded and finally exit the browser. 


"""
I often use prettyprint functions to tell me what's happening with the code, feel free to delete them if you like they are not required.
"""
pp('{} running holdings download..[start]'.format(symbol)) 
driver = webdriver.Chrome(executable_path=chromedriver_path, chrome_options=chromeOptions)
driver.set_page_load_timeout(90) # avoid hanging browser
try:
	driver.get(url)
    holdings_element = WebDriverWait(driver, 30) \
		.until(EC.presence_of_element_located((By.XPATH, holdings_xpath)))
    holdings_element.click()
    csv_element = WebDriverWait(driver, 30) \
		.until(EC.presence_of_element_located((By.XPATH, csv_xpath)))
    csv_element.click() # start download
    # the code below checks the file exists before exiting the browser
    for i in range(1,10,2):
    	time.sleep(i/20)
        if os.path.isfile(file_string)==True:
        	break
except Exception as e:
	print(e)
finally:
	driver.quit()
    pp('{} running holdings download..[complete]'.format(symbol))            

That's it. Now you should have the SPY holdings .xls file on your local hard drive.  If you want to get fancy you can throw this code into a function or class structure like I have. This allows you to run the code in a loop if, for example, you have 10 different State Street ETFs whose holdings data you need.

Composite Macro ETF Cumulative Return Momentum (08.16.2015)

Here are the updated ETF components I'm using to construct the ETF composites. 


’Large Cap’ :[‘SPY’],
‘Mid Cap’ :[‘MDY’],
‘Small Cap’ :[‘IWM’],
‘Global Equity’ :[‘VEU’,’ACWI’,’VXUS’,’DGT’],
‘AsiaPac Equity’ :[‘EWT’,’EWY’,’EWA’,’EWS’,’AAXJ’,’FXI’,’EWH’,’EWM’,’EPI’,’INDA’,’RSX’],
‘Europe Equity’ :[‘FEZ’,’EZU’,’VGK’,’HEDJ’,’EWU’,’EWI’,’EWP’,’EWQ’,’EWL’,’EWD’],
‘Emerging | Frontier’ :[‘EWZ’,’EWW’,’ECH’,’GAF’,’FM’,’EEM’,’VWO’],
‘Real Estate’ :[‘RWO’,’RWX’,’RWR’,’IYR’,’VNQ’],
‘Consumer Discretionary’:[‘XLY’,’XRT’],
‘Consumer Staples’ :[‘XLP’,’FXG’],
‘Energy’ :[‘XLE’,’IPW’,’XOP’],
‘Financials’ :[‘XLF’,’KBE’,’KIE’,’IYG’,’KRE’],
‘Healthcare’ :[‘XLV’,’XBI’,’IBB’],
‘Industrial’ :[‘XLI’,’IYT’],
‘Materials’ :[‘XLB’,’XHB’,’XME’,’IGE’,’MOO’,’GDX’,’GDXJ’],
‘Technology’ :[‘XLK’,’SMH’,’HACK’,’FDN’],
‘Telecom’ :[‘IYZ’],
‘Utilities’ :[‘IDU’,’XLU’],
‘Oil | Gas’ :[‘UNG’,’BNO’,’OIL’],
‘Precious Metals’ :[‘GLD’,’SLV’,’IAU’],
‘Technology’ :[‘XLK’,’SMH’,’HACK’,’FDN’],
‘Bonds’ :[‘TLT’,’AGG’,’JNK’,’LQD’],
‘T-Bond Yields’ :[‘^TYX’,’^TNX’,’^FVX’]
— blackarbsCEO

Last 504 Trading Days

Composite ETF Cumulative Returns

Last 252 Trading Days

Composite ETF Cumulative Returns

Last 126 Trading Days

Composite ETF Cumulative Returns

Last 63 Trading Days

Composite ETF Cumulative Returns

last 21 Trading days

Composite ETF Cumulative Returns

Last 10 Trading days

Composite ETF Cumulative Returns

Price Dispersion as a Smart Money Indicator

Before I get into the topic at hand, let me say I have not seen the following stock price data interpreted or studied like I am about to show you. As far as I am aware my approach is unique in that it is not overly complicated, can be generalized across a large cross section of asset class ETFs, and makes intuitive sense regarding market structure. 

Before I introduce the chart it is important that I clarify some definitions. 

What is Price Dispersion?

I'm sure this may have many meanings among market participants but for our purposes the Blackarbs definition of price dispersion is as follows:

Price Dispersion is an alternative measure of a security’s volatility. Specifically, it is used to track market participant’s agreement regarding a security’s value. Major value disagreements show up as spikes in the level of price dispersion. Equation: (bar.high - bar.low) / (bar.close)
— blackarbs.com/glossary

In this study price dispersion is the stock's daily price range expressed as a percentage of the Adjusted Close price.

How Does Price dispersion work as a smart money indicator?

The interpretation is based on the following assumptions.

  • Assumption (1):  Smart money is big money. These are the major players, the whales if you will, who move markets when they make trading decisions. These players tend to hold medium to long term views on positions and as such are primarily concerned with value. They tend to be buyers when others are selling and sellers when others are buying. 
  • Assumption (2):  Daily range is inherently a measure of market participants agreement or disagreement regarding the price(value) of a security. If the daily range is increasing or relatively large there is value disagreement. If the daily range is decreasing or relatively small there is value agreement.

There are a few more assumptions that will make more sense after viewing the chart. I must warn you the chart may look complicated on the surface but I assure you, the interpretation is relatively simple after I explain the details. 

XLK - Technology Select Sector SPDR ETF L/63 Days

What is this chart? what is contained in the two subplots?

Above is a chart of XLK. I have numbered the two subplots respectively.  The plot marked (1) shows an exponentially weighted cumulative return over the last 63 days. On the secondary axis I have plotted the daily adjusted close price. The black horizontal line is the 0% return value. 

The plot marked (2) contains a barplot of the daily price dispersion. The black line is a threshold value calculated as the top quintile (top 20%) of dispersion values over the period studied. On the right hand axis or secondary axis, is an exponentially weighted moving average of the daily dispersion. The red dotted line is also a threshold value defining the top quintile (top 20%) of all EMA values over the period. 

The blue vertical lines represent the bars where price dispersion exceeded the threshold value. The blue verticals are plotted on both subplots and correspond to the same dates. Note: due to formatting issues, at times the blue vertical lines are not aligned perfectly however, they still represent the same dates as the dispersion subplot. 

What are the final Three assumptions used to interpret this plot?

  1. Assumption (3): Smart money traders create the largest value disagreements therefore spikes in price dispersion indicate areas of trading opportunity and/or significant support and resistance levels.
  2. Assumption (4): Single interspersed vertical lines are more often associated with position closing events (liquidation/profit taking/short covering). Clustered or consecutive vertical lines (>=2) are indicators of buying and/or an interim bottom, however this is not always true. More importantly, as the size of the vertical cluster grows the more likely a sustainable trend change is occurring.
  3. Assumption (5): Rising dispersion as measured by the 21 Day EMA indicates increased risk of declining(negative) returns. Declining dispersion is associated with increased probability of increasing(positive) returns. 

put it all together, what is the chart saying about xlk?

Examining the plot we can see there has been much disagreement over the ETF value during the 63 day period. Resistance is ~$43.50 which coincided with a clustered dispersion spike in late May.  Price trended negatively over the period until disagreement in the ~$41.50 range indicating an interim bottom formed during late June/early July. However the outlook moving forward is mixed with a negative bias. Cumulative returns over the period are slightly negative and the dispersion EMA trend is clearly elevated above the threshold value. 

Let's look at another one.

XLK - TECHNOLOGY SELECT SPDR ETF  L/126 DAYS

This is the same ETF over the last 126 days or roughly 6 trading months. We can see the similarities in structure which reinforces some of the interpretations made previously. I've poorly circle the clustered areas. Notice how they coincide with high conviction trend changes.

The first occurred late March and happened to form a significant bottom ~$41.25. This value was not retested until July. The second cluster occurred  in early May and also formed an interim bottom around $42. From there price advanced until the next cluster which formed a significant top ~$43.50 in late May. 

To reiterate the outlook for XLK is mixed. The most recent cluster triggered during August 11/12. Generally this is a bullish sign, however with price dispersion clearly elevated on two timeframes and cumulative rolling returns below zero I would have a bearish to neutral bias.

how is this intuitive to understand??

Big money moves markets. Big money is opportunistic and likely to get involved at advantageous prices. By measuring the disagreement over a security's value, as measured by price dispersion, we can identify significant areas of perceived value.  Everything else provided in the chart is simply used to help identify and contextualize these areas. 


Composite Equity ETF Analysis (8/10/2015)

While I continue to update the ICC Valuation methodology I plan to post more of the custom charts I use to gain insight into current market structure, momentum, and relative value. 

Updated Composite ETF List


cat = {
       'Large Cap'             :['SPY'],
       'Mid Cap'               :['MDY'], 
       'Small Cap'             :['IWM'],
       'Global Equity'         :['VEU','ACWI','VXUS'],
       'AsiaPac Equity'        :['EWT','EWY','EWA','EWS',\
       							'AAXJ','FXI','EWH','EWM',\
                            	'EPI','INDA','RSX'],
       'Europe Equity'         :['FEZ','EZU','VGK','HEDJ',\
       							'EWU','EWI','EWP','EWQ',\
      							 'EWL','EWD'],
       'Emerging | Frontier'   :['EWZ','EWW','ECH','GAF',\
       							'FM','EEM','VWO'],
       'Real Estate'           :['RWO','RWX','RWR','IYR','VNQ'],
       'Consumer Discretionary':['XLY','XRT'],
       'Consumer Staples'      :['XLP','FXG'],                         
       'Energy'                :['XLE','IPW','XOP'],                   
       'Financials'            :['XLF','KBE','KIE','IYG','KRE'],
       'Healthcare'            :['XLV','XBI','IBB'],
       'Industrial'            :['XLI','IYT'],
       'Materials'             :['XLB','XHB','XME','IGE','MOO'],
       'Technology'            :['XLK','SMH','HACK','FDN'],
       'Telecom'               :['IYZ'],                            
       'Utilities'             :['IDU','XLU']
        }  

Best vs Worst Performing ETF Composite L/252 Days

Best vs Worst Performing ETF Composite L/63 Days

Best vs Worst Performing ETF Composite L/21 Days

BarPlot Cumulative Returns L/4 Weeks

Z - Score Average Risk-Adjusted Returns L/21 Days