Overall Statistics |
Total Trades 1673 Average Win 0.40% Average Loss -0.41% Compounding Annual Return 9.223% Drawdown 31.400% Expectancy 0.119 Net Profit 55.499% Sharpe Ratio 0.51 Probabilistic Sharpe Ratio 10.618% Loss Rate 43% Win Rate 57% Profit-Loss Ratio 0.98 Alpha 0.101 Beta -0.05 Annual Standard Deviation 0.184 Annual Variance 0.034 Information Ratio -0.209 Tracking Error 0.238 Treynor Ratio -1.864 Total Fees $2555.89 Estimated Strategy Capacity $16000000.00 Lowest Capacity Asset CYPH R735QTJ8XC9X |
import configs as cfg class FatApricotElephant(QCAlgorithm): def Initialize(self): self.SetStartDate(2010, 2, 4) self.SetEndDate(2015, 2, 4) self.SetCash(100000) self.AddUniverseSelection(QC500UniverseSelectionModel()) self.UniverseSettings.Resolution = cfg.resolution self.symbolDataDict = {} self.next_rebalance = self.Time self.divisor = { Resolution.Daily: 1, Resolution.Hour: 24, Resolution.Minute: 24 * 60, Resolution.Second: 24 * 60 * 60 } self.mkt = self.AddEquity(cfg.mkt, cfg.resolution).Symbol self.mkt_sma = self.SMA(self.mkt, cfg.mkt_sma_period, cfg.resolution) self.SetWarmUp(cfg.warmup_period) def OnData(self, data): if self.next_rebalance >= self.Time or self.IsWarmingUp: return self.Log('Beginning rebalance logic') if not self.mkt_sma.IsReady: self.Log('Market is bearished, ejecting') return self.next_rebalance = self.Time + timedelta(cfg.rebalance_period / self.divisor[cfg.resolution]) if self.GetPrice(self.mkt) < self.mkt_sma.Current.Value: self.Liquidate() return filtered_rdy = [sd for sd in self.symbolDataDict.values() if sd.IsReady] topROC = sorted(filtered_rdy, key=lambda sd: sd.get_roc(), reverse=True) filtered_sma = [sd for sd in topROC if sd.is_buy(self.GetPrice(sd.symbol))][:cfg.num_stocks] weights = {sd.symbol:self.GetPrice(sd.symbol)/sd.get_atr() for sd in filtered_sma} total_weight = sum(weights.values()) adj_weights = {symbol: weight/total_weight for symbol, weight in weights.items()} invested = [kvp.Key for kvp in self.Portfolio if kvp.Value.Invested] to_buy = adj_weights.keys() for symbol in invested: if symbol not in to_buy: self.Liquidate(symbol) if len(adj_weights) > 0: self.Log('Going LONG') if cfg.enable_atr: for symbol, weight in adj_weights.items(): self.SetHoldings(symbol, weight) else: weight = 1/len(adj_weights) for symbol in adj_weights: self.SetHoldings(symbol, weight) def OnSecuritiesChanged(self, changed): for security in changed.AddedSecurities: if security.Symbol == self.mkt: continue self.symbolDataDict[security.Symbol] = SymbolData(self, security.Symbol) for security in changed.RemovedSecurities: self.symbolDataDict.pop(security.Symbol, None) def GetPrice(self, symbol): return self.Securities[symbol].Price class SymbolData: def __init__(self, algo, symbol): self.symbol = symbol self.roc = algo.ROCP(symbol, cfg.roc_period, cfg.resolution) self.sma = algo.SMA(symbol, cfg.sma_period, cfg.resolution) self.atr = algo.ATR(symbol, cfg.atr_period, cfg.resolution) hist = algo.History(symbol, cfg.warmup_period, cfg.resolution).loc[symbol] if hist.empty or len(hist)==0: return for time, row in hist.iterrows(): tradeBar = TradeBar(time, symbol, row.open, row.high, row.low, row.close, row.volume, timedelta(1)) self.atr.Update(tradeBar) self.roc.Update(time, row.close) self.sma.Update(time, row.close) @property def IsReady(self): return self.atr.IsReady and self.roc.IsReady and self.sma.IsReady def get_roc(self): return self.roc.Current.Value def get_sma(self): return self.sma.Current.Value def get_atr(self): return self.atr.Current.Value def is_buy(self, price): return price > self.get_sma()
# ---configurables that won't be changed often--- # resolution for data/indicators resolution = Resolution.Daily # wait how many periods of above resolution to rebalance rebalance_period = 30 # market proxy - used to turn on/off bull mkt = 'SPY' # ---configurables you might want to change--- # SMA period of the market proxy chosen mkt_sma_period = 100 # ROC period for sorting roc_period = 30 # SMA period of individual stocks to determine uptrend sma_period = 30 # ATR for weighting, where lower ATR means higher weight atr_period = 14 # if enabled, use inverse adj ATR weight # if disabled, use equal weighting enable_atr = True # how many stocks you want # used to choose how many of the top ROC stocks num_stocks = 20 # probably don't touch this warmup_period = max(mkt_sma_period, roc_period, sma_period, atr_period)