| Overall Statistics |
|
Total Orders 187 Average Win 0.96% Average Loss -0.68% Compounding Annual Return 15.412% Drawdown 20.400% Expectancy 0.477 Start Equity 100000 End Equity 115503.18 Net Profit 15.503% Sharpe Ratio 0.34 Sortino Ratio 0.437 Probabilistic Sharpe Ratio 28.752% Loss Rate 39% Win Rate 61% Profit-Loss Ratio 1.42 Alpha -0.031 Beta 1.671 Annual Standard Deviation 0.24 Annual Variance 0.058 Information Ratio 0.076 Tracking Error 0.184 Treynor Ratio 0.049 Total Fees $226.78 Estimated Strategy Capacity $200000000.00 Lowest Capacity Asset GME SC72NCBXXAHX Portfolio Turnover 4.31% |
# region imports
from AlgorithmImports import *
# endregion
class HipsterAsparagusDinosaur(QCAlgorithm):
def initialize(self):
self.set_start_date(2023, 8, 5)
self.set_end_date(2024, 8, 5)
self.set_cash(100000)
self.rebalanceTime = datetime.min #rebalnace time has been set to most recent time to start trading immediately
self.activeStocks = set()
self.add_universe(self.CoarseFilter, self.FineFilter)
#default resolution is min
self.universe_settings.resolution = Resolution.DAILY
self.portfolioTargets = []
def CoarseFilter(self, coarse):
if self.time < self.rebalanceTime:
return self.universe.unchanged
self.rebalanceTime = self.time + timedelta(30) #timedelta of 30 days include weekends and hence adds 1 month to current time
sortedByDollarVolume = sorted(coarse, key = lambda x: x.dollar_volume, reverse=True)
#25 here is completely arbitrary choice as number of stocks from coarse filter
return [x.symbol for x in sortedByDollarVolume if x.price > 10 and x.has_fundamental_data][:25]
def FineFilter(self, fine):
sortedByPE = sorted(fine, key = lambda x: x.market_cap)
#if market cap is missing for a security its default value is zero, we want to filter these too
return [x.symbol for x in sortedByPE if x.market_cap > 0][:10]
def on_securities_changed(self, changes):
for x in changes.removed_securities:
self.liquidate(x.symbol)
self.activeStocks.remove(x.symbol)
for x in changes.added_securities:
self.activeStocks.add(x.symbol)
#we create a portfolio target object--> PortfolioTarget is a class
self.portfolioTargets = [PortfolioTarget(symbol, float(1/len(self.activeStocks))) for symbol in self.activeStocks]
def on_data(self, data: Slice): #here we are just rebalancing
if self.portfolioTargets == []: #portfoliotarget is unchanged and there is nothing to rebalance
return
#we check if each symbol has data
for symbol in self.activeStocks:
if symbol not in data:
return
self.set_holdings(self.portfolioTargets)
self.portfolioTargets = [] #so that we don't try to rebalance before universe changes again