| 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}")