Overall Statistics
Total Trades
36
Average Win
0.02%
Average Loss
-0.03%
Compounding Annual Return
-1.228%
Drawdown
0.200%
Expectancy
-0.205
Net Profit
-0.097%
Sharpe Ratio
-2.371
Probabilistic Sharpe Ratio
13.060%
Loss Rate
50%
Win Rate
50%
Profit-Loss Ratio
0.59
Alpha
-0.002
Beta
-0.004
Annual Standard Deviation
0.004
Annual Variance
0
Information Ratio
-13.211
Tracking Error
0.146
Treynor Ratio
2.38
Total Fees
$20.00
from datetime import timedelta
from clr import AddReference
# import numpy as np
# from QuantConnect.Securities.Option import OptionPriceModels

class CondorAlgorithm(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2017, 2, 1)
        self.SetEndDate(2017, 3, 1)
        self.SetCash(500000)
        equity = self.AddEquity("GOOG", Resolution.Minute)
        equity_cost = self.AddEquity("COST", Resolution.Minute)
        equity.SetDataNormalizationMode(DataNormalizationMode.Raw)
        self.underlyingsymbol = equity.Symbol
        self.underlyingsymbol_cost = equity_cost.Symbol
        self.SetBenchmark(equity.Symbol)
        self.SetBenchmark(equity_cost.Symbol)
        
        # Jing's code:
        # self.Securities["GOOG"].MarginModel =  PatternDayTradingMarginModel()
        # self.Securities["GOOG"].SetLeverage(2)
        # option = self.AddOption("GOOG")
        # option.SetFilter(-10, +10, timedelta(0), timedelta(30))
        # option.PriceModel = OptionPriceModels.CrankNicolsonFD()
        # self.SetWarmUp(TimeSpan.FromDays(7))

    def OnData(self,slice):

        ''' OptionChainProvider gets the option chain provider
            used to get the list of option contracts for an underlying symbol.
            Then you can manually filter the contract list returned by GetOptionContractList.
            The manual filtering will be limited to the information
            included in the Symbol (strike, expiration, type, style) and/or prices from a History call '''
        # slice.Contains(symbol)
        if self.Portfolio[self.underlyingsymbol].Quantity != 0 and self.Portfolio[self.underlyingsymbol_cost].Quantity != 0:
            self.Liquidate()
            
      
        if not self.Portfolio.Invested:
            contracts = self.OptionChainProvider.GetOptionContractList(self.underlyingsymbol, self.Time.date())
            self.TradeOptions(contracts)

                        
    def TradeOptions(self,contracts):
        if len(contracts) == 0 : return
        filtered_contracts = self.InitialFilter(self.underlyingsymbol, contracts, -50, 50, 0, 7)
        # sorted the optionchain by expiration date and choose the furthest date
        expiry = sorted(filtered_contracts,key = lambda x: x.ID.Date)[-1].ID.Date

        # filter the call and put options from the contracts
        call = [i for i in filtered_contracts if i.ID.OptionRight == 0 and i.ID.Date == expiry]
        # put = [i for i in filtered_contracts if i.ID.OptionRight == 1 and i.ID.Date == expiry]
        # sorted the contracts according to their strike prices 
        call_contracts = sorted(call,key = lambda x: x.ID.StrikePrice)    
        # put_contracts = sorted(put,key = lambda x: x.ID.StrikePrice)
        
        
        
        # my long condor components
        itm_call_lower = call_contracts[-15]
        itm_call_upper = call_contracts[-10]
        otm_call_lower = call_contracts[-5]
        otm_call_upper = call_contracts[-1]
        
        if itm_call_lower == 0:
            return
        if itm_call_upper == 0:
            return
        if otm_call_lower == 0:
            return
        if otm_call_upper == 0:
            return
        
        print(itm_call_lower)
        
        
        # QC's iron condor components
        # otm_put_lower = put_contracts[0]
        # otm_put = put_contracts[10]
        # otm_call = call_contracts[-10]
        # otm_call_higher = call_contracts[-1]
        
        self.trade_contracts = [itm_call_lower,itm_call_upper,otm_call_lower,otm_call_upper]
        for contract in self.trade_contracts:
            self.AddOptionContract(contract, Resolution.Minute) 
      
        self.Buy(itm_call_lower, 1) # Buy 1 ITM Call
        self.Sell(itm_call_upper, 1) # Sell 1 ITM Call 
        self.Sell(otm_call_lower, 1) # Sell 1 OTM Call 
        self.Buy(otm_call_upper, 1) # Buy 1 OTM Call
        
    
        
            
    def InitialFilter(self, underlyingsymbol, symbol_list, min_strike_rank, max_strike_rank, min_expiry, max_expiry):
        
        ''' This method is an initial filter of option contracts
            based on the range of strike price and the expiration date '''
            
        if len(symbol_list) == 0 : return
        # fitler the contracts based on the expiry range
        contract_list = [i for i in symbol_list if min_expiry <= (i.ID.Date.date() - self.Time.date()).days <= max_expiry]
        # find the strike price of ATM option
        atm_strike = sorted(contract_list,
                            key = lambda x: abs(x.ID.StrikePrice - self.Securities[underlyingsymbol].Price))[0].ID.StrikePrice
        strike_list = sorted(set([i.ID.StrikePrice for i in contract_list]))
        # find the index of ATM strike in the sorted strike list
        atm_strike_rank = strike_list.index(atm_strike)
        try: 
            min_strike = strike_list[atm_strike_rank + min_strike_rank + 1]
            max_strike = strike_list[atm_strike_rank + max_strike_rank - 1]

        except:
            min_strike = strike_list[0]
            max_strike = strike_list[-1]
           
        filtered_contracts = [i for i in contract_list if i.ID.StrikePrice >= min_strike and i.ID.StrikePrice <= max_strike]

        return filtered_contracts
    
    def OnOrderEvent(self, orderEvent):
        self.Log(str(orderEvent))