Overall Statistics
Total Trades
50
Average Win
0%
Average Loss
-0.05%
Compounding Annual Return
-8.409%
Drawdown
0.600%
Expectancy
-1
Net Profit
-0.616%
Sharpe Ratio
-7.327
Probabilistic Sharpe Ratio
0.012%
Loss Rate
100%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
-0.043
Beta
-0.017
Annual Standard Deviation
0.008
Annual Variance
0
Information Ratio
-17.106
Tracking Error
0.065
Treynor Ratio
3.629
Total Fees
$48.00
Estimated Strategy Capacity
$34000.00
Lowest Capacity Asset
CALM WR5SUSCWO892|CALM R735QTJ8XC9X
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Common")
from decimal import Decimal
from System import *
from QuantConnect import *
from QuantConnect.Algorithm import QCAlgorithm
from QuantConnect.Data.UniverseSelection import *
from QuantConnect.Securities.Option import OptionPriceModels
import base64
import numpy as np
import pandas as pd
import requests
from io import StringIO
import requests
from datetime import timedelta, datetime, date

class EarningsAlgorithm(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2018,1,1)
        self.SetEndDate(2018,1,26)
        self.SetCash(1000000)
        self.backtestSymbolsPerDay = {}
        self.current_universe = []
        self.symbols = []
        self.syms = []
        self.equitiesSyms = []
        self.sold = {}
       
        # Set the security initializer with the characteristics defined in CustomSecurityInitializer
        self.UniverseSettings.Resolution = Resolution.Minute
        self.UniverseSettings.DataNormalizationMode = DataNormalizationMode.Raw
        self.SetUniverseSelection(ScheduledUniverseSelectionModel(self.DateRules.Every(DayOfWeek.Monday, DayOfWeek.Tuesday, 
        DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday),self.TimeRules.At(9, 00, 00), self.selector ))
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, 
                                AccountType.Margin)

        self.changes = None                   
        bench = self.AddEquity("SPY", Resolution.Minute)
        self.SetBenchmark(bench.Symbol)                        
        self.x = 0
        self.Schedule.On(self.DateRules.Every(DayOfWeek.Monday, DayOfWeek.Tuesday, 
        DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday), self.TimeRules.At(10, 0), self.reb)
        
        

    def CustomSecurityInitializer(self, security):
        security.SetDataNormalizationMode(DataNormalizationMode.Raw)
        
    def selector(self, datetime):
        self.current_universe = []
        if len(self.backtestSymbolsPerDay) == 0:
            csv = self.Download("https://www.dropbox.com/s/gewbcrjrlcpslzd/er.csv?dl=1")
            raw_er = pd.read_csv(StringIO(csv))

            self.Debug('raw_er.head : ' + str(raw_er.head()))
            for d in raw_er['trade_date'].unique():
                i = str(d)
                self.backtestSymbolsPerDay[i] = (raw_er[raw_er['trade_date']== d]).symbol.tolist()

        index = self.Time.strftime("%Y%m%d")

        try:
            self.current_universe = self.backtestSymbolsPerDay[index]
        except:
            self.current_universe = []

        equities = []
        #equitiesSyms = []
        self.symbols = []
        for ticker in self.current_universe:
            equities.append(Symbol.Create(ticker, SecurityType.Equity, Market.USA))
        
        # set a marker to only run after daily universe creation, otherwise it runs at midnight too and liquidates on open.
        self.x = 1 
        return equities
        
    def reb(self):
        for i in self.Portfolio.Values:
            self.Debug('liquidate')
            #self.Buy(i.Symbol, self.sold[i.Symbol])
            self.Liquidate(i.Symbol)
        self.sold = {}

    def OnData(self, slice):
        if self.Time.hour == 13 and self.Time.minute == 1:
            for security in self.ActiveSecurities.Keys:
                self.Log(f'{self.Time} >> Security in Universe: {security.Value}')
                
        if slice.Bars.Count == 0: return
        if self.changes is None: return
        #if self.Portfolio.Invested: return
        if self.Time.hour < 15: return
        for security in self.ActiveSecurities.Keys:
            self.Log(f'Security in Universe: {security.Value}')
            
        self.option_data = slice
        self.TradeOptions(slice) 
        #self.TradeEquities(slice)

        self.Debug(str(self.Time) +'  '+'end OnData')
    
    def TradeOptions(self,slice):
        self.Debug('trading options')
        #for symbol in self.symbols:
            #if slice.OptionChains.Count == 0: return

        for i in slice.OptionChains:
            self.Debug('now chains')
            #if i.Key == symbol:

            chains = i.Value
                  
            if i.Key in self.equitiesSyms:  return
                  
            # divide option chains into call and put options 
            self.calls = list(filter(lambda x: x.Right == OptionRight.Call, chains))
            self.puts = list(filter(lambda x: x.Right == OptionRight.Put, chains))
            
            # if lists are empty return
            if not self.calls or not self.puts: return
            
            #expries should be same for put and calls so collecting only puts for now. (convinience)
            expiries = [i.Expiry for i in self.puts]
            
            # determine expiration date
            self.expiry = min(expiries, key=lambda x: abs((x.date()-self.Time.date()).days-90))
            self.Debug(str(expiries))
            self.Debug("we choose this expiry...     " + str(self.expiry))
            self.Debug(i.Key)
            #self.symbol = epic
           
            self.straddle()
            self.trade_execution(i.Key)
        #return
    
    
    def straddle(self):
        #strikes
        put_strikes = [i.Strike for i in self.puts]
        call_strikes = [i.Strike for i in self.calls]
        underlying_price = self.calls[0].UnderlyingLastPrice 
        
        # determine at-the-money strike. Again doesnt matter call or put using put.
        strike = min(put_strikes, key=lambda x: abs(x-underlying_price))
       
        # Set up the legs.
        self.atm_call = [i for i in self.calls if i.Expiry == self.expiry and i.Strike == strike]
        self.atm_put = [i for i in self.puts if i.Expiry == self.expiry and i.Strike == strike]    
        self.Debug(' ...underlying is...   ' + 
        '  ...put strike...' + str(self.atm_put[0].Strike) +
        '  ...put delta...' + str(self.atm_put[0].Greeks.Delta)+
        '  ...put atm expiry...' + str(self.atm_put[0].Expiry) +
        '  ...call atm strike...' + str(self.atm_call[0].Strike)+
        '  ...call atm delta...' + str(self.atm_call[0].Greeks.Delta)+
        '  ...call atm expiry...' + str(self.atm_call[0].Expiry))
    
    
    def trade_execution(self, underlying):
        self.Debug("lets check the legs exist and are sensible")
        if self.atm_put and self.atm_call:
            
            #if self.atm_put[0].Greeks.Delta < -0.55 or self.atm_put[0].Greeks.Delta > -0.40:return
            #if self.atm_call[0].Greeks.Delta < 0.40 or self.atm_call[0].Greeks.Delta > 0.55:return
            #if self.atm_put[0].AskPrice < 0.30 :return
            #if self.atm_call[0].AskPrice < 0.30:return
            
            trade_cost = self.atm_put[0].AskPrice +self.atm_call[0].AskPrice
            self.Debug('Cost is :')
            self.Debug(trade_cost)
            if trade_cost == 0: return

            #set the size
            self.Debug('  ...atm strike...' + str(self.atm_put[0].AskPrice)+
            '  ...atm strike...' + str(self.atm_call[0].AskPrice))
            size = int(1000 / (trade_cost * 100))
            #self.Portfolio.TotalPortfolioValue 
            #deltas = 20000 / (self.Securities[self.symbol].Price * 100)
            #self.Debug(str(deltas))
            #size = int(deltas)
            self.equitiesSyms.append(underlying)
            self.sold["self.atm_call[0].Symbol"] = size
            self.sold["self.atm_put[0].Symbol"] = size
            self.Buy(self.atm_call[0].Symbol, size)
            self.Buy(self.atm_put[0].Symbol, size)

        else:
            # no options so try tomorrow
            self.Debug(str(self.Time) + '   ...No optons...  ' )
            return
    
    
        
    def OnSecuritiesChanged(self, changes):
        self.changes = changes
        if self.x == 1: 

            for x in changes.AddedSecurities:
                if x.Symbol.Value == 'SPY': continue
                if x.Symbol.SecurityType != SecurityType.Equity: continue
                
                
                option = self.AddOption(x.Symbol.Value, Resolution.Minute)
                option.PriceModel = OptionPriceModels.BinomialCoxRossRubinstein()
                option.SetFilter(self.UniverseFunc)
                self.symbols.append(option.Symbol)
                self.Log(f'{self.Time} >> {x.Symbol} and Options added to Universe')

            for x in changes.RemovedSecurities:
                    if x.Symbol.Value == 'SPY': continue   
                    
                    for symbol in self.Securities.Keys:
                        if symbol.SecurityType == SecurityType.Option and symbol.Underlying == x.Symbol:
                            self.RemoveSecurity(symbol)
                            self.RemoveSecurity(x.Symbol)
                            self.Log(f'{self.Time} >> {x.Symbol} and {symbol} removed from the Universe')
        self.x = 0 
        

    def UniverseFunc(self, universe):
        return universe.IncludeWeeklys().Strikes(-1, 1).Expiration(timedelta(2), timedelta(8))