Overall Statistics
Total Trades
937
Average Win
0.16%
Average Loss
-0.09%
Compounding Annual Return
3.655%
Drawdown
2.700%
Expectancy
0.409
Net Profit
19.707%
Sharpe Ratio
1.593
Probabilistic Sharpe Ratio
90.800%
Loss Rate
49%
Win Rate
51%
Profit-Loss Ratio
1.75
Alpha
0.029
Beta
0.047
Annual Standard Deviation
0.022
Annual Variance
0
Information Ratio
-0.506
Tracking Error
0.181
Treynor Ratio
0.747
Total Fees
$937.00
from decimal import Decimal

class FibonacciOptionStraddle(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2015, 7, 29)  # Set Start Date
        
        self.SetCash(30000)  # Set Strategy Cash
        self.SetBenchmark("SPY")
        
        #Ticker list
        self.tickers =  ["MSFT", "GS", "V", "CSCO", "PG", "VZ", "TGT", "COST", "PEP", "WMT", "AAPL", "NEE"]
            
        self.maxBySymbol = {}
        
        self.totalPercentInv = 1/len(self.tickers)
        
        self.EnableAutomaticIndicatorWarmUp = True
        for ticker in self.tickers:
            symbol = self.AddEquity(ticker, Resolution.Hour).Symbol
            self.Securities[ticker].SetDataNormalizationMode(DataNormalizationMode.Raw)
            self.maxBySymbol[symbol] = self.MAX(symbol, 5, Resolution.Daily)
            
        self.SetBrokerageModel(BrokerageName.AlphaStreams)

        #create empy dictionary for purchase price of equity
        self.purchasedPrice = {}
        
        self.running = False
        
        #Runs strategy
        self.Schedule.On(self.DateRules.EveryDay(), 
                 self.TimeRules.Every(timedelta(hours=2)),
                 self.runAlg)
                
    def runAlg(self):
        self.running = True
    
    def OnData(self, data):
        
        if not self.running:
            return

        for symbol, max_indicator in self.maxBySymbol.items():

            #gets first option contract
            contract = self.GetContract(symbol)
            
            #if there is no contract then continue to next symbol
            if contract == None:
                continue
            
            fib100 = max_indicator.Current.Value
            
            #BufferFib subtracts 30% from the max Fibonacci Retracement to reach a 70% retracement
            bufferFib = self.Securities[symbol].Price * 0.3
                
            #checks if equity price is below its maximum retracement. If equity price is above then strategy will automatically buy with no rational
            if self.Securities[symbol].Price < fib100:
                
                #if option premium is within a range between 130% and 70% of the Fibonacci Retracement continue
                if (fib100+bufferFib) >= self.Securities[contract].BidPrice >= (fib100-bufferFib):
                    
                    #If portfolio holds equity, move to next symbol
                    if self.Portfolio[symbol].Invested: 
                        continue
                    
                    #saves price to priceSec right before purchase
                    priceSec = self.Securities[symbol].Price
                    self.SetHoldings(symbol, self.totalPercentInv, False, "Set Holdings")
                    if self.Portfolio[symbol].Quantity == 0:
                        continue
                    self.LimitOrder(symbol, -(self.Portfolio[symbol].Quantity), round(round(priceSec, 2) * 1.03, 2))
                    
                    #conducts an accurate rounding of priceSec to 2 decimal points and saves the purchase price to a dictionary
                    numDec = Decimal(str(priceSec))
                    self.purchasedPrice[symbol] = round(numDec, 2)

                else:
                    #if portfolio is invested and Bid Price and Fibonacci Retracement cross over a second time, sell
                    if self.Portfolio[symbol].Invested:
                        
                        numDec2 = Decimal(str(self.Securities[symbol].Price))
                        
                        #ensure equity is not sold at the same price
                        if round(numDec2, 2) != self.purchasedPrice[symbol]:
                            self.Liquidate(symbol)

            #stops alg
            self.running = False
            
    def GetContract(self, symbol):
        
        #pulls contract data for select equity at current time
        contracts = self.OptionChainProvider.GetOptionContractList(symbol, self.Time)
        
        #selects the type of option to be Put contract
        calls = [x for x in contracts if x.ID.OptionRight == OptionRight.Call]
        
        if len(calls) == 0:
            return None
        
        #sorts contracts by closet expiring date date and closest strike price (sorts in ascending order)
        calls = sorted(sorted(calls, key = lambda x: x.ID.Date), 
            key = lambda x: x.ID.StrikePrice)
        
        #then selects all contracts that meet our expiration criteria
        contract = calls[0]
        
        #adds contract
        self.AddOptionContract(contract, Resolution.Minute)
        
        #return the call contract
        return contract