Overall Statistics |
Total Trades 546 Average Win 1.45% Average Loss -1.20% Compounding Annual Return 14.886% Drawdown 16.700% Expectancy 0.267 Net Profit 129.150% Sharpe Ratio 0.81 Sortino Ratio 0.737 Probabilistic Sharpe Ratio 46.142% Loss Rate 42% Win Rate 58% Profit-Loss Ratio 1.20 Alpha 0.057 Beta 0.379 Annual Standard Deviation 0.105 Annual Variance 0.011 Information Ratio 0.076 Tracking Error 0.134 Treynor Ratio 0.224 Total Fees $1213.12 Estimated Strategy Capacity $960000000.00 Lowest Capacity Asset SPY R735QTJ8XC9X Portfolio Turnover 24.94% |
# region imports from AlgorithmImports import * # endregion from collections import defaultdict from collections import deque from QuantConnect.Securities.Equity import Equity # reference from https://github.com/QuantConnect/Lean/blob/master/Algorithm.Python/CustomIndicatorAlgorithm.py#L31 for custom Indicator # reference from https://www.investopedia.com/terms/a/advancedeclineline.asp for Advance/Decline (A/D) Line Logic ''' A/D=Net Advances+{ PA, if PA value exists 0, if no PA value where: Net Advances=Difference between number of daily ascending and declining stocks PA=Previous Advances Previous Advances=Prior indicator reading ''' class AdvanceDeclineLine(PythonIndicator): def __init__(self, name: str = "ADL", period: int = 1) -> None: self.advance_decline_values = deque(maxlen=period) self.previous_close = {} self.previous_adline = None self.value = None super().__init__(name) def Update(self, slice): self.advance = 0 self.decline = 0 if len(self.advance_decline_values) > 0: advance, decline = self.advance_decline_values[-1] for data in slice: if data.Key.Value == 'SPY': continue if data.Key.Value in self.previous_close and self.previous_close[data.Key.Value] is not None: if data.Value.Close > self.previous_close[data.Key.Value]: self.advance += 1 elif data.Value.Close < self.previous_close[data.Key.Value]: self.decline += 1 self.previous_close[data.Key.Value] = data.Value.Close self.advance_decline_values.append((self.advance, self.decline)) if self.previous_adline is not None: self.value = sum(adv - dec for adv, dec in self.advance_decline_values) + self.previous_adline self.previous_adline = sum(adv - dec for adv, dec in self.advance_decline_values) @property def IsReady(self): return bool(self.advance_decline_values) and self.value is not None def Reset(self) -> None: self.advance_decline_values.clear() super().Reset() def GetValue(self) -> float: return self.value class DancingYellowGreenGorilla(QCAlgorithm): def Initialize(self) -> None: self.SetStartDate(2018, 1, 1) self.SetCash(100000) self.index = self.AddEquity("SPY", Resolution.Daily).Symbol self.UniverseSettings.Resolution = Resolution.Daily self.AddUniverse(self.Universe.ETF(self.index, self.UniverseSettings, self.ETFConstituentsFilter)) self.adl_indicator = AdvanceDeclineLine(period=1) self.SetWarmup(10) def ETFConstituentsFilter(self, constituents: List[ETFConstituentData]) -> List[Symbol]: return [x.Symbol for x in constituents] def OnData(self, slice: Slice) -> None: self.Debug(f"Triggered Ondata : {self.Time}") self.adl_indicator.Update(slice) if self.adl_indicator.IsReady: ad_line_value = self.adl_indicator.GetValue() self.Plot("AD for SP500", "AD Line", ad_line_value) # simple trading logic if ad_line_value > 0 and not self.Portfolio.Invested: self.SetHoldings("SPY", 1.0) if ad_line_value < 0 and self.Portfolio.Invested: self.Liquidate()