| Overall Statistics |
|
Total Trades 395 Average Win 2.49% Average Loss -1.70% Compounding Annual Return 174.031% Drawdown 50.800% Expectancy 0.441 Net Profit 174.788% Sharpe Ratio 2.184 Probabilistic Sharpe Ratio 65.137% Loss Rate 42% Win Rate 58% Profit-Loss Ratio 1.46 Alpha 1.565 Beta 0.282 Annual Standard Deviation 0.738 Annual Variance 0.545 Information Ratio 1.899 Tracking Error 0.761 Treynor Ratio 5.718 Total Fees $27044.98 Estimated Strategy Capacity $0 Lowest Capacity Asset AMS R735QTJ8XC9X |
from AlgorithmImports import *
class MaximumDrawdownPercentPerSecurity(RiskManagementModel):
'''Provides an implementation of IRiskManagementModel that limits the drawdown per holding to the specified percentage'''
def __init__(self, maximumDrawdownPercent = 0.05):
# '''Initializes a new instance of the MaximumDrawdownPercentPerSecurity class
# Args:
# maximumDrawdownPercent: The maximum percentage drawdown allowed for any single security holding'''
self.maximumDrawdownPercent = -abs(maximumDrawdownPercent)
def ManageRisk(self, algorithm, targets):
'''Manages the algorithm's risk at each time step
Args:
algorithm: The algorithm instance
targets: The current portfolio targets to be assessed for risk'''
targets = []
for kvp in algorithm.Securities:
security = kvp.Value
if not security.Invested:
continue
pnl = security.Holdings.UnrealizedProfitPercent
if pnl < self.maximumDrawdownPercent:
# liquidate
targets.append(PortfolioTarget(security.Symbol, 0))
return targets
from AlgorithmImports import *
from MaximumDrawdownPercentPerSecurity import MaximumDrawdownPercentPerSecurity
class WellDressedSkyBlueSardine(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 1, 1)
self.SetEndDate(2020,12, 31)
self.SetCash(100000)
self.rebalanceTime = datetime.min
self.activeStocks = set()
self.AddUniverse(self.CoarseFilter, self.FineFilter)
self.UniverseSettings.Resolution = Resolution.Daily
#self.AddRiskManagement(MaximumDrawdownPercentPerSecurity(self))
self.portfolioTargets = []
#Parameters
timedelta = self.GetParameter("timedelta")
sortedByPE = self.GetParameter("sortedByPE")
MarketCap = self.GetParameter("MarketCap")
self.PRAM_timedelta = 16 if timedelta is None else int(timedelta)
self.PRAM_sortedByPE = 8 if sortedByPE is None else int(sortedByPE)
self.PRAM_MarketCap = 10000000 if MarketCap is None else int(MarketCap)
def CoarseFilter(self, coarse):
# Rebalancing monthly
if self.Time <= self.rebalanceTime:
return self.Universe.Unchanged
self.rebalanceTime = self.Time + timedelta(self.PRAM_timedelta)
sortedByDollarVolume = sorted(coarse, key=lambda x: x.DollarVolume, reverse=False)
return [x.Symbol for x in sortedByDollarVolume if x.Price < 10
and x.HasFundamentalData][:200]
def FineFilter(self, fine):
sortedByPE = sorted(fine, key=lambda x: x.MarketCap, reverse=False)
return [x.Symbol for x in sortedByPE if x.MarketCap > self.PRAM_MarketCap][:self.PRAM_sortedByPE]
def OnSecuritiesChanged(self, changes):
# close positions in removed securities
for x in changes.RemovedSecurities:
self.Liquidate(x.Symbol)
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)
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 = []