# Original paper

**Abstract**

Non-fundamental demand shocks have significant effects on asset prices, but observing these shocks is challenging. We use the exchange traded fund (ETF) primary market to study non-fundamental demand. Unique to the ETF market, specialized arbitrageurs called authorized participants correct violations of the law of one price between an ETF and its underlying assets by creating or redeeming ETF shares. We show theoretically and empirically that creation and redemption activity (ETF flows) provides signals of non-fundamental demand shocks. A portfolio which is short high flow ETFs and long low flow ETFs earns excess returns of 1% to 4% per month, consistent with non-fundamental demand distorting asset prices away from fundamental values. Moreover, non-fundamental demand imposes non-trivial costs on ETF investors, leading to underperformance.

**Keywords:**Â Exchange Traded Funds (ETFs), ETF Flows, Non-Fundamental Demand, Return Predictability

# Trading rules

- Focus on >1,200 American ETFs.
- Inclusion criteria: ETFs with a minimum of $50 million in assets and one month where half the trading days saw creation/redemption movements.
- Dual sorting: Using variables of ShareChange and BidAskSpread.
- BidAskSpread indicator: Average difference between Ask and Bid, divided by the midpoint price.
- Initial sorting: Classify ETFs into five groups based on the previous month's bid-ask spread values.
- Secondary sorting: Within each bid-ask-spread group, further divide into quintiles according to the last monthâ€™s creation/redemption movements.
- Long position: Choose ETFs with the lowest ShareChange (indicating most redemptions) paired with the smallest bid-ask spread.
- Short position: Choose ETFs with the highest ShareChange (indicating most creations) paired with the smallest bid-ask spread.
- Portfolio weighting: Equally weighted.
- Rebalancing: Monthly.

# Python code

## Backtrader

```
import backtrader as bt
import pandas as pd
import numpy as np
class EtfStrategy(bt.Strategy):
def __init__(self):
self.ETFs = self.datas
def next(self):
asset_filter = [
etf for etf in self.ETFs if etf.close[0] * etf.volume[0] >= 50000000
]
if len(asset_filter) == 0:
return
activity_filter = [
etf for etf in asset_filter if sum(etf.volume[-21:] / 2 >= etf.volume[-1]) >= 10
]
if len(activity_filter) == 0:
return
spreads = [
(etf.ask[0] - etf.bid[0]) / ((etf.ask[0] + etf.bid[0]) / 2)
for etf in activity_filter
]
sorted_etfs = sorted(zip(activity_filter, spreads), key=lambda x: x[1])
share_changes = [
(etf.close[0] - etf.close[-21]) / etf.close[-21]
for etf, _ in sorted_etfs
]
portfolio_size = len(sorted_etfs) // 5
sorted_portfolios = [
sorted_etfs[i * portfolio_size: (i + 1) * portfolio_size]
for i in range(5)
]
quintiles = [
sorted(
portfolio,
key=lambda x: share_changes[activity_filter.index(x[0])],
reverse=True,
)
for portfolio in sorted_portfolios
]
long_etfs = [quintile[-1] for quintile in quintiles]
short_etfs = [quintile[0] for quintile in quintiles]
long_weight = 1.0 / len(long_etfs)
short_weight = -1.0 / len(short_etfs)
for etf, spread in long_etfs:
self.order_target_percent(etf, target=long_weight)
for etf, spread in short_etfs:
self.order_target_percent(etf, target=short_weight)
for etf in set(self.ETFs) - set([etf for etf, _ in long_etfs + short_etfs]):
self.order_target_percent(etf, target=0.0)
if __name__ == "__main__":
cerebro = bt.Cerebro()
# Add ETF data feeds (use appropriate data source)
# Make sure data feeds include bid and ask prices
# Here's an example with random data
for i in range(1200):
data = bt.feeds.PandasData(
dataname=pd.DataFrame({
"open": np.random.rand(1000),
"high": np.random.rand(1000),
"low": np.random.rand(1000),
"close": np.random.rand(1000),
"volume": np.random.rand(1000),
"bid": np.random.rand(1000),
"ask": np.random.rand(1000),
})
)
cerebro.adddata(data)
cerebro.addstrategy(EtfStrategy)
cerebro.broker.setcash(1000000)
cerebro.run()
```

Please note that this code snippet is a starting point for implementing the given trading strategy. You may need to modify and adjust the code based on your data source, requirements, and specific trading conditions. Additionally, the performance and behavior of the strategy should be thoroughly tested before using it in live trading.