Overall Statistics |
Total Orders 70028 Average Win 0.02% Average Loss -0.02% Compounding Annual Return 15.149% Drawdown 41.400% Expectancy 0.254 Start Equity 100000000 End Equity 813809136.46 Net Profit 713.809% Sharpe Ratio 0.593 Sortino Ratio 0.647 Probabilistic Sharpe Ratio 5.407% Loss Rate 36% Win Rate 64% Profit-Loss Ratio 0.95 Alpha 0.009 Beta 1.047 Annual Standard Deviation 0.172 Annual Variance 0.03 Information Ratio 0.148 Tracking Error 0.086 Treynor Ratio 0.097 Total Fees $8449395.39 Estimated Strategy Capacity $26000000.00 Lowest Capacity Asset WMT R735QTJ8XC9X Portfolio Turnover 6.68% |
# region imports from AlgorithmImports import * # endregion class SwimmingLightBrownGalago(QCAlgorithm): def initialize(self): self.set_start_date(2010, 1, 1) self.set_cash(100_000_000) self.settings.daily_precise_end_time = False self.settings.minimum_order_margin_portfolio_percentage = 0#.01 lookback_years = 12 # 12 years includes 3 non-leap years & 3 election cycles self._lookback = lookback_years * 252 self._lookback_calendar_time = timedelta(lookback_years*365) self._period = 21 # Trading days self.universe_settings.resolution = Resolution.DAILY self._universe = self.add_universe(self._select_uncorrelated_assets) spy = Symbol.create('SPY', SecurityType.EQUITY, Market.USA) self.schedule.on( self.date_rules.every_day(spy), self.time_rules.at(0, 1), self._rebalance ) self.set_portfolio_construction(InsightWeightingPortfolioConstructionModel(portfolio_bias=PortfolioBias.LONG)) self._all_history = pd.DataFrame() sectors = [ MorningstarSectorCode.BASIC_MATERIALS, MorningstarSectorCode.CONSUMER_CYCLICAL, MorningstarSectorCode.FINANCIAL_SERVICES, MorningstarSectorCode.REAL_ESTATE, MorningstarSectorCode.CONSUMER_DEFENSIVE, MorningstarSectorCode.HEALTHCARE, MorningstarSectorCode.UTILITIES, MorningstarSectorCode.COMMUNICATION_SERVICES, MorningstarSectorCode.ENERGY, MorningstarSectorCode.INDUSTRIALS, MorningstarSectorCode.TECHNOLOGY, ] self._sector = sectors[self.get_parameter('sector', 0)] self._universe_size = self.get_parameter('universe_size', 10) def _select_uncorrelated_assets(self, fundamentals): #return [Symbol.create('AMZN', SecurityType.EQUITY, Market.USA)] ## Select assets with sufficient history. selected = [f for f in fundamentals if f.symbol.id.date <= self.time - self._lookback_calendar_time] ## Select largest assets in the selected sector. selected = sorted( selected,#[f for f in selected if f.asset_classification.morningstar_sector_code == self._sector], key=lambda f: f.market_cap )[-self._universe_size:] return [f.symbol for f in selected] def on_securities_changed(self, changes): #self.insights.cancel([security.symbol for security in changes.removed_securities]) for security in changes.removed_securities: if security.symbol not in self._all_history.columns: continue self._all_history.drop(security.symbol, axis=1, inplace=True) history = self.history([security.symbol for security in changes.added_securities], self._lookback, Resolution.DAILY) if not history.empty: self._all_history = self._all_history.join(history.open.unstack(0), how='outer') def _rebalance(self): # Append new rows. self._all_history = pd.concat([ self._all_history, self.history(list(self._universe.selected), 1, Resolution.DAILY).open.unstack(0) ]) # Drop rows with duplicate indices, then trim to lookback window size. self._all_history = self._all_history.loc[~self._all_history.index.duplicated(keep='last')].iloc[-self._lookback:] # Calculate signal for this trade. period_returns = self._all_history.dropna(axis=1).pct_change(self._period).shift(-self._period).dropna() expected_return_by_day = period_returns.groupby(period_returns.index.strftime('%m-%d')).mean() signals = expected_return_by_day.iloc[ self._wrapped_indices( len(expected_return_by_day.index), expected_return_by_day.index.get_loc((self.time + timedelta(1)).strftime('%m-%d')), self._period ) ].sum() # Rebalance. self.emit_insights([Insight.price(symbol, timedelta(30), InsightDirection.UP, weight=1000*signal) for symbol, signal in signals.items() if signal > 0]) def _wrapped_indices(self, index_length, start_index, count): start_index = start_index % index_length indices = [(start_index + i) % index_length for i in range(count)] return indices