Overall Statistics |
Total Orders 898 Average Win 0.55% Average Loss -0.21% Compounding Annual Return 31.414% Drawdown 42.200% Expectancy 1.498 Start Equity 100000 End Equity 384958.61 Net Profit 284.959% Sharpe Ratio 0.875 Sortino Ratio 0.957 Probabilistic Sharpe Ratio 37.251% Loss Rate 32% Win Rate 68% Profit-Loss Ratio 2.66 Alpha 0.106 Beta 1.23 Annual Standard Deviation 0.252 Annual Variance 0.063 Information Ratio 0.926 Tracking Error 0.137 Treynor Ratio 0.179 Total Fees $1095.04 Estimated Strategy Capacity $420000000.00 Lowest Capacity Asset COST R735QTJ8XC9X Portfolio Turnover 4.13% |
# region imports from AlgorithmImports import * import math # endregion class DeterminedGreenKangaroo(QCAlgorithm): def initialize(self): # self.set_start_date(2007, 1, 2) # self.set_start_date(2008, 4, 2) # self.set_start_date(2009, 7, 2) self.set_start_date(2020, 1, 2) # self.set_start_date(2022, 1, 2) # self.set_start_date(2024, 1, 2) # self.set_start_date(2024, 10, 2) self.set_cash(100000) self.universe_settings.resolution = Resolution.Daily # Hour # self._universe = self.add_universe(self.universe.dollar_volume.top(100)) self.creamOnTop = 10 # self.min_portfolio_size = 20 self._portfolio = [] self.vwap_size = 1 self.ema_size = 63 # int(3 * 6.5) # 5, 21, 63, 252 self.set_warmup(2 * self.ema_size) self.expected_value_threshold = 1 # 1 + (0.96 - 1) / (252 * 6.5) # self.settings.free_portfolio_value_percentage = 0.1 # # https://www.quantconnect.com/docs/v2/writing-algorithms/trading-and-orders/position-sizing self.buy_spy = True self.spy_symbol = self.add_equity("SPY", Resolution.DAILY).symbol self.portfolio_target_len = self.creamOnTop def on_securities_changed(self, changes: SecurityChanges): for security in changes.added_securities: # indicator outline # rocp_of_vwap = rocp( vwap ) # i.e. how fast the price is changing # ema_of_rocp = ema( rocp_of_vwap) # i.e. exponential moving average # ema_of_roc = ema( roc( rocp_of_vwap)) # i.e. how fast the price is accelerating # ema_of_std = sqrt( ema( square( minus( rocp_of_vwap, ema_of_rocp )))) # i.e. exponential moving average of standard deviation of rocp_of_vwap vwap = self.vwap(security.symbol, self.vwap_size) rocp_of_vwap = IndicatorExtensions.of( RateOfChangePercent(1), vwap) security.ema_of_rocp = IndicatorExtensions.of( ExponentialMovingAverage(self.ema_size), rocp_of_vwap) roc_of_rocp = IndicatorExtensions.of( RateOfChange(1), rocp_of_vwap) # security.ema_of_roc = IndicatorExtensions.of( # ExponentialMovingAverage(self.ema_size), roc_of_rocp) # security.ema_of_std = 0 # sqrt( ema( square( minus( rocp_of_vwap, ema_of_rocp )))) rocp_minus_ema = IndicatorExtensions.minus(rocp_of_vwap, security.ema_of_rocp) square_of_minus = IndicatorExtensions.times(rocp_minus_ema, rocp_minus_ema) ema_of_square = IndicatorExtensions.of( ExponentialMovingAverage(self.ema_size), square_of_minus) security.ema_of_variance = ema_of_square security.initialize = True for security in changes.removed_securities: if security.Invested: self.Liquidate(security.Symbol) if security.Symbol in self._portfolio: self._portfolio.remove(security.Symbol) def on_data(self, data: Slice): if not self.is_warming_up and data.bars: cash = self.portfolio.cash price = data[self.spy_symbol].price shares = math.floor(cash / price) self.market_order("SPY", shares) securities = [self.securities[symbol] for symbol in self._universe.selected] currentValues = {} for security in securities: conditions = [] conditions.append(security.ema_of_rocp.is_ready) # conditions.append(security.ema_of_roc.is_ready) conditions.append(security.ema_of_variance.is_ready) if all(conditions): ema_of_rocp = security.ema_of_rocp.Current.Value # ema_of_roc = security.ema_of_roc.Current.Value ema_of_variance = security.ema_of_variance.Current.Value ema_of_standard_deviation = math.sqrt(ema_of_variance) uncertainty_of_rocp = 1.14 * ema_of_standard_deviation / self.ema_size # if security.initialize and ema_of_rocp >= 0: # if security.Symbol not in self._portfolio: # self._portfolio.append(security.Symbol) # if ema_of_rocp <= 0 and ema_of_rocp + ema_of_roc > 0: # if security.Symbol not in self._portfolio: # self._portfolio.append(security.Symbol) # if ema_of_rocp >= 0 and ema_of_rocp + ema_of_roc < 0: # if security.Symbol in self._portfolio: # self._portfolio.remove(security.Symbol) # if ema_of_rocp > uncertainty_of_rocp: # if security.Symbol not in self._portfolio: # self._portfolio.append(security.Symbol) # if ema_of_rocp < uncertainty_of_rocp: # if security.Symbol in self._portfolio: # self._portfolio.remove(security.Symbol) # currentValues[security.Symbol] = ema_of_rocp / uncertainty_of_rocp next_step = ema_of_rocp - 2 * uncertainty_of_rocp #+ ema_of_roc rocp_fraction = 1 + next_step / 100 log_rocp_fraction = np.log(rocp_fraction) std_fraction = 1 + (next_step + ema_of_standard_deviation) / 100 rocp_std = log_rocp_fraction - np.log(std_fraction) expected_value = np.exp(log_rocp_fraction + rocp_std **2 / 2) currentValues[security.Symbol] = expected_value currentValues = dict(sorted(currentValues.items(), key=lambda item: item[1])) current_value_keys = list(currentValues.keys()) # current_value_keys.reverse() notCream = len(currentValues) - self.creamOnTop ranks = {} for symbol in current_value_keys[:notCream]: # self.SetHoldings(symbol, 0) if symbol in self._portfolio: self._portfolio.remove(symbol) for n, symbol in enumerate(current_value_keys[notCream:]): ranks[symbol] = n if symbol not in self._portfolio and symbol != self.spy_symbol: self._portfolio.append(symbol) if currentValues[symbol] < self.expected_value_threshold: if symbol in self._portfolio: self._portfolio.remove(symbol) self.Plot("len(self._portfolio)", "len", len(self._portfolio)) # if len(self._portfolio) > 0: # # cash_on_hand = 0.5 / self.creamOnTop # percent of portfolio to remain cash # # gld_weight = 0 # # if len(self._portfolio) < self.min_portfolio_size: # # gld_weight = self.min_portfolio_size - len(self._portfolio) # # symbol_weight = (1 - cash_on_hand) / (len(self._portfolio) )#+ gld_weight) # # gld_weight = (1 - cash_on_hand) * gld_weight / self.min_portfolio_size # # if len(self._portfolio) < self.min_portfolio_size: # # pass # # symbol_weight = (1 - cash_on_hand) / self.creamOnTop # portfolio_targets = [] # for symbol in self._portfolio: # # self.SetHoldings(symbol, 1 / self.creamOnTop) # # symbol_weight = 2 * ranks[symbol] / self.creamOnTop ** 2 # # self.SetHoldings(symbol, symbol_weight) # portfolio_targets.append(PortfolioTarget(symbol, 1 / self.creamOnTop)) # # portfolio_targets.append(PortfolioTarget(symbol, symbol_weight)) # # self.SetHoldings("GLD", gld_weight) # self.set_holdings(portfolio_targets) spy_price = self.portfolio["SPY"].price spy_holding = self.portfolio["SPY"].quantity * spy_price if len(self._portfolio) > 0: for symbol in self._portfolio: already_invested = self.portfolio[symbol].quantity > 0 if not already_invested: value = self.portfolio.total_portfolio_value target = value / self.portfolio_target_len if spy_holding > target and symbol in data.keys(): sell_spy = math.floor(target / spy_price) self.market_order("SPY", -sell_spy) cash_value = sell_spy * spy_price spy_holding -= cash_value buy_symbol = math.floor(target / data[symbol].price) self.market_order(symbol, buy_symbol) for symbol in self.portfolio.keys(): if symbol not in self._portfolio and symbol != self.spy_symbol: if self.portfolio[symbol].holdings_cost < self.portfolio[symbol].holdings_value: quantity = self.portfolio[symbol].quantity self.market_order(symbol, -quantity) cash_value = quantity * self.portfolio[symbol].price buy_spy = math.floor(cash_value / data[self.spy_symbol].price) self.market_order("SPY", buy_spy) cash_value = buy_spy * spy_price spy_holding += cash_value # vwap # https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/supported-indicators/volume-weighted-average-price-indicator # rocp # https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/supported-indicators/rate-of-change-percent # ema # https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/supported-indicators/exponential-moving-average # Combining Indicators # https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/combining-indicators # weighted standard deviation # https://www.itl.nist.gov/div898/software/dataplot/refman2/ch2/weightsd.pdf