Introduction
Members of Congress in the Senate and the House of Representatives have access to information that can potentially make significant market moves long before the members of the public are informed. This strategy seeks to mirror those congress trades, assuming they are investing on the basis of undisclosed information. In this post, we’ll implement a trading algorithm that buys the same stocks they are buying, and applies portfolio volatility and risk controls. This strategy relies on the US Congress Trading dataset, which is a paid dataset.
Background
The “Stop Trading on Congressional Knowledge” (STOCK) Act of 2012 is legislation designed to combat insider trading by government employees. It requires officials to publicly disclose their financial transactions within 45 days. Quiver Quantitative scrapes the information from SEC reports to produce the US Congress Trading stock-universe dataset in the QuantConnect Dataset Market, which we'll be using in this trading algorithm.
In this micro-study we implement an inverse-volatility weighting scheme to form the portfolio of assets. In inverse-volatility weighting, assets with lower volatility receive higher weights and those with higher volatility receive lower weights. The purpose of this portfolio construction technique is to equalize the risk contribution of all the assets in the portfolio and avoid large exposure to highly volatile assets.
Initially the strategy took large single-stock positions during periods of low congress trading activity resulting in high concentration risk and volatility. To reduce concentration risk in the algorithm we limited the exposure of each asset to 10%.
Implementation
To implement this strategy on QuantConnect, we'll use the Quiver Quantitative US Congress Trading dataset.
- We added US Equities that have been recently bought by members of Congress:
self._universe = self.add_universe(
QuiverQuantCongressUniverse,
lambda constituents: [c.symbol for c in constituents if c.transaction == OrderDirection.BUY]
)
2. Schedule a callback of the _trade function to rebalance the portfolio on the first trading day of each week, shortly after market open.
spy = Symbol.create('SPY', SecurityType.EQUITY, Market.USA)
self.schedule.on(self.date_rules.week_start(spy), self.time_rules.after_market_open(spy, 30), self._trade)
3. During a rebalance, we calculate the trailing 6-month volatility of all the assets in the universe and form an inverse-volatility weighted portfolio. This lets us allocate more capital to the least volatile assets. We multiple the volatility by 1.5 to aim for 150% buying power invested. The set_holdings method closes any unselected positions from last week during the rebalance.
def _trade(self):
symbols = list(self._universe.selected)
if len(symbols) == 0: return
inv_volatility_by_symbol = 1 / self.history(symbols, timedelta(6*30), Resolution.DAILY)['close'].unstack(0).pct_change().iloc[1:].std()
targets = [
PortfolioTarget(symbol, min(0.1, 1.5 * (inv_volatility_by_symbol[symbol] / inv_volatility_by_symbol.sum())) )
for symbol in symbols
if symbol in inv_volatility_by_symbol
]
self.set_holdings(targets, True)
Conclusion
Members of Congress have privileged access to nonpublic information, which some members exploit for their own private gain. The STOCK Act requires these officials to disclose their trades within 45 days. These disclosures are public information, which we have used in a trading algorithm to copy their trades.
With a light risk control overlay, we could effectively harness the trade information from members of congress to build an interesting investment strategy. The algorithm achieved a 0.934 Sharpe ratio, while the S&P 500 Index ETF (SPY) achieved a 0.7 Sharpe ratio in the same period - outperforming the benchmark in risk-adjusted returns.
Laurentiu Raducu
Seems like this returns an error in the attached main.py file:
symbols = list(self._universe.selected)
As self._universe.selected is None.
THIAN SEONG YEE
Returns similar to buy and hold benchmark SPY lower draw down, better Sharpe ratio
Laurentiu Raducu
Is there anything we need to do to adapt this to go live with the strategy? I am getting this error:
This seems to make the algorithm fail when trying to fetch the list of symbols, which is always of None type.
Did anyone experience anything like this?
Derek Melchin
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!