Overall Statistics |
Total Orders 7706 Average Win 0.18% Average Loss -0.18% Compounding Annual Return -32.492% Drawdown 82.200% Expectancy -0.191 Start Equity 10000000 End Equity 1881810.63 Net Profit -81.182% Sharpe Ratio -1.965 Sortino Ratio -1.962 Probabilistic Sharpe Ratio 0.000% Loss Rate 59% Win Rate 41% Profit-Loss Ratio 0.96 Alpha -0.256 Beta -0.145 Annual Standard Deviation 0.134 Annual Variance 0.018 Information Ratio -1.557 Tracking Error 0.205 Treynor Ratio 1.822 Total Fees $535534.61 Estimated Strategy Capacity $28000.00 Lowest Capacity Asset AFRI XZ72L9APSVOL Portfolio Turnover 6.86% |
# region imports from AlgorithmImports import * # endregion class GeekyAsparagusViper(QCAlgorithm): def initialize(self): self.set_start_date(2021, 1, 1) self.set_end_date(2025, 4, 1) self.set_cash(10_000_000) # Set some parameter values. self._universe_size = self.get_parameter('universe_size', 100) # Add a universe of the 100 smallest assets in IWM. etf = Symbol.create('IWM', SecurityType.EQUITY, Market.USA) date_rule = self.date_rules.week_start(etf, 1) self.universe_settings.schedule.on(date_rule) self._week = 0 self._universe = self.add_universe(self.universe.etf(etf, universe_filter_func=self._select_assets)) # Rebalance the portfolio every 2 weeks. self.schedule.on(date_rule, self.time_rules.after_market_open(etf, 1), self._rebalance) def _select_assets(self, constituents): # Only update the universe every 2 weeks. week = self.time.isocalendar()[1] if abs(week - self._week) < 2: return [] self._week = week # Select the 100 smallest constituents. symbols = [c.symbol for c in sorted(constituents, key=lambda c: c.weight)[:self._universe_size]] # Calculate factors for all the assets. history = self.history(symbols, timedelta(1), Resolution.MINUTE) factors = pd.DataFrame(columns=['price_volatility', 'volume_volatility', 'vwap_deviation'], index=symbols) for symbol in symbols: if symbol not in history.index: continue df = history.loc[symbol][['close', 'volume']] df['vwap'] = self.indicator_history(IntradayVwap(''), symbol, timedelta(1)).data_frame.current.reindex(df.index).ffill() factors.loc[symbol] = [ -df.close.std() / df.close.mean(), # price_volatility df.volume.std() / df.volume.mean(), # volume_volatility -np.mean(np.abs(df.close - df.vwap)) / df.vwap.mean() # vwap_deviation ] factors.dropna(inplace=True) # Split assets into long/short groups based on factor values. sorted_by_factors = list(factors.rank().sum(axis=1).sort_values().index) assets_per_side = int(len(sorted_by_factors)/2) self._longs = sorted_by_factors[-assets_per_side:] self._shorts = sorted_by_factors[:assets_per_side] return self._longs + self._shorts def _rebalance(self): if not self._longs: return self.set_holdings( [PortfolioTarget(s, 0.5/len(self._longs)) for s in self._longs] + [PortfolioTarget(s, -0.5/len(self._shorts)) for s in self._shorts], True ) self._longs = [] self._shorts = []