| Overall Statistics |
|
Total Orders
20351
Average Win
0.15%
Average Loss
-0.14%
Compounding Annual Return
4.472%
Drawdown
31.900%
Expectancy
0.077
Start Equity
100000
End Equity
298881.38
Net Profit
198.881%
Sharpe Ratio
0.163
Sortino Ratio
0.195
Probabilistic Sharpe Ratio
0.137%
Loss Rate
48%
Win Rate
52%
Profit-Loss Ratio
1.07
Alpha
0.01
Beta
0.001
Annual Standard Deviation
0.064
Annual Variance
0.004
Information Ratio
-0.19
Tracking Error
0.171
Treynor Ratio
15.672
Total Fees
$774.67
Estimated Strategy Capacity
$1000.00
Lowest Capacity Asset
GATE XTQ56K4YHZL1
Portfolio Turnover
0.90%
|
# https://quantpedia.com/strategies/asset-growth-effect/
#
# The investment universe consists of all non-financial U.S. stocks listed on NYSE, AMEX, and NASDAQ. Stocks are then sorted each year at the end
# of June into ten equal groups based on the percentage change in total assets for the previous year. The investor goes long decile with low asset
# growth firms and short decile with high asset growth firms. The portfolio is weighted equally and rebalanced every year.
#
# QC implementation changes:
# - The investment universe consists of 3000 largest US stocks listed on NYSE, AMEX, and NASDAQ.
#region imports
from AlgorithmImports import *
import numpy as np
#endregion
class AssetGrowthEffect(QCAlgorithm):
def Initialize(self) -> None:
self.SetStartDate(2000, 1, 1)
self.SetCash(100_000)
self.UniverseSettings.Leverage = 5
self.UniverseSettings.Resolution = Resolution.Daily
self.AddUniverse(self.FundamentalFunction)
self.settings.daily_precise_end_time = False
self.settings.minimum_order_margin_portfolio_percentage = 0.
# Latest assets data.
self.total_assets: dict[Symbol, float] = {}
self.long_symbols: list[Symbol] = []
self.short_symbols: list[Symbol] = []
self.selection_flag: bool = False
# Filter Parameters
self.exchange_codes: List[str] = ['NYS', 'NAS', 'ASE']
self.quantile: int = 10
self.rebalancing_month: int = 6
self.min_share_price: float = 5.
# self.fin_sector_code: int = 103
self.fundamental_count:int = 3000
self.fundamental_sorting_key = lambda x: x.MarketCap
self.exchange: Symbol = self.AddEquity("SPY", Resolution.Daily).Symbol
self.Schedule.On(self.DateRules.MonthEnd(self.exchange),
self.TimeRules.AfterMarketOpen(self.exchange),
self.Selection)
def FundamentalFunction(self, fundamental: List[Fundamental]) -> List[Symbol]:
if not self.selection_flag:
return Universe.Unchanged
filtered: List[Fundamental] = [f for f in fundamental if f.HasFundamentalData
and f.SecurityReference.ExchangeId in self.exchange_codes
and f.Price >= self.min_share_price
# and f.AssetClassification.MorningstarSectorCode != self.fin_sector_code
and not np.isnan(f.FinancialStatements.BalanceSheet.TotalAssets.TwelveMonths)
and f.FinancialStatements.BalanceSheet.TotalAssets.TwelveMonths > 0]
if len(filtered) > self.fundamental_count:
filtered = [x for x in sorted(filtered, key=self.fundamental_sorting_key, reverse=True)[:self.fundamental_count]]
assets_growth: dict[Symbol, float] = {}
for security in filtered:
symbol: Symbol = security.Symbol
if symbol not in self.total_assets:
self.total_assets[symbol] = None
current_assets: float = security.FinancialStatements.BalanceSheet.TotalAssets.TwelveMonths
# There is not previous assets data.
if not self.total_assets[symbol]:
self.total_assets[symbol] = current_assets
continue
# Assets growth calc.
assets_growth[symbol] = (current_assets - self.total_assets[symbol]) / self.total_assets[symbol]
# Update data.
self.total_assets[symbol] = current_assets
# Asset growth sorting.
if len(assets_growth) >= self.quantile:
sorted_by_assets_growth: dict[Symbol, float] = sorted(assets_growth.items(),
key = lambda x: x[1],
reverse = True)
quantile: int = int(len(sorted_by_assets_growth) / self.quantile)
self.long_symbols = [x[0] for x in sorted_by_assets_growth[-quantile:]]
self.short_symbols = [x[0] for x in sorted_by_assets_growth[:quantile]]
return self.long_symbols + self.short_symbols
def OnData(self, slice: Slice) -> None:
if not self.selection_flag:
return
self.selection_flag = False
# Trade execution.
targets: List[PortfolioTarget] = []
for i, portfolio in enumerate([self.long_symbols, self.short_symbols]):
for symbol in portfolio:
if slice.ContainsKey(symbol) and slice[symbol] is not None:
targets.append(PortfolioTarget(
symbol, ((-1) ** i) / len(portfolio)))
self.SetHoldings(targets, True)
self.long_symbols.clear()
self.short_symbols.clear()
def Selection(self) -> None:
if self.Time.month == self.rebalancing_month:
self.selection_flag = True
def OnSecuritiesChanged(self, changes: SecurityChanges) -> None:
for security in changes.AddedSecurities:
security.SetFeeModel(CustomFeeModel())
# Custom fee model
class CustomFeeModel(FeeModel):
def GetOrderFee(self, parameters: OrderFeeParameters) -> OrderFee:
fee: float = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005
return OrderFee(CashAmount(fee, "USD"))