| Overall Statistics |
|
Total Orders 1978 Average Win 0.07% Average Loss -0.07% Compounding Annual Return -1.868% Drawdown 15.300% Expectancy -0.014 Start Equity 1000000 End Equity 910000.67 Net Profit -9.000% Sharpe Ratio -1.017 Sortino Ratio -0.955 Probabilistic Sharpe Ratio 0.064% Loss Rate 52% Win Rate 48% Profit-Loss Ratio 1.04 Alpha -0.047 Beta -0.013 Annual Standard Deviation 0.047 Annual Variance 0.002 Information Ratio -0.786 Tracking Error 0.151 Treynor Ratio 3.779 Total Fees $3625.46 Estimated Strategy Capacity $4000.00 Lowest Capacity Asset MHUA XW2TMLLH2VDX Portfolio Turnover 0.20% Drawdown Recovery 577 |
#region imports
from AlgorithmImports import *
#endregion
class NetCurrentAssetValue(QCAlgorithm):
def initialize(self):
self.set_start_date(self.end_date - timedelta(5*365))
self.set_cash(1_000_000)
self.settings.seed_initial_prices = True
self.settings.minimum_order_margin_portfolio_percentage = 0
self.universe_settings.resolution = Resolution.DAILY
self._rebalance_month = 6 # June
self._previous_accrual_factors_by_symbol = {}
self._sorted_assets = []
date_rule = self.date_rules.month_start('SPY')
self.add_universe(date_rule, self._select_assets)
# monthly scheduled event but will only rebalance once a year
self.schedule.on(date_rule, self.time_rules.midnight, self._rebalance)
self.set_warmup(timedelta(365))
def _select_assets(self, fundamentals):
if self.time.month != self._rebalance_month:
return []
# Filters out companies that don't contain the necessary data.
accrual_factors_by_symbol = {
f.symbol: AccrualFactors(f)
for f in fundamentals if AccrualFactors.are_available(f)
}
# Calculate and sort the balance sheet accruals.
score_by_symbol = {}
for symbol, previous_factors in self._previous_accrual_factors_by_symbol.items():
current_factors = accrual_factors_by_symbol.get(symbol, None)
if not current_factors:
continue
score_by_symbol[symbol] = AccrualFactors.calculate_score(previous_factors, current_factors)
self._sorted_assets = sorted(score_by_symbol, key=lambda symbol: score_by_symbol[symbol])
self._previous_accrual_factors_by_symbol = accrual_factors_by_symbol
return self._sorted_assets
def _rebalance(self):
if self.time.month != self._rebalance_month:
return
# Drop assets that don't have a price yet, to avoid trading errors.
self._sorted_assets = [symbol for symbol in self._sorted_assets if self.securities[symbol].price]
if not self._sorted_assets:
return
# Pick the upper decile to short and the lower decile to long
portfolio_size = int(len(self._sorted_assets)/10)
targets = self._create_targets(self._sorted_assets[:portfolio_size], 0.25)
targets += self._create_targets(self._sorted_assets[-portfolio_size:], -0.25)
self.set_holdings(targets, True)
def _create_targets(self, symbols, total_weight):
weight = total_weight / len(symbols)
return [PortfolioTarget(symbol, weight) for symbol in symbols]
class AccrualFactors:
def __init__(self, fundamental):
self.cash = fundamental.financial_statements.balance_sheet.cash_and_cash_equivalents.value
self.current_assets = fundamental.financial_statements.balance_sheet.current_assets.value
self.total_assets = fundamental.financial_statements.balance_sheet.total_assets.value
self.liabilities = fundamental.financial_statements.balance_sheet.current_liabilities.value
self.debt = fundamental.financial_statements.balance_sheet.current_debt.value
self.tax = fundamental.financial_statements.balance_sheet.income_tax_payable.value
self.depreciation = fundamental.financial_statements.income_statement.depreciation_and_amortization.value
@staticmethod
def are_available(fundamental):
return not any([
np.isnan(factor) for factor in [
fundamental.financial_statements.balance_sheet.cash_and_cash_equivalents.value,
fundamental.financial_statements.balance_sheet.current_assets.value,
fundamental.financial_statements.balance_sheet.total_assets.value,
fundamental.financial_statements.balance_sheet.current_liabilities.value,
fundamental.financial_statements.balance_sheet.current_debt.value,
fundamental.financial_statements.balance_sheet.income_tax_payable.value,
fundamental.financial_statements.income_statement.depreciation_and_amortization.value
]
])
@staticmethod
def calculate_score(previous_factors, current_factors):
delta_assets = current_factors.current_assets - previous_factors.current_assets
delta_cash = current_factors.cash - previous_factors.cash
delta_liabilities = current_factors.liabilities - previous_factors.liabilities
delta_debt = current_factors.debt - previous_factors.debt
delta_tax = current_factors.tax - previous_factors.tax
avg_total_assets = (current_factors.total_assets + previous_factors.total_assets) / 2
depreciation = current_factors.depreciation
return (
((delta_assets - delta_cash) - (delta_liabilities - delta_debt - delta_tax) - depreciation)
/ avg_total_assets
)