| Overall Statistics |
|
Total Trades 163 Average Win 0.73% Average Loss -0.62% Compounding Annual Return 32.810% Drawdown 11.400% Expectancy 0.761 Net Profit 79.428% Sharpe Ratio 1.517 Probabilistic Sharpe Ratio 73.538% Loss Rate 20% Win Rate 80% Profit-Loss Ratio 1.19 Alpha 0 Beta 0 Annual Standard Deviation 0.152 Annual Variance 0.023 Information Ratio 1.517 Tracking Error 0.152 Treynor Ratio 0 Total Fees $163.00 Estimated Strategy Capacity $100000000.00 Lowest Capacity Asset TSM 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.15):
# '''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(2022,1,1)
self.SetCash(25000)
self.rebalanceTime = self.Time
#self.rebalanceTime = datetime.min
self.activeStocks = set()
#self.AddEquity("SPY", Resolution.Daily)
self.AddRiskManagement(MaximumDrawdownPercentPerSecurity())
self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Cash)
self.VixNegative = True
self.UniverseSettings.Resolution = Resolution.Daily
self.AddUniverse(self.CoarseFilter, self.FineFilter)
#VIX block
self.previous = 0.0
self.lastTrade = datetime(1, 1, 1)
self.cboeVix = self.AddData(CBOE, "VIX").Symbol
self.portfolioTargets = []
#price_plot = Chart('My Plot') #name of plot => what chart object
#price_plot.AddSeries(Series('Portfolio', SeriesType.Candle, 0)) #one tab with daily prices => what series connected to
#price_plot.AddSeries(Series('Monthly Open Price', SeriesType.Candle,1)) # the second series it is connected to
#self.AddChart(price_plot) #two with monthly prices
#Parameters
timedelta = self.GetParameter("timedelta")
FinalSharesAmount = self.GetParameter("FinalSharesAmount")
MarketCap = self.GetParameter("MarketCap")
self.PRAM_timedelta = 6 #if timedelta is None else int(timedelta)
self.PRAM_FinalSharesAmount = 10# if sortedByPE is None else int(FinalSharesAmount)
self.PRAM_MarketCap = 100000000 #if MarketCap is None else int(MarketCap)
def CoarseFilter(self, coarse):
# Rebalancing monthly
if self.Time <= self.rebalanceTime:
return self.Universe.Unchanged
self.rebalanceTime = timedelta(self.PRAM_timedelta)+ self.Time
sortedByDollarVolume = sorted(coarse, key=lambda x: x.DollarVolume, reverse=True)
return [x.Symbol for x in sortedByDollarVolume if x.Price > 10
and x.HasFundamentalData][:100]
def FineFilter(self, fine):
sortedByPE = sorted(fine, key=lambda x: x.MarketCap, reverse=True)
return [x.Symbol for x in sortedByPE if x.MarketCap > self.PRAM_MarketCap][:self.PRAM_FinalSharesAmount]
def OnSecuritiesChanged(self, changes):
# close positions in removed securities
if self.VixNegative is False:
return
for x in changes.RemovedSecurities:
self.Liquidate(x.Symbol)
self.activeStocks.remove(x.Symbol)
self.lastTrade = self.Time
# can't open positions here since data might not be added correctly yet
for x in changes.AddedSecurities:
self.activeStocks.add(x.Symbol)
self.lastTrade = self.Time
# adjust targets if universe has changed
if self.SetRiskManagement is True:
self.SetHoldings(-1)
self.lastTrade = self.Time
else:
self.portfolioTargets = [PortfolioTarget(symbol, 1/self.PRAM_FinalSharesAmount)#len(self.activeStocks))
for symbol in self.activeStocks]
self.lastTrade = self.Time
def OnData(self, data):
# if self.Portfolio.Invested:
# self.Plot('My Plot','My Portfolio',self.activeStocks.Open)
# if self.Time.day == 1: #if its the first of the month ( monthly forst day)
# self.Plot('My Plot','SPY',self.Securities['SPY'].Open)
if not data.ContainsKey(self.cboeVix):
return
vix = data.Get(CBOE, self.cboeVix)
current = vix.Close
if self.previous != 0:
# Calculate the percentage, and if it is greater than 10%
percentageChange = ((current - self.previous) / self.previous)-1
if percentageChange < 0.01:
self.VixNegative is True
self.lastTrade = self.Time
if percentageChange > 0.1:
self.Liquidate()
self.VixNegative is False
self.lastTrade = self.Time
if self.portfolioTargets == []:
return
for symbol in self.activeStocks:
if symbol not in data:
return
self.SetHoldings(self.portfolioTargets)
self.portfolioTargets = []
self.previous = vix.Close
def OnOrderEvent(self, orderEvent):
if orderEvent.Status == OrderStatus.Filled:
self.Debug(f'Processed Order: {orderEvent.Symbol}, Quantity: {orderEvent.FillQuantity}' )