Overall Statistics |
Total Orders 2370 Average Win 2.29% Average Loss -0.90% Compounding Annual Return 33.236% Drawdown 64.700% Expectancy 0.980 Start Equity 100000 End Equity 124362204.69 Net Profit 124262.205% Sharpe Ratio 0.881 Sortino Ratio 1.229 Probabilistic Sharpe Ratio 15.951% Loss Rate 44% Win Rate 56% Profit-Loss Ratio 2.54 Alpha 0.225 Beta 0.362 Annual Standard Deviation 0.273 Annual Variance 0.074 Information Ratio 0.691 Tracking Error 0.285 Treynor Ratio 0.663 Total Fees $16329798.27 Estimated Strategy Capacity $12000.00 Lowest Capacity Asset ARTL X5IQJTEKSAJP Portfolio Turnover 0.92% |
from AlgorithmImports import * import numpy as np class NetCurrentAssetValueEffect(QCAlgorithm): def Initialize(self) -> None: self.SetStartDate(2000, 1, 1) #self.set_end_date(2019, 1, 1) self.SetCash(100_000) self.UniverseSettings.Leverage = 1.5 self.UniverseSettings.Resolution = Resolution.Daily self.AddUniverse(self.FundamentalFunction) self.Settings.MinimumOrderMarginPortfolioPercentage = 0.0 self.settings.daily_precise_end_time = False self.fundamental_count: int = 3_000 self.market: str = 'usa' self.country_id: str = 'USA' self.fin_sector_code: int = 103 self.ncav_threshold: float = 1.50 self.illiquid_market_cap_threshold: float = 0.5e9 self.long_symbols: List[Symbol] = [] self.rebalance_months: List[int] = [1, 4, 7, 10] self.selection_flag: bool = True self.spy = self.AddEquity('SPY', Resolution.Daily).Symbol self.spy_sma = self.SMA(self.spy, 200, Resolution.Daily) self.Schedule.On(self.DateRules.MonthStart(self.spy), self.TimeRules.AfterMarketOpen(self.spy), self.Selection) def FundamentalFunction(self, fundamental: List[Fundamental]) -> List[Symbol]: if not self.selection_flag or self.IsSPYBelow200SMA(): return Universe.Unchanged filtered: List[Fundamental] = [f for f in fundamental if f.HasFundamentalData and f.Market == self.market and f.CompanyReference.CountryId == self.country_id and f.AssetClassification.MorningstarSectorCode != self.fin_sector_code and not np.isnan(f.EarningReports.BasicAverageShares.TwelveMonths) and f.EarningReports.BasicAverageShares.TwelveMonths != 0 and not np.isnan(f.MarketCap) and f.MarketCap != 0 and f.MarketCap <= 0.2e9 and f.ValuationRatios.WorkingCapitalPerShare != 0 and f.Volume > 0 # Filter out stocks with no volume data ] # No more filtering based on stock splits sorted_by_market_cap: List[Fundamental] = sorted(filtered, key=lambda f: f.MarketCap, reverse=True)[:self.fundamental_count] self.long_symbols = [x.Symbol for x in sorted_by_market_cap if ((x.ValuationRatios.WorkingCapitalPerShare * x.EarningReports.BasicAverageShares.TwelveMonths) / x.MarketCap) > self.ncav_threshold] return self.long_symbols def OnData(self, slice: Slice) -> None: if not self.selection_flag or self.IsSPYBelow200SMA(): return self.selection_flag = False portfolio: List[PortfolioTarget] = [PortfolioTarget(symbol, 1 / len(self.long_symbols)) for symbol in self.long_symbols if slice.ContainsKey(symbol) and slice[symbol] is not None] self.SetHoldings(portfolio, True) self.long_symbols.clear() def Selection(self) -> None: if self.Time.month in self.rebalance_months: self.selection_flag = True def OnSecuritiesChanged(self, changes: SecurityChanges) -> None: for security in changes.AddedSecurities: security.SetLeverage(1.5) security.SetFeeModel(RealisticFeeModel(self.illiquid_market_cap_threshold)) if self.IsSPYBelow200SMA(): self.Liquidate() def IsSPYBelow200SMA(self) -> bool: if not self.spy_sma.IsReady: return False return self.Securities[self.spy].Price < self.spy_sma.Current.Value class RealisticFeeModel(FeeModel): def __init__(self, illiquid_market_cap_threshold): self.illiquid_market_cap_threshold = illiquid_market_cap_threshold def GetOrderFee(self, parameters: OrderFeeParameters) -> OrderFee: market_cap = parameters.Security.Fundamentals.MarketCap quantity = parameters.Order.AbsoluteQuantity price = parameters.Security.Price per_share_fee = 0.005 min_fee = 1.00 illiquid_fee = 0.01 if market_cap <= self.illiquid_market_cap_threshold: fee = price * quantity * illiquid_fee else: fee = max(quantity * per_share_fee, min_fee) return OrderFee(CashAmount(fee, "USD"))