Overall Statistics
Total Orders
0
Average Win
0%
Average Loss
0%
Compounding Annual Return
0%
Drawdown
0%
Expectancy
0
Start Equity
1000000
End Equity
1000000
Net Profit
0%
Sharpe Ratio
0
Sortino Ratio
0
Probabilistic Sharpe Ratio
0%
Loss Rate
0%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
0
Beta
0
Annual Standard Deviation
0
Annual Variance
0
Information Ratio
-1.673
Tracking Error
0.109
Treynor Ratio
0
Total Fees
$0.00
Estimated Strategy Capacity
$0
Lowest Capacity Asset
Portfolio Turnover
0%
from AlgorithmImports import *

class FutureOptionExampleAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2023, 1, 1)  # After CME Bitcoin options launch
        self.set_end_date(2023, 12, 31)
        self.set_cash(1000000)  # Initial capital

        # Subscribe the underlying since the updated price is needed for filtering
        self.underlying = self.add_future(Futures.Currencies.BTC,
            extended_market_hours=True,
            data_mapping_mode=DataMappingMode.OPEN_INTEREST,
            data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO,
            contract_depth_offset=0)
        # Filter the underlying continuous Futures to narrow the FOP spectrum
        self.underlying.set_filter(0, 182)
        # Filter for the current-week-expiring calls to formulate a covered call that expires at the end of week
        self.add_future_option(self.underlying.symbol, lambda u: u.include_weeklys().calls_only().expiration(0, 5))

    def on_data(self, slice: Slice) -> None:
        # Create canonical symbol for the mapped future contract, since option chains are mapped by canonical symbol
        symbol = Symbol.create_canonical_option(self.underlying.mapped)

        # Get option chain data for the mapped future, as both the underlying and FOP have the highest liquidity among all other contracts
        chain = slice.option_chains.get(symbol)
        if not self.portfolio.invested and chain:
            # Obtain the ATM call that expires at the end of week, such that both underlying and the FOP expires the same time
            expiry = max(x.expiry for x in chain)
            atm_call = sorted([x for x in chain if x.expiry == expiry],
                key=lambda x: abs(x.strike - x.underlying_last_price))[0]

            self.log(f"ATM Call symbol {atm_call.symbol}")

            # Use abstraction method to order a covered call to avoid manual error
            option_strategy = OptionStrategies.covered_call(symbol, atm_call.strike, expiry)
            self.buy(option_strategy, 1)

    def on_securities_changed(self, changes: SecurityChanges) -> None:
        for security in changes.added_securities:
            if security.type == SecurityType.FUTURE_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}")
from AlgorithmImports import *

class FutureOptionExampleAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        self.set_start_date(2023, 1, 1)  # After CME Bitcoin options launch
        self.set_end_date(2023, 12, 31)
        self.set_cash(1000000)  # Initial capital

        # Add CME Bitcoin Futures
        self.underlying = self.add_future(Futures.Currencies.BTC,
            extended_market_hours=True)
        self.underlying.set_filter(0, 182)

        # Add CME Bitcoin Options
        self.add_future_option(self.underlying.symbol, 
            lambda u: u.include_weeklys().expiration(0, 365).strikes(-100000, 100000))

    def on_data(self, slice: Slice) -> None:

        # Log the underlying mapped symbol
        self.log(f"Underlying Mapped Symbol: {self.underlying.mapped}")

        # Log Futures Chain
        self.log_futures_chain(slice)

        # Log Option Chain Keys
        self.log("Option Chains Keys:")
        for key in slice.option_chains.keys():
            self.log(f"Key: {key}")

        # Create canonical symbol for the options
        symbol = Symbol.create_canonical_option(self.underlying.mapped)
        self.log(f"Canonical Option Symbol: {symbol}")

        # Attempt to fetch historical data for the symbol
        history = self.history(symbol, 10, Resolution.Daily)
        self.log(f"Historical Data for {symbol}: {len(history)} rows")

        # Check if canonical symbol matches
        if symbol not in slice.option_chains.keys():
            self.log(f"Canonical symbol {symbol} does not match any option chain key.")
            return

        # Access option chain data
        chain = slice.option_chains[symbol]
        self.log("Iterating through all options in the chain:")
        for option in chain:
            self.log(f"Option: {option.symbol}, Strike: {option.strike}, Expiry: {option.expiry}, Right: {option.right}")

    def log_futures_chain(self, slice: Slice):
        if self.underlying.symbol in slice.future_chains:
            futures_chain = slice.future_chains[self.underlying.symbol]
            self.log("Available Futures Contracts:")
            for contract in futures_chain:
                self.log(f"Contract: {contract.symbol}, Expiry: {contract.expiry}, Last Price: {contract.last_price}")
        else:
            self.log("No futures contracts available.")
from AlgorithmImports import *

class FutureOptionExampleAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2023, 1, 1)  # After CME Bitcoin options launch
        self.set_end_date(2023, 12, 31)
        self.set_cash(1000000)  # Initial capital

        # Subscribe the underlying since the updated price is needed for filtering
        self.underlying = self.add_future(Futures.Indices.SP_500_E_MINI,
            extended_market_hours=True,
            data_mapping_mode=DataMappingMode.OPEN_INTEREST,
            data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO,
            contract_depth_offset=0)
        # Filter the underlying continuous Futures to narrow the FOP spectrum
        self.underlying.set_filter(0, 182)
        # Filter for the current-week-expiring calls to formulate a covered call that expires at the end of week
        self.add_future_option(self.underlying.symbol, lambda u: u.include_weeklys().calls_only().expiration(0, 5))

    def on_data(self, slice: Slice) -> None:
        # Create canonical symbol for the mapped future contract, since option chains are mapped by canonical symbol
        symbol = Symbol.create_canonical_option(self.underlying.mapped)

        # Get option chain data for the mapped future, as both the underlying and FOP have the highest liquidity among all other contracts
        chain = slice.option_chains.get(symbol)
        if not self.portfolio.invested and chain:
            # Obtain the ATM call that expires at the end of week, such that both underlying and the FOP expires the same time
            expiry = max(x.expiry for x in chain)
            atm_call = sorted([x for x in chain if x.expiry == expiry],
                key=lambda x: abs(x.strike - x.underlying_last_price))[0]

            self.log(f"ATM Call symbol {atm_call.symbol}")

            # Use abstraction method to order a covered call to avoid manual error
            option_strategy = OptionStrategies.covered_call(symbol, atm_call.strike,expiry)
            self.buy(option_strategy, 1)
        
    def on_securities_changed(self, changes: SecurityChanges) -> None:
        for security in changes.added_securities:
            if security.type == SecurityType.FUTURE_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}")