Overall Statistics
Total Trades
190
Average Win
1.08%
Average Loss
-0.94%
Compounding Annual Return
16.712%
Drawdown
6.400%
Expectancy
0.190
Net Profit
16.859%
Sharpe Ratio
1.086
Probabilistic Sharpe Ratio
50.668%
Loss Rate
44%
Win Rate
56%
Profit-Loss Ratio
1.14
Alpha
0.12
Beta
0.129
Annual Standard Deviation
0.133
Annual Variance
0.018
Information Ratio
-0.163
Tracking Error
0.29
Treynor Ratio
1.121
Total Fees
$246.75
class TrailingStopRiskManagementModel(RiskManagementModel):
    
    def __init__(self):
        self.liquidated = set()
        self.lastmonth=-1
        
    def ManageRisk(self, algorithm, targets):
        month= algorithm.Time.month
        if month!= self.lastmonth:
            self.liquidated.clear()
            self.lastmonth= month
        riskAdjustedTargets = list()

        for kvp in algorithm.Securities:
            symbol = kvp.Key
            security = kvp.Value

            if security.Holdings.UnrealizedProfitPercent > 0.04 or security.Holdings.UnrealizedProfitPercent < -0.03 or security.Symbol in self.liquidated:
                riskAdjustedTargets.append(PortfolioTarget(symbol, 0))
                if algorithm.Securities[security.Symbol].Invested:
                    self.liquidated.add(security.Symbol)
        return riskAdjustedTargets
class FibonacciOptionStraddle(QCAlgorithm):
    def __init__(self,period=15, resolution=Resolution.Hour):
        
        self.period=period
        self.resolution=resolution
        self.symbolDataBySymbol = {}
        self.optionDataBySymbol={}
        self.hour = None
        self.removed = []
        
    def Update(self, algorithm, data):
        insights = []
        
        if algorithm.Time.hour == self.hour:
            return []
    
        for symbol, sd in self.symbolDataBySymbol.items():    
            maximum=sd.max
            value=algorithm.Securities[symbol].Price
            
            error=value*0.30
        
            call=None
       
            for contract ,info in self.optionDataBySymbol.items():    
                if info.Underlying.Value==symbol.Value:
                    call=info
                    continue
        
            if (value < (maximum.Current.Value)) and call is not None:
                if (maximum.Current.Value+error) >= call.BidPrice >= (maximum.Current.Value-error):
                    insights.append(Insight.Price(symbol,timedelta(hours=1), InsightDirection.Up))
                else:
                    insights.append(Insight.Price(symbol,timedelta(hours=1), InsightDirection.Flat))
            else:
                insights.append(Insight.Price(symbol,timedelta(hours=1), InsightDirection.Flat))
            
            if insights:
                self.hour = algorithm.Time.hour
        return insights
        
    def OnSecuritiesChanged(self, algorithm, changes):
    
        for y in  changes.RemovedSecurities:
            if y.Symbol.SecurityType ==SecurityType.Equity:   
                self.removed.clear()
                for contract ,info in self.optionDataBySymbol.items():
                
                    if info.Underlying.Value==y.Symbol.Value:
                        
                        self.removed.append(info.Underlying)
                
                for x in self.removed :
                    optionData=self.optionDataBySymbol.pop(x,None)
                    
                symbolData = self.symbolDataBySymbol.pop(y.Symbol, None)
               
                if symbolData:
                    algorithm.SubscriptionManager.RemoveConsolidator(y.Symbol, symbolData.Consolidator)
            
            elif y.Symbol.SecurityType ==SecurityType.Option:
                if y.Underlying not in [x.Symbol for x in changes.RemovedSecurities]:
                    optionData=self.optionDataBySymbol.pop(y.Underlying,None)
     
        addedSymbols = [ x.Symbol for x in changes.AddedSecurities if (x.Symbol not in self.symbolDataBySymbol and x.Symbol.SecurityType ==SecurityType.Equity)]
     
        if len(addedSymbols) == 0: return
        
        history = algorithm.History(addedSymbols, self.period, self.resolution)
 
        for symbol in addedSymbols:
            Max=Maximum(5)
            Min=Minimum(5)
            consolidator = algorithm.ResolveConsolidator(symbol, Resolution.Daily)
            algorithm.RegisterIndicator(symbol, Max, consolidator)
            algorithm.RegisterIndicator(symbol, Min, consolidator)
            
            if not history.empty:
                ticker = SymbolCache.GetTicker(symbol)
                
                for tuple in history.loc[ticker].itertuples():
                    Max.Update(tuple.Index, tuple.close)
                    Min.Update(tuple.Index, tuple.close)
            
            self.symbolDataBySymbol[symbol] = SymbolData(symbol,Max,Min,consolidator)
            
        options= [ x.Symbol for x in changes.AddedSecurities if (x.Symbol not in self.optionDataBySymbol and x.Symbol.SecurityType ==SecurityType.Option)]
        if len(options) == 0: return
    
        for option in options:
            algorithm.Securities[option].Underlying = algorithm.Securities[option.Underlying] 
       
        newhistory = algorithm.History(options, self.period, Resolution.Minute)
    
        if  newhistory.empty: return
    
        for contract in options:
            underlying=contract.Underlying 
            bidPrice= algorithm.Securities[contract].BidPrice
           
            self.optionDataBySymbol[underlying] = OptionData(contract, underlying,bidPrice)
        
class SymbolData:
    def __init__(self, symbol, Max,Min,consolidator):
        
        self.Symbol = symbol
        self.max = Max
        self.min = Min
        self.Consolidator=consolidator
        
class OptionData:
    
    def __init__(self,contract, underlying,bidPrice):
        
        self.Contract=contract
        self.Underlying=underlying
        self.BidPrice=bidPrice
from CustomUniverse import OptionsUniverse
from CustomAlpha import fib
from CustomRiskManagement import takeprofit

class test (QCAlgorithm):
    
    def Initialize(self):
        self.SetStartDate(2019,8,13)  
        self.SetEndDate(2020,8,13)
        self.SetCash(100000)
        self.SetTimeZone(TimeZones.Chicago)
        self.SetSecurityInitializer(lambda s: s.SetMarketPrice(self.GetLastKnownPrice(s))) 

        self.AddUniverseSelection(OptionsUniverse.universe())
     
        self.UniverseSettings.Resolution = Resolution.Minute
        
        self.UniverseSettings.DataNormalizationMode=DataNormalizationMode.Raw
        
        self.UniverseSettings.FillForward = True
        self.UniverseSettings.ExtendedMarketHours = False
        self.UniverseSettings.MinimumTimeInUniverse = 1
        self.UniverseSettings.Leverage=1
      
        self.AddAlpha(fib.FibonacciOptionStraddle())
        
        self.Settings.RebalancePortfolioOnInsightChanges = False;
     
        self.Settings.RebalancePortfolioOnSecurityChanges = False;
        
        self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())
   
        self.SetRiskManagement(takeprofit.TrailingStopRiskManagementModel())
 
        self.SetExecution(ImmediateExecutionModel())
        
        self.SetWarmUp(timedelta(days=5))
    
    def OnData(self, slice):
      if self.IsWarmingUp: return
from Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel
from datetime import timedelta, datetime
from math import ceil
from itertools import chain
import numpy as np

class universe(FundamentalUniverseSelectionModel):

    def __init__(self, filterFineData = True, universeSettings = None, securityInitializer = None):
        super().__init__(filterFineData, universeSettings, securityInitializer)

        self.NumberOfSymbolsCoarse = 2500
        self.NumberOfSymbolsFine =  100
        self.NumberOfSymbolsInPortfolio = 15
       
        self.lastmonth = -1
        self.dollarVolumeBySymbol = {}

    def SelectCoarse(self, algorithm, coarse):
        month= algorithm.Time.month/6
        if month == self.lastmonth:
            return Universe.Unchanged
        self.lastmonth= month

        top = sorted([x for x in coarse if x.HasFundamentalData],
                    key=lambda x: x.DollarVolume, reverse=True)[:self.NumberOfSymbolsCoarse]
        
        self.dollarVolumeBySymbol = { i.Symbol: i.DollarVolume for i in top }

        return list(self.dollarVolumeBySymbol.keys())

    def SelectFine(self, algorithm, fine):
       
        self.priceAllowance = 100
    
        filteredFine = [x for x in fine if x.CompanyReference.CountryId == "USA"
                                        and x.Price > self.priceAllowance
                                        and (x.CompanyReference.PrimaryExchangeID == "NYS" or x.CompanyReference.PrimaryExchangeID == "NAS")
                                        and (algorithm.Time - x.SecurityReference.IPODate).days > 1200
                                        and (x.EarningReports.BasicAverageShares.ThreeMonths * x.EarningReports.BasicEPS.TwelveMonths * x.ValuationRatios.PERatio >= 5e10)
                                    ]
       
        self.valuationRatio = { i.Symbol: i.ValuationRatios.SustainableGrowthRate for i in filteredFine }
      
        count = len(filteredFine)
        if count == 0: return []

        myDict = dict()
        percent = self.NumberOfSymbolsFine / count

        value3 = sorted(filteredFine, key = lambda x: self.valuationRatio[x.Symbol], reverse = True)
        value4 = value3[:ceil(len(value3) * percent)]
    
        self.stocks = value4[:self.NumberOfSymbolsInPortfolio]
        
        self.contract=[]
  
        for x in self.stocks:
            self.currentSymbol = x.Symbol
            self.contract.append(self.GetContract(algorithm))
     
        self.newstocks= [x.Symbol for x in self.stocks]
        
        return [x for x in self.newstocks + self.contract]
    
    def GetContract(self, algorithm):
        
        contracts=algorithm.OptionChainProvider.GetOptionContractList(self.currentSymbol, algorithm.Time)
   
        call = [x for x in contracts if x.ID.OptionRight ==OptionRight.Call]
        
        call = sorted(sorted(call, key = lambda x: x.ID.Date), 
            key = lambda x: x.ID.StrikePrice)

        if not call:
           return
       
        return  call[0]