Overall Statistics
Total Trades
133
Average Win
0.06%
Average Loss
-0.04%
Compounding Annual Return
0.334%
Drawdown
1.200%
Expectancy
0.090
Net Profit
0.234%
Sharpe Ratio
0.211
Probabilistic Sharpe Ratio
21.747%
Loss Rate
57%
Win Rate
43%
Profit-Loss Ratio
1.51
Alpha
0.007
Beta
-0.024
Annual Standard Deviation
0.011
Annual Variance
0
Information Ratio
-1.568
Tracking Error
0.114
Treynor Ratio
-0.097
Total Fees
$76.20
Estimated Strategy Capacity
$3000.00
Lowest Capacity Asset
SPY YB1F5SRQ7R6U|SPY R735QTJ8XC9X
Portfolio Turnover
3.22%
#region imports
from AlgorithmImports import *
import datetime
#endregion

class IronCondorAlgorithm(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2023, 1, 1)
        self.SetCash(100000)
        
        option = self.AddOption("SPY")
        self.symbol = option.Symbol

        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)

        option.SetFilter(lambda universe:
            universe.Strikes(-15, 15).Expiration(0, 0))
        
        # use the underlying instrument as the benchmark
        self.SetBenchmark(self.symbol.Underlying)

        self.Schedule.On(self.DateRules.EveryDay(self.symbol), self.TimeRules.AfterMarketOpen(self.symbol, 30), self.OpenIronCondor)

    def OpenIronCondor(self):
        
        # Print the underlying price
        underlying_price = self.Securities[self.symbol.Underlying].Price 
        self.Debug(f"Opening Iron Condor position. Underlying Price: {underlying_price}")
        
        # Get the Option chain        
        chain = self.CurrentSlice.OptionChains.get(self.symbol)
        if not chain:
            self.Debug(f"No Option Chain")
            return

        # Find put and call contracts with the farthest expiry     
        expiry = max([x.Expiry for x in chain])
        chain = sorted([x for x in chain if x.Expiry == expiry], key = lambda x: x.Strike)
            
        put_contracts = [x for x in chain if x.Right == OptionRight.Put]
        call_contracts = [x for x in chain if x.Right == OptionRight.Call]
            
        if len(call_contracts) < 10 or len(put_contracts) < 10:
            return

        # Select the strikes in the strategy legs
        far_put = put_contracts[0].Strike
        near_put = put_contracts[10].Strike
        near_call = call_contracts[-10].Strike
        far_call = call_contracts[-1].Strike

        self.Debug(f"Strike Price -> {far_put}/{near_put}/{near_call}/{far_call}")

        iron_condor = OptionStrategies.IronCondor(
            self.symbol, 
            far_put,
            near_put,
            near_call,
            far_call,
            expiry)

        self.Buy(iron_condor, 2)
        

    def OnData(self,slice):
        # If there is underlying assets in portfolio at expiration, liquidate the stocks in order to roll into new contracts
        if self.Portfolio[self.symbol.Underlying].Invested:
            self.Liquidate()
        
        if self.Portfolio.Invested or not self.IsMarketOpen(self.symbol):
            return