| Overall Statistics |
|
Total Trades 1197 Average Win 0.11% Average Loss -0.04% Compounding Annual Return 58.957% Drawdown 10.400% Expectancy 0.669 Net Profit 24.086% Sharpe Ratio 2.188 Probabilistic Sharpe Ratio 71.688% Loss Rate 51% Win Rate 49% Profit-Loss Ratio 2.44 Alpha 0.478 Beta 0.072 Annual Standard Deviation 0.229 Annual Variance 0.053 Information Ratio 0.697 Tracking Error 0.25 Treynor Ratio 6.943 Total Fees $1433.33 Estimated Strategy Capacity $360000.00 Lowest Capacity Asset HWKN R735QTJ8XC9X |
class WellDressedSkyBlueSardine(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2021, 2, 25)
# self.SetEndDate(2020, 3, 1)
self.SetCash(100000)
self.SetBenchmark("SPY")
self.rebalanceTime = datetime.min
self.activeStocks = set()
self.AddUniverse(self.CoarseFilter, self.FineFilter)
self.UniverseSettings.Resolution = Resolution.Daily
#self.brokerage = BrokerageName.InteractiveBrokersBrokerage
self.SetBrokerageModel(BrokerageName.AlphaStreams)
#self.SetBrokerageModel(self.brokerage, AccountType.Margin)
self.portfolioTargets = []
self.UniverseSettings.DataNormalizationMode=DataNormalizationMode.SplitAdjusted
#self.UniverseSettings.ExtendedMarketHours = False
self.AddRiskManagement(MyRiskModel(.21))
self.SetExecution(ImmediateExecutionModel())
self.SetAlpha(LongOnlyConstantAlphaCreationModel())
def CoarseFilter(self, coarse):
# Rebalancing monthly
if self.Time <= self.rebalanceTime:
return self.Universe.Unchanged
self.rebalanceTime = self.Time + timedelta(2)
myuniverse = [x for x in coarse if x.Price < 50 and x.DollarVolume > 1000000]
return [x.Symbol for x in myuniverse if x.Price < 50
and x.HasFundamentalData][:2000]
def FineFilter(self, fine):
security_filter = [x for x in fine if x.EarningReports.DilutedEPS.Value > .1
and x.ValuationRatios.PERatio < 25
and x.OperationRatios.RevenueGrowth.ThreeMonths > .05
and x.MarketCap > 100000000
and x.OperationRatios.ROA.ThreeMonths > .02
and x.OperationRatios.ROE.ThreeMonths > .03
and x.EarningRatios.DilutedEPSGrowth.ThreeMonths > .03
and x.OperationRatios.NetMargin.ThreeMonths > .08
and x.ValuationRatios.PSRatio < 1.5]
sorting = sorted(security_filter, key = lambda x: x.ValuationRatios.PSRatio, reverse=False)
self.Log(str([x.OperationRatios.RevenueGrowth.ThreeMonths for x in sorting[:50]]))
self.Log(str([x.MarketCap for x in sorting[:50]]))
self.Log(str([x.OperationRatios.ROE.ThreeMonths for x in sorting[:50]]))
self.Log(str([x.OperationRatios.ROA.ThreeMonths for x in sorting[:50]]))
self.Log(str([x.EarningRatios.DilutedEPSGrowth.ThreeMonths for x in sorting[:50]]))
self.Log(str([x.OperationRatios.NetMargin.ThreeMonths for x in sorting[:50]]))
self.Log(str([x.ValuationRatios.PSRatio for x in sorting[:50]]))
return [x.Symbol for x in sorting[:50]]
def OnSecuritiesChanged(self, changes):
# close positions in removed securities
for x in changes.RemovedSecurities:
self.Liquidate(x.Symbol)
if x.Symbol in self.activeStocks:
self.activeStocks.remove(x.Symbol)
# can't open positions here since data might not be added correctly yet
for x in changes.AddedSecurities:
self.activeStocks.add(x.Symbol)
# adjust targets if universe has changed
self.portfolioTargets = [PortfolioTarget(symbol, 1/len(self.activeStocks))
for symbol in self.activeStocks]
def OnData(self, data):
if self.portfolioTargets == []:
return
for symbol in self.activeStocks:
if symbol not in data:
return
self.SetHoldings(self.portfolioTargets)
# self.portfolioTargets = []
#Riskmodel not working properly
class MyPortfolioModel(EqualWeightingPortfolioConstructionModel):
def __init__(self):
pass
def CreateTargets(self, algorithm, insights):
# Simple insight weighting PCM
targets = []
for insight in insights:
targ = PortfolioTarget(insight.Symbol, insight.Direction*insight.Weight)
targets.append(targ)
return targets
class MyRiskModel(RiskManagementModel):
def __init__(self, maxDrawdown=.21):
self.maxDrawdown = maxDrawdown
self.liquidatedSymbols = set() # Tracks symbols that have been liquidated
self.currentTargets = [] # Tracks state of current targets
def ManageRisk(self, algorithm, targets):
# Reset trackers on new targets
if (set(targets) != self.currentTargets) and len(targets)>0:
algorithm.Log(f'New Targets. Quantity: {targets[0].Quantity}')
self.liquidatedSymbols = set()
self.currentTargets = set(targets)
riskAdjustedTargets = []
for _ in algorithm.Securities:
symbol = _.Key # Symbol object
security = _.Value # Security object
ticker = symbol.Value # String ticker
symbolPnL = security.Holdings.UnrealizedProfitPercent # Current PnL
#Liquidate if exceed drawdown
if (symbolPnL < -self.maxDrawdown) or (ticker in self.liquidatedSymbols):
riskAdjustedTargets.append(PortfolioTarget(symbol, 0))
if algorithm.Securities[symbol].Invested:
self.liquidatedSymbols.add(ticker)
algorithm.Log(f'Trailing stop loss triggered for {ticker}.')
return riskAdjustedTargets
class LongOnlyConstantAlphaCreationModel(AlphaModel):
'''
Description:
This Alpha model creates InsightDirection.Up (to go Long) for a duration of 1 day, every day for all active securities in our Universe
Details:
The important thing to understand here is the concept of Insight:
- A prediction about the future of the security, indicating an expected Up, Down or Flat move
- This prediction has an expiration time/date, meaning we think the insight holds for some amount of time
- In the case of a constant long-only strategy, we are just updating every day the Up prediction for another extra day
- In other words, every day we are making the conscious decision of staying invested in the security one more day
'''
def __init__(self, resolution = Resolution.Daily):
self.insightExpiry = Time.Multiply(Extensions.ToTimeSpan(resolution), 0.25) # insight duration
self.insightDirection = InsightDirection.Up # insight direction
self.securities = [] # list to store securities to consider
def Update(self, algorithm, data):
insights = [] # list to store the new insights to be created
# loop through securities and generate insights
for security in self.securities:
# check if there's new data for the security or we're already invested
# if there's no new data but we're invested, we keep updating the insight since we don't really need to place orders
if data.ContainsKey(security.Symbol) or algorithm.Portfolio[security.Symbol].Invested:
# append the insights list with the prediction for each symbol
insights.append(Insight.Price(security.Symbol, self.insightExpiry, self.insightDirection))
else:
algorithm.Log('excluding this security due to missing data: ' + str(security.Symbol.Value))
return insights
def OnSecuritiesChanged(self, algorithm, changes):
'''
Description:
Event fired each time the we add/remove securities from the data feed
Args:
algorithm: The algorithm instance that experienced the change in securities
changes: The security additions and removals from the algorithm
'''
# add new securities
for added in changes.AddedSecurities:
self.securities.append(added)
# remove securities
for removed in changes.RemovedSecurities:
if removed in self.securities:
self.securities.remove(removed)