Overall Statistics |
Total Trades 22 Average Win 0.00% Average Loss 0.00% Compounding Annual Return -0.018% Drawdown 0.000% Expectancy -0.869 Net Profit -0.006% Sharpe Ratio -0.252 Probabilistic Sharpe Ratio 21.742% Loss Rate 90% Win Rate 10% Profit-Loss Ratio 0.31 Alpha 0 Beta -0.001 Annual Standard Deviation 0.001 Annual Variance 0 Information Ratio 0.264 Tracking Error 0.471 Treynor Ratio 0.176 Total Fees $22.00 Estimated Strategy Capacity $1200000000.00 |
from SymbolData import SymbolData from TradeManagement import TradeManagement class CryingBlueFlamingo(QCAlgorithm): def Initialize(self): self.SetStartDate(2020, 1, 1) # Set Start Date self.SetCash(100000) # Set Strategy Cash self.benchmark = "SPY" self.AddEquity(self.benchmark) self.AddUniverse(self.CoarseSelection) self.UniverseSettings.Resolution = Resolution.Minute self.universe_size = 100 self.symbol_data = {} self.trade_managers = {} self.Schedule.On(self.DateRules.EveryDay(self.benchmark), self.TimeRules.AfterMarketOpen(self.benchmark, 15), self.Rebalance) def Rebalance(self): '''Fires everyday 15 minutes after market open''' for symbol, symbol_data in self.symbol_data.items(): if not symbol_data.IsReady: continue signal = self.CalculateSignal(symbol_data) if signal: trade_manager = self.trade_managers[symbol] trade_manager.CreateEntry(-1) def OnData(self, data): for symbol, trade_manager in self.trade_managers.items(): if not self.Portfolio[symbol].Invested: continue current_price = self.Securities[symbol].Price stop_loss = trade_manager.stop_loss take_profit = trade_manager.take_profit if current_price > stop_loss or current_price < take_profit: trade_manager.Liquidate() def CalculateSignal(self, symbol_data): sma = symbol_data.sma.Current.Value atr = symbol_data.atr.Current.Value bars = symbol_data.bar_window latest_daily_bar = bars[0] # checking if the high of the latest daily bar is greater than the high # of all following bars uptrend = all([latest_daily_bar.High > bar.High for bar in list(bars)[1:]]) red_bar = latest_daily_bar.Close < latest_daily_bar.Open body = abs(latest_daily_bar.Open - latest_daily_bar.Close) tail = abs(latest_daily_bar.Close - latest_daily_bar.Low) head = abs(latest_daily_bar.High - latest_daily_bar.Open) hanging_man = (tail > 2 * body) and (head < 0.3 * body) # latest_market_price price = self.Securities[symbol_data.symbol].Price above_sma = price > sma signal = red_bar and uptrend and hanging_man and above_sma return signal def CoarseSelection(self, coarse): # list of ~8500 stocks (coarse data) # coarse is a list of CoarseFundamental objects # Descending order sorted_by_liquidity = sorted(coarse, key=lambda c:c.DollarVolume, reverse=True) most_liquid_coarse = sorted_by_liquidity[:self.universe_size] # needs to return a list of Symbol object most_liquid_symbols = [c.Symbol for c in most_liquid_coarse] return most_liquid_symbols def OnSecuritiesChanged(self, changes): '''Fires after universe selection if there are any changes''' for security in changes.AddedSecurities: symbol = security.Symbol if symbol not in self.symbol_data and symbol.Value != self.benchmark: self.symbol_data[symbol] = SymbolData(self, symbol) self.trade_managers[symbol] = TradeManagement(self, symbol) for security in changes.RemovedSecurities: symbol = security.Symbol if symbol in self.symbol_data: symbol_data_object = self.symbol_data.pop(symbol, None) symbol_data_object.KillConsolidators() if symbol in self.trade_managers: self.trade_managers.pop(symbol, None)
class TradeManagement: def __init__(self, algorithm, symbol): self.algorithm = algorithm self.symbol = symbol self.days_active = 0 self.entry_price = None self.stop_loss = None self.take_profit = None def CreateEntry(self, quantity): # initial entry market order self.algorithm.MarketOrder(self.symbol, quantity) current_price = self.algorithm.Securities[self.symbol].Price atr = self.algorithm.symbol_data[self.symbol].atr.Current.Value self.entry_price = current_price self.stop_loss = self.entry_price + 0.5 * atr self.take_profit = self.entry_price - 1 * atr self.algorithm.Debug(f"Entering {self.symbol}...Entry Price: {current_price}, Take Profit: {self.take_profit}, StopLoss: {self.stop_loss}") def Liquidate(self): self.algorithm.Debug(f"Liquidating.. {self.symbol}....{self.algorithm.Securities[self.symbol].Price}") self.algorithm.Liquidate(self.symbol) self.entry_price = None self.stop_loss = None self.take_profit = None self.days_active = 0
class SymbolData: '''Containers to hold relevant data for each symbol''' def __init__(self, algorithm, symbol): self.algorithm = algorithm self.symbol = symbol # defines consolidator and then registers to receive data self.daily_consolidator = TradeBarConsolidator(timedelta(days=1)) self.algorithm.SubscriptionManager.AddConsolidator(symbol, self.daily_consolidator) self.daily_consolidator.DataConsolidated += self.OnDailyBar # 1. instantiantes a SimpleMovingAverage object # 2. subscribes it to receive data self.sma = SimpleMovingAverage(20) self.algorithm.RegisterIndicator(symbol, self.sma, self.daily_consolidator) self.atr = AverageTrueRange(1) self.algorithm.RegisterIndicator(symbol, self.atr, self.daily_consolidator) # holds recent bars self.bar_window = RollingWindow[TradeBar](5) self.WarmUpIndicators() def WarmUpIndicators(self): # returns a dataframe history = self.algorithm.History(self.symbol, 20, Resolution.Daily) for bar in history.itertuples(): time = bar.Index[1] open = bar.open high = bar.high low = bar.low close = bar.close volume = bar.volume trade_bar = TradeBar(time, self.symbol, open, high, low, close, volume) self.sma.Update(time, close) self.atr.Update(trade_bar) self.bar_window.Add(trade_bar) def OnDailyBar(self, sender, bar): '''Fires each time our daily_consolidator produces a bar that bar is passed in through the bar parameter''' # save that bar to our rolling window self.bar_window.Add(bar) def KillConsolidators(self): self.algorithm.SubscriptionManager.RemoveConsolidator(self.symbol, self.daily_consolidator) def IsReady(self): return self.sma.IsReady and self.atr.IsReady and self.bar_window.IsReady