Overall Statistics
Total Trades
181
Average Win
0.97%
Average Loss
-0.59%
Compounding Annual Return
15.243%
Drawdown
9.500%
Expectancy
0.006
Net Profit
1.725%
Sharpe Ratio
0.57
Probabilistic Sharpe Ratio
42.712%
Loss Rate
62%
Win Rate
38%
Profit-Loss Ratio
1.66
Alpha
-0.463
Beta
0.403
Annual Standard Deviation
0.324
Annual Variance
0.105
Information Ratio
-4.24
Tracking Error
0.335
Treynor Ratio
0.458
Total Fees
$450.00
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Common")

from System import *
from QuantConnect import *
from QuantConnect.Data import *
from QuantConnect.Algorithm import *
from QuantConnect.Securities.Option import OptionPriceModels
import numpy as np
from datetime import timedelta

### <summary>
### Demonstration of the Option Chain Provider -- a much faster mechanism for manually specifying the option contracts you'd like to recieve
### data for and manually subscribing to them.
### </summary>
### <meta name="tag" content="strategy example" />
### <meta name="tag" content="options" />
### <meta name="tag" content="using data" />
### <meta name="tag" content="selecting options" />
### <meta name="tag" content="manual selection" />

class OptionChainProviderAlgorithm(QCAlgorithm):
    
    highestPrice = 0
    trailing_stop_pct = .93
    trailing_stop_price = 0

    def Initialize(self):
        self.SetStartDate(2020, 4, 21)
        #self.SetEndDate(2016, 6, 24)
        self.SetCash(20000)
        # add the underlying asset
        #self.equity = self.AddEquity("SPY", Resolution.Minute)
        self.equity = self.AddEquity("SPY R735QTJ8XC9X", Resolution.Minute)
        #self.equity = self.Symbol("SPY R735QTJ8XC9X", Resolution.Minute)
        #spy = self.Symbol("SPY R735QTJ8XC9X")
        #SPY R735QTJ8XC9X
        
        self.syl = "SPY"
        self.spy = "SPY"
        
        self.equity.SetDataNormalizationMode(DataNormalizationMode.Raw)
        
        # create a XX-period exponential moving average
        self.fast = self.EMA("SPY", 30, Resolution.Minute)
        # create a XX-period exponential moving average since this is minute, may have to multiply by 2 if want 2 min chart
        #self.slow = self.EMA("SPY", 50, Resolution.Minute)
        
        slow_ema_lookback = 100
        self.slow = self.EMA("SPY", slow_ema_lookback, Resolution.Minute);
        self.SetWarmUp(slow_ema_lookback)
        
        # initialize the option contract with empty string
        self.contract_put = str()
        self.contract_call = str ()
        self.contractsAdded = set()
        
        equity = self.AddEquity(self.syl, Resolution.Minute)
        self.underlyingsymbol = equity.Symbol

    def OnData(self, data):
        
        if self.IsWarmingUp:
            return

        #if not self.Portfolio[self.equity.Symbol].Invested:
        #    self.MarketOrder(self.equity.Symbol, 100)
        
        #put entry
        if not (self.Portfolio.Invested) and (self.fast.Current.Value <= self.slow.Current.Value) and (self.fast.Current.Value >= self.equity.Price >= (self.fast.Current.Value - 0.03)):
        # worked self.equity.Price > 0:
        #(self.fast.Current.Value < self.slow.Current.Value): 
        #and (self.equity ["GOOG"] >= (self.fast.Current.Value - 0.03)):
        
        #if not (self.Portfolio[self.contract_put].Invested)
        #(self.Securities.ContainsKey(self.contract_put) and ):
            
            
        #(self.fast.Current.Value < self.slow.Current.Value) and (slice["SPY"].Price >= (self.fast.Current.Value - 0.03)
            
            self.contract_put = self.OptionsFilter(data)
            self.MarketOrder(self.contract_put, +10)
            self.trailing_stop_price = self.trailing_stop_pct * self.Securities[self.contract_put].Close  
        
       
        #call entry
        if not (self.Portfolio.Invested) and (self.fast.Current.Value >= self.slow.Current.Value) and (self.fast.Current.Value <= self.equity.Price <= (self.fast.Current.Value + 0.03)):
        
        #(self.Securities.ContainsKey(self.contract_call) and self.Portfolio[self.contract_call].Invested):
            self.contract_call = self.OptionsFilter(data)
            self.MarketOrder(self.contract_call, +10)
            self.trailing_stop_price = self.trailing_stop_pct * self.Securities[self.contract_call].Close
        
        #exit
        if (self.Portfolio.Invested):
            contract_price_call = self.Securities[self.contract_call].Close
            contract_price_put = self.Securities[self.contract_put].Close
            
            self.Log(f"Stop price: {self.trailing_stop_price}")
            
            #near 20% exit
            if (self.Securities[self.contract_call].Close >= (self.trailing_stop_price * 1.3)):
                self.Log(f"20% profit call contract price: {contract_price_call}")
                self.MarketOrder(self.contract_call, -10)
                self.Log("Liquidated")
                
            elif (self.Securities[self.contract_put].Close >= (self.trailing_stop_price * 1.3)):
                self.MarketOrder(self.contract_put, -10) 
                self.Log(f"20% profit put contract price: {contract_price_put}")
                self.Log("Liquidated")
                
            #set trailing stop for calls
            elif (self.Portfolio[self.contract_call].Invested) and (contract_price_call < self.trailing_stop_price):
                self.Log(f"Call contract price: {contract_price_call}")
                self.MarketOrder(self.contract_call, -10)
                self.Log("Liquidated")
            
            elif (self.Portfolio[self.contract_call].Invested) and (contract_price_call > self.highestPrice):
                self.Log(f"Call contract price: {contract_price_call}")
                self.highestPrice = contract_price_call
                self.trailing_stop_price = self.highestPrice * self.trailing_stop_pct
            
            #take 20% off puts
            
                
            #set trailing stop for puts
            elif (self.Portfolio[self.contract_put].Invested) and (contract_price_put < self.trailing_stop_price):
                self.Log(f"Put contract price: {contract_price_put}")
                self.MarketOrder(self.contract_put, -10)
                self.trailing_stop_price = 0
                self.highestPrice = 0
                self.Log("Liquidated")
            
            elif (self.Portfolio[self.contract_put].Invested) and (contract_price_put > self.highestPrice):
                self.Log(f"Put contract price: {contract_price_put}")
                self.highestPrice = contract_price_put
                self.trailing_stop_price = self.highestPrice * self.trailing_stop_pct
                
        #if not (self.Securities.ContainsKey(self.contract) and self.Portfolio[self.contract].Invested):
        #    self.contract = self.OptionsFilter(data)

        #if self.Securities.ContainsKey(self.contract) and not self.Portfolio[self.contract].Invested:
        #    self.MarketOrder(self.contract, -1)

    #def contract_put(self):
    #    contracts = self.OptionChainProvider.GetOptionContractList(self.security.symbol, self.Time.date())
    #    if len(contracts) == 0: return
    #    filtered_contracts = self.InitialFilter(self.underlyingsymbol, contracts, -1, 1, 0, 14)
    #    put = [x for x in filtered_contracts if x.ID.OptionRight == OptionRight.Put] 
        
    #    if len(put) == 0:
    #        return
        
        # sorted the contracts according to their expiration dates and choose the ATM options
    #    contracts = sorted(sorted(put, key = lambda x: abs(self.Securities[self.syl].Price - x.ID.StrikePrice)), 
    #                                    key = lambda x: x.ID.Date, reverse=False)
    #    self.contract = contracts[0]
    #    self.AddOptionContract(self.contract, Resolution.Minute)
    #    self.Buy(self.contract, 5)
    
    
    def OptionsFilter(self, data):
        ''' OptionChainProvider gets a list of option contracts for an underlying symbol at requested date.
            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 '''

        contracts = self.OptionChainProvider.GetOptionContractList(self.equity.Symbol, data.Time)
        self.underlyingPrice = self.Securities[self.equity.Symbol].Price
        # filter the out-of-money call options from the contract list which expire in 0 to 7 days from now on
        
        otm_calls = [i for i in contracts if i.ID.OptionRight == OptionRight.Call] #and
                                            #i.ID.StrikePrice - self.underlyingPrice > 0 and
                                            #0 < (i.ID.Date - data.Time).days < 3]
        
        #filter the out-of-money put options fromt the contract list which expire in 0 to 7 days from now    
        otm_puts = [i for i in contracts if i.ID.OptionRight == OptionRight.Put] #and
                                            #i.ID.StrikePrice - self.underlyingPrice > 0 and
                                            #0 < (i.ID.Date - data.Time).days < 3]
        
        #this should help select the soonest exp.
        #if len(otm_puts) == 0:
        #    return
        
        #this should help select the soonest exp.
        #if len(otm_calls) == 0:
        #    return
        
        if len(otm_calls) > 0 and (self.fast.Current.Value >= self.slow.Current.Value) and (self.fast.Current.Value <= self.equity.Price <= (self.fast.Current.Value + 0.03)):
            
            contract_call = sorted(sorted(otm_calls, key = lambda x: x.ID.Date, reverse=False),
                                                    key = lambda x: abs(self.underlyingPrice - x.ID.StrikePrice))[0]
            
            if contract_call not in self.contractsAdded:
                self.contractsAdded.add(contract_call)
                # use AddOptionContract() to subscribe the data for specified contract
                self.AddOptionContract(contract_call, Resolution.Minute)
            return contract_call
        
        if len(otm_puts) > 0 and (self.fast.Current.Value <= self.slow.Current.Value) and (self.fast.Current.Value >= self.equity.Price >= (self.fast.Current.Value - 0.03)):
                                        
            contract_put = sorted(sorted(otm_puts, key = lambda x: x.ID.Date, reverse=False),
                                                     key = lambda x: abs(self.underlyingPrice - x.ID.StrikePrice))[0]
            
            if contract_put not in self.contractsAdded:
                self.contractsAdded.add(contract_put)
                # use AddOptionContract() to subscribe the data for specified contract
                self.AddOptionContract(contract_put, Resolution.Minute)
            return contract_put
            
        else:
            return str()
    
    
    




        #attempt to select closest expiry. 
        #def contract_put(self):
        #    ontracts = self.OptionChainProvider.GetOptionContractList(self.underlyingsymbol, self.Time.date())
        #    if len(contracts) == 0: return
        #    filtered_contracts = self.InitialFilter(self.underlyingsymbol, contracts, -1, 1, 0, 14)
        #    put = [x for x in filtered_contracts if x.ID.OptionRight == OptionRight.Put] 
        
        #    if len(put) == 0:
        #        return
        
        # sorted the contracts according to their expiration dates and choose the ATM options
        #    contracts = sorted(sorted(put, key = lambda x: abs(self.Securities[self.syl].Price - x.ID.StrikePrice)), 
        #                                key = lambda x: x.ID.Date, reverse=False)
        #    self.contract = contracts[0]
        #    self.AddOptionContract(self.contract, Resolution.Minute)
        #    self.Buy(self.contract_put, 1)
    
        # def BuyCall(self):
        #    contracts = self.OptionChainProvider.GetOptionContractList(self.underlyingsymbol, self.Time.date())
        #    if len(contracts) == 0: return
        #    filtered_contracts = self.InitialFilter(self.underlyingsymbol, contracts, -3, 3, 0, 30)
        #    call = [x for x in filtered_contracts if x.ID.OptionRight == 1] 
        # sorted the contracts according to their expiration dates and choose the ATM options
        #    contracts = sorted(sorted(call, key = lambda x: abs(self.Securities[self.syl].Price - x.ID.StrikePrice)), 
        #                                    key = lambda x: x.ID.Date, reverse=True)
        #    self.contract = contracts[0]
        #    self.AddOptionContract(self.contract, Resolution.Minute)
        #    self.Buy(self.contract, 1)
        
        

        #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
        #        according to 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]
        
        #    if len(contract_list) == 0:
        #        return []
        
        # 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]
        #        max_strike = strike_list[atm_strike_rank + max_strike_rank]
        #    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