Overall Statistics |
Total Orders 2412 Average Win 0.80% Average Loss -1.04% Compounding Annual Return 1.528% Drawdown 76.800% Expectancy 0.039 Start Equity 10000000 End Equity 11110901.04 Net Profit 11.109% Sharpe Ratio 0.167 Sortino Ratio 0.185 Probabilistic Sharpe Ratio 0.685% Loss Rate 41% Win Rate 59% Profit-Loss Ratio 0.77 Alpha 0.015 Beta 0.602 Annual Standard Deviation 0.394 Annual Variance 0.155 Information Ratio -0.048 Tracking Error 0.387 Treynor Ratio 0.109 Total Fees $858586.37 Estimated Strategy Capacity $9300000.00 Lowest Capacity Asset NOVA X6G7QZUBDY05 Portfolio Turnover 8.32% |
# region imports from AlgorithmImports import * import itertools # endregion class VirtualYellowGreenLlama(QCAlgorithm): _blocked_assets = [ 'AMMA WEHWVFZT6VZ9', # Halted for several months in 2020, causes trading issues. See https://finance.yahoo.com/news/nasdaq-halts-scworx-corp-135720862.html and https://www.stocktitan.net/news/WORX/sc-worx-to-resume-trading-on-nasdaq-monday-august-10-doocrwwrw9f6.html 'PAY T8834TOLIRFP', # https://www.quantconnect.com/datasets/issue/16989 'HCP R735QTJ8XC9X', # https://www.quantconnect.com/datasets/issue/17042 'TOPT T0KDYN9C3IHX', # https://www.quantconnect.com/datasets/issue/15180 'VSCI R735QTJ8XC9X' # https://www.quantconnect.com/datasets/issue/18448 ] def initialize(self): self.set_start_date(2018, 1, 1) self.set_cash(10_000_000) self.settings.automatic_indicator_warm_up = True self._universe_size = 1_000 self._beta_period = self.get_parameter('beta_period', 6) * 21 self._assets_per_industry = self.get_parameter('assets_per_industry', 3) self._spy = self.add_equity('SPY', Resolution.DAILY) self.universe_settings.resolution = Resolution.DAILY self.universe_settings.schedule.on(self.date_rules.week_start(self._spy.symbol)) self._universe = self.add_universe(self._select_assets) self.schedule.on(self.date_rules.week_start(self._spy.symbol), self.time_rules.at(0, 1), self._rebalance) def _select_assets(self, fundamentals): #return [f.symbol for f in fundamentals if f.asset_classification.morningstar_industry_code and str(f.symbol.id) not in self._blocked_assets] # Narrow the universe to the most liquid for now and remove blocked assets. return [f.symbol for f in sorted([f for f in fundamentals if f.asset_classification.morningstar_industry_code and str(f.symbol.id) not in self._blocked_assets], key=lambda f: f.dollar_volume)[-self._universe_size:]] def on_securities_changed(self, changes): # Create a Beta indicator for each asset that enters the universe. for security in changes.added_securities: security.beta = self.b(security.symbol, self._spy.symbol, self._beta_period) def _rebalance(self): # Select securities with negative beta and sort them by their beta values. securities = [self.securities[symbol] for symbol in self._universe.selected] self.plot('Securities', 'Have a Price', len([s for s in securities if s.price])) self.plot('Securities', 'Beta is Ready', len([s for s in securities if s.beta.is_ready])) self.plot('Securities', 'Beta is Negative', len([s for s in securities if s.beta.current.value < 0])) securities = sorted([s for s in securities if s.price and s.beta.is_ready and s.beta.current.value < 0], key=lambda s: (s.fundamentals.asset_classification.morningstar_industry_code, s.beta.current.value)) # Group assets by their industry. securities_by_industry = { industry_code: list(industry_securities)[:self._assets_per_industry] # Select the assets with the lowest beta values in this industry. for industry_code, industry_securities in itertools.groupby(securities, lambda s: s.fundamentals.asset_classification.morningstar_industry_code) } # Give an equal weight to each industry and give an equal weight to each asset in each industry. targets = [] for industry_securities in securities_by_industry.values(): for industry_security in industry_securities: targets.append(PortfolioTarget(industry_security.symbol, 1/len(securities_by_industry)/len(industry_securities))) # Rebalance the portfolio. self.set_holdings(targets, True)