Overall Statistics |
Total Orders 9126 Average Win 0.08% Average Loss -0.08% Compounding Annual Return 17.979% Drawdown 37.200% Expectancy 0.590 Start Equity 1000000 End Equity 11532669.83 Net Profit 1053.267% Sharpe Ratio 0.755 Sortino Ratio 0.81 Probabilistic Sharpe Ratio 19.145% Loss Rate 18% Win Rate 82% Profit-Loss Ratio 0.93 Alpha 0.026 Beta 1.006 Annual Standard Deviation 0.156 Annual Variance 0.024 Information Ratio 0.429 Tracking Error 0.063 Treynor Ratio 0.117 Total Fees $16826.95 Estimated Strategy Capacity $690000000.00 Lowest Capacity Asset BRKB R735QTJ8XC9X Portfolio Turnover 1.05% |
# region imports from AlgorithmImports import * # endregion class MaintainHistoricalDailyUniversePriceDataAlgorithm(QCAlgorithm): def initialize(self): self.set_start_date(2010, 2, 1) #self.set_end_date(2020, 3, 1) self.set_cash(1_000_000) # Add a universe of daily data. self.universe_settings.resolution = Resolution.DAILY self._universe = self.add_universe( lambda fundamentals: [f.symbol for f in sorted(fundamentals, key=lambda f: f.market_cap)[-10:]] ) # Create a DataFrame to store the historical data. self._all_history = pd.DataFrame() # Define the lookback period. self._lookback = 252 # Trading days. # Create a Scheduled Event to record new daily prices and # rebelance the portfolio. spy = Symbol.create('SPY', SecurityType.EQUITY, Market.USA) self.schedule.on( self.date_rules.every_day(spy), self.time_rules.at(0, 1), # One minute after `on_securities_changed` runs (in backtests) self._rebalance ) def on_securities_changed(self, changes): # Remove the historical prices of assets that leave the universe. for security in changes.removed_securities: if security.symbol in self._all_history.columns: self._all_history.drop(security.symbol, axis=1, inplace=True) # Warm-up the historical data of assets that enter the universe. history = self.history( [security.symbol for security in changes.added_securities], self._lookback, Resolution.DAILY ) if not history.empty: # If you trade at market open, it might make more sense to generate signals # based on daily opening prices. self._all_history = self._all_history.join(history.open.unstack(0), how='outer') def _rebalance(self): # Add yesterday's open price to the DataFrame of historical prices. self._all_history = pd.concat([ self._all_history, self.history(list(self._universe.selected), 1, Resolution.DAILY).open.unstack(0) ]) # Yesterday's open price has been added twice for assets that just entered the universe. # The first time was in `on_securities_changed` and the second time was in the line above. # Let's 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:] history = self._all_history[[symbol for symbol in self._all_history.columns if self.securities[symbol].price]] # Calculate asset signals for this rebalance. # For example, set the signal to give greater weight to uncorrelated assets. signal_by_symbol = 1/history.dropna(axis=1).corr().abs().sum() signal_by_symbol /= signal_by_symbol.sum() # Rebalance the portfolio based on the signals. self.set_holdings([PortfolioTarget(symbol, signal) for symbol, signal in signal_by_symbol.items()], True)