| 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 riskAdjustedTargetsclass 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=bidPricefrom 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: returnfrom 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]