Hi! I've been working on an options market making algorithm and have been having a little bit of trouble getting it work properly- I was hoping for some feedback.
The idea is to be making offers on a handful of ATM options to buy at bid and sell at ask simultaneously, updating unfilled orders with new bid-ask prices if the market significantly moves (checking for this condition every 15 minutes). I'd then have a hedging system that would be activated hourly. I'd calculate the total delta of my portfolio and hedge it out with underlying stock every 1 hour.
The Alpha from this strategy would simply be generated from the frequent intra-day buying and selling of the same option contracts with the bid-ask price difference as profit and the hedged P/L bid-ask spread edge for the positions that aren't closed.
All input would be appreciated. Thanks!
algo rough draft:
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Common")
from System import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from datetime import timedelta
from QuantConnect.Orders import OrderStatus
class OptionMarketMaking(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2021, 1, 1)
self.SetEndDate(2021, 1, 31)
self.SetCash(100000)
self.orderTickets = {}
option = self.AddOption("PEP", Resolution.Minute)
self.option_symbol = option.Symbol
option.SetFilter(-3, 3, timedelta(0), timedelta(180))
self.Schedule.On(self.DateRules.EveryDay(self.option_symbol),
self.TimeRules.Every(timedelta(minutes=15)),
self.CheckAndReplaceOrders)
self.Schedule.On(self.DateRules.EveryDay(self.option_symbol),
self.TimeRules.Every(timedelta(hours=1)),
self.ScheduledCalculateAndHedgeDelta)
def OnData(self, slice):
for kvp in slice.OptionChains:
if kvp.Key != self.option_symbol: continue
chain = kvp.Value
contracts = [i for i in chain if i.Right == OptionRight.Call or i.Right == OptionRight.Put]
if len(contracts) == 0: continue
contracts = sorted(contracts, key = lambda x: abs(chain.Underlying.Price - x.Strike))
contracts = contracts[:3]
for contract in contracts:
if contract.BidPrice > 0:
orderTicket = self.LimitOrder(contract.Symbol, 2, contract.BidPrice)
self.orderTickets[orderTicket.OrderId] = orderTicket # Store the order ticket
if contract.AskPrice > 0:
orderTicket = self.LimitOrder(contract.Symbol, -2, contract.AskPrice)
self.orderTickets[orderTicket.OrderId] = orderTicket # Store the order ticket
self.CalculateAndHedgeDelta(slice)
def CheckAndReplaceOrders(self):
self.Debug("Remaining Margin: " + str(self.Portfolio.MarginRemaining))
open_orderTickets = list(self.Transactions.GetOpenOrderTickets())
for orderTicket in open_orderTickets:
if orderTicket.Status != OrderStatus.Filled:
contract = self.Securities[orderTicket.Symbol]
if abs(contract.BidPrice - orderTicket.Get(OrderField.LimitPrice)) > 0.01: # Check if the price has moved more than 1 cent
self.Transactions.CancelOrder(orderTicket.OrderId)
if contract.BidPrice > 0:
self.LimitOrder(contract.Symbol, 2, contract.BidPrice)
if abs(contract.AskPrice - orderTicket.Get(OrderField.LimitPrice)) > 0.01: # Check if the price has moved more than 1 cent
self.Transactions.CancelOrder(orderTicket.OrderId)
if contract.AskPrice > 0:
self.LimitOrder(contract.Symbol, -2, contract.AskPrice)
def get_greeks(self, slice):
self.myOptionPortfolioKeys = [x.Key for x in self.Portfolio if x.Value.Invested and x.Value.Type==SecurityType.Option]
self.myOptionPortfolio = []
for chain in slice.OptionChains.Values:
for i in chain:
for o in self.myOptionPortfolioKeys:
if i.Symbol == o:
self.myOptionPortfolio.append(i)
self.Delta = sum([opt.Greeks.Delta for opt in self.myOptionPortfolio])
self.Gamma = sum([opt.Greeks.Gamma for opt in self.myOptionPortfolio])
self.Vega = sum([opt.Greeks.Vega for opt in self.myOptionPortfolio])
def CalculateAndHedgeDelta(self, slice):
self.get_greeks(slice) # Calculate the Greeks for the portfolio
total_long_delta = sum([opt.Greeks.Delta for opt in self.myOptionPortfolio if opt.Greeks.Delta > 0])
total_short_delta = sum([abs(opt.Greeks.Delta) for opt in self.myOptionPortfolio if opt.Greeks.Delta < 0])
net_delta = total_long_delta - total_short_delta
self.Debug("Net Delta: " + str(net_delta))
# Check the current quantity of PEP
current_quantity = self.Portfolio["PEP"].Quantity
if net_delta > 0:
if current_quantity < 0:
# Buy shares to hedge
quantity_to_buy = net_delta + abs(current_quantity)
else:
quantity_to_buy = net_delta - current_quantity
if quantity_to_buy != 0:
self.Buy("PEP", abs(quantity_to_buy))
elif net_delta < 0:
if current_quantity > 0:
# Sell shares to hedge
quantity_to_sell = abs(net_delta) + current_quantity
else:
quantity_to_sell = abs(net_delta) - abs(current_quantity)
if quantity_to_sell != 0:
self.Sell("PEP", abs(quantity_to_sell))
def ScheduledCalculateAndHedgeDelta(self):
if self.CurrentSlice is not None:
self.CalculateAndHedgeDelta(self.CurrentSlice)
Ashutosh
Hi Jack,
Interesting take on the strategy.
One key error I noticed in the code was how to extract the limit price or the order ticket.
In your code you have used “orderTicket.Get(OrderField.LimitPrice)” but there is no field OrderField.
I replaced this piece of code with the following check and limit price extraction through debugging of the orderTicket object:
Keep in mind that processing minute data was time-consuming due to the substantial Option dataset. For debugging purposes, I switched to hour and daily resolutions, default slippage, fill price methods, and used the BuyingPowerModel.Null for margin checks. Please adjust these settings based on your strategy and preferences.
Cheers,
Ashutosh
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.
Jack
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!