Overall Statistics |
Total Trades 630 Average Win 0% Average Loss 0.60% Compounding Annual Return 27.786% Drawdown 20.100% Expectancy -1 Net Profit 82.143% Sharpe Ratio 1.213 Probabilistic Sharpe Ratio 58.806% Loss Rate 100% Win Rate 0% Profit-Loss Ratio 0 Alpha 0.201 Beta -0.012 Annual Standard Deviation 0.165 Annual Variance 0.027 Information Ratio 0.604 Tracking Error 0.228 Treynor Ratio -16.812 Total Fees $1807.00 Estimated Strategy Capacity $620000.00 Lowest Capacity Asset SPXW Y98J76OI23E6|SPX 31 Portfolio Turnover 0.46% |
from __future__ import annotations import option import spread from AlgorithmImports import * class FocusedApricotScorpion(QCAlgorithm): def Initialize(self): self.SetStartDate(2021, 1, 1) self.SetCash(300000) self.SetSecurityInitializer(self.SecurityInitializer) self.spx = self.AddIndex("SPX").Symbol self.spxw = self.AddIndexOption(self.spx, "SPXW").Symbol self.Schedule.On( self.DateRules.EveryDay(self.spx), self.TimeRules.AfterMarketOpen(self.spx, 30), self._after_open_of_market, ) def SecurityInitializer(self, security): security.SetMarketPrice(self.GetLastKnownPrice(security)) if security.Type in [SecurityType.Equity, SecurityType.Index]: periods = 5 security.VolatilityModel = StandardDeviationOfReturnsVolatilityModel(periods) trade_bars = self.History[TradeBar](security.Symbol, periods, Resolution.Daily) for trade_bar in trade_bars: security.VolatilityModel.Update(security, trade_bar) # if security.Type is SecurityType.Option: # security.PriceModel = OptionPriceModels.BjerksundStensland() def OnData(self, data: Slice): if not self.Portfolio.Invested: return def _after_open_of_market(self): option_symbols = self.OptionChainProvider.GetOptionContractList(self.spxw, self.Time) option_chains = option.sort_listed_options(option_symbols) today = self.Time.date() # Check if today is an option expiration day if today not in option_chains: return # Get the straddle price chain = option_chains[today] spot_price = self.Securities[self.spx].Close volatility = self.Securities[self.spx].VolatilityModel.Volatility atm_options = option.get_atm_options(spot_price, chain) leg_1 = self.AddOptionContract(atm_options[0].symbol) leg_2 = self.AddOptionContract(atm_options[1].symbol) straddle = spread.get_straddle_price(spot_price, volatility, leg_1, leg_2) # Filter trades based on factors cond_0 = straddle.percent_price > 1 cond_1 = straddle.percent_price < 1 cond_2 = straddle.relative_percent_price > 4 cond_3 = straddle.relative_percent_price < 5 # if cond_1 and cond_2: # return if cond_3: return # Send straddle order trade_risk = 10_000 try: quantity = round(trade_risk / straddle.straddle_premium) legs = [] legs.append(Leg.Create(leg_1.Symbol, 1)) legs.append(Leg.Create(leg_2.Symbol, 1)) self.ComboMarketOrder(legs, quantity, tag=str(straddle.relative_percent_price)) except ZeroDivisionError: print("Missing straddle price.")
from __future__ import annotations from collections import defaultdict import datetime as dt from AlgorithmImports import * def sort_listed_options(symbols: list) -> dict: """Group all active options for symbol by expiration date.""" chains = defaultdict(list) for s in symbols: option = OptionSymbol(s).compute() chains[option.expiration].append(option) return chains def get_atm_options(spot_price: float, options: list) -> list: sorted_options = sorted(options, key=lambda x: x.distance_from_atm(spot_price)) return sorted_options[:2] class OptionSymbol: def __init__(self, symbol): self.symbol = symbol def compute(self) -> self: self.expiration = self._expiration() self.strike = self._strike() self.put_call = self._put_call() return self def distance_from_atm(self, price) -> float: return abs(self.strike - price) def _expiration(self) -> dt.date: return dt.datetime.strptime(self.symbol.Value[6:12], "%y%m%d").date() def _strike(self) -> float: return float(self.symbol.Value[-8:]) / 1000 def _put_call(self) -> str: return self.symbol.Value[12:13]
from __future__ import annotations from dataclasses import dataclass from AlgorithmImports import * def get_straddle_price(spot_price: float, volatility: float, leg_1, leg_2) -> StraddlePrice: straddle_price = _mid_price(leg_1) + _mid_price(leg_2) return StraddlePrice(straddle_price, spot_price, volatility) def _mid_price(contract) -> float: return (contract.AskPrice + contract.BidPrice) / 2 @dataclass class StraddlePrice: """Keep record of straddle price and percent price.""" straddle_price: float stock_price: float volatility: float @property def straddle_premium(self) -> float: return self.straddle_price * 100 @property def percent_price(self) -> float: """Straddle price as percentage of stock price.""" return round(self._percent_price() * 100, 3) @property def relative_percent_price(self) -> float: """Straddle percent price divided by historical volatility.""" return round(self._percent_price() / self.volatility * 100, 3) def _percent_price(self) -> float: return self.straddle_price / self.stock_price