Overall Statistics |
Total Orders 2236 Average Win 0.19% Average Loss -0.17% Compounding Annual Return 0.511% Drawdown 13.600% Expectancy 0.038 Start Equity 1000000 End Equity 1058604.54 Net Profit 5.860% Sharpe Ratio -0.236 Sortino Ratio -0.256 Probabilistic Sharpe Ratio 0.016% Loss Rate 51% Win Rate 49% Profit-Loss Ratio 1.14 Alpha -0.009 Beta -0.009 Annual Standard Deviation 0.038 Annual Variance 0.001 Information Ratio -0.379 Tracking Error 0.169 Treynor Ratio 0.997 Total Fees $6163.04 Estimated Strategy Capacity $12000.00 Lowest Capacity Asset SSNT WJTX1OBQ409X Portfolio Turnover 0.26% |
#region imports from AlgorithmImports import * #endregion class NetCurrentAssetValue(QCAlgorithm): def initialize(self): #rebalancing should occur in July self.set_start_date(2007, 5, 15) self.set_end_date(2018, 7, 15) self.set_cash(1_000_000) self.settings.minimum_order_margin_portfolio_percentage = 0 self.universe_settings.resolution = Resolution.DAILY self._previous_fine = None self._filtered_fine = None self.add_universe(self._coarse_selection_function, self._fine_selection_function) spy = Symbol.create("SPY", SecurityType.EQUITY, Market.USA) #monthly scheduled event but will only rebalance once a year self.schedule.on(self.date_rules.month_start(spy), self.time_rules.at(23, 0), self._rebalance) self._months = -1 self._yearly_rebalance = False self.set_warmup(timedelta(365)) def _coarse_selection_function(self, coarse): if self._yearly_rebalance: # drop stocks which have no fundamental data return [x.symbol for x in coarse if x.has_fundamental_data and x.market == Market.USA] return [] def _fine_selection_function(self, fine): if self._yearly_rebalance: #filters out the companies that don't contain the necessary data fine = [x for x in fine if (float(x.financial_statements.balance_sheet.current_assets.value) > 0) and (float(x.financial_statements.balance_sheet.cash_and_cash_equivalents.value) > 0) and (float(x.financial_statements.balance_sheet.current_liabilities.value) > 0) and (float(x.financial_statements.balance_sheet.current_debt.value) > 0) and (float(x.financial_statements.balance_sheet.income_tax_payable.value) > 0) and (float(x.financial_statements.income_statement.depreciation_and_amortization.value) > 0)] if not self._previous_fine: #will wait one year in order to have the historical fundamental data self._previous_fine = fine self._yearly_rebalance = False return [] else: #will calculate and sort the balance sheet accruals self._filtered_fine = self._calculate_accruals(fine, self._previous_fine) sorted_filter = sorted(self._filtered_fine, key=lambda x: x.bs_acc) self._filtered_fine = [i.symbol for i in sorted_filter] #we save the fine data for the next year's analysis self._previous_fine = fine return self._filtered_fine else: return [] def _calculate_accruals(self, current, previous): accruals = [] for stock_data in current: #compares this and last year's fine fundamental objects try: prev_data = None for x in previous: if x.symbol == stock_data.symbol: prev_data = x break #calculates the balance sheet accruals and adds the property to the fine fundamental object delta_assets = float(stock_data.financial_statements.balance_sheet.current_assets.value)-float(prev_data.financial_statements.balance_sheet.current_assets.value) delta_cash = float(stock_data.financial_statements.balance_sheet.cash_and_cash_equivalents.value)-float(prev_data.financial_statements.balance_sheet.cash_and_cash_equivalents.value) delta_liabilities = float(stock_data.financial_statements.balance_sheet.current_liabilities.value)-float(prev_data.financial_statements.balance_sheet.current_liabilities.value) delta_debt = float(stock_data.financial_statements.balance_sheet.current_debt.value)-float(prev_data.financial_statements.balance_sheet.current_debt.value) delta_tax = float(stock_data.financial_statements.balance_sheet.income_tax_payable.value)-float(prev_data.financial_statements.balance_sheet.income_tax_payable.value) dep = float(stock_data.financial_statements.income_statement.depreciation_and_amortization.value) avg_total = (float(stock_data.financial_statements.balance_sheet.total_assets.value)+float(prev_data.financial_statements.balance_sheet.total_assets.value))/2 #accounts for the size difference stock_data.bs_acc = ((delta_assets-delta_cash)-(delta_liabilities-delta_debt-delta_tax)-dep)/avg_total accruals.append(stock_data) except: #value in current universe does not exist in the previous universe pass return accruals def _rebalance(self): #yearly rebalance self._months += 1 if self._months % 12 == 0: self._yearly_rebalance = True def on_data(self, data): if not self._yearly_rebalance or self.is_warming_up: return if self._filtered_fine: filtered_fine = [s for s in self._filtered_fine if s in data] portfolio_size = int(len(filtered_fine)/10) #pick the upper decile to short and the lower decile to long targets = [] longs = filtered_fine[:portfolio_size] long_weight = 0.25/len(longs) for symbol in longs: targets.append(PortfolioTarget(symbol, long_weight)) shorts = filtered_fine[-portfolio_size:] short_weight = -0.25/len(shorts) for symbol in shorts: targets.append(PortfolioTarget(symbol, short_weight)) self.set_holdings(targets, True) self._yearly_rebalance = False self._filtered_fine = False