
The strategy sells monthly delta-neutral TLT ETF strangles, delta-hedged daily, with proceeds earning the 3-month Treasury rate, while managing transaction costs for options and TLT share trades.
ASSET CLASS: options | REGION: United States | FREQUENCY:
Monthly | MARKET: bonds | KEYWORD: Selling, Options, Bond, ETFs
I. STRATEGY IN A NUTSHEL
The strategy uses TLT ETF for option selling, forming monthly delta-neutral strangles with the first out-of-the-money calls and puts. Delta-hedging is performed daily, rebalancing with TLT shares when deltas exceed ±500 shares. Option trades are executed at the bid-ask midpoint, with transaction costs of $1 per contract. TLT share trades incur $5 in brokerage fees and a 1-cent bid-ask spread per transaction. Proceeds from shorting options and TLT shares, or funds used to purchase TLT shares, earn the 3-month Treasury bill rate. This approach seeks to profit from option premiums while managing risk and transaction costs.
II. ECONOMIC RATIONALE
Most researchers speculate that the volatility premium is caused by investors who strongly dislike negative asset returns and are therefore willing to pay a premium for portfolio insurance offered by options.
III. SOURCE PAPER
An Examination of Long-Term Bond iShare Option Selling Strategies [Click to Open PDF]
Simon
<Abstract>
This paper examines volatility trades in Lehman Brothers 20+ Year US Treasury Index iShare (TLT) options from July 2003 through May 2007. The results indicate that implied volatility is persistently above actual volatility and that unconditionally selling front contract strangles and straddles and holding for one month is highly profitable after transactions costs. The paper also demonstrates that the profitability of short-term strategies is enhanced when strangles and straddles are sold when implied volatility is high relative to out of sample time series volatility forecasts. Profitability owes both to winning trades outpacing losing trades by 2:1 margins and to profits of winning trades exceeding losses of losing trades, despite the limited return and unlimited risk profiles of short option strategies. A decomposition of the results indicates that most of the profitability can be attributed to theta gains outpacing gamma losses. Risk management strategies such as stop loss strategies detract from the profitability of short term option selling strategies, while take profit orders have only modest favorable effects on the results. Overall, the results demonstrate that TLT option selling strategies offered attractive risk-return tradeoffs over the sample period.
IV. BACKTEST PERFORMANCE
| Annualised Return | 17.44% |
| Volatility | 18.41% |
| Beta | 0.09 |
| Sharpe Ratio | 0.73 |
| Sortino Ratio | -0.271 |
| Maximum Drawdown | N/A |
| Win Rate | 45% |
V. FULL PYTHON CODE
from AlgorithmImports import *
class SellingOptionsonBondETFs(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2000, 1, 1)
self.SetCash(100000)
data = self.AddEquity("BIL", Resolution.Minute)
self.bills = data.Symbol
data.SetLeverage(5)
data = self.AddEquity("TLT", Resolution.Minute)
self.symbol = data.Symbol
data.SetLeverage(5)
option = self.AddOption("TLT", Resolution.Minute)
option.SetFilter(-20, 20, 25, 35)
self.last_day = -1
def OnData(self,slice):
# Check once a day.
if self.Time.day == self.last_day:
return
self.last_day = self.Time.day
for i in slice.OptionChains:
chains = i.Value
# Only bill position is opened.
invested = [x.Key for x in self.Portfolio if x.Value.Invested]
if len(invested) <= 1:
calls = list(filter(lambda x: x.Right == OptionRight.Call, chains))
puts = list(filter(lambda x: x.Right == OptionRight.Put, chains))
if not calls or not puts: return
underlying_price = self.Securities[self.symbol].Price
expiries = [i.Expiry for i in puts]
# Determine expiration date nearly one month.
expiry = min(expiries, key=lambda x: abs((x.date()-self.Time.date()).days-30))
strikes = [i.Strike for i in puts]
# Determine at-the-money strike.
strike = min(strikes, key=lambda x: abs(x-underlying_price))
atm_call = [i for i in calls if i.Expiry == expiry and i.Strike == strike]
atm_put = [i for i in puts if i.Expiry == expiry and i.Strike == strike]
if atm_call and atm_put:
options_q = int(self.Portfolio.MarginRemaining / (underlying_price * 100))
# Set max leverage.
self.Securities[atm_call[0].Symbol].MarginModel = BuyingPowerModel(5)
self.Securities[atm_put[0].Symbol].MarginModel = BuyingPowerModel(5)
# Sell at-the-money straddle.
self.Sell(atm_call[0].Symbol, options_q)
self.Sell(atm_put[0].Symbol, options_q)
# Buy treasury bill.
self.SetHoldings(self.bills, 1)
if self.Portfolio.Invested:
self.Liquidate(self.symbol)
VI. Backtest Performance