Overall Statistics |
Total Orders 10 Average Win 7.44% Average Loss -7.20% Compounding Annual Return 0.491% Drawdown 0.300% Expectancy 0.016 Start Equity 200000 End Equity 200985 Net Profit 0.492% Sharpe Ratio -0.519 Sortino Ratio -1.007 Probabilistic Sharpe Ratio 25.593% Loss Rate 50% Win Rate 50% Profit-Loss Ratio 1.03 Alpha -0.003 Beta 0.003 Annual Standard Deviation 0.006 Annual Variance 0 Information Ratio -0.592 Tracking Error 0.277 Treynor Ratio -0.864 Total Fees $0.00 Estimated Strategy Capacity $18000000.00 Lowest Capacity Asset SPX XL80P4UFCXOU|SPX 31 Portfolio Turnover 0.14% |
from AlgorithmImports import * class IndexOptionsDataAlgorithm(QCAlgorithm): def initialize(self) -> None: self.set_start_date(2020, 1, 1) self.set_end_date(2021, 1, 1) self.set_cash(200000) # Asynchronous can use computational resources efficiently self.universe_settings.asynchronous = True # Filter to get ATM calls expiring in 180 days to form the Bull Call Spread option = self.add_index_option("SPX") option.set_filter(lambda u: u.calls_only().strikes(-2, +2).expiration(0, 180)) self.option_symbol = option.symbol def on_data(self, slice: Slice) -> None: if not self.portfolio.invested and self.is_market_open(self.option_symbol): # Make sure getting the updated VIX option chain chain = slice.option_chains.get(self.option_symbol) if chain: expiry = max([c.expiry for c in chain]) call_contracts = sorted([c for c in chain if c.expiry == expiry], key=lambda c: c.strike) # Need 2 contracts to form a call spread if len(call_contracts) < 2: return # Obtain 2 call contracts with different strike price to form the call spread longCall, shortCall = call_contracts[0:2] # Use all the buying power, but need to ensure the order size of the long and short call are the same quantity = min([ abs(self.calculate_order_quantity(shortCall.symbol, -0.5)), abs(self.calculate_order_quantity(longCall.symbol, 0.5))]) self.market_order(shortCall.symbol, -quantity) self.market_order(longCall.symbol, quantity) expected_margin_usage = max((longCall.strike - shortCall.strike) * self.securities[longCall.symbol].symbol_properties.contract_multiplier * quantity, 0) if expected_margin_usage != self.portfolio.total_margin_used: raise Exception("Unexpect margin used!") def on_securities_changed(self, changes: SecurityChanges) -> None: for security in changes.added_securities: if security.type == SecurityType.INDEX_OPTION: # Historical data history = self.history(security.symbol, 10, Resolution.MINUTE) self.debug(f"We got {len(history)} from our history request for {security.symbol}")