| 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()