Overall Statistics
Total Trades
440
Average Win
1.78%
Average Loss
-1.37%
Compounding Annual Return
-99.699%
Drawdown
64.500%
Expectancy
-0.214
Net Profit
-51.900%
Sharpe Ratio
-1.284
Probabilistic Sharpe Ratio
0.172%
Loss Rate
66%
Win Rate
34%
Profit-Loss Ratio
1.30
Alpha
-0.439
Beta
-0.305
Annual Standard Deviation
0.769
Annual Variance
0.592
Information Ratio
-3.448
Tracking Error
0.809
Treynor Ratio
3.24
Total Fees
$1100.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):
    
    trailing_stop_pct = .93
    highestPrice_call = 0
    highestPrice_put = 0
    trailing_stop_price_call = 0
    trailing_stop_price_put = 0

    def Initialize(self):
        self.SetStartDate(2020, 4, 21)
        self.SetCash(20000)
        
        # add the underlying asset
        self.equity = self.AddEquity("SPY R735QTJ8XC9X", Resolution.Minute)
        
        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()
        
        self.underlyingsymbol = self.AddEquity(self.syl, Resolution.Minute).Symbol
        

    def OnData(self, data):
        
        if self.IsWarmingUp:
            return
        
        if data.Time.hour < 10 or (data.Time.hour == 15 and data.Time.minute >= 30) or data.Time.hour > 15:
            return

        if not self.Portfolio.Invested:
            fast = self.fast.Current.Value
            slow = self.slow.Current.Value
            
            call_entry = (fast >= slow) and (fast <= self.equity.Price <= (fast + 0.03))
            put_entry = (fast <= slow) and (fast >= self.equity.Price >= (fast - 0.03))
            
            if call_entry:
                self.contract_call = self.OptionsFilter(data, OptionRight.Call)
                if self.contract_call in data:
                    self.MarketOrder(self.contract_call, +10)
                    price = self.Securities[self.contract_call].Close
                    self.trailing_stop_price_call = self.trailing_stop_pct * price
                    self.highestPrice_call = price
            
            if put_entry:
                self.contract_put = self.OptionsFilter(data, OptionRight.Put)
                if self.contract_put in data:
                    self.MarketOrder(self.contract_put, +10)
                    price = self.Securities[self.contract_put].Close
                    self.trailing_stop_price_put = self.trailing_stop_pct * price  
                    self.highestPrice_put = price
        
        #exit
        if self.Portfolio.Invested:
            if self.contract_call in self.Securities and self.Portfolio[self.contract_call].Invested:
                contract_price_call = self.Securities[self.contract_call].Close
                
                take_profit = contract_price_call >= self.trailing_stop_price_call * 1.3
                stop_loss_hit = contract_price_call < self.trailing_stop_price_call
                
                if take_profit or stop_loss_hit:
                    self.MarketOrder(self.contract_call, -10)
                    self.trailing_stop_price_call = 0
                    self.highestPrice_call = 0
                    
                # Updating trailing stop loss level
                elif contract_price_call > self.highestPrice_call:
                    self.highestPrice_call = contract_price_call
                    self.trailing_stop_price_call = self.highestPrice_call * self.trailing_stop_pct
                    
            if self.contract_put in self.Securities and self.Portfolio[self.contract_put].Invested:
                contract_price_put = self.Securities[self.contract_put].Close
                
                take_profit = contract_price_put >= self.trailing_stop_price_put * 1.3
                stop_loss_hit = contract_price_put < self.trailing_stop_price_put
                
                if take_profit or stop_loss_hit:
                    self.MarketOrder(self.contract_put, -10)
                    self.trailing_stop_price_put = 0
                    self.highestPrice_put = 0
                    
                # Updating trailing stop loss level
                elif contract_price_put > self.highestPrice_put:
                    self.highestPrice_put = contract_price_put
                    self.trailing_stop_price_put = self.highestPrice_put * self.trailing_stop_pct
    
    
    def OptionsFilter(self, data, optionRight):
        ''' 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
        
        otm_contracts = [i for i in contracts if i.ID.OptionRight == optionRight and i.ID.Date > data.Time]
        if len(otm_contracts) == 0:
            return str()

        contract = sorted(sorted(otm_contracts, key=lambda x: abs(self.underlyingPrice - x.ID.StrikePrice)),
                                                key=lambda x: x.ID.Date)[0]
        if contract not in self.contractsAdded:
            self.contractsAdded.add(contract)
            # use AddOptionContract() to subscribe the data for specified contract
            self.AddOptionContract(contract, Resolution.Minute)
            
        return contract