Overall Statistics |
Total Orders 29548 Average Win 0.10% Average Loss -0.10% Compounding Annual Return 2.261% Drawdown 30.000% Expectancy 0.014 Start Equity 10000000 End Equity 11680261.69 Net Profit 16.803% Sharpe Ratio 0.038 Sortino Ratio 0.043 Probabilistic Sharpe Ratio 0.446% Loss Rate 51% Win Rate 49% Profit-Loss Ratio 1.06 Alpha -0.05 Beta 0.682 Annual Standard Deviation 0.187 Annual Variance 0.035 Information Ratio -0.484 Tracking Error 0.16 Treynor Ratio 0.01 Total Fees $2179101.14 Estimated Strategy Capacity $0 Lowest Capacity Asset CCCR VJ1KRXBP79ET Portfolio Turnover 9.15% |
# 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 'IPDN VEN1SVFIVSKL', ] 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] securities = sorted([s for s in securities if s.price and s.beta.is_ready], key=lambda s: (s.fundamentals.asset_classification.morningstar_industry_code, s.beta.current.value)) # Group assets by their industry. trades_by_industry = {} for industry_code, industry_securities in itertools.groupby(securities, lambda s: s.fundamentals.asset_classification.morningstar_industry_code): industry_securities = list(industry_securities) positive_betas = [s for s in industry_securities if s.beta.current.value > 0] negative_betas = [s for s in industry_securities if s.beta.current.value < 0] assets_per_leg = min(len(positive_betas), len(negative_betas), self._assets_per_industry) if assets_per_leg: trades_by_industry[industry_code] = {'short': negative_betas[:assets_per_leg], 'long': positive_betas[-assets_per_leg:]} # Give an equal weight to each industry and give an equal weight to each asset in each industry. targets = [] for securities_by_trade_bias in trades_by_industry.values(): for trade_bias, industry_securities in securities_by_trade_bias.items(): weight = (1 if trade_bias == 'long' else -1) / len(trades_by_industry) / len(industry_securities) / 1.5 for security in industry_securities: targets.append(PortfolioTarget(security.symbol, weight)) # Rebalance the portfolio. self.set_holdings(targets, True)