Overall Statistics |
Total Trades 2 Average Win 0% Average Loss 0% Compounding Annual Return 213.127% Drawdown 1.900% Expectancy 0 Net Profit 3.072% Sharpe Ratio 10.117 Sortino Ratio 18.98 Probabilistic Sharpe Ratio 91.635% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0.273 Beta 2.65 Annual Standard Deviation 0.149 Annual Variance 0.022 Information Ratio 11.042 Tracking Error 0.094 Treynor Ratio 0.57 Total Fees $0.00 Estimated Strategy Capacity $9000.00 Lowest Capacity Asset SPXW 32DTGRDSQMWZY|SPX 31 Portfolio Turnover 0.36% |
from QuantConnect.Algorithm import QCAlgorithm from QuantConnect.Data.Custom import * from QuantConnect.Orders import * from QuantConnect.Securities.Option import OptionPriceModels from datetime import timedelta, datetime import csv import io from QuantConnect import OptionRight ''' REFERENCE: https://www.quantconnect.com/docs/v2/writing-algorithms/trading-and-orders/order-types/combo-leg-limit-orders https://www.quantconnect.com/docs/v2/writing-algorithms/securities/asset-classes/us-equity/handling-data ''' class SPXWTradingAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2023, 12, 20) self.SetCash(1000000) spx = self.AddIndex("SPX").Symbol option = self.AddIndexOption(spx, "SPXW") # SPXW is the target non-standard contract option.SetFilter(lambda universe: universe.IncludeWeeklys().Strikes(-2, 2).Expiration(timedelta(days=0), timedelta(days=15))) # Note: IncludeWeeklys and get a good range of days self.symbol = option.Symbol option.PriceModel = OptionPriceModels.CrankNicolsonFD() option.EnableGreekApproximation = True self.Schedule.On(self.DateRules.EveryDay(self.symbol), self.TimeRules.Every(timedelta(days=1)), self.Trade) self.last_row = None self.SetWarmup(timedelta(days=5)) # Log the last row during initialization self.Trade() if self.last_row is not None: self.Debug("Last row during initialization: " + str(self.last_row)) def Trade(self): if self.IsWarmingUp: return csv_string = self.Download('https://docs.google.com/spreadsheets/d/1wwadCU8msu6FEUJt1ANoZS2qMO2MWiheARrdm7zaQlM/export?format=csv') csv_reader = csv.DictReader(io.StringIO(csv_string)) for i, order in enumerate(csv_reader): self.last_row = order if self.last_row is None: self.Debug("Last row is None") return slice = self.CurrentSlice expiry = datetime.strptime(str(self.last_row['TWS Contract Date']), '%Y%m%d') if self.symbol in slice.OptionChains: optionchain = slice.OptionChains[self.symbol] contracts = [i for i in optionchain if i.Expiry == expiry] if len(contracts) < 1: self.Debug(f"Not enough option contracts for order: {self.last_row}") return contracts.sort(key=lambda x: abs(x.Strike - float(self.last_row['Strike 1']))) quantity = int(self.last_row['Order Quantity']) if self.Securities.ContainsKey(contracts[0].Symbol.Value): contract = self.Securities[contracts[0].Symbol.Value] legs = [] total_limit_price = 0 quantities = [1, -1] for i in range(1, 3): if self.last_row[f'Right {i}']: right = OptionRight.Call if self.last_row[f'Right {i}'] == 'C' else OptionRight.Put limit_price = (contract.AskPrice + contract.BidPrice) / 2 legs.append(Leg.Create(contracts[i-1].Symbol.Value, quantities[i-1], limit_price)) total_limit_price += limit_price self.Debug(f"Added leg: {contracts[i-1].Symbol.Value}, {right}, { quantities[i-1]}") ticket = self.ComboLegLimitOrder(legs, quantity) for individual_ticket in ticket: if individual_ticket.Status == OrderStatus.Filled: # check the status of Order and then debug values self.Debug(f"Symbol: {individual_ticket.Symbol.Value}; Quantity filled: {individual_ticket.QuantityFilled}; Fill price: {individual_ticket.AverageFillPrice}") def OnOrderEvent(self, orderEvent): if orderEvent.Status == OrderStatus.Filled: self.Debug("Order filled: " + str(orderEvent))