| Overall Statistics |
|
Total Trades 117 Average Win 0.07% Average Loss -0.05% Compounding Annual Return 17.668% Drawdown 1.500% Expectancy 0.700 Net Profit 0.402% Sharpe Ratio 2.145 Probabilistic Sharpe Ratio 55.174% Loss Rate 31% Win Rate 69% Profit-Loss Ratio 1.46 Alpha 0.162 Beta 0.049 Annual Standard Deviation 0.087 Annual Variance 0.008 Information Ratio -3.019 Tracking Error 0.106 Treynor Ratio 3.832 Total Fees $117.35 Estimated Strategy Capacity $3100000.00 Lowest Capacity Asset COO R735QTJ8XC9X |
import pandas as pd
class WellDressedSkyBlueSardine(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 12, 21)
self.SetEndDate(2020, 12, 31)
self.SetCash(1000000)
self.SPY = self.AddEquity('SPY', Resolution.Hour).Symbol
self.AddUniverse(self.CoarseFilter, self.FineFilter)
self.UniverseSettings.Resolution = Resolution.Hour
self.UniverseSettings.SetDataNormalizationMode = DataNormalizationMode.SplitAdjusted
self.SCTRuniverse = []
self.potential = []
self.Data = {}
self.indicators = {}
self.percentagebuy = 0.05
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen('SPY', 30), self.daily_check)
def CoarseFilter(self, coarse):
sortedByDollarVolume = sorted(coarse, key=lambda x: x.DollarVolume, reverse=True)
return [x.Symbol for x in sortedByDollarVolume if x.Price > 10
and x.HasFundamentalData][:1000]
def FineFilter(self, fine):
sortedByPE = sorted(fine, key=lambda x: x.MarketCap)
self.universe = [x.Symbol for x in sortedByPE if x.MarketCap > 10e9][:500]
# self.Debug(self.universe)
return self.universe
def OnSecuritiesChanged(self, changes):
# close positions in removed securities
for x in changes.RemovedSecurities:
self.Liquidate()
if x.Symbol in self.Data:
del self.Data[x.Symbol]
# can't open positions here since data might not be added correctly yet
for x in changes.AddedSecurities:
self.Data[x.Symbol] = SymbolData(self, x.Symbol)
SCTR = {}
for symboldata in self.Data.values():
if symboldata.IsReady():
SCTR[symboldata.symbol] = symboldata.SCTR()
sctr_df = pd.DataFrame.from_dict(SCTR, orient='index')
sctr_df['Decile_rank'] = pd.qcut(sctr_df.iloc[:, 0], 10000, labels = False) /100
sctr_df = sctr_df.drop(sctr_df.columns[0], axis=1)
sctr_df = sctr_df.loc[sctr_df['Decile_rank'] >= 85]
self.SCTRuniverse = sctr_df
# self.Debug(SCTR)
return self.SCTRuniverse
def OnData(self, data):
for symboldata in self.SCTRuniverse.itertuples():
symbol = symboldata.Index
D_rank = symboldata.Decile_rank
if not symbol in self.potential:
self.potential.append(symbol)
for symbol in self.potential:
if not data.ContainsKey(symbol):
continue
self.indicators[symbol] = Entry_Indicator(self, symbol)
if self.IsWarmingUp: continue
tradeBars = data.Bars
symbolTradeBar = tradeBars[symbol]
symbolOpen = symbolTradeBar.Open ## Open price
symbolClose = symbolTradeBar.Close ## Close price
self.slow_stoch = self.indicators[symbol].get_sto()
self.sma = self.indicators[symbol].get_sma()
def daily_check(self):
for symbol in self.potential:
if not self.Securities.ContainsKey(symbol):
continue
if self.IsWarmingUp: continue
symbolClose = float(self.Securities[symbol].Close)
self.buyquantity = round((self.percentagebuy*self.Portfolio.TotalPortfolioValue)/symbolClose) if symbolClose != 0 else 0
if (not self.Portfolio[symbol].Invested) and (self.Portfolio.MarginRemaining > 0.9*self.percentagebuy*self.Portfolio.TotalPortfolioValue):
if ((symbolClose/self.sma)> 0 and self.slow_stoch<=30):
self.MarketOrder(symbol, self.buyquantity)
self.Log(str(symbol) + " -sto: " + str(self.slow_stoch))
self.Log(str(symbol) + " -sma: " + str(symbolClose/self.sma))
for kvp in self.Portfolio:
holdings = kvp.Value
symbol = holdings.Symbol
if holdings.Invested:
if not symbol in self.potential:
self.Liquidate(symbol)
self.Log(str(symbol) + " : " + str(holdings.Price))
class SymbolData:
def __init__(self, algo, symbol):
self.symbol = symbol
self.EMA200 = algo.EMA(symbol, 200, Resolution.Daily)
self.EMA50 = algo.EMA(symbol, 50, Resolution.Daily)
self.ROC125 = algo.ROC(symbol, 125, Resolution.Daily)
self.ROC20 = algo.ROC(symbol, 20, Resolution.Daily)
self.PPO = algo.PPO(symbol, 12, 26, MovingAverageType.Exponential, Resolution.Daily)
self.RSI14 = algo.RSI(symbol, 14, MovingAverageType.Simple, Resolution.Daily)
self.PPOWindow = RollingWindow[float](3)
history = algo.History(symbol, 200, Resolution.Daily)
for index, bar in history.loc[symbol].iterrows():
self.EMA200.Update(index, bar.close)
self.EMA50.Update(index, bar.close)
self.ROC125.Update(index, bar.close)
self.ROC20.Update(index, bar.close)
self.PPO.Update(index, bar.close)
self.RSI14.Update(index, bar.close)
self.PPOWindow.Add(self.PPO.Current.Value)
def SCTR(self):
return self.EMA200.Current.Value*0.3 + self.EMA50.Current.Value*0.15 + self.ROC125.Current.Value*0.3 + self.ROC20.Current.Value*0.15 \
+ (self.PPOWindow[0] - self.PPOWindow[2])/(3*self.PPOWindow[2]) *0.05 + self.RSI14.Current.Value*0.05
def IsReady(self):
return self.EMA200.IsReady and self.EMA50.IsReady \
and self.ROC125.IsReady and self.ROC20.IsReady\
and self.PPO.IsReady and self.RSI14.IsReady
class Entry_Indicator:
def __init__(self, algo, symbol):
self.SMA200 = algo.SMA(symbol, 200, Resolution.Daily)
self.Slow_Stoch5 = algo.STO(symbol, 5, Resolution.Daily)
history = algo.History(symbol, 200, Resolution.Daily)
for index, bar in history.loc[symbol].iterrows():
self.SMA200.Update(index, bar.close)
for bar in history.itertuples():
tradeBar = TradeBar(bar.Index[1], bar.Index[0], bar.open, bar.high, bar.low, bar.close, bar.volume, timedelta(1))
self.Slow_Stoch5.Update(tradeBar)
def get_sma(self):
return self.SMA200.Current.Value
def get_sto(self):
return self.Slow_Stoch5.Current.Value