Overall Statistics |
Total Trades 449 Average Win 11.49% Average Loss -0.15% Compounding Annual Return 36.528% Drawdown 48.000% Expectancy 14.095 Net Profit 2836.220% Sharpe Ratio 1.221 Probabilistic Sharpe Ratio 57.257% Loss Rate 80% Win Rate 20% Profit-Loss Ratio 74.14 Alpha 0.365 Beta -0.173 Annual Standard Deviation 0.282 Annual Variance 0.079 Information Ratio 0.66 Tracking Error 0.336 Treynor Ratio -1.993 Total Fees $1186.03 |
import pandas as pd #https://www.quantconnect.com/forum/discussion/7867/tim-sykes-and-penny-stocks/p1 class CalibratedUncoupledCoreWave(QCAlgorithm): def Initialize(self): self.SetStartDate(2020, 5, 1) # Set Start Date #self.SetEndDate(2020, 3, 11) # Set End Date self.SetCash(100000) # Set Strategy Cash # Setup universe self.UniverseSettings.Resolution = Resolution.Minute self.AddUniverse(self.SelectCoarse,self.SelectFine) self.AddEquity("SPY", Resolution.Minute) # Liquidate all positions before the market close each day self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 30), self.ClosePositions) self.coarseclose={} # Store yesterday's close in a dictionary for reference self.volumeprior = {} # Last x days' average dollar volume self.stddev={} # Standard Deviation self.minmax={} # Ratio of Hi Lo of last x days' close price self.cumvol={} # Cumulative volume throughout trading day self.traded={} # Ensure only 1 trade per day per stock self.targetentry=1.05 # Only enter a trade if today's price doubles yesterday's close self.stops = {} # Keep track of stop loss orders so we can update them #self.coarsefilter = 50 # return the top x stocks sorted by [momentum] etc self.histlength = 60 # lngth of history to call to ensure sufficient data def SelectCoarse(self, coarse): # Penny Stock filter myuniverse = [x for x in coarse if x.HasFundamentalData and \ float(x.Volume) > 2000000 and \ 300 >= float(x.Price) >= 20] # rank the stocks by dollar volume and choose the top 150 myuniverse = sorted(myuniverse, key=lambda x: x.DollarVolume, reverse=True)[:150] self.coarseclose.clear() # Clear the dictionary each day before re-populating it self.volumeprior.clear() self.stddev.clear() self.minmax.clear() stocks = {x.Symbol: x for x in myuniverse} histStocks=list(stocks.keys()) history = self.History(histStocks, self.histlength, Resolution.Daily) scam={} for stock in histStocks: if stock in history.index: df = pd.DataFrame() if not history.loc[stock].empty: df = history.loc[stock].dropna() if df.empty or len(df)<self.histlength: continue # Some alternative filters self.volumeprior[stock] = df['volume'][-5:].mean() self.stddev[stock]=((df["close"][-5:].pct_change()).std())*15.87*100 self.minmax[stock]=((df["close"][-10:].max()/df["close"][-10:].min())-1)*100 if self.minmax[stock] < 10.00 and self.volumeprior[stock]< 50000 : scam[stock]= self.minmax[stock] #scammed=[key for (key, value) in sorted(scam.items(),reverse=False)] scammed=[key for (key, value) in scam.items()] # Save yesterday's close for c in myuniverse: self.coarseclose[c.Symbol] = c.AdjustedPrice return scammed[:] # Return filtered stocks for further filtering by the SelectFine def SelectFine(self,fine): ''' This function takes the stock of the CoarceFundamental function and narrow the list adding specific fundamental filters Largely to ensure that only common stock is traded and not EG Preference Shares or ETFs ''' # Primary share, common stock, not limited partnership, and not ADR fine_filter = [x.Symbol for x in fine if x.SecurityReference.IsPrimaryShare == 1 and \ x.SecurityReference.SecurityType == 'ST00000001' and \ x.CompanyReference.IsLimitedPartnership == 0 and \ x.SecurityReference.IsDepositaryReceipt == 0 ] self.traded.clear() self.traded = {k: 0 for k in fine_filter} self.cumvol.clear() self.cumvol = {k: 0 for k in fine_filter} return fine_filter def OnData(self, data): '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here. Arguments: data: Slice object keyed by symbol containing the stock data ''' for kvp in data.Bars: symbol = kvp.Key close = kvp.Value.Close if symbol in self.cumvol.keys(): self.cumvol[symbol]=self.cumvol[symbol]+(kvp.Value.Close*kvp.Value.Volume) # Entry conditions: # - We have no position in this stock # - We haven't traded this symbol today # - Bar closed above our target # - Before 10:30am # - Cumulative Volume for the day exceeds average total dollar volume for past x days if (self.Portfolio[symbol].Quantity == 0.0 and symbol in self.traded.keys() and self.traded[symbol] == 0 and close >= self.coarseclose[symbol]*self.targetentry and self.Time.hour <= 10 and self.Time.minute <=30 and self.cumvol[symbol] >= self.volumeprior[symbol]): # Signal today's entry self.traded[symbol] = 1 # Determine position size quantity = int(self.Portfolio.TotalPortfolioValue / 100 / data[symbol].Close) #self.CalculateOrderQuantity(symbol, 0.01) // 2 if quantity < 4: continue # Enter with market order enter_ticket = self.MarketOrder(symbol, quantity) # Set profit targets quarter = int(quantity / 4) final = quantity - 3 * quarter for i in range(3): order = self.LimitOrder(symbol, -quarter, enter_ticket.AverageFillPrice * (1 + (i+1)*0.05)) updateSettings = UpdateOrderFields() updateSettings.Tag = "pt" order.Update(updateSettings) order = self.LimitOrder(symbol, -final, enter_ticket.AverageFillPrice * 1.20) order.Update(updateSettings) # Set stop loss self.stops[symbol] = self.StopMarketOrder(symbol, -quantity, enter_ticket.AverageFillPrice * 0.50) updateSettings.Tag = "sl" self.stops[symbol].Update(updateSettings) def OnOrderEvent(self, orderEvent): if orderEvent.Status == OrderStatus.Filled: order = self.Transactions.GetOrderById(orderEvent.OrderId) if order.Tag == 'pt': # If hit profit target, update stop order quantity updateSettings = UpdateOrderFields() updateSettings.Quantity = self.stops[orderEvent.Symbol].Quantity - orderEvent.Quantity self.stops[orderEvent.Symbol].Update(updateSettings) elif order.Tag == 'sl': # If hit stop loss, cancel profit target orders self.Transactions.CancelOpenOrders(orderEvent.Symbol, "Hit stop price") def ClosePositions(self): if self.Portfolio.Invested: self.Transactions.CancelOpenOrders() self.Liquidate()
import pandas as pd import numpy as np class VixFix: def __init__(self, data, ticker, *args, **kwargs): super(VixFix, self).__init__() self.data = data self.ticker = ticker self.length = 22 self.bbl = 20 self.mult = 2.0 self.lb = 50 self.ph = 0.85 self.pl = 1.01 def sma(self): cumsum = np.cumsum(np.insert(self.wvf, 0, 0)) return (cumsum[-self.bbl:] - cumsum[:self.bbl])/float(self.bbl) def run(self): self.prices = self.data.loc[str(self.ticker)]['close'] def wvf(x): low = x[-1] return ((max(x) - low)/max(x)) * 100 rolling_wvf = self.prices.rolling(self.length).apply(wvf) rolling_wvf_sDev = self.mult * rolling_wvf.rolling(self.bbl).std() midline = rolling_wvf.rolling(self.bbl).mean().tolist() lowerBand = midline - rolling_wvf_sDev upperBand = midline + rolling_wvf_sDev rangeHigh = max(self.prices.rolling(self.lb).apply(wvf)) * self.ph rangeLow = max(self.prices.rolling(self.lb).apply(wvf)) * self.pl # Test market bottom previous_wvf, current_wvf = rolling_wvf.drop(rolling_wvf.tail(1).index), rolling_wvf.tail(1).values[0] if (current_wvf >= max(upperBand)): # or (current_wvf >= max(rangeHigh)): if current_wvf > previous_wvf.drop(previous_wvf.tail(1).index): return 'Yes' else: return 'No'
import pandas as pd import numpy as np class VixFix: def __init__(self, data, ticker, *args, **kwargs): super(VixFix, self).__init__() self.data = data self.ticker = ticker self.length = 22 self.bbl = 20 self.mult = 2.0 self.lb = 50 self.ph = 0.85 self.pl = 1.01 def sma(self): cumsum = np.cumsum(np.insert(self.wvf, 0, 0)) return (cumsum[-self.bbl:] - cumsum[:self.bbl])/float(self.bbl) def run(self): self.prices = self.data.loc[str(self.ticker)]['close'] def wvf(x): low = x[-1] return ((max(x) - low)/max(x)) * 100 rolling_wvf = self.prices.rolling(self.length).apply(wvf) rolling_wvf_sDev = self.mult * rolling_wvf.rolling(self.bbl).std() midline = rolling_wvf.rolling(self.bbl).mean().tolist() lowerBand = midline - rolling_wvf_sDev upperBand = midline + rolling_wvf_sDev rangeHigh = max(self.prices.rolling(self.lb).apply(wvf)) * self.ph rangeLow = max(self.prices.rolling(self.lb).apply(wvf)) * self.pl # Test market bottom previous_wvf, current_wvf = rolling_wvf.drop(rolling_wvf.tail(1).index), rolling_wvf.tail(1).values[0] if (current_wvf >= max(upperBand)): # or (current_wvf >= max(rangeHigh)): if current_wvf > previous_wvf.drop(previous_wvf.tail(1).index): return 'Yes' else: return 'No'
from QuantConnect.Data.UniverseSelection import * import pandas as pd import numpy as np import DeltaModel as DeltaModel class EnhancedShortTermMeanReversionAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2016, 1, 1) #Set Start Date self.SetEndDate(2020, 9, 24) #Set Start Date self.SetCash(50000) #Set Strategy Cash self.UniverseSettings.Resolution = Resolution.Daily self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction) self.AddEquity("SPY", Resolution.Daily) # rebalance the universe selection once a month self.rebalence_flag = 0 # make sure to run the universe selection at the start of the algorithm even it's not the month start self.first_month_trade_flag = 1 self.trade_flag = 0 # Number of quantiles for sorting returns for mean reversion self.nq = 5 # Number of quantiles for sorting volatility over five-day mean reversion period self.nq_vol = 3 # the symbol list after the coarse and fine universe selection self.universe = None self.Schedule.On(self.DateRules.MonthStart("SPY"), self.TimeRules.At(0, 0), Action(self.monthly_rebalance)) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 304), Action(self.get_prices)) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 303), Action(self.daily_rebalance)) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 302), Action(self.calc_avg_delta)) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 301), Action(self.short)) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 300), Action(self.long)) def monthly_rebalance(self): # rebalance the universe every month self.rebalence_flag = 1 def CoarseSelectionFunction(self, coarse): if self.rebalence_flag or self.first_month_trade_flag: # drop stocks which have no fundamental data or have too low prices selected = [x for x in coarse if (x.HasFundamentalData) and (float(x.Price) > 15) and (float(x.Volume) >= 1000000)] # rank the stocks by dollar volume and choose the top 50 filtered = sorted(selected, key=lambda x: x.DollarVolume, reverse=True) return [x.Symbol for x in filtered[:20]] else: return self.universe def FineSelectionFunction(self, fine): if self.rebalence_flag or self.first_month_trade_flag: # filter the stocks which have positive EV To EBITDA filtered_fine = [x for x in fine if x.ValuationRatios.EVToEBITDA > 0] self.universe = [x.Symbol for x in filtered_fine] self.rebalence_flag = 0 self.first_month_trade_flag = 0 self.trade_flag = 1 return self.universe def OnData(self, data): pass def short(self): if self.universe is None: return SPY_Velocity = 0 self.long_leverage = 0 self.short_leverage = 0 # request the history of benchmark pri = self.History(["SPY"], 200, Resolution.Daily) pos_one = (pri.loc["SPY"]['close'][-1]) pos_six = (pri.loc["SPY"]['close'][-75:].mean()) # calculate velocity of the benchmark velocity_stop = (pos_one - pos_six)/100.0 SPY_Velocity = velocity_stop if SPY_Velocity > 0.0: self.long_leverage = 1.8 self.short_leverage = -0.0 else: self.long_leverage = 1.1 self.short_leverage = -0.7 for symbol in self.shorts: if len(self.shorts) + self.existing_shorts == 0: return self.AddEquity(symbol, Resolution.Daily) self.SetHoldings(symbol, self.short_leverage/(len(self.shorts) + self.existing_shorts)) def long(self): if self.universe is None: return for symbol in self.longs: if len(self.longs) + self.existing_longs == 0: return self.AddEquity(symbol, Resolution.Daily) self.SetHoldings(symbol, self.long_leverage/(len(self.longs) + self.existing_longs)) def get_prices(self): if self.universe is None: return # Get the last 15 days of prices for every stock in our universe prices = {} hist = self.History(self.universe, 15, Resolution.Daily) for i in self.universe: if str(i) in hist.index.levels[0]: prices[i.Value] = hist.loc[str(i)]['close'] df_prices = pd.DataFrame(prices, columns = prices.keys()) # calculate the daily log return daily_rets = np.log(df_prices/df_prices.shift(1)) # calculate the latest return but skip the most recent price rets = (df_prices.iloc[-2] - df_prices.iloc[0]) / df_prices.iloc[0] # standard deviation of the daily return stdevs = daily_rets.std(axis = 0) self.ret_qt = pd.qcut(rets, 5, labels=False) + 1 self.stdev_qt = pd.qcut(stdevs, 3, labels=False) + 1 self.longs = list((self.ret_qt[self.ret_qt == 1].index) & (self.stdev_qt[self.stdev_qt < 3].index)) self.shorts = list((self.ret_qt[self.ret_qt == self.nq].index) & (self.stdev_qt[self.stdev_qt < 3].index)) def calc_avg_delta(self): gains = list() delta_percent_ten = list() delta_avg_percent = 0.0 prices = self.History([str(self.symbol)], 20, Resolution.Daily)['close'].values.tolist() prices = list(reversed(prices)) for i in range(0, len(prices)-1): current = prices[i] previous = prices[i+1] gains_percent = ((current - previous)/previous) * 100.0 gains.append(gains_percent) for i in range(0, len(gains)-1): current_gain = gains[i] last_ten_gain = gains[i+9] delta_percent = ((current_gain - last_ten_gain)/last_ten_gain) * 100.0 if len(delta_percent_ten) < 10: delta_percent_ten.append(delta_percent) elif len(delta_percent_ten) == 10: delta_avg_percent = float(sum(delta_percent_ten)/len(delta_percent_ten)) return delta_avg_percent def daily_rebalance(self): # rebalance the position in portfolio every day if self.universe is None: return self.existing_longs = 0 self.existing_shorts = 0 for symbol in self.Portfolio.Keys: if (symbol.Value != 'SPY') and (symbol.Value in self.ret_qt.index): current_quantile = self.ret_qt.loc[symbol.Value] avg_delta = self.calc_avg_delta(symbol) if self.Portfolio[symbol].Quantity > 0 and avg_delta > -5.5: if (current_quantile == 1) and (symbol not in self.longs): self.existing_longs += 2 elif (current_quantile > 1) and (symbol not in self.shorts): self.SetHoldings(symbol, 0) elif self.Portfolio[symbol].Quantity < 0 and avg_delta > -5.5: if (current_quantile == self.nq) and (symbol not in self.shorts): self.existing_shorts += 1 elif (current_quantile < self.nq) and (symbol not in self.longs): self.SetHoldings(symbol, 0) def OnOrderEvent(self, orderEvent): if orderEvent.Status == OrderStatus.Invalid: self.Log(str(orderEvent)) # multi-task, multi-level learning
import pandas as pd import numpy as np class DeltaModel: """ This class continues to buy an equity until the average delta over the last 10 periods exceeds -5.5% """ def __init__(self, data, ticker, *args, **kwargs): super(DeltaModel, self).__init__() self.data = data self.ticker = ticker self.prices = list() self.gains = list() self.delta_percent_ten = list() self.delta_avg_percent = 0.0 def run(self): self.prices = list(reversed(self.data.loc[str(self.ticker)]['close'].tolist())) if len(self.prices) >= 2: current = self.prices[-1] previous = self.prices[-2] gains_percent = ((current - previous)/previous) * 100.0 if len(self.gains) < 15: self.gains.append(gains_percent) else: del self.gains[0] self.gains.append(gains_percent) if len(self.delta_percent_ten) >= 10: smallest_gain = min(self.gains[-10:]) largest_gain = max(self.gains[-10:]) delta_percent = ((smallest_gain - largest_gain)/largest_gain) * 100.0 if len(self.delta_percent_ten) < 10: self.delta_percent_ten.append(delta_percent) else: del self.delta_percent_ten[0] self.delta_percent_ten.append(delta_percent) self.delta_avg_percent = sum(self.delta_percent_ten)/len(self.delta_percent_ten) return self.delta_avg_percent
from System import * from QuantConnect import * from QuantConnect.Indicators import * from QuantConnect.Data.Consolidators import * from QuantConnect.Data.UniverseSelection import * from QuantConnect.Orders.Fees import ConstantFeeModel from QuantConnect.Algorithm import * from QuantConnect.Algorithm.Framework.Alphas import * from QuantConnect.Algorithm.Framework.Portfolio import EqualWeightingPortfolioConstructionModel, NullPortfolioConstructionModel from QuantConnect.Algorithm.Framework.Risk import MaximumDrawdownPercentPortfolio, MaximumUnrealizedProfitPercentPerSecurity, MaximumDrawdownPercentPerSecurity from Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel from datetime import datetime, timedelta import pandas as pd import numpy as np import re import scipy as sp from collections import deque from math import ceil from itertools import chain from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import train_test_split from DeltaModel import DeltaModel from VixFix import VixFix from UniverseSelectionHelperFunctions import SelectionData, SymbolData from CompositeTradeSignals import CompositeTradeSignals from SequoiaAlphaModel import SequoiaAlphaModel from IchimokuCloudCrossOverAlphaModel import IchimokuCloudCrossOverAlphaModel from trading_algorithm import TradingAlgorithm_main class EnhancedShortTermMeanReversionAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2010, 1, 1) #Set Start Date self.SetCash(10000) #Set Strategy Cash self.SetWarmUp(timedelta(minutes=1)) # Set Algorithm to 21 days #self.SetBrokerageModel(BrokerageName.AlphaStreams) self.UniverseSettings.Resolution = Resolution.Daily self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction) # rebalance the universe selection once a month self.rebalance_flag = 0 # make sure to run the universe selection at the start of the algorithm even it's not the month start self.first_month_trade_flag = 1 self.trade_flag = 0 # Number of quantiles for sorting returns for mean reversion self.nq = 5 # Number of quantiles for sorting volatility over five-day mean reversion period self.nq_vol = 3 # the symbol list after the coarse and fine universe selection self.universe = None self.NumberOfSymbolsFine = 500 self.NumberOfSymbolsCoarse = 150 self.dollarVolumeBySymbol = {} # setup state storage in initialize method self.dataDict = {} # Benchmark self.AddEquity("SPY", Resolution.Daily) self.SetBenchmark("SPY") # weighting stuffs self.lookback = 20*6 self.portfolios = deque(maxlen=6) # SPY HIGH self.correction_flag = 0 self.SPY_avg_delta = DeltaModel(self.History(self.Symbol('SPY'), 25, Resolution.Daily), self.Symbol('SPY')).run() self.SPY_vix_data = VixFix(self.History(self.Symbol('SPY'), 100, Resolution.Daily), self.Symbol('SPY')).run() self.SPY_mom = self.MOM('SPY', 10, Resolution.Daily).Current.Value def monthly_rebalance(self): # rebalance the universe every month self.rebalance_flag = 1 def CoarseSelectionFunction(self, coarse): if self.rebalance_flag or self.first_month_trade_flag: # drop stocks which have no fundamental data or have too low prices coarse_filtered = [x for x in coarse if (x.HasFundamentalData) and (20 <= float(x.Price) <= 300) and (float(x.Volume) >= 2000000)] # We are going to use a dictionary to refer the object that will keep the moving averages for c in coarse_filtered: if c.Symbol not in self.stateData: self.stateData[c.Symbol] = SelectionData(c.Symbol, 10) # Updates the SymbolData object with current EOD price avg = self.stateData[c.Symbol] avg.update(c.EndTime, c.Price, c.Volume, c.DollarVolume) # filter the values of selectionData(sd) above SMA values = [sd for sd in self.stateData.values() if (sd.Volume > (sd.Sma.Current.Value * 1.25)) and (sd.Volume_Ratio > 2) and (sd.Fast_Vol_Ratio > 2) and (sd.Med_Vol_Ratio > 2)] # sort sd by the largest % jump in volume. values.sort(key=lambda sd: sd.Volume_Ratio, reverse=True) self.dollarVolumeBySymbol = {i.Symbol: i.DollarVolume for i in values[:self.NumberOfSymbolsCoarse]} return [x.Symbol for x in values[:self.NumberOfSymbolsCoarse]] else: return self.universe def FineSelectionFunction(self, fine): def upper_groups(x): x = np.asarray(x, dtype=np.float32) x = x[x >= 0] cutoff = np.mean(x, dtype=np.float64) #+ (np.std(x, dtype=np.float64) * 2) return cutoff if self.rebalance_flag or self.first_month_trade_flag: """ Need to do a better job with filter system with multiple parameters - currently takes the top 50% in each category and checks if stock is top 50% in each category """ # filter the stocks which have positive EV To EBITDA # filtered_fine = [x for x in fine if x.ValuationRatios.EVToEBITDA > 0 # and x.ValuationRatios.PriceChange1M > 0 # and x.CompanyReference.CountryId == "USA" # and (x.CompanyReference.PrimaryExchangeID == "NYS" or x.CompanyReference.PrimaryExchangeID == "NAS") # and x.CompanyReference.IndustryTemplateCode == "N" # and x.ValuationRatios.PERatio > 20 # and x.ValuationRatios.FirstYearEstimatedEPSGrowth > 0.2 # and x.EarningReports.BasicAverageShares.ThreeMonths * x.EarningReports.BasicEPS.TwelveMonths * x.ValuationRatios.PERatio > 5e8 # and (x.Time - x.SecurityReference.IPODate).days > 180 # and x.ValuationRatios.PEGRatio > 2 # and x.FinancialStatements.CashFlowStatement.FreeCashFlow.ThreeMonths > 10] # if len(filtered_fine) <= 0: initial_filter = [x for x in fine if x.ValuationRatios.EVToEBITDA > 0 and x.ValuationRatios.PriceChange1M > 0 and x.EarningReports.BasicAverageShares.ThreeMonths * x.EarningReports.BasicEPS.TwelveMonths * x.ValuationRatios.PERatio > 5e8 and (x.Time - x.SecurityReference.IPODate).days > 180 and x.CompanyReference.CountryId == "USA" and (x.CompanyReference.PrimaryExchangeID == "NYS" or x.CompanyReference.PrimaryExchangeID == "NAS") and x.CompanyReference.IndustryTemplateCode == "N" and x.AssetClassification.MorningstarSectorCode in [MorningstarSectorCode.FinancialServices, MorningstarSectorCode.Technology, MorningstarSectorCode.Industrials, MorningstarSectorCode.CommunicationServices, MorningstarSectorCode.ConsumerCyclical, MorningstarSectorCode.ConsumerDefensive, MorningstarSectorCode.Healthcare, MorningstarSectorCode.Utilities]] PERatio_mean = upper_groups([float(x.ValuationRatios.PERatio) for x in initial_filter if x.ValuationRatios.PERatio > 0]) PEGRatio_mean = upper_groups([float(x.ValuationRatios.PEGRatio) for x in initial_filter if x.ValuationRatios.PEGRatio >= 0]) #SustainGrowthRate_mean = upper_groups([float(x.ValuationRatios.SustainableGrowthRate) for x in initial_filter if x.ValuationRatios.SustainableGrowthRate > 0]) #GrossMargin_mean = upper_groups([float(x.OperationRatios.GrossMargin.ThreeMonths) for x in initial_filter if x.OperationRatios.GrossMargin.ThreeMonths > 0]) #NetProfitMargin_mean = upper_groups([float(x.OperationRatios.NormalizedNetProfitMargin.ThreeMonths) for x in initial_filter if x.OperationRatios.NormalizedNetProfitMargin.ThreeMonths > 0]) FreeCashFlow_mean = upper_groups([float(x.FinancialStatements.CashFlowStatement.FreeCashFlow.ThreeMonths) for x in initial_filter if x.FinancialStatements.CashFlowStatement.FreeCashFlow.ThreeMonths > 0]) FirstYREPSGrowth_mean = upper_groups([float(x.ValuationRatios.FirstYearEstimatedEPSGrowth) for x in initial_filter if x.ValuationRatios.FirstYearEstimatedEPSGrowth > 0]) second_filter = sorted(initial_filter, key = lambda x: x.ValuationRatios.PERatio > PERatio_mean, reverse = True) third_filter = sorted(initial_filter, key = lambda x: x.ValuationRatios.PEGRatio > PEGRatio_mean, reverse = True) #fourth_filter = sorted(initial_filter, key = lambda x: x.ValuationRatios.SustainableGrowthRate > SustainGrowthRate_mean, reverse = True) #fifth_filter = sorted(initial_filter, key = lambda x: x.OperationRatios.GrossMargin.ThreeMonths > NetProfitMargin_mean, reverse = True) #six_filter = sorted(initial_filter, key = lambda x: x.OperationRatios.NormalizedNetProfitMargin.ThreeMonths > NetProfitMargin_mean, reverse = True) seventh_filter = sorted(initial_filter, key = lambda x: x.FinancialStatements.CashFlowStatement.FreeCashFlow.ThreeMonths > FreeCashFlow_mean, reverse = True) eigth_filter = sorted(initial_filter, key = lambda x: x.ValuationRatios.FirstYearEstimatedEPSGrowth > FirstYREPSGrowth_mean, reverse = True) filtered_fine = list(set.intersection(*map(set, [initial_filter, second_filter, third_filter, # fourth_filter, fifth_filter, six_filter, seventh_filter, eigth_filter]))) count = len(filtered_fine) if count == 0: return [] myDict = dict() percent = float(self.NumberOfSymbolsFine / count) # select stocks with top dollar volume in every single sector for key in ["N"]: value = [x for x in filtered_fine if x.CompanyReference.IndustryTemplateCode == key] value = sorted(value, key=lambda x: self.dollarVolumeBySymbol[x.Symbol], reverse = True) myDict[key] = value[:ceil(len(value) * percent)] topFine = list(chain.from_iterable(myDict.values()))[:self.NumberOfSymbolsFine] self.universe = [f.Symbol for f in topFine[:int(len(topFine) * 0.5)]] # [:int(len(topFine) * 0.33)] self.rebalance_flag = 0 self.first_month_trade_flag = 0 self.trade_flag = 1 return self.universe def OnData(self, data): pass def short(self): if self.universe is None: return SPY_Velocity = 0 self.long_leverage = 0 self.short_leverage = 0 # request the history of benchmark pri = self.History(["SPY"], 75, Resolution.Daily) pos_one = (pri.loc["SPY"]['close'][-1]) pos_six = (pri.loc["SPY"]['close'][-75:].mean()) # calculate velocity of the benchmark velocity_stop = (pos_one - pos_six)/100.0 SPY_Velocity = velocity_stop if SPY_Velocity > 0.0: self.long_leverage = 1.8 self.short_leverage = -0.0 else: self.long_leverage = 1.1 self.short_leverage = -0.7 if (self.SPY_avg_delta <= -5.5 and self.SPY_vix_data == 'No'): return for symbol in self.shorts: if len(self.shorts) + self.existing_shorts == 0: return self.AddEquity(symbol, Resolution.Daily) self.SetHoldings(symbol, 0.8*(self.short_leverage/(len(self.shorts) + self.existing_shorts))) def long(self): if self.universe is None: return if (self.SPY_avg_delta <= -5.5 and self.SPY_vix_data == 'No'): return for symbol in self.longs: if len(self.longs) + self.existing_longs == 0: return self.AddEquity(symbol, Resolution.Daily) self.SetHoldings(symbol, 1*(self.long_leverage/(len(self.longs) + self.existing_longs))) def get_prices(self): if self.universe is None: return # Get the last 30 days of prices for every stock in our universe prices = {} hist = self.History(self.universe, 30, Resolution.Daily) for i in self.universe: try: if str(i) in hist.index.levels[0]: if self.technical_setup(i): prices[i.Value] = hist.loc[str(i)]['close'] except: pass if len(prices.keys()) <= 0: return df_prices = pd.DataFrame(prices, columns = prices.keys()) # calculate the daily log return daily_rets = np.log(df_prices/df_prices.shift(1)) # calculate the latest return but skip the most recent price rets = (df_prices.iloc[-2] - df_prices.iloc[0]) / df_prices.iloc[0] # standard deviation of the daily return stdevs = daily_rets.std(axis = 0) try: self.ret_qt = pd.qcut(rets, 5, labels=False) + 1 self.stdev_qt = pd.qcut(stdevs, 3, labels=False) + 1 except: self.ret_qt = pd.qcut(rets, 5, labels=False, duplicates='drop') + 1 self.stdev_qt = pd.qcut(stdevs, 3, labels=False, duplicates='drop') + 1 self.longs = list((self.ret_qt[self.ret_qt == 1].index) & (self.stdev_qt[self.stdev_qt < 3].index)) self.shorts = list((self.ret_qt[self.ret_qt == self.nq].index) & (self.stdev_qt[self.stdev_qt < 3].index)) def daily_rebalance(self): # rebalance the position in portfolio every day if self.universe is None: return if self.longs is None: return if self.shorts is None: return self.existing_longs = 0 self.existing_shorts = 0 if (self.SPY_avg_delta <= - 5.5 and self.SPY_vix_data == 'No') or (self.SPY_mom < 0.0): for symbol in self.Portfolio.Invested: self.Liquidate(symbol) elif (self.SPY_avg_delta <= -5.5 and self.SPY_vix_data == 'Yes'): self.monthly_rebalance self.daily_rebalance elif (self.SPY_avg_delta > -5.5) or (self.SPY_avg_delta <= -5.5 and self.SPY_vix_data == 'Yes') or (self.SPY_mom >= 0.25): for symbol in self.Portfolio.Keys: if (symbol.Value != 'SPY') and (symbol.Value in self.ret_qt.index) and (self.Portfolio[symbol].IsLong): current_quantile = self.ret_qt.loc[symbol.Value] avg_delta = DeltaModel(self.History(symbol, 25, Resolution.Daily), symbol).run() vix_data = VixFix(self.History(symbol, 50, Resolution.Daily), symbol).run() if avg_delta > -5.5 or (avg_delta <= -5.5 and vix_data == 'Yes'): # create a 20 day exponential moving average self.fast = self.EMA(symbol, 20, Resolution.Daily).Current.Value # create a 50 day simple moving average self.slow = self.EMA(symbol, 50, Resolution.Daily).Current.Value # create a 200 day simple moving average self.baseline = self.SMA(symbol, 200, Resolution.Daily).Current.Value if (self.fast >= self.slow > self.baseline): if self.ttm_squeeze(symbol) or (self.ema_squeeze(symbol) >= 4) or (self.go_time_indicators(symbol) >= 3): self.TradeOptions(symbol) self.MarketOrder(symbol, 100) self.update_ticket(symbol) elif self.ttm_squeeze(symbol) or (self.ema_squeeze(symbol) >= 3) or (self.go_time_indicators(symbol) >= 2): if self.Portfolio[symbol].Quantity > 0: if (current_quantile == 1) and (symbol not in self.longs): self.existing_longs += 2 elif (current_quantile > 1) and (symbol not in self.shorts): self.Liquidate(symbol) self.longs.remove(symbol) self.shorts.append(symbol) elif self.Portfolio[symbol].Quantity < 0: if (current_quantile == self.nq) and (symbol not in self.shorts): self.existing_shorts += 1 elif (current_quantile < self.nq) and (symbol not in self.longs): self.Liquidate(symbol) elif avg_delta <= -5.5: try: self.Liquidate(symbol) self.universe.remove(symbol) self.longs.remove(symbol) self.shorts.remove(symbol) except: pass self.AddEquity('SPY', Resolution.Daily) self.SPY_avg_delta = DeltaModel(self.History(self.Symbol('SPY'), 25, Resolution.Daily), self.Symbol('SPY')).run() self.SPY_vix_data = VixFix(self.History(self.Symbol('SPY'), 100, Resolution.Daily), self.Symbol('SPY')).run() self.SPY_mom = self.MOM('SPY', 10, Resolution.Daily).Current.Value self.take_profit() def technical_setup(self, i): x = re.sub('[(){}<>| ]', '', str(i)) self.AddEquity(x, Resolution.Daily) self.History(self.Symbol(x), 200, Resolution.Daily) if self.EMA(x, 50, Resolution.Daily).Current.Value >= self.SMA(x, 200, Resolution.Daily).Current.Value: if self.RSI(x, 14, Resolution.Daily).Current.Value < 30: return x def ttm_squeeze(self, symbol): self.AddEquity(symbol, Resolution.Daily) #TTM Squeeze sma_twenty = self.SMA(symbol, 20, Resolution.Daily).Current.Value std = self.STD(symbol, 20, Resolution.Daily).Current.Value lower_band = sma_twenty - 2*std upper_band = sma_twenty + 2*std atr = self.ATR(symbol, 20, Resolution.Daily).Current.Value lower_keltner = sma_twenty - 2*atr upper_keltner = sma_twenty + 2*atr return lower_band > lower_keltner and upper_band < upper_keltner def ema_squeeze(self, symbol): self.AddEquity(symbol, Resolution.Daily) # create a 8 day exponential moving average fastest = self.EMA(symbol, 8, Resolution.Daily).Current.Value # create a 14 day exponential moving average faster = self.EMA(symbol, 14, Resolution.Daily).Current.Value # create a 21 day exponential moving average fast = self.EMA(symbol, 21, Resolution.Daily).Current.Value # create a 34 day exponential moving average slow = self.EMA(symbol, 34, Resolution.Daily).Current.Value # create a 50 day exponential moving average slower = self.EMA(symbol, 55, Resolution.Daily).Current.Value # creat a 200 day simple moving average slowest = self.SMA(symbol, 200, Resolution.Daily).Current.Value # define a small tolerance on our checks to avoid bouncing tolerance = 0.025 condition_one = ((fastest * (1-tolerance)) >= (faster * (1+tolerance))) condition_two = ((faster * (1-tolerance)) >= (fast * (1+tolerance))) condition_three = ((fast * (1-tolerance)) >= (slow * (1+tolerance))) condition_four = ((slow * (1-tolerance)) >= (slower * (1+tolerance))) condition_five = (slower > slowest) conditions = [condition_one, condition_two, condition_three, condition_four, condition_five] count = 0 for condition in conditions: if condition is True: count += 1 return count def go_time_indicators(self, symbol): self.AddEquity(symbol, Resolution.Daily) # Momentum & Volume & Trend # RSI <= 30 = buy rsi = self.RSI(symbol, 14, MovingAverageType.Simple, Resolution.Daily).Current.Value # William's %R <= -20 = buy wilr = self.WILR(symbol, 14, Resolution.Daily).Current.Value # MACD current < signal = buy macd = self.MACD(symbol, 12, 26, 9, Resolution.Daily) macd_current = macd.Current.Value macd_signal = macd.Signal.Current.Value macd_fast = macd.Fast.Current.Value macd_signal_delta = (macd_current - macd_signal)/(macd_fast) tolerance = 0.0025 condition_one = (rsi <= 30) condition_two = (wilr <= -20) condition_three = (macd_signal_delta > tolerance) conditions = [condition_one, condition_two, condition_three] count = 0 for condition in conditions: if condition is True: count += 1 return count def OnOrderEvent(self, orderEvent): if orderEvent.Status == OrderStatus.Invalid: self.Log(str(orderEvent)) if orderEvent.Status == OrderStatus.Filled: order = self.Transactions.GetOrderById(orderEvent.OrderId) if order.Tag == 'pt': # If hit profit target, update stop order quantity updateSettings = UpdateOrderFields() updateSettings.Quantity = self.stops[orderEvent.Symbol].Quantity - orderEvent.Quantity self.stops[orderEvent.Symbol].Update(updateSettings) elif order.Tag == 'sl': # If hit stop loss, cancel profit target orders self.Transactions.CancelOpenOrders(orderEvent.Symbol, "Hit stop price") def take_profit(self): if self.Portfolio.TotalUnrealizedProfit > (0.2 * self.Portfolio.TotalPortfolioValue): stocks_invested = [x.Key for x in self.Portfolio if x.Value.Invested] stocks_invested_by_profit = sorted(stocks_invested, key=lambda x: self.Portfolio[x].UnrealizedProfit, reverse=True) for stock in stocks_invested_by_profit: holding = self.Portfolio[stock].Quantity avg_price = self.Portfolio[stock].AveragePrice holdings_profit = self.Portfolio[stock].UnrealizedProfit holdings_cost = avg_price * holding if stock in self.shorts: if holdings_profit >= holdings_cost * 1.5: self.MarketOrder(stock, -holding) elif stock in self.longs: half_holding = int(holding/2.0) if holdings_profit >= holdings_cost * 1.5: self.MarketOrder(stock, -half_holding) elif self.SPY_mom <= 0.0: for stock in self.Portfolio: try: if stock.Value.Invested: self.Liquidate(stock) except: pass try: if self.longs and self.shorts: stocks_invested = [x.Key for x in self.Portfolio if x.Value.Invested] # liquidate stocks not in the trading list for stock in stocks_invested: holding = self.Portfolio[stock].Quantity avg_price = self.Portfolio[stock].AveragePrice holdings_profit = self.Portfolio[stock].UnrealizedProfit holdings_cost = avg_price * holding if holdings_profit > holdings_cost * 1.5: self.Liquidate(stock) except: pass def update_ticket(self, stock): """ Uses mean reversion to update stop loss and limit orders. """ self.mean = self.EMA(stock, 8, Resolution.Daily) self.atr = self.ATR(stock, 8, Resolution.Daily) # # Mean reversion when price is too low if self.Securities[stock].Price > (self.Portfolio[stock].AveragePrice * 0.95): limitTicket = self.LimitOrder(stock, -self.Portfolio[stock].Quantity, self.Securities[stock].Price + (0.5 * self.atr.Current.Value)) stopTicket = self.StopMarketOrder(stock, -self.Portfolio[stock].Quantity, (self.Portfolio[stock].AveragePrice * 0.90)) # Mean reversion when price is too high if self.Securities[stock].Close > self.mean.Current.Value + (1.5 * self.atr.Current.Value): limitTicket = self.LimitOrder(stock, -self.Portfolio[stock].Quantity, self.Securities[stock].Price + (self.atr.Current.Value)) stopTicket = self.StopMarketOrder(stock, -self.Portfolio[stock].Quantity, (self.Securities[stock].Price * 0.95)) def TradeOptions(self, symbol): equity = self.AddEquity(symbol, Resolution.Minute) option = self.AddOption(symbol, Resolution.Minute) self.symbol = option.Symbol equity.SetDataNormalizationMode(DataNormalizationMode.Raw) option.SetFilter(-3, +3, timedelta(0), timedelta(60)) # use the underlying equity as the benchmark self.SetBenchmark(equity.Symbol) self.call = symbol # Initialize the call contract if slice.OptionChains.Count == 0: return for i in slice.OptionChains: if i.Key != self.symbol: continue chain = i.Value call = [x for x in chain if x.Right == 0] # filter the call options contracts # sorted the contracts according to their expiration dates and choose the ATM options contracts = sorted(sorted(call, key = lambda x: x.Expiry, reverse=True), key = lambda x: abs(chain.Underlying.Price - x.Strike)) if len(contracts) == 0: return contract = contracts[0] self.call = contract.Symbol if self.Securities[self.call].Price < (0.01 * self.Portfolio.TotalPortfolioValue): num_contracts = int((self.Portfolio.TotalPortfolioValue * 0.01)/self.Securities[self.call].Price) #if num_contracts > 0: self.Sell(symbol, num_contracts) # short the call options if self.Portfolio[symbol].Quantity == 0: self.Buy(symbol, 100) # buy 100 the underlying stock self.Log("The stock price at time 0 S(0): {}".format(self.Securities[symbol].Price)) def MarketOpen(self): return self.Time.hour != 0 and self.Time.minute == 1
from clr import AddReference AddReference("QuantConnect.Algorithm.Framework") AddReference("QuantConnect.Indicators") AddReference("QuantConnect.Common") from QuantConnect import * from QuantConnect.Indicators import * from QuantConnect.Algorithm.Framework.Alphas import * from QuantConnect.Data.UniverseSelection import * import math import numpy as np import pandas as pd import scipy as sp from collections import deque from datetime import timedelta from clr import AddReference class SelectionData(object): def __init__(self, symbol, period): self.Symbol = symbol self.Volume = 0 self.Volume_Ratio = 0 self.DollarVolume = 0 self.Fast_Vol_Ratio = 0 self.Med_Vol_Ratio = 0 self.Slow_Vol_Ratio = 0 self.Sma = SimpleMovingAverage(period) self.Slow = SimpleMovingAverage(100) self.Med = SimpleMovingAverage(50) self.Fast = SimpleMovingAverage(20) def update(self, time, Price, Volume, DollarVolume): self.Volume = Volume self.DollarVolume = DollarVolume self.Price = Price if self.Sma.Update(time,Volume): # get ratio of this volume bar vs previous 10 before it. self.Volume_Ratio = Volume / self.Sma.Current.Value if self.Fast.Update(time,Volume): self.Fast_Vol_Ratio = Volume / self.Fast.Current.Value if self.Med.Update(time,Volume): self.Med_Vol_Ratio = Volume / self.Med.Current.Value if self.Slow.Update(time,Volume): self.Slow_Vol_Ratio = Volume / self.Slow.Current.Value class SymbolData: def __init__(self, symbol, lookback): self.Symbol = symbol self.ROC = RateOfChange(lookback) self.Volume = None
from System import * from QuantConnect import * from QuantConnect.Indicators import * from QuantConnect.Data.Consolidators import * from QuantConnect.Data.UniverseSelection import * from QuantConnect.Orders.Fees import ConstantFeeModel from QuantConnect.Algorithm import * from QuantConnect.Algorithm.Framework.Alphas import * from QuantConnect.Algorithm.Framework.Portfolio import EqualWeightingPortfolioConstructionModel, NullPortfolioConstructionModel from QuantConnect.Algorithm.Framework.Risk import MaximumDrawdownPercentPortfolio, MaximumUnrealizedProfitPercentPerSecurity, MaximumDrawdownPercentPerSecurity from Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel from datetime import datetime, timedelta import pandas as pd import numpy as np import re from math import ceil from itertools import chain from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import train_test_split from DeltaModel import DeltaModel from VixFix import VixFix from MyAlphaModel import MyAlphaModel, SymbolData from UniverseSelectionHelperFunctions import SelectionData from CompositeTradeSignals import CompositeTradeSignals from SequoiaAlphaModel import SequoiaAlphaModel from IchimokuCloudCrossOverAlphaModel import IchimokuCloudCrossOverAlphaModel from trading_algorithm import TradingAlgorithm_main class EnhancedShortTermMeanReversionAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2010, 1, 1) #Set Start Date self.SetEndDate(2020, 10, 15) #Set End Date self.SetCash(10000) #Set Strategy Cash self.SetWarmUp(timedelta(minutes=1)) # Set Algorithm to 21 days #self.SetBrokerageModel(BrokerageName.AlphaStreams) self.UniverseSettings.Resolution = Resolution.Daily self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction) # rebalance the universe selection once a month self.rebalance_flag = 0 # make sure to run the universe selection at the start of the algorithm even it's not the month start self.first_month_trade_flag = 1 self.trade_flag = 0 # Number of quantiles for sorting returns for mean reversion self.nq = 5 # Number of quantiles for sorting volatility over five-day mean reversion period self.nq_vol = 3 # the symbol list after the coarse and fine universe selection self.universe = None self.NumberOfSymbolsFine = 500 self.NumberOfSymbolsCoarse = 150 self.dollarVolumeBySymbol = {} # setup state storage in initialize method self.stateData = { } # Benchmark self.AddEquity("SPY", Resolution.Daily) self.SetBenchmark("SPY") # SPY HIGH self.correction_flag = 0 self.SPY_avg_delta = DeltaModel(self.History(self.Symbol('SPY'), 25, Resolution.Daily), self.Symbol('SPY')).run() self.SPY_vix_data = VixFix(self.History(self.Symbol('SPY'), 100, Resolution.Daily), self.Symbol('SPY')).run() self.SPY_mom = self.MOM('SPY', 10, Resolution.Daily).Current.Value self.Schedule.On(self.DateRules.MonthStart("SPY"), self.TimeRules.At(0, 0), Action(self.monthly_rebalance)) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 303), Action(self.get_prices)) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 302), Action(self.daily_rebalance)) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 301), Action(self.short)) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 300), Action(self.long)) def monthly_rebalance(self): # rebalance the universe every month self.rebalance_flag = 1 def CoarseSelectionFunction(self, coarse): if self.rebalance_flag or self.first_month_trade_flag: # drop stocks which have no fundamental data or have too low prices selected = [x for x in coarse if (x.HasFundamentalData) and (20 <= float(x.Price) <= 300) and (float(x.Volume) >= 2000000)] # rank the stocks by dollar volume and choose the top 150 filtered = sorted(selected, key=lambda x: x.DollarVolume > 4e7, reverse=True)[:self.NumberOfSymbolsCoarse] self.dollarVolumeBySymbol = { i.Symbol: i.DollarVolume for i in filtered} return [x.Symbol for x in filtered] # [:self.NumberOfSymbolsCoarse] else: return self.universe def FineSelectionFunction(self, fine): if self.rebalance_flag or self.first_month_trade_flag: # filter the stocks which have positive EV To EBITDA filtered_fine = [x for x in fine if x.ValuationRatios.EVToEBITDA > 0 and x.ValuationRatios.PriceChange1M > 0 and x.CompanyReference.CountryId == "USA" and (x.CompanyReference.PrimaryExchangeID == "NYS" or x.CompanyReference.PrimaryExchangeID == "NAS") and x.CompanyReference.IndustryTemplateCode == "N" and x.ValuationRatios.PERatio > 20 and x.ValuationRatios.FirstYearEstimatedEPSGrowth > 0.2 and x.EarningReports.BasicAverageShares.ThreeMonths * x.EarningReports.BasicEPS.TwelveMonths * x.ValuationRatios.PERatio > 5e8 and (x.Time - x.SecurityReference.IPODate).days > 180 and x.ValuationRatios.PEGRatio > 2 and x.FinancialStatements.CashFlowStatement.FreeCashFlow.ThreeMonths > 10] if len(filtered_fine) <= 0: initial_filter = [x for x in fine if x.ValuationRatios.EVToEBITDA > 0 and x.ValuationRatios.PriceChange1M > 0 and x.EarningReports.BasicAverageShares.ThreeMonths * x.EarningReports.BasicEPS.TwelveMonths * x.ValuationRatios.PERatio > 5e8 and (x.Time - x.SecurityReference.IPODate).days > 180 and x.CompanyReference.CountryId == "USA" and (x.CompanyReference.PrimaryExchangeID == "NYS" or x.CompanyReference.PrimaryExchangeID == "NAS") and x.CompanyReference.IndustryTemplateCode == "N" and x.AssetClassification.MorningstarSectorCode in [MorningstarSectorCode.FinancialServices, MorningstarSectorCode.Technology, MorningstarSectorCode.Industrials, MorningstarSectorCode.CommunicationServices, MorningstarSectorCode.ConsumerCyclical, MorningstarSectorCode.ConsumerDefensive, MorningstarSectorCode.Healthcare, MorningstarSectorCode.Utilities]] second_filter = sorted(initial_filter, key = lambda x: x.ValuationRatios.PERatio > 5, reverse = True)[:int(len(initial_filter)/3)] third_filter = sorted(initial_filter, key = lambda x: x.ValuationRatios.PEGRatio > 0.5, reverse = True)[:int(len(initial_filter)/2.5)] fourth_filter = sorted(initial_filter, key = lambda x: x.FinancialStatements.CashFlowStatement.FreeCashFlow.ThreeMonths > 5, reverse = True)[:int(len(initial_filter)/2.5)] fifth_filter = sorted(initial_filter, key = lambda x: x.ValuationRatios.FirstYearEstimatedEPSGrowth > 0.1, reverse = True)[:int(len(initial_filter)/2.5)] filtered_fine = list() for symbol in initial_filter: if symbol in second_filter: if symbol in third_filter: if symbol in fourth_filter: if symbol in fifth_filter: filtered_fine.append(symbol) count = len(filtered_fine) if count == 0: return [] myDict = dict() percent = float(self.NumberOfSymbolsFine / count) # select stocks with top dollar volume in every single sector for key in ["N"]: value = [x for x in filtered_fine if x.CompanyReference.IndustryTemplateCode == key] value = sorted(value, key=lambda x: self.dollarVolumeBySymbol[x.Symbol], reverse = True) myDict[key] = value[:ceil(len(value) * percent)] topFine = list(chain.from_iterable(myDict.values()))[:self.NumberOfSymbolsFine] self.universe = [f.Symbol for f in topFine] self.rebalance_flag = 0 self.first_month_trade_flag = 0 self.trade_flag = 1 return self.universe def OnData(self, data): pass def short(self): if self.universe is None: return SPY_Velocity = 0 self.long_leverage = 0 self.short_leverage = 0 # request the history of benchmark pri = self.History(["SPY"], 75, Resolution.Daily) pos_one = (pri.loc["SPY"]['close'][-1]) pos_six = (pri.loc["SPY"]['close'][-75:].mean()) # calculate velocity of the benchmark velocity_stop = (pos_one - pos_six)/100.0 SPY_Velocity = velocity_stop if SPY_Velocity > 0.0: self.long_leverage = 1.8 self.short_leverage = -0.0 else: self.long_leverage = 1.1 self.short_leverage = -0.7 for symbol in self.shorts: if len(self.shorts) + self.existing_shorts == 0: return self.AddEquity(symbol, Resolution.Daily) self.SetHoldings(symbol, 0.8*(self.short_leverage/(len(self.shorts) + self.existing_shorts))) def long(self): if self.universe is None: return for symbol in self.longs: if len(self.longs) + self.existing_longs == 0: return self.AddEquity(symbol, Resolution.Daily) self.SetHoldings(symbol, (self.long_leverage/(len(self.longs) + self.existing_longs))) def get_prices(self): if self.universe is None: return # Get the last 25 days of prices for every stock in our universe prices = {} hist = self.History(self.universe, 25, Resolution.Daily) for i in self.universe: if str(i) in hist.index.levels[0]: if self.technical_setup(i): prices[i.Value] = hist.loc[str(i)]['close'] df_prices = pd.DataFrame(prices, columns = prices.keys()) # calculate the daily log return daily_rets = np.log(df_prices/df_prices.shift(1)) # calculate the latest return but skip the most recent price rets = (df_prices.iloc[-2] - df_prices.iloc[0]) / df_prices.iloc[0] # standard deviation of the daily return stdevs = daily_rets.std(axis = 0) try: self.ret_qt = pd.qcut(rets, 5, labels=False) + 1 self.stdev_qt = pd.qcut(stdevs, 3, labels=False) + 1 except: self.ret_qt = pd.qcut(rets, 5, labels=False, duplicates='drop') + 1 self.stdev_qt = pd.qcut(stdevs, 3, labels=False, duplicates='drop') + 1 self.longs = list((self.ret_qt[self.ret_qt == 1].index) & (self.stdev_qt[self.stdev_qt < 3].index)) self.shorts = list((self.ret_qt[self.ret_qt == self.nq].index) & (self.stdev_qt[self.stdev_qt < 3].index)) def daily_rebalance(self): # rebalance the position in portfolio every day if self.universe is None: return if self.longs is None: return if self.shorts is None: return self.existing_longs = 0 self.existing_shorts = 0 if (self.SPY_avg_delta <= - 5.5 and self.SPY_vix_data == 'No') or (self.SPY_mom < 0.0): if self.correction_flag == 0: for symbol in self.Portfolio.Invested: self.Liquidate(symbol) self.correction_flag = 1 elif self.correction_flag == 1: return elif (self.SPY_avg_delta <= -5.5 and self.SPY_vix_data == 'Yes'): if self.correction_flag == 1: self.monthly_rebalance self.daily_rebalance self.correction_flag = 0 return elif self.correction_flag == 0: pass elif (self.SPY_avg_delta > -5.5) or (self.SPY_avg_delta <= -5.5 and self.SPY_vix_data == 'Yes') or (self.SPY_mom >= 0.0): for symbol in self.Portfolio.Keys: if (symbol.Value != 'SPY') and (symbol.Value in self.ret_qt.index) and (self.Portfolio[symbol].IsLong): current_quantile = self.ret_qt.loc[symbol.Value] avg_delta = DeltaModel(self.History(symbol, 25, Resolution.Daily), symbol).run() vix_data = VixFix(self.History(symbol, 50, Resolution.Daily), symbol).run() if avg_delta > -5.5 or (avg_delta <= -5.5 and vix_data == 'Yes'): def ttm_squeeze(symbol): #TTM Squeeze sma_twenty = self.SMA(symbol, 20, Resolution.Daily).Current.Value std = self.STD(symbol, 20, Resolution.Daily).Current.Value lower_band = sma_twenty - 2*std upper_band = sma_twenty + 2*std atr = self.ATR(symbol, 20, Resolution.Daily).Current.Value lower_keltner = sma_twenty - 2*atr upper_keltner = sma_twenty + 2*atr return lower_band > lower_keltner and upper_band < upper_keltner def ema_squeeze(symbol): # create a 8 day exponential moving average fastest = self.EMA(symbol, 8, Resolution.Daily).Current.Value # create a 14 day exponential moving average faster = self.EMA(symbol, 14, Resolution.Daily).Current.Value # create a 21 day exponential moving average fast = self.EMA(symbol, 21, Resolution.Daily).Current.Value # create a 34 day exponential moving average slow = self.EMA(symbol, 34, Resolution.Daily).Current.Value # create a 50 day exponential moving average slower = self.EMA(symbol, 55, Resolution.Daily).Current.Value # creat a 200 day simple moving average slowest = self.SMA(symbol, 200, Resolution.Daily).Current.Value # define a small tolerance on our checks to avoid bouncing tolerance = 0.025 condition_one = ((fastest * (1-tolerance)) >= (faster * (1+tolerance))) condition_two = ((faster * (1-tolerance)) >= (fast * (1+tolerance))) condition_three = ((fast * (1-tolerance)) >= (slow * (1+tolerance))) condition_four = ((slow * (1-tolerance)) >= (slower * (1+tolerance))) condition_five = (slower > slowest) conditions = [condition_one, condition_two, condition_three, condition_four] count = 0 for condition in conditions: if condition is True: count += 1 if condition_five is True: return count # create a 50 day exponential moving average self.slow = self.EMA(symbol, 21, Resolution.Daily).Current.Value # creat a 200 day simple moving average self.slower = self.SMA(symbol, 55, Resolution.Daily).Current.Value if (self.slow > self.slower): if ttm_squeeze(symbol) or (ema_squeeze(symbol) >= 3): if self.Portfolio[symbol].Quantity > 0: if (current_quantile == 1) and (symbol not in self.longs): self.existing_longs += 2 self.update_ticket(symbol) elif (current_quantile > 1) and (symbol not in self.shorts): #self.Liquidate(symbol) self.longs.remove(symbol) self.shorts.append(symbol) elif self.Portfolio[symbol].Quantity < 0: if (current_quantile == self.nq) and (symbol not in self.shorts): self.existing_shorts += 1 self.update_ticket(symbol) elif (current_quantile < self.nq) and (symbol not in self.longs): self.Liquidate(symbol) elif avg_delta <= -5.5: try: self.Liquidate(symbol) self.universe.remove(symbol) self.longs.remove(symbol) self.shorts.remove(symbol) except: pass self.AddEquity('SPY', Resolution.Daily) self.SPY_avg_delta = DeltaModel(self.History(self.Symbol('SPY'), 25, Resolution.Daily), self.Symbol('SPY')).run() self.SPY_vix_data = VixFix(self.History(self.Symbol('SPY'), 100, Resolution.Daily), self.Symbol('SPY')).run() self.SPY_mom = self.MOM('SPY', 10, Resolution.Daily).Current.Value self.take_profit() def OnOrderEvent(self, orderEvent): if orderEvent.Status == OrderStatus.Invalid: self.Log(str(orderEvent)) if orderEvent.Status == OrderStatus.Filled: order = self.Transactions.GetOrderById(orderEvent.OrderId) if order.Tag == 'pt': # If hit profit target, update stop order quantity updateSettings = UpdateOrderFields() updateSettings.Quantity = self.stops[orderEvent.Symbol].Quantity - orderEvent.Quantity self.stops[orderEvent.Symbol].Update(updateSettings) elif order.Tag == 'sl': # If hit stop loss, cancel profit target orders self.Transactions.CancelOpenOrders(orderEvent.Symbol, "Hit stop price") def technical_setup(self, i): x = re.sub('[(){}<>| ]', '', str(i)) self.AddEquity(x, Resolution.Daily) self.History(self.Symbol(x), 200, Resolution.Daily) if self.EMA(x, 50, Resolution.Daily).Current.Value >= self.EMA(x, 200, Resolution.Daily).Current.Value: if self.RSI(x, 14, Resolution.Daily).Current.Value < 30: return x def take_profit(self): if self.Portfolio.TotalUnrealizedProfit > (0.2 * self.Portfolio.TotalPortfolioValue): stocks_invested = [x.Key for x in self.Portfolio if x.Value.Invested] stocks_invested_by_profit = sorted(stocks_invested, key=lambda x: self.Portfolio[x].UnrealizedProfit, reverse=True) for stock in stocks_invested_by_profit: holding = self.Portfolio[stock].Quantity avg_price = self.Portfolio[stock].AveragePrice holdings_profit = self.Portfolio[stock].UnrealizedProfit holdings_cost = avg_price * holding if stock in self.shorts: if holdings_profit >= holdings_cost * 1.5: self.MarketOrder(stock, -holding) elif stock in self.longs: half_holding = int(holding/2.0) if holdings_profit >= holdings_cost * 1.5: self.MarketOrder(stock, -half_holding) elif self.longs and self.shorts: stocks_invested = [x.Key for x in self.Portfolio if x.Value.Invested] # liquidate stocks not in the trading list for stock in stocks_invested: holding = self.Portfolio[stock].Quantity avg_price = self.Portfolio[stock].AveragePrice holdings_profit = self.Portfolio[stock].UnrealizedProfit holdings_cost = avg_price * holding if holdings_profit > holdings_cost * 1.5: self.Liquidate(stock) elif self.SPY_mom <= 0.0: for stock in self.Portfolio: try: if stock.Value.Invested: self.Liquidate(stock) except: pass def update_ticket(self, stock): """ Uses mean reversion to update stop loss and limit orders. """ self.mean = self.EMA(stock, 20, Resolution.Daily) self.atr = self.ATR(stock, 20, Resolution.Daily) # Mean reversion when price is too low if self.Securities[stock].Price < self.mean.Current.Value - 2.0 * self.atr.Current.Value: holding = self.Portfolio[stock].Quantity marketTicket = self.MarketOrder(stock, quantity) limitTicket = self.LimitOrder(stock, -self.Portfolio[stock].Quantity, self.Securities[stock].Price + self.atr.Current.Value) stopTicket = self.StopMarketOrder(stock, -self.Portfolio[stock].Quantity, self.Securities[stock].Price - self.atr.Current.Value) # Mean reversion when price is too high if self.Securities[stock].Price > self.mean.Current.Value + 2.0 * self.atr.Current.Value: holding = self.Portfolio[stock].Quantity marketTicket = self.MarketOrder(stock, -holding) limitTicket = self.LimitOrder(stock, self.Portfolio[stock].Quantity, self.Securities[stock].Price + self.atr.Current.Value) stopTicket = self.StopMarketOrder(stock, self.Portfolio[stock].Quantity, self.Securities[stock].Price + self.atr.Current.Value) def TradeOptions(self, symbol): equity = self.AddEquity(symbol, Resolution.Minute) option = self.AddOption(symbol, Resolution.Minute) self.symbol = option.Symbol equity.SetDataNormalizationMode(DataNormalizationMode.Raw) option.SetFilter(-3, +3, timedelta(0), timedelta(60)) # use the underlying equity as the benchmark self.SetBenchmark(equity.Symbol) self.call = symbol # Initialize the call contract if slice.OptionChains.Count == 0: return for i in slice.OptionChains: if i.Key != self.symbol: continue chain = i.Value call = [x for x in chain if x.Right == 0] # filter the call options contracts # sorted the contracts according to their expiration dates and choose the ATM options contracts = sorted(sorted(call, key = lambda x: x.Expiry, reverse=True), key = lambda x: abs(chain.Underlying.Price - x.Strike)) if len(contracts) == 0: return contract = contracts[0] self.call = contract.Symbol self.Sell(symbol, 1) # short the call options if self.Portfolio[symbol].Quantity == 0: self.Buy(symbol, 100) # buy 100 the underlying stock self.Log("The stock price at time 0 S(0): {}".format(self.Securities[symbol].Price)) def MarketOpen(self): return self.Time.hour != 0 and self.Time.minute == 1
import pandas as pd import numpy as np from System import * from QuantConnect import * from QuantConnect.Data import * from QuantConnect.Algorithm import * from QuantConnect.Indicators import * from System.Collections.Generic import List class CompositeTradeSignals: """ Bullish Count > 7. Max 14 Bearish Count < -10. Max -21 """ def __init__(self, symbol, trade, *args, **kwargs): super(CompositeTradeSignals, self).__init__() self.symbol = symbol self.trade = trade self.count = 0 def StochRSI(self, period): RSI = self.RSI(self.symbol, period, Resolution.Daily).Current.Value HH_RSI = IndicatorExtensions.MAX(RSI, period).Current.Value LL_RSI = IndicatorExtensions.MIN(RSI, period).Current.Value StochRSI = (RSI - LL_RSI) / (HH_RSI - LL_RSI).Current.Value StochRSI_Avg = IndicatorExtensions.SMA(StochRSI, period).Current.Value return StochRSI, StochRSI_Avg if trade == 'Buy': # EMA Cross fastest = self.EMA(self.symbol, 8, Resolution.Daily).Current.Value fast = self.EMA(self.symbol, 14, Resolution.Daily).Current.Value slow = self.EMA(self.symbol, 20, Resolution.Daily).Current.Value if fast > slow: self.count += 1 if fastest > fast: self.count += 1 # StochRSI and RSI Crossover StochRSI, StochRSI_Avg = StochRSI(self.symbol, period = 14) if (StochRSI < 30) and (StochRSI_Avg < 30) and (StochRSI > StochRSI_Avg): self.count += 5 else: if StochRSI < 30: self.count += 1 if StochRSI_Avg < 30: self.count += 1 if StochRSI > StochRSI_Avg: self.count += 1 # Momentum mom = self.MOM(self.symbol, 10, Resolution.Daily).Current.Value if mom > 0.0: self.count += 1 # Ultimate Oscillator ultosc = self.ULTOSC(self.symbol, 7, 14, 28, Resolution.Daily).Current.Value if ultosc > 50: self.count += 1 # CandlestickPatterns if self.CandlestickPatterns.MatHold(self.symbol) == 1: self.count += 1 if self.CandlestickPatterns.UpsideGapTwoCrows(self.symbol) == 1: self.count += 1 elif trade == 'Sell': # EMA Cross fastest = self.EMA(self.symbol, 8, Resolution.Daily).Current.Value fast = self.EMA(self.symbol, 14, Resolution.Daily).Current.Value slow = self.EMA(self.symbol, 20, Resolution.Daily).Current.Value if fast < slow: self.count -= 1 if fastest < fast: self.count -= 1 # RSI and RSI Crossover for period in [14, 21]: StochRSI, StochRSI_Avg = StochRSI(self.symbol, period) if (StochRSI > 70) and (StochRSI_Avg > 70) and (StochRSI < StochRSI_Avg): self.count -= 5 else: if StochRSI > 70: self.count -= 1 if StochRSI_Avg > 70: self.count -= 1 if StochRSI < StochRSI_Avg: self.count -= 1 # Momentum mom = self.MOM(self.symbol, 10, Resolution.Daily).Current.Value if mom <= 0.0: self.count -= 1 # Ultimate Oscillator ultosc = self.ULTOSC(self.symbol, 7, 14, 28, Resolution.Daily).Current.Value if ultosc < 50: self.count -= 1 # CandlestickPatterns if self.CandlestickPatterns.TwoCrows(self.symbol) == -1: self.count -= 1 return self.count class StochRSI: """ Bullish Count > 4. Max 6 Bearish Count < -5. Max -10 """ def __init__(self, symbol, period, *args, **kwargs): super(StochRSI, self).__init__() self.symbol = symbol RSI = self.RSI(self.symbol, period, Resolution.Daily).Current.Value HH_RSI = IndicatorExtensions.MAX(RSI, period).Current.Value LL_RSI = IndicatorExtensions.MIN(RSI, period).Current.Value StochRSI = (RSI - LL_RSI) / (HH_RSI - LL_RSI).Current.Value StochRSI_Avg = IndicatorExtensions.SMA(StochRSI, period).Current.Value return StochRSI, StochRSI_Avg
# Your New Python File
from clr import AddReference AddReference("QuantConnect.Algorithm.Framework") AddReference("QuantConnect.Indicators") AddReference("QuantConnect.Common") from QuantConnect import * from QuantConnect.Indicators import * from QuantConnect.Algorithm.Framework.Alphas import * from datetime import timedelta class MyAlphaModel(AlphaModel): def __init__(self): self.symbolData = {} def Update(self, algorithm, data): insights = [] for symbol in self.symbolData: if symbol in data.Bars: self.symbolData[symbol].update(data.Time, data[symbol].Close) if self.symbolData[symbol].RSIcross: insights.append(Insight(symbol, timedelta(1), InsightType.Price, InsightDirection.Up)) return Insight.Group(insights) def OnSecuritiesChanged(self, algorithm, changes): added_symbols = [s.Symbol for s in changes.AddedSecurities] history = algorithm.History(added_symbols, 14, Resolution.Daily) for s in changes.AddedSecurities: self.symbolData[s.Symbol] = SymbolData(s.Symbol, history.loc[s.Symbol]) for s in changes.RemovedSecurities: self.symbolData.pop(s.Symbol, None) class SymbolData: def __init__(self, symbol, history, period): self.RSIcross = False for idx, row in history.iterrows(): self.update(idx, row.close) def update(self, time, price): self.rsi.Update(time, price) if not self.rsi.IsReady: return self.rollingRSI.Add(self.rsi.Current.Value) if not self.rollingRSI.IsReady: return self.RSIcross = self.rollingRSI[1] < 30 and self.rollingRSI[0] > 30
from QuantConnect import * from QuantConnect.Indicators import * from QuantConnect.Algorithm import * from QuantConnect.Algorithm.Framework import * from QuantConnect.Algorithm.Framework.Alphas import * class SequoiaAlphaModel(AlphaModel): """ This class takes a comprehensive approach to assess whether an intrument is bullish or bearish. Hull_Moving_Average: Price = Close, Length = 20, Displace = 0 Ichimoku: Tekan = 9, Kijun = 26 Simple_Moving_Average: Length = 200, Length = 50 Shared_Floor_Pivots: Time_Frame = Month RSI: Length = 10, Average_Type = Wilders MACDHistogram: Fast_Length = 12, Slow_Length = 26, MACD_Length = 9, Average_Type = Exponential This class emits insights to hold a long (short) position after the chikou line of a security's Ichimoku Cloud crosses over (under) the top (bottom) of the cloud. """ symbol_data_by_symbol = {} def Update(self, algorithm, data): """ Called each time our alpha model receives a new data slice. Input: - algorithm Algorithm instance running the backtest - data A data structure for all of an algorithm's data at a single time step Returns a list of Insights to the portfolio construction model. """ insights = [] for symbol, symbol_data in self.symbol_data_by_symbol.items(): if not data.ContainsKey(symbol) or data[symbol] is None: continue # Update indicator with the latest TradeBar symbol_data.ichimoku.Update(data[symbol]) symbol_data.hma.Update(data[symbol]) symbol_data.twohundred_sma.Update(data[symbol]) symbol_data.fifty_sma.Update(data[symbol]) symbol_data.macd.Update(data[symbol]) symbol_data.rsi.Update(data[symbol]) # Determine insight direction current_location = symbol_data.get_location() if symbol_data.previous_location is not None: # Indicator is ready if symbol_data.previous_location != 1 and current_location == 1: symbol_data.direction = InsightDirection.Up if symbol_data.previous_location != -1 and current_location == -1: symbol_data.direction = InsightDirection.Down symbol_data.previous_location = current_location # Emit insight if symbol_data.direction: insight = Insight.Price(symbol, timedelta(days=1), symbol_data.direction) insights.append(insight) return insights def OnSecuritiesChanged(self, algorithm, changes): """ Called each time our universe has changed. Input: - algorithm Algorithm instance running the backtest - changes The additions and subtractions to the algorithm's security subscriptions """ for security in changes.AddedSecurities: symbol = security.Symbol self.symbol_data_by_symbol[symbol] = SymbolData(symbol, algorithm) for security in changes.RemovedSecurities: self.symbol_data_by_symbol.pop(security.Symbol, None) class SymbolData: """ This class is used to store information on each security in the universe. It is responsible for initializing and warming up the Ichimoku indicator and determining the position of the chikou line in respect to the cloud. """ previous_location = None direction = None def __init__(self, symbol, algorithm): """ Input: - symbol Symbol of the security - algorithm Algorithm instance running the backtest """ # Create Ichimoku Indicator self.ichimoku = IchimokuKinkoHyo() # Warm up indicator history = algorithm.History(symbol, self.ichimoku.WarmUpPeriod + 1, Resolution.Daily).loc[symbol] for idx, row in history.iterrows(): if self.ichimoku.IsReady: self.previous_location = self.get_location() tradebar = TradeBar(idx, symbol, row.open, row.high, row.low, row.close, row.volume) self.ichimoku.Update(tradebar) # Create Hull Moving Average Indicator self.hma = self.HMA(symbol, 20, Resolution.Daily) # Warm up indicator history = algorithm.History(symbol, self.hma.WarmUpPeriod + 1, Resolution.Daily).loc[symbol] for idx, row in history.iterrows(): if self.hma.IsReady: self.signal = 1 if self.hma.Current.Value > row.close else 0 tradebar = TradeBar(idx, symbol, row.open, row.high, row.low, row.close, row.volume) self.hma.Update(tradebar) # Create Exponential Moving Average Indicator (200 Length) self.twohundred_ema = self.EMA(symbol, 200, Resolution.Daily) # Warm up Indicator history = algorithm.History(symbol, self.twohundred_sma.WarmUpPeriod + 1, Resolution.Daily).loc[symbol] for idx, row in history.iterrows(): if self.twohundred_ema.IsReady: self.signal = 1 if self.twohundred_ema.Current.Value > row.close else 0 tradebar = TradeBar(idx, symbol, row.open, row.high, row.low, row.close, row.volume) self.twohundred_ema.Update(tradebar) # Create Exponential Moving Average Indicator (50 Length) self.fifty_ema = self.EMA(symbol, 50, Resolution.Daily) # Warm up Indicator history = algorithm.History(symbol, self.fifty_ema.WarmUpPeriod + 1, Resolution.Daily).loc[symbol] for idx, row in history.iterrows(): if self.fifty_ema.IsReady: self.signal = 1 if self.fifty_ema.Current.Value > row.close else 0 tradebar = TradeBar(idx, symbol, row.open, row.high, row.low, row.close, row.volume) self.fifty_ema.Update(tradebar) # Create MACD Fast_Length = 12, Slow_Length = 26, MACD_Length = 9, Average_Type = Exponential self.macd = self.MACD(symbol, 12, 26, 9, MovingAverageType.Exponential, Resolution.Daily) # Warm up Indicator history = algorithm.History(symbol, self.macd.WarmUpPeriod + 1, Resolution.Daily).loc[symbol] for idx, row in history.iterrows(): if self.macd.IsReady: tolerance = 0.0025 signalDeltaPercent = (self.macd.Current.Value - self.macd.Signal.Current.Value)/self.macd.Fast.Current.Value if signalDeltaPercent > tolerance: self.signal = 1 elif signalDeltaPercent < -tolerance: self.signal = 0 tradebar = TradeBar(idx, symbol, row.open, row.high, row.low, row.close, row.volume) self.macd.Update(tradebar) # Create RSI self.rsi = self.RSI(symbol, 14, MovingAverageType.Wilders, Resolution.Daily) # Warm up Indicator history = algorithm.History(symbol, self.rsi.WarmUpPeriod + 1, Resolution.Daily).loc[symbol] for idx, row in history.iterrows(): if self.rsi.IsReady: tolerance = 0.0025 if self.Current.Value * (1 + tolerance) < 30: self.signal = 1 elif self.Current.Value * (1 + tolerance) > 70: self.signal = 0 tradebar = TradeBar(idx, symbol, row.open, row.high, row.low, row.close, row.volume) self.rsi.Update(tradebar) def get_location(self): """ Determines the location of the chikou line in respect to the cloud. Returns an integer in the interval [-1, 1], representing the location. 1 => Above cloud; 0 => Inside cloud; -1 => Below cloud """ chikou = self.ichimoku.Chikou.Current.Value senkou_span_a = self.ichimoku.SenkouA.Current.Value senkou_span_b = self.ichimoku.SenkouB.Current.Value cloud_top = max(senkou_span_a, senkou_span_b) cloud_bottom = min(senkou_span_a, senkou_span_b) if chikou > cloud_top: return 1 # Above cloud if chikou < cloud_bottom: return -1 # Below cloud return 0 # Inside cloud
# Imports from clr import AddReference AddReference("System") AddReference("QuantConnect.Common") AddReference("QuantConnect.Jupyter") AddReference("QuantConnect.Indicators") AddReference("QuantConnect.Algorithm") from System import * from QuantConnect import * from QuantConnect.Algorithm import * from QuantConnect.Data import * from QuantConnect.Data.Custom import * from QuantConnect.Data.Market import TradeBar, QuoteBar from QuantConnect.Data.Consolidators import * from QuantConnect.Jupyter import * from QuantConnect.Indicators import * from datetime import datetime, timedelta import matplotlib.pyplot as plt import pandas as pd import numpy as np class BasicTemplateAlgorithm(QCAlgorithm): def Initialize(self): self.symbol = "SPY" self.highprice = None self.lowprice = None self.tidetrend = None self.wavetrend = None self.closeWindow = RollingWindow[float](4) self.SetStartDate(2018,9,1) #Set Start Date self.SetEndDate(datetime.now()) #Set End Date to Now self.SetCash(1122) #Set Strategy Cash self.AddEquity(self.symbol, Resolution.Minute).SetLeverage(50.0) # Create the rolling windows # Creates MACDOH indicator and add to a rolling window when it is updated self.macd1 = self.MACD(self.symbol, 12, 26, 9, MovingAverageType.Exponential) self.macd1.Updated += self.MACDOHUpdated self.macdoh = RollingWindow[IndicatorDataPoint](5) # Creates MACDTM indicator and add to a rolling window when it is updated self.macd2 = self.MACD(self.symbol, 12, 26, 9, MovingAverageType.Exponential) self.macd2.Updated += self.MACDTMUpdated self.macdtm = RollingWindow[IndicatorDataPoint](5) # Creates BB indicator and add to a rolling window when it is updated self.boll = self.BB(self.symbol, 20, 1, MovingAverageType.Exponential, Resolution.Minute) self.boll.Updated += self.BBUpdated self.bb = RollingWindow[IndicatorDataPoint](5) # Creates RSI indicator and add to a rolling window when it is updated self.strength = self.RSI(self.symbol, 14, MovingAverageType.Simple, Resolution.Minute) self.strength.Updated += self.RSIUpdated self.rsi = RollingWindow[IndicatorDataPoint](5) oneHourConsolidator = QuoteBarConsolidator(timedelta(minutes=60)) oneHourConsolidator.DataConsolidated += self.OneHourBarHandler self.RegisterIndicator(self.symbol, self.macd1, oneHourConsolidator) self.SubscriptionManager.AddConsolidator(self.symbol, oneHourConsolidator) tenMinuteConsolidator = QuoteBarConsolidator(timedelta(minutes=10)) tenMinuteConsolidator.DataConsolidated += self.TenMinuteBarHandler self.RegisterIndicator(self.symbol, self.macd2, tenMinuteConsolidator) self.RegisterIndicator(self.symbol, self.boll, tenMinuteConsolidator) self.RegisterIndicator(self.symbol, self.strength, tenMinuteConsolidator) self.SubscriptionManager.AddConsolidator(self.symbol, tenMinuteConsolidator) # twoMinuteConsolidator = QuoteBarConsolidator(timedelta(minutes=2)) # twoMinuteConsolidator.DataConsolidated += self.TwoMinuteBarHandler # self.SubscriptionManager.AddConsolidator(self.symbol, twoMinuteConsolidator) self.SetWarmUp(120, Resolution.Minute) #Pass (cause this is only for minute data) def OnData(self, data): pass # Adds updated values to MACDOH rolling window def MACDOHUpdated(self, sender, updated): self.macdoh.Add(updated) # Adds updated values to MACDTM rolling window def MACDTMUpdated(self, sender, updated): self.macdtm.Add(updated) # Adds updated values to BB rolling window def BBUpdated(self, sender, updated): self.bb.Add(updated) # Adds updated values to RSI rolling window def RSIUpdated(self, sender, updated): self.rsi.Add(updated) def OneHourBarHandler(self, sender, consolidated): if not (self.macdoh.IsReady): return macd_hist_0 = self.macdoh[0].Current.Value - self.macdoh[0].Signal.Current.Value macd_hist_1 = self.macdoh[1].Current.Value - self.macdoh[1].Signal.Current.Value macd_hist_2 = self.macdoh[2].Current.Value - self.macdoh[2].Signal.Current.Value macd_hist_3 = self.macdoh[3].Current.Value - self.macdoh[3].Signal.Current.Value def TenMinuteBarHandler(self, sender, consolidated): self.closeWindow.Add(self.Securities[self.symbol].Close) if not (self.closeWindow.IsReady and self.macdoh.IsReady and self.macdtm.IsReady and self.bb.IsReady and self.rsi.IsReady): return price_0 = self.closeWindow[0] price_1 = self.closeWindow[1] price_2 = self.closeWindow[2] self.highprice = max(price_0, price_1, price_2) self.lowprice = min(price_0, price_1, price_2) currrsi = self.rsi[0].Current.Value currbbub = self.bb[0].UpperBand.Current.Value currbblb = self.bb[0].LowerBand.Current.Value macd_hist_0 = self.macdtm[0].Current.Value - self.macdtm[0].Signal.Current.Value macd_hist_1 = self.macdtm[1].Current.Value - self.macdtm[1].Signal.Current.Value macd_hist_2 = self.macdtm[2].Current.Value - self.macdtm[2].Signal.Current.Value macd_hist_3 = self.macdtm[3].Current.Value - self.macdtm[3].Signal.Current.Value
# Your New Python File
# Your New Python File
# Your New Python File
# Your New Python File
# Your New Python File
# Your New Python File
from datetime import timedelta class BasicTemplateOptionsAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2018, 1, 1) #Set Start Date self.SetEndDate(2018, 10, 15) #Set Start Date self.SetCash(100000) #Set Strategy Cash equity = self.AddEquity("IBM", Resolution.Minute) option = self.AddOption("IBM", Resolution.Minute) self.symbol = option.Symbol equity.SetDataNormalizationMode(DataNormalizationMode.Raw) # set our strike/expiry filter for this option chain option.SetFilter(-3, +3, timedelta(0), timedelta(30)) # use the underlying equity as the benchmark self.SetBenchmark(equity.Symbol) self.call = "IBM" # Initialize the call contract def OnData(self,slice): if not self.Portfolio[self.call].Invested and self.MarketOpen(): self.TradeOptions(slice) # sell the call option # if the option contract expires, print out the price and position information if slice.Delistings.Count > 0: if [x.Key == self.call for x in slice.Delistings]: self.Log("stock IBM quantity: {0}".format(self.Portfolio["IBM"].Quantity)) self.Log("{0} quantity: {1}".format(self.call.Value, self.Portfolio[self.call].Quantity)) self.Log("The stock price at Expiry S(T): {}".format(self.Securities["IBM"].Price)) def TradeOptions(self,slice): if slice.OptionChains.Count == 0: return for i in slice.OptionChains: if i.Key != self.symbol: continue chain = i.Value call = [x for x in chain if x.Right == 0] # filter the call options contracts # sorted the contracts according to their expiration dates and choose the ATM options contracts = sorted(sorted(call, key = lambda x: x.Expiry, reverse=True), key = lambda x: abs(chain.Underlying.Price - x.Strike)) if len(contracts) == 0: return contract = contracts[0] self.call = contract.Symbol self.Sell(self.call, 1) # short the call options if self.Portfolio["IBM"].Quantity == 0: self.Buy("IBM",100) # buy 100 the underlying stock self.Log("The stock price at time 0 S(0): {}".format(self.Securities["IBM"].Price)) def MarketOpen(self): return self.Time.hour != 0 and self.Time.minute == 1
from System import * from QuantConnect import * from QuantConnect.Indicators import * from QuantConnect.Data.Consolidators import * from QuantConnect.Data.UniverseSelection import * from QuantConnect.Orders.Fees import ConstantFeeModel from QuantConnect.Algorithm import * from QuantConnect.Algorithm.Framework.Alphas import * from QuantConnect.Algorithm.Framework.Portfolio import EqualWeightingPortfolioConstructionModel, NullPortfolioConstructionModel from QuantConnect.Algorithm.Framework.Risk import MaximumDrawdownPercentPortfolio, MaximumUnrealizedProfitPercentPerSecurity, MaximumDrawdownPercentPerSecurity from Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel from datetime import datetime, timedelta import pandas as pd import numpy as np import re from math import ceil from itertools import chain from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import train_test_split from DeltaModel import DeltaModel from VixFix import VixFix from MyAlphaModel import MyAlphaModel, SymbolData from UniverseSelectionHelperFunctions import SelectionData from CompositeTradeSignals import CompositeTradeSignals from SequoiaAlphaModel import SequoiaAlphaModel from IchimokuCloudCrossOverAlphaModel import IchimokuCloudCrossOverAlphaModel from trading_algorithm import TradingAlgorithm_main class EnhancedShortTermMeanReversionAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2010, 1, 1) #Set Start Date self.SetCash(10000) #Set Strategy Cash self.SetWarmUp(timedelta(minutes=1)) # Set Algorithm to 21 days #self.SetBrokerageModel(BrokerageName.AlphaStreams) self.UniverseSettings.Resolution = Resolution.Daily self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction) # rebalance the universe selection once a month self.rebalance_flag = 0 # make sure to run the universe selection at the start of the algorithm even it's not the month start self.first_month_trade_flag = 1 self.trade_flag = 0 # Number of quantiles for sorting returns for mean reversion self.nq = 5 # Number of quantiles for sorting volatility over five-day mean reversion period self.nq_vol = 3 # the symbol list after the coarse and fine universe selection self.universe = None self.NumberOfSymbolsFine = 500 self.NumberOfSymbolsCoarse = 150 self.dollarVolumeBySymbol = {} # setup state storage in initialize method self.stateData = { }; # Benchmark self.AddEquity("SPY", Resolution.Daily) self.SetBenchmark("SPY") # SPY HIGH self.correction_flag = 0 self.SPY_avg_delta = DeltaModel(self.History(self.Symbol('SPY'), 25, Resolution.Daily), self.Symbol('SPY')).run() self.SPY_vix_data = VixFix(self.History(self.Symbol('SPY'), 100, Resolution.Daily), self.Symbol('SPY')).run() self.SPY_mom = self.MOM('SPY', 10, Resolution.Daily).Current.Value try: self.Schedule.On(self.DateRules.MonthStart("SPY"), self.TimeRules.At(0, 0), Action(self.monthly_rebalance)) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.At(9, 0), Action(self.get_prices)) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.AfterMarketOpen("SPY", 0), Action(self.take_profit)) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.AfterMarketOpen("SPY", 1), Action(self.daily_rebalance)) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.AfterMarketOpen("SPY", 1), Action(self.short)) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.AfterMarketOpen("SPY", 1), Action(self.long)) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 10), Action(self.take_profit)) except: self.Liquidate() return def monthly_rebalance(self): # rebalance the universe every month self.rebalance_flag = 1 def CoarseSelectionFunction(self, coarse): if self.rebalance_flag or self.first_month_trade_flag: # drop stocks which have no fundamental data or have too low prices coarse_filtered = [x for x in coarse if (x.HasFundamentalData) and (20 <= float(x.Price) <= 300) and (float(x.Volume) >= 2000000)] if (self.SPY_avg_delta > -5.5) or (self.SPY_avg_delta <= -5.5 and self.SPY_vix_data == 'Yes') and (self.SPY_mom >= 0.025): # We are going to use a dictionary to refer the object that will keep the moving averages for c in coarse_filtered: if c.Symbol not in self.stateData: self.stateData[c.Symbol] = SelectionData(c.Symbol, 10) # Updates the SymbolData object with current EOD price avg = self.stateData[c.Symbol] avg.update(c.EndTime, c.Price, c.Volume, c.DollarVolume) # filter the values of selectionData(sd) above SMA values = [sd for sd in self.stateData.values() if (sd.Volume > (sd.Sma.Current.Value * 1.25)) and (sd.Volume_Ratio > 2) and (sd.Fast_Vol_Ratio > 2) and (sd.Med_Vol_Ratio > 2)] # sort sd by the largest % jump in volume. values.sort(key=lambda sd: sd.Volume_Ratio, reverse=True) self.dollarVolumeBySymbol = {i.Symbol: (i.DollarVolume, i.Volume_Ratio) for i in values[:self.NumberOfSymbolsCoarse]} return [x.Symbol for x in values[:self.NumberOfSymbolsCoarse]] elif (self.SPY_avg_delta <= - 5.5 and self.SPY_vix_data == 'No') or (self.SPY_mom < 0.0): # rank the stocks by dollar volume and choose the top 150 filtered = sorted(coarse_filtered, key=lambda x: x.DollarVolume, reverse=True)[:self.NumberOfSymbolsCoarse] self.dollarVolumeBySymbol = { i.Symbol: i.DollarVolume for i in filtered} return [x.Symbol for x in filtered] else: return self.universe def FineSelectionFunction(self, fine): def upper_groups(x): x = np.asarray(x, dtype=np.float32) x = x[x >= 0] cutoff = np.mean(x, dtype=np.float64) #+ (np.std(x, dtype=np.float64) * 2) return cutoff if self.rebalance_flag or self.first_month_trade_flag: """ Need to do a better job with filter system with multiple parameters - currently takes the top 50% in each category and checks if stock is top 50% in each category """ initial_filter = [x for x in fine if x.ValuationRatios.EVToEBITDA > 0 and x.ValuationRatios.PriceChange1M > 0 and x.EarningReports.BasicAverageShares.ThreeMonths * x.EarningReports.BasicEPS.TwelveMonths * x.ValuationRatios.PERatio > 5e8 and (x.Time - x.SecurityReference.IPODate).days > 180 and x.CompanyReference.CountryId == "USA" and (x.CompanyReference.PrimaryExchangeID == "NYS" or x.CompanyReference.PrimaryExchangeID == "NAS") and x.CompanyReference.IndustryTemplateCode == "N" and x.AssetClassification.MorningstarSectorCode in [MorningstarSectorCode.FinancialServices, MorningstarSectorCode.Technology, MorningstarSectorCode.Industrials, MorningstarSectorCode.CommunicationServices, MorningstarSectorCode.ConsumerCyclical, MorningstarSectorCode.ConsumerDefensive, MorningstarSectorCode.Healthcare, MorningstarSectorCode.Utilities]] PERatio_mean = upper_groups([float(x.ValuationRatios.PERatio) for x in initial_filter if x.ValuationRatios.PERatio > 0]) PEGRatio_mean = upper_groups([float(x.ValuationRatios.PEGRatio) for x in initial_filter if x.ValuationRatios.PEGRatio >= 0]) FreeCashFlow_mean = upper_groups([float(x.FinancialStatements.CashFlowStatement.FreeCashFlow.ThreeMonths) for x in initial_filter if x.FinancialStatements.CashFlowStatement.FreeCashFlow.ThreeMonths > 0]) FirstYREPSGrowth_mean = upper_groups([float(x.ValuationRatios.FirstYearEstimatedEPSGrowth) for x in initial_filter if x.ValuationRatios.FirstYearEstimatedEPSGrowth > 0.05]) second_filter = sorted(initial_filter, key = lambda x: x.ValuationRatios.PERatio > PERatio_mean, reverse = True) third_filter = sorted(initial_filter, key = lambda x: x.ValuationRatios.PEGRatio > PEGRatio_mean, reverse = True) fourth_filter = sorted(initial_filter, key = lambda x: x.FinancialStatements.CashFlowStatement.FreeCashFlow.ThreeMonths > FreeCashFlow_mean, reverse = True) fifth_filter = sorted(initial_filter, key = lambda x: x.ValuationRatios.FirstYearEstimatedEPSGrowth > FirstYREPSGrowth_mean, reverse = True) filtered_fine = list(set.intersection(*map(set, [initial_filter, second_filter, third_filter, fourth_filter, fifth_filter]))) count = len(filtered_fine) if count == 0: return [] myDict = dict() percent = float(self.NumberOfSymbolsFine / count) # select stocks with top dollar volume in every single sector for key in ["N"]: value = [x for x in filtered_fine if x.CompanyReference.IndustryTemplateCode == key] value = sorted(value, key=lambda x: self.dollarVolumeBySymbol[x.Symbol], reverse = True) myDict[key] = value[:ceil(len(value) * percent)] topFine = list(chain.from_iterable(myDict.values()))#[:self.NumberOfSymbolsFine] self.universe = [f.Symbol for f in topFine] # [:self.NumberOfSymbolsFine] self.rebalance_flag = 0 self.first_month_trade_flag = 0 self.trade_flag = 1 return self.universe def OnData(self, data): pass def short(self): if self.universe is None: return SPY_Velocity = 0 self.long_leverage = 0 self.short_leverage = 0 # request the history of benchmark pri = self.History(["SPY"], 75, Resolution.Daily) pos_one = (pri.loc["SPY"]['close'][-1]) pos_six = (pri.loc["SPY"]['close'][-75:].mean()) # calculate velocity of the benchmark velocity_stop = (pos_one - pos_six)/100.0 SPY_Velocity = velocity_stop if SPY_Velocity > 0.0: self.long_leverage = 1.8 self.short_leverage = -0.0 else: self.long_leverage = 1.1 self.short_leverage = -0.7 if (self.SPY_avg_delta <= -5.5 and self.SPY_vix_data == 'No'): return elif (self.SPY_avg_delta > -5.5): for symbol in self.shorts: if len(self.shorts) + self.existing_shorts == 0: return self.AddEquity(symbol, Resolution.Daily) self.SetHoldings(symbol, 0.8*(self.short_leverage/(len(self.shorts) + self.existing_shorts))) def long(self): if self.universe is None: return if (self.SPY_avg_delta <= -5.5 and self.SPY_vix_data == 'No'): return elif (self.SPY_avg_delta > -5.5): for symbol in self.longs: if len(self.longs) + self.existing_longs == 0: return self.AddEquity(symbol, Resolution.Daily) self.SetHoldings(symbol, 1*(self.long_leverage/(len(self.longs) + self.existing_longs))) def get_prices(self): if self.universe is None: return # Get the last 30 days of prices for every stock in our universe prices = {} hist = self.History(self.universe, 30, Resolution.Daily) for i in self.universe: try: if str(i) in hist.index.levels[0]: if self.technical_setup(i): prices[i.Value] = hist.loc[str(i)]['close'] except: pass if len(prices.keys()) <= 0: return df_prices = pd.DataFrame(prices, columns = prices.keys()) # calculate the daily log return daily_rets = np.log(df_prices/df_prices.shift(1)) # calculate the latest return but skip the most recent price rets = (df_prices.iloc[-2] - df_prices.iloc[0]) / df_prices.iloc[0] # standard deviation of the daily return stdevs = daily_rets.std(axis = 0) try: self.ret_qt = pd.qcut(rets, 5, labels=False) + 1 self.stdev_qt = pd.qcut(stdevs, 3, labels=False) + 1 except: self.ret_qt = pd.qcut(rets, 5, labels=False, duplicates='drop') + 1 self.stdev_qt = pd.qcut(stdevs, 3, labels=False, duplicates='drop') + 1 self.longs = list((self.ret_qt[self.ret_qt == 1].index) & (self.stdev_qt[self.stdev_qt < 3].index)) self.shorts = list((self.ret_qt[self.ret_qt == self.nq].index) & (self.stdev_qt[self.stdev_qt < 3].index)) def daily_rebalance(self): # rebalance the position in portfolio every day if self.universe is None: return if self.longs is None: return if self.shorts is None: return self.existing_longs = 0 self.existing_shorts = 0 if (self.SPY_avg_delta <= - 5.5 and self.SPY_vix_data == 'No') or (self.SPY_mom < 0.0): for symbol in self.Portfolio.Invested: self.Liquidate(symbol) elif (self.SPY_avg_delta <= -5.5 and self.SPY_vix_data == 'Yes'): self.monthly_rebalance self.get_prices elif (self.SPY_avg_delta > -5.5) or (self.SPY_avg_delta <= -5.5 and self.SPY_vix_data == 'Yes') and (self.SPY_mom >= 0.025): for symbol in self.Portfolio.Keys: if (symbol.Value != 'SPY') and (symbol.Value in self.ret_qt.index) and (self.Portfolio[symbol].IsLong): current_quantile = self.ret_qt.loc[symbol.Value] avg_delta = DeltaModel(self.History(symbol, 25, Resolution.Daily), symbol).run() vix_data = VixFix(self.History(symbol, 50, Resolution.Daily), symbol).run() if avg_delta > -5.5 or (avg_delta <= -5.5 and vix_data == 'Yes'): # create a 50 day exponential moving average self.fast = self.EMA(symbol, 20, Resolution.Daily).Current.Value # creat a 200 day simple moving average self.slow = self.SMA(symbol, 55, Resolution.Daily).Current.Value # create a 200 day simple moving average self.baseline = self.SMA(symbol, 200, Resolution.Daily).Current.Value if (self.fast > self.slow): if self.ttm_squeeze(symbol) or (self.ema_squeeze(symbol) >= 4) or (self.go_time_indicators(symbol) >= 3): #self.TradeOptions(symbol) self.MarketOrder(symbol, 100) self.update_ticket(symbol) elif self.ttm_squeeze(symbol) or (self.ema_squeeze(symbol) >= 3) or (self.go_time_indicators(symbol) >= 2): if self.Portfolio[symbol].Quantity > 0: if (current_quantile == 1) and (symbol not in self.longs): self.existing_longs += 2 elif (current_quantile > 1) and (symbol not in self.shorts): self.Liquidate(symbol) self.longs.remove(symbol) self.shorts.append(symbol) elif self.Portfolio[symbol].Quantity < 0: if (current_quantile == self.nq) and (symbol not in self.shorts): self.existing_shorts += 1 elif (current_quantile < self.nq) and (symbol not in self.longs): self.Liquidate(symbol) elif avg_delta <= -5.5: try: self.Liquidate(symbol) self.universe.remove(symbol) self.longs.remove(symbol) self.shorts.remove(symbol) except: pass self.AddEquity('SPY', Resolution.Daily) self.SPY_avg_delta = DeltaModel(self.History(self.Symbol('SPY'), 25, Resolution.Daily), self.Symbol('SPY')).run() self.SPY_vix_data = VixFix(self.History(self.Symbol('SPY'), 100, Resolution.Daily), self.Symbol('SPY')).run() self.SPY_mom = self.MOM('SPY', 10, Resolution.Daily).Current.Value self.take_profit() def technical_setup(self, i): x = re.sub('[(){}<>| ]', '', str(i)) self.AddEquity(x, Resolution.Daily) self.History(self.Symbol(x), 200, Resolution.Daily) if self.EMA(x, 50, Resolution.Daily).Current.Value >= self.SMA(x, 200, Resolution.Daily).Current.Value: if self.RSI(x, 14, Resolution.Daily).Current.Value < 30: return x def ttm_squeeze(self, symbol): self.AddEquity(symbol, Resolution.Daily) #TTM Squeeze sma_twenty = self.SMA(symbol, 20, Resolution.Daily).Current.Value std = self.STD(symbol, 20, Resolution.Daily).Current.Value lower_band = sma_twenty - 2*std upper_band = sma_twenty + 2*std atr = self.ATR(symbol, 20, Resolution.Daily).Current.Value lower_keltner = sma_twenty - 2*atr upper_keltner = sma_twenty + 2*atr return lower_band > lower_keltner and upper_band < upper_keltner def ema_squeeze(self, symbol): self.AddEquity(symbol, Resolution.Daily) # create a 8 day exponential moving average fastest = self.EMA(symbol, 8, Resolution.Daily).Current.Value # create a 14 day exponential moving average faster = self.EMA(symbol, 14, Resolution.Daily).Current.Value # create a 21 day exponential moving average fast = self.EMA(symbol, 21, Resolution.Daily).Current.Value # create a 34 day exponential moving average slow = self.EMA(symbol, 34, Resolution.Daily).Current.Value # create a 50 day exponential moving average slower = self.EMA(symbol, 55, Resolution.Daily).Current.Value # creat a 200 day simple moving average slowest = self.SMA(symbol, 200, Resolution.Daily).Current.Value # define a small tolerance on our checks to avoid bouncing tolerance = 0.025 condition_one = ((fastest * (1-tolerance)) >= (faster * (1+tolerance))) condition_two = ((faster * (1-tolerance)) >= (fast * (1+tolerance))) condition_three = ((fast * (1-tolerance)) >= (slow * (1+tolerance))) condition_four = ((slow * (1-tolerance)) >= (slower * (1+tolerance))) condition_five = (slower > slowest) conditions = [condition_one, condition_two, condition_three, condition_four, condition_five] count = 0 for condition in conditions: if condition is True: count += 1 return count def go_time_indicators(self, symbol): self.AddEquity(symbol, Resolution.Daily) # Momentum & Volume & Trend # RSI <= 30 = buy rsi = self.RSI(symbol, 14, MovingAverageType.Simple, Resolution.Daily).Current.Value # William's %R <= -20 = buy wilr = self.WILR(symbol, 14, Resolution.Daily).Current.Value # MACD current < signal = buy macd = self.MACD(symbol, 12, 26, 9, Resolution.Daily) macd_current = macd.Current.Value macd_signal = macd.Signal.Current.Value macd_fast = macd.Fast.Current.Value macd_signal_delta = (macd_current - macd_signal)/(macd_fast) tolerance = 0.0025 condition_one = (rsi <= 30) condition_two = (wilr <= -20) condition_three = (macd_signal_delta > tolerance) conditions = [condition_one, condition_two, condition_three] count = 0 for condition in conditions: if condition is True: count += 1 return count def OnOrderEvent(self, orderEvent): if orderEvent.Status == OrderStatus.Invalid: self.Log(str(orderEvent)) if orderEvent.Status == OrderStatus.Filled: order = self.Transactions.GetOrderById(orderEvent.OrderId) if order.Tag == 'pt': # If hit profit target, update stop order quantity updateSettings = UpdateOrderFields() updateSettings.Quantity = self.stops[orderEvent.Symbol].Quantity - orderEvent.Quantity self.stops[orderEvent.Symbol].Update(updateSettings) elif order.Tag == 'sl': # If hit stop loss, cancel profit target orders self.Transactions.CancelOpenOrders(orderEvent.Symbol, "Hit stop price") def take_profit(self): if self.Portfolio.TotalUnrealizedProfit > (0.2 * self.Portfolio.TotalPortfolioValue): stocks_invested = [x.Key for x in self.Portfolio if x.Value.Invested] stocks_invested_by_profit = sorted(stocks_invested, key=lambda x: self.Portfolio[x].UnrealizedProfit, reverse=True) for stock in stocks_invested_by_profit: holding = self.Portfolio[stock].Quantity avg_price = self.Portfolio[stock].AveragePrice holdings_profit = self.Portfolio[stock].UnrealizedProfit holdings_cost = avg_price * holding if stock in self.shorts: if holdings_profit >= holdings_cost * 1.5: self.MarketOrder(stock, -holding) elif stock in self.longs: half_holding = int(holding/2.0) if holdings_profit >= holdings_cost * 1.5: self.MarketOrder(stock, -holding) elif self.SPY_mom <= 0.0: for stock in self.Portfolio: try: if stock.Value.Invested: self.Liquidate(stock) except: pass try: if self.longs and self.shorts: stocks_invested = [x.Key for x in self.Portfolio if x.Value.Invested] # liquidate stocks not in the trading list for stock in stocks_invested: holding = self.Portfolio[stock].Quantity avg_price = self.Portfolio[stock].AveragePrice holdings_profit = self.Portfolio[stock].UnrealizedProfit holdings_cost = avg_price * holding if holdings_profit > holdings_cost * 1.5: self.Liquidate(stock) except: pass def update_ticket(self, stock): """ Uses mean reversion to update stop loss and limit orders. """ self.mean = self.EMA(stock, 8, Resolution.Daily) self.atr = self.ATR(stock, 8, Resolution.Daily) # # Mean reversion when price is too low if self.Securities[stock].Price > (self.Portfolio[stock].AveragePrice * 0.95): limitTicket = self.LimitOrder(stock, -self.Portfolio[stock].Quantity, self.Securities[stock].Price + (0.5 * self.atr.Current.Value)) stopTicket = self.StopMarketOrder(stock, -self.Portfolio[stock].Quantity, (self.Portfolio[stock].AveragePrice * 0.90)) # Mean reversion when price is too high if self.Securities[stock].Close > self.mean.Current.Value + (1.5 * self.atr.Current.Value): limitTicket = self.LimitOrder(stock, -self.Portfolio[stock].Quantity, self.Securities[stock].Price + (self.atr.Current.Value)) stopTicket = self.StopMarketOrder(stock, -self.Portfolio[stock].Quantity, (self.Securities[stock].Price * 0.95)) def TradeOptions(self, symbol): equity = self.AddEquity(symbol, Resolution.Minute) option = self.AddOption(symbol, Resolution.Minute) self.symbol = option.Symbol equity.SetDataNormalizationMode(DataNormalizationMode.Raw) option.SetFilter(-3, +3, timedelta(0), timedelta(60)) # use the underlying equity as the benchmark self.SetBenchmark(equity.Symbol) self.call = symbol # Initialize the call contract if slice.OptionChains.Count == 0: return for i in slice.OptionChains: if i.Key != self.symbol: continue chain = i.Value call = [x for x in chain if x.Right == 0] # filter the call options contracts # sorted the contracts according to their expiration dates and choose the ATM options contracts = sorted(sorted(call, key = lambda x: x.Expiry, reverse=True), key = lambda x: abs(chain.Underlying.Price - x.Strike)) if len(contracts) == 0: return contract = contracts[0] self.call = contract.Symbol if self.Securities[self.call].Price < (0.01 * self.Portfolio.TotalPortfolioValue): num_contracts = int((self.Portfolio.TotalPortfolioValue * 0.01)/self.Securities[self.call].Price) num_conracts = max([5, num_contracts]) #if num_contracts > 0: self.Sell(symbol, num_contracts) # short the call options if self.Portfolio[symbol].Quantity == 0: self.Buy(symbol, 100) # buy 100 the underlying stock self.Log("The stock price at time 0 S(0): {}".format(self.Securities[symbol].Price)) def MarketOpen(self): return self.Time.hour != 0 and self.Time.minute == 1
from QuantConnect.Indicators import IchimokuKinkoHyo class IchimokuCloudCrossOverAlphaModel(AlphaModel): """ This class emits insights to hold a long (short) position after the chikou line of a security's Ichimoku Cloud crosses over (under) the top (bottom) of the cloud. """ symbol_data_by_symbol = {} def Update(self, algorithm, data): """ Called each time our alpha model receives a new data slice. Input: - algorithm Algorithm instance running the backtest - data A data structure for all of an algorithm's data at a single time step Returns a list of Insights to the portfolio construction model. """ insights = [] for symbol, symbol_data in self.symbol_data_by_symbol.items(): if not data.ContainsKey(symbol) or data[symbol] is None: continue # Update indicator with the latest TradeBar symbol_data.ichimoku.Update(data[symbol]) # Determine insight direction current_location = symbol_data.get_location() if symbol_data.previous_location is not None: # Indicator is ready if symbol_data.previous_location != 1 and current_location == 1: symbol_data.direction = InsightDirection.Up if symbol_data.previous_location != -1 and current_location == -1: symbol_data.direction = InsightDirection.Down symbol_data.previous_location = current_location # Emit insight if symbol_data.direction: insight = Insight.Price(symbol, timedelta(days=1), symbol_data.direction) insights.append(insight) return insights def OnSecuritiesChanged(self, algorithm, changes): """ Called each time our universe has changed. Input: - algorithm Algorithm instance running the backtest - changes The additions and subtractions to the algorithm's security subscriptions """ for security in changes.AddedSecurities: symbol = security.Symbol self.symbol_data_by_symbol[symbol] = SymbolData(symbol, algorithm) for security in changes.RemovedSecurities: self.symbol_data_by_symbol.pop(security.Symbol, None) class SymbolData: """ This class is used to store information on each security in the universe. It is responsible for initializing and warming up the Ichimoku indicator and determining the position of the chikou line in respect to the cloud. """ previous_location = None direction = None def __init__(self, symbol, algorithm): """ Input: - symbol Symbol of the security - algorithm Algorithm instance running the backtest """ # Create Ichimoku indicator self.ichimoku = IchimokuKinkoHyo() # Warm up indicator history = algorithm.History(symbol, self.ichimoku.WarmUpPeriod + 1, Resolution.Daily).loc[symbol] for idx, row in history.iterrows(): if self.ichimoku.IsReady: self.previous_location = self.get_location() tradebar = TradeBar(idx, symbol, row.open, row.high, row.low, row.close, row.volume) self.ichimoku.Update(tradebar) def get_location(self): """ Determines the location of the chikou line in respect to the cloud. Returns an integer in the interval [-1, 1], representing the location. 1 => Above cloud; 0 => Inside cloud; -1 => Below cloud """ chikou = self.ichimoku.Chikou.Current.Value senkou_span_a = self.ichimoku.SenkouA.Current.Value senkou_span_b = self.ichimoku.SenkouB.Current.Value cloud_top = max(senkou_span_a, senkou_span_b) cloud_bottom = min(senkou_span_a, senkou_span_b) if chikou > cloud_top: return 1 # Above cloud if chikou < cloud_bottom: return -1 # Below cloud return 0 # Inside cloud
from QuantConnect import * from QuantConnect.Indicators import * from QuantConnect.Algorithm import * from QuantConnect.Algorithm.Framework import * from QuantConnect.Algorithm.Framework.Alphas import * class SequoiaAlphaModel(AlphaModel): """ This class takes a comprehensive approach to assess whether an intrument is bullish or bearish. Hull_Moving_Average: Price = Close, Length = 20, Displace = 0 Ichimoku: Tekan = 9, Kijun = 26 Simple_Moving_Average: Length = 200, Length = 50 Shared_Floor_Pivots: Time_Frame = Month RSI: Length = 10, Average_Type = Wilders MACDHistogram: Fast_Length = 12, Slow_Length = 26, MACD_Length = 9, Average_Type = Exponential This class emits insights to hold a long (short) position after the chikou line of a security's Ichimoku Cloud crosses over (under) the top (bottom) of the cloud. """ symbol_data_by_symbol = {} def Update(self, algorithm, data): """ Called each time our alpha model receives a new data slice. Input: - algorithm Algorithm instance running the backtest - data A data structure for all of an algorithm's data at a single time step Returns a list of Insights to the portfolio construction model. """ insights = [] for symbol, symbol_data in self.symbol_data_by_symbol.items(): if not data.ContainsKey(symbol) or data[symbol] is None: continue # Update indicator with the latest TradeBar symbol_data.ichimoku.Update(data[symbol]) symbol_data.hma.Update(data[symbol]) symbol_data.twohundred_sma.Update(data[symbol]) symbol_data.fifty_sma.Update(data[symbol]) symbol_data.macd.Update(data[symbol]) symbol_data.rsi.Update(data[symbol]) # Determine insight direction current_location = symbol_data.get_location() if symbol_data.previous_location is not None: # Indicator is ready if symbol_data.previous_location != 1 and current_location == 1: symbol_data.direction = InsightDirection.Up if symbol_data.previous_location != -1 and current_location == -1: symbol_data.direction = InsightDirection.Down symbol_data.previous_location = current_location # Emit insight if symbol_data.direction: insight = Insight.Price(symbol, timedelta(days=1), symbol_data.direction) insights.append(insight) return insights def OnSecuritiesChanged(self, algorithm, changes): """ Called each time our universe has changed. Input: - algorithm Algorithm instance running the backtest - changes The additions and subtractions to the algorithm's security subscriptions """ for security in changes.AddedSecurities: symbol = security.Symbol self.symbol_data_by_symbol[symbol] = SymbolData(symbol, algorithm) for security in changes.RemovedSecurities: self.symbol_data_by_symbol.pop(security.Symbol, None) class SymbolData: """ This class is used to store information on each security in the universe. It is responsible for initializing and warming up the Ichimoku indicator and determining the position of the chikou line in respect to the cloud. """ previous_location = None direction = None def __init__(self, symbol, algorithm): """ Input: - symbol Symbol of the security - algorithm Algorithm instance running the backtest """ # Create Ichimoku Indicator self.ichimoku = IchimokuKinkoHyo() # Warm up indicator history = algorithm.History(symbol, self.ichimoku.WarmUpPeriod + 1, Resolution.Daily).loc[symbol] for idx, row in history.iterrows(): if self.ichimoku.IsReady: self.previous_location = self.get_location() tradebar = TradeBar(idx, symbol, row.open, row.high, row.low, row.close, row.volume) self.ichimoku.Update(tradebar) # Create Hull Moving Average Indicator self.hma = self.HMA(symbol, 20, Resolution.Daily) # Warm up indicator history = algorithm.History(symbol, self.hma.WarmUpPeriod + 1, Resolution.Daily).loc[symbol] for idx, row in history.iterrows(): if self.hma.IsReady: self.signal = 1 if self.hma.Current.Value > row.close else 0 tradebar = TradeBar(idx, symbol, row.open, row.high, row.low, row.close, row.volume) self.hma.Update(tradebar) # Create Exponential Moving Average Indicator (200 Length) self.twohundred_ema = self.EMA(symbol, 200, Resolution.Daily) # Warm up Indicator history = algorithm.History(symbol, self.twohundred_sma.WarmUpPeriod + 1, Resolution.Daily).loc[symbol] for idx, row in history.iterrows(): if self.twohundred_ema.IsReady: self.signal = 1 if self.twohundred_ema.Current.Value > row.close else 0 tradebar = TradeBar(idx, symbol, row.open, row.high, row.low, row.close, row.volume) self.twohundred_ema.Update(tradebar) # Create Exponential Moving Average Indicator (50 Length) self.fifty_ema = self.EMA(symbol, 50, Resolution.Daily) # Warm up Indicator history = algorithm.History(symbol, self.fifty_ema.WarmUpPeriod + 1, Resolution.Daily).loc[symbol] for idx, row in history.iterrows(): if self.fifty_ema.IsReady: self.signal = 1 if self.fifty_ema.Current.Value > row.close else 0 tradebar = TradeBar(idx, symbol, row.open, row.high, row.low, row.close, row.volume) self.fifty_ema.Update(tradebar) # Create MACD Fast_Length = 12, Slow_Length = 26, MACD_Length = 9, Average_Type = Exponential self.macd = self.MACD(symbol, 12, 26, 9, MovingAverageType.Exponential, Resolution.Daily) # Warm up Indicator history = algorithm.History(symbol, self.macd.WarmUpPeriod + 1, Resolution.Daily).loc[symbol] for idx, row in history.iterrows(): if self.macd.IsReady: tolerance = 0.0025 signalDeltaPercent = (self.macd.Current.Value - self.macd.Signal.Current.Value)/self.macd.Fast.Current.Value if signalDeltaPercent > tolerance: self.signal = 1 elif signalDeltaPercent < -tolerance: self.signal = 0 tradebar = TradeBar(idx, symbol, row.open, row.high, row.low, row.close, row.volume) self.macd.Update(tradebar) # Create RSI self.rsi = self.RSI(symbol, 14, MovingAverageType.Wilders, Resolution.Daily) # Warm up Indicator history = algorithm.History(symbol, self.rsi.WarmUpPeriod + 1, Resolution.Daily).loc[symbol] for idx, row in history.iterrows(): if self.rsi.IsReady: tolerance = 0.0025 if self.Current.Value * (1 + tolerance) < 30: self.signal = 1 elif self.Current.Value * (1 + tolerance) > 70: self.signal = 0 tradebar = TradeBar(idx, symbol, row.open, row.high, row.low, row.close, row.volume) self.rsi.Update(tradebar) def get_location(self): """ Determines the location of the chikou line in respect to the cloud. Returns an integer in the interval [-1, 1], representing the location. 1 => Above cloud; 0 => Inside cloud; -1 => Below cloud """ chikou = self.ichimoku.Chikou.Current.Value senkou_span_a = self.ichimoku.SenkouA.Current.Value senkou_span_b = self.ichimoku.SenkouB.Current.Value cloud_top = max(senkou_span_a, senkou_span_b) cloud_bottom = min(senkou_span_a, senkou_span_b) if chikou > cloud_top: return 1 # Above cloud if chikou < cloud_bottom: return -1 # Below cloud return 0 # Inside cloud
# Your New Python File
# Your New Python File
# Your New Python File
# Your New Python File
# Your New Python File
# Your New Python File
# Your New Python File
# Your New Python File
# Your New Python File
import os, sys, copy import numpy as np class TradingAlgorithm_main: """ This function deploys and employs various trading strategies to arrive at the best decision to buy/sell/hold a stock. """ def __init__(self, data, *args, **kwargs): """ :param data: :param args: :param kwargs: """ super(LongTermAlgos, self).__init__() self.data = data self.algos = {} # import libraries from MeanReversion.main import MeanReversion from StatisticalArbitrage.main import StatsArbitrage from CrowdSourcing.main import CrowdSource from MachineLearning.main import MachineLearning from Momentum.main import Momentum from TrendFollowing.main import TrendFollowing from MarketMaking.main import MarketMaking from SentimentAnalysis.main import Sentiment self.algos['MeanReversion'] = MeanReversion self.algos['StatsticalArbitrage'] = StatsticalArbitrage self.algos['CrowdSourcing'] = CrowdSourcing self.algos['MachineLearning'] = MachineLearning self.algos['Momentum'] = Momentum self.algos['TrendFollowing'] = TrendFollowing self.algos['MarketMaking'] = MarketMaking self.algos['SentimentAnalysis'] = SentimentAnalysis def run(self): """ :return: """ for algo in self.algos.keys(): trading_strategy = self.algos[algo](data = self.data) results = trading_strategy.run() # Your New Python File
from System import * from QuantConnect import * from QuantConnect.Indicators import * from QuantConnect.Data.Consolidators import * from QuantConnect.Data.UniverseSelection import * from QuantConnect.Orders.Fees import ConstantFeeModel from QuantConnect.Algorithm import * from QuantConnect.Algorithm.Framework.Alphas import * from QuantConnect.Algorithm.Framework.Portfolio import EqualWeightingPortfolioConstructionModel, NullPortfolioConstructionModel from QuantConnect.Algorithm.Framework.Risk import MaximumDrawdownPercentPortfolio, MaximumUnrealizedProfitPercentPerSecurity, MaximumDrawdownPercentPerSecurity from Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel from datetime import datetime, timedelta import pandas as pd import numpy as np import re from math import ceil from itertools import chain from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import train_test_split from DeltaModel import DeltaModel from VixFix import VixFix from MyAlphaModel import MyAlphaModel, SymbolData from UniverseSelectionHelperFunctions import SelectionData from CompositeTradeSignals import CompositeTradeSignals from SequoiaAlphaModel import SequoiaAlphaModel from IchimokuCloudCrossOverAlphaModel import IchimokuCloudCrossOverAlphaModel from trading_algorithm import TradingAlgorithm_main class EnhancedShortTermMeanReversionAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2010, 1, 1) #Set Start Date self.SetEndDate(datetime.now()) #Set End Date self.SetCash(10000) #Set Strategy Cash self.SetWarmUp(timedelta(minutes=1)) # Set Algorithm to 21 days #self.SetBrokerageModel(BrokerageName.AlphaStreams) self.UniverseSettings.Resolution = Resolution.Daily self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction) # rebalance the universe selection once a month self.rebalance_flag = 0 # make sure to run the universe selection at the start of the algorithm even it's not the month start self.first_month_trade_flag = 1 self.trade_flag = 0 # Number of quantiles for sorting returns for mean reversion self.nq = 5 # Number of quantiles for sorting volatility over five-day mean reversion period self.nq_vol = 3 # the symbol list after the coarse and fine universe selection self.universe = None self.NumberOfSymbolsFine = 500 self.NumberOfSymbolsCoarse = 150 self.dollarVolumeBySymbol = {} # setup state storage in initialize method self.stateData = { }; # Benchmark self.AddEquity("SPY", Resolution.Daily) self.SetBenchmark("SPY") # SPY HIGH self.correction_flag = 0 self.SPY_avg_delta = DeltaModel(self.History(self.Symbol('SPY'), 25, Resolution.Daily), self.Symbol('SPY')).run() self.SPY_vix_data = VixFix(self.History(self.Symbol('SPY'), 100, Resolution.Daily), self.Symbol('SPY')).run() self.SPY_mom = self.MOM('SPY', 10, Resolution.Daily).Current.Value self.Schedule.On(self.DateRules.MonthStart("SPY"), self.TimeRules.At(0, 0), Action(self.monthly_rebalance)) self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose("SPY", 305), Action(self.weekly_rebalance)) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 304), Action(self.get_prices)) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 303), Action(self.daily_rebalance)) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 302), Action(self.short)) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 301), Action(self.long)) def monthly_rebalance(self): # rebalance the universe every month self.rebalance_flag = 1 # symbols = {} # assets = [] # for i in self.universe: # assets.append(i) # for i in range(len(assets)): # symbols[assets[i]] = self.AddEquity(assets[i], Resolution.Minute).Symbol # regressor = RandomForestRegressor(n_estimators=100, min_samples_split=5, random_state = 1990) # df = self.History(self.universe, 50, Resolution.Hour) # X = df.unstack(level=1).transpose().pct_change().dropna() # y = np.random.normal(100000, 5, X.shape[0]) # X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 1990) # # Fit Regressor # regressor.fit(X_train, y_train) # # Get long-only predictions # weights = regressor.feature_importances_ # symbols = X.columns[np.where(weights)] # self.selected = zip(symbols, weights) def weekly_rebalance(self): # rebalance coarse universe self.rebalance_flag = 1 def CoarseSelectionFunction(self, coarse): if self.rebalance_flag or self.first_month_trade_flag: # drop stocks which have no fundamental data or have too low prices coarse_filtered = [x for x in coarse if (x.HasFundamentalData) and (20 <= float(x.Price) <= 300) and (float(x.Volume) >= 2000000)] # We are going to use a dictionary to refer the object that will keep the moving averages for c in coarse_filtered: if c.Symbol not in self.stateData: self.stateData[c.Symbol] = SelectionData(c.Symbol, 10) # Updates the SymbolData object with current EOD price avg = self.stateData[c.Symbol] avg.update(c.EndTime, c.Price, c.Volume, c.DollarVolume) # filter the values of selectionData(sd) above SMA values = [sd for sd in self.stateData.values() if (sd.Volume > (sd.Sma.Current.Value * 1.25)) and (sd.Volume_Ratio > 2) and (sd.Fast_Vol_Ratio > 2) and (sd.Med_Vol_Ratio > 2)] # sort sd by the largest % jump in volume. values.sort(key=lambda sd: sd.Volume_Ratio, reverse=True) self.dollarVolumeBySymbol = {i.Symbol: i.DollarVolume for i in values[:self.NumberOfSymbolsCoarse]} return [x.Symbol for x in values[:self.NumberOfSymbolsCoarse]] else: return self.universe def FineSelectionFunction(self, fine): if self.rebalance_flag or self.first_month_trade_flag: """ Need to do a better job with filter system with multiple parameters - currently takes the top 50% in each category and checks if stock is top 50% in each category """ # filter the stocks which have positive EV To EBITDA # filtered_fine = [x for x in fine if x.ValuationRatios.EVToEBITDA > 0 # and x.ValuationRatios.PriceChange1M > 0 # and x.CompanyReference.CountryId == "USA" # and (x.CompanyReference.PrimaryExchangeID == "NYS" or x.CompanyReference.PrimaryExchangeID == "NAS") # and x.CompanyReference.IndustryTemplateCode == "N" # and x.ValuationRatios.PERatio > 20 #20 # and x.ValuationRatios.FirstYearEstimatedEPSGrowth > 0.2 # and x.ValuationRatios.BookValueYield < 0.0 # and x.EarningReports.BasicAverageShares.ThreeMonths * x.EarningReports.BasicEPS.TwelveMonths * x.ValuationRatios.PERatio > 5e8 # and (x.Time - x.SecurityReference.IPODate).days > 180 # and x.ValuationRatios.PEGRatio > 2 # and x.FinancialStatements.CashFlowStatement.FreeCashFlow.ThreeMonths > 10] initial_filter = [x for x in fine if x.ValuationRatios.EVToEBITDA > 0 and x.ValuationRatios.PriceChange1M > 0 and x.EarningReports.BasicAverageShares.ThreeMonths * x.EarningReports.BasicEPS.TwelveMonths * x.ValuationRatios.PERatio > 5e8 and (x.Time - x.SecurityReference.IPODate).days > 180 and x.CompanyReference.CountryId == "USA" and (x.CompanyReference.PrimaryExchangeID == "NYS" or x.CompanyReference.PrimaryExchangeID == "NAS") and x.CompanyReference.IndustryTemplateCode == "N" and x.AssetClassification.MorningstarSectorCode in [MorningstarSectorCode.FinancialServices, MorningstarSectorCode.Technology, MorningstarSectorCode.Industrials, MorningstarSectorCode.CommunicationServices, MorningstarSectorCode.ConsumerCyclical, MorningstarSectorCode.ConsumerDefensive, MorningstarSectorCode.Healthcare, MorningstarSectorCode.Utilities]] second_filter = sorted(initial_filter, key = lambda x: x.ValuationRatios.PERatio > 5, reverse = True)[:int(len(initial_filter)/3)] third_filter = sorted(initial_filter, key = lambda x: x.ValuationRatios.PEGRatio > 0.5, reverse = True)[:int(len(initial_filter)/2.5)] fourth_filter = sorted(initial_filter, key = lambda x: x.FinancialStatements.CashFlowStatement.FreeCashFlow.ThreeMonths > 5, reverse = True)[:int(len(initial_filter)/2.5)] fifth_filter = sorted(initial_filter, key = lambda x: x.ValuationRatios.FirstYearEstimatedEPSGrowth > 0.1, reverse = True)[:int(len(initial_filter)/2.5)] filtered_fine = list() for symbol in initial_filter: if symbol in second_filter: if symbol in third_filter: if symbol in fourth_filter: if symbol in fifth_filter: filtered_fine.append(symbol) count = len(filtered_fine) if count == 0: return [] myDict = dict() percent = float(self.NumberOfSymbolsFine / count) # select stocks with top dollar volume in every single sector for key in ["N"]: value = [x for x in filtered_fine if x.CompanyReference.IndustryTemplateCode == key] value = sorted(value, key=lambda x: self.dollarVolumeBySymbol[x.Symbol], reverse = True) myDict[key] = value[:ceil(len(value) * percent)] topFine = list(chain.from_iterable(myDict.values()))[:self.NumberOfSymbolsFine] self.universe = [f.Symbol for f in topFine[:int(self.NumberOfSymbolsFine)]] # [:self.NumberOfSymbolsFine] self.rebalance_flag = 0 self.first_month_trade_flag = 0 self.trade_flag = 1 return self.universe def OnData(self, data): pass def short(self): if self.universe is None: return SPY_Velocity = 0 self.long_leverage = 0 self.short_leverage = 0 # request the history of benchmark pri = self.History(["SPY"], 75, Resolution.Daily) pos_one = (pri.loc["SPY"]['close'][-1]) pos_six = (pri.loc["SPY"]['close'][-75:].mean()) # calculate velocity of the benchmark velocity_stop = (pos_one - pos_six)/100.0 SPY_Velocity = velocity_stop if SPY_Velocity > 0.0: self.long_leverage = 1.8 self.short_leverage = -0.0 else: self.long_leverage = 1.1 self.short_leverage = -0.7 for symbol in self.shorts: if len(self.shorts) + self.existing_shorts == 0: return self.AddEquity(symbol, Resolution.Daily) self.SetHoldings(symbol, 0.8*(self.short_leverage/(len(self.shorts) + self.existing_shorts))) def long(self): if self.universe is None: return for symbol in self.longs: if len(self.longs) + self.existing_longs == 0: return self.AddEquity(symbol, Resolution.Daily) self.SetHoldings(symbol, 1*(self.long_leverage/(len(self.longs) + self.existing_longs))) def get_prices(self): def technical_setup(i): x = re.sub('[(){}<>| ]', '', str(i)) self.AddEquity(x, Resolution.Daily) self.History(self.Symbol(x), 200, Resolution.Daily) if self.EMA(x, 50, Resolution.Daily).Current.Value >= self.EMA(x, 200, Resolution.Daily).Current.Value: RSI = self.RSI(x, 14, Resolution.Daily).Current.Value if RSI <= 30: return x if self.universe is None: return # Get the last 30 days of prices for every stock in our universe prices = {} hist = self.History(self.universe, 30, Resolution.Daily) for i in self.universe: try: if str(i) in hist.index.levels[0]: if technical_setup(i): prices[i.Value] = hist.loc[str(i)]['close'] except: pass if len(prices.keys()) <= 0: return df_prices = pd.DataFrame(prices, columns = prices.keys()) # calculate the daily log return daily_rets = np.log(df_prices/df_prices.shift(1)) # calculate the latest return but skip the most recent price rets = (df_prices.iloc[-2] - df_prices.iloc[0]) / df_prices.iloc[0] # standard deviation of the daily return stdevs = daily_rets.std(axis = 0) try: self.ret_qt = pd.qcut(rets, 5, labels=False) + 1 self.stdev_qt = pd.qcut(stdevs, 3, labels=False) + 1 except: self.ret_qt = pd.qcut(rets, 5, labels=False, duplicates='drop') + 1 self.stdev_qt = pd.qcut(stdevs, 3, labels=False, duplicates='drop') + 1 self.longs = list((self.ret_qt[self.ret_qt == 1].index) & (self.stdev_qt[self.stdev_qt < 3].index)) self.shorts = list((self.ret_qt[self.ret_qt == self.nq].index) & (self.stdev_qt[self.stdev_qt < 3].index)) def daily_rebalance(self): # rebalance the position in portfolio every day if self.universe is None: return if self.longs is None: return if self.shorts is None: return self.existing_longs = 0 self.existing_shorts = 0 if (self.SPY_avg_delta <= - 5.5 and self.SPY_vix_data == 'No') or (self.SPY_mom < 0.0): if self.correction_flag == 0: for symbol in self.Portfolio.Invested: self.Liquidate(symbol) self.correction_flag = 1 elif self.correction_flag == 1: return elif (self.SPY_avg_delta <= -5.5 and self.SPY_vix_data == 'Yes'): if self.correction_flag == 1: self.monthly_rebalance self.weekly_rebalance self.get_prices self.daily_rebalance self.short self.long self.correction_flag = 0 return elif self.correction_flag == 0: pass elif (self.SPY_avg_delta > -5.5) or (self.SPY_avg_delta <= -5.5 and self.SPY_vix_data == 'Yes') or (self.SPY_mom >= 0.0): for symbol in self.Portfolio.Keys: if (symbol.Value != 'SPY') and (symbol.Value in self.ret_qt.index) and (self.Portfolio[symbol].IsLong): current_quantile = self.ret_qt.loc[symbol.Value] avg_delta = DeltaModel(self.History(symbol, 25, Resolution.Daily), symbol).run() vix_data = VixFix(self.History(symbol, 50, Resolution.Daily), symbol).run() if avg_delta > -5.5 or (avg_delta <= -5.5 and vix_data == 'Yes'): def ttm_squeeze(symbol): #TTM Squeeze sma_twenty = self.SMA(symbol, 20, Resolution.Daily).Current.Value std = self.STD(symbol, 20, Resolution.Daily).Current.Value lower_band = sma_twenty - 2*std upper_band = sma_twenty + 2*std atr = self.ATR(symbol, 20, Resolution.Daily).Current.Value lower_keltner = sma_twenty - 2*atr upper_keltner = sma_twenty + 2*atr return lower_band > lower_keltner and upper_band < upper_keltner def ema_squeeze(symbol): # create a 8 day exponential moving average fastest = self.EMA(symbol, 8, Resolution.Daily).Current.Value # create a 14 day exponential moving average faster = self.EMA(symbol, 14, Resolution.Daily).Current.Value # create a 21 day exponential moving average fast = self.EMA(symbol, 21, Resolution.Daily).Current.Value # create a 34 day exponential moving average slow = self.EMA(symbol, 34, Resolution.Daily).Current.Value # create a 50 day exponential moving average slower = self.EMA(symbol, 55, Resolution.Daily).Current.Value # creat a 200 day simple moving average slowest = self.SMA(symbol, 200, Resolution.Daily).Current.Value # define a small tolerance on our checks to avoid bouncing tolerance = 0.025 condition_one = ((fastest * (1-tolerance)) >= (faster * (1+tolerance))) condition_two = ((faster * (1-tolerance)) >= (fast * (1+tolerance))) condition_three = ((fast * (1-tolerance)) >= (slow * (1+tolerance))) condition_four = ((slow * (1-tolerance)) >= (slower * (1+tolerance))) condition_five = (slower > slowest) conditions = [condition_one, condition_two, condition_three, condition_four] count = 0 for condition in conditions: if condition is True: count += 1 if condition_five is True: return count # create a 50 day exponential moving average self.slow = self.EMA(symbol, 21, Resolution.Daily).Current.Value # creat a 200 day simple moving average self.slower = self.SMA(symbol, 55, Resolution.Daily).Current.Value if (self.slow > self.slower): if ttm_squeeze(symbol) or (ema_squeeze(symbol) >= 3): if self.Portfolio[symbol].Quantity > 0: if (current_quantile == 1) and (symbol not in self.longs): self.existing_longs += 2 self.update_ticket(symbol) elif (current_quantile > 1) and (symbol not in self.shorts): self.Liquidate(symbol) self.longs.remove(symbol) self.shorts.append(symbol) elif self.Portfolio[symbol].Quantity < 0: if (current_quantile == self.nq) and (symbol not in self.shorts): self.existing_shorts += 1 self.update_ticket(symbol) elif (current_quantile < self.nq) and (symbol not in self.longs): self.Liquidate(symbol) elif avg_delta <= -5.5: try: self.Liquidate(symbol) self.universe.remove(symbol) self.longs.remove(symbol) self.shorts.remove(symbol) except: pass self.AddEquity('SPY', Resolution.Daily) self.SPY_avg_delta = DeltaModel(self.History(self.Symbol('SPY'), 25, Resolution.Daily), self.Symbol('SPY')).run() self.SPY_vix_data = VixFix(self.History(self.Symbol('SPY'), 100, Resolution.Daily), self.Symbol('SPY')).run() self.SPY_mom = self.MOM('SPY', 10, Resolution.Daily).Current.Value self.take_profit() def OnOrderEvent(self, orderEvent): if orderEvent.Status == OrderStatus.Invalid: self.Log(str(orderEvent)) if orderEvent.Status == OrderStatus.Filled: order = self.Transactions.GetOrderById(orderEvent.OrderId) if order.Tag == 'pt': # If hit profit target, update stop order quantity updateSettings = UpdateOrderFields() updateSettings.Quantity = self.stops[orderEvent.Symbol].Quantity - orderEvent.Quantity self.stops[orderEvent.Symbol].Update(updateSettings) elif order.Tag == 'sl': # If hit stop loss, cancel profit target orders self.Transactions.CancelOpenOrders(orderEvent.Symbol, "Hit stop price") def take_profit(self): if self.Portfolio.TotalUnrealizedProfit > (0.2 * self.Portfolio.TotalPortfolioValue): stocks_invested = [x.Key for x in self.Portfolio if x.Value.Invested] stocks_invested_by_profit = sorted(stocks_invested, key=lambda x: self.Portfolio[x].UnrealizedProfit, reverse=True) for stock in stocks_invested_by_profit: holding = self.Portfolio[stock].Quantity avg_price = self.Portfolio[stock].AveragePrice holdings_profit = self.Portfolio[stock].UnrealizedProfit holdings_cost = avg_price * holding if stock in self.shorts: if holdings_profit >= holdings_cost * 1.5: self.MarketOrder(stock, -holding) elif stock in self.longs: half_holding = int(holding/2.0) if holdings_profit >= holdings_cost * 1.5: self.MarketOrder(stock, -half_holding) elif self.longs and self.shorts: stocks_invested = [x.Key for x in self.Portfolio if x.Value.Invested] # liquidate stocks not in the trading list for stock in stocks_invested: holding = self.Portfolio[stock].Quantity avg_price = self.Portfolio[stock].AveragePrice holdings_profit = self.Portfolio[stock].UnrealizedProfit holdings_cost = avg_price * holding if holdings_profit > holdings_cost * 1.5: self.Liquidate(stock) elif self.SPY_mom <= 0.0: for stock in self.Portfolio: try: if stock.Value.Invested: self.Liquidate(stock) except: pass def update_ticket(self, stock): """ Uses mean reversion to update stop loss and limit orders. """ self.mean = self.EMA(stock, 20, Resolution.Daily) self.atr = self.ATR(stock, 20, Resolution.Daily) # Mean reversion when price is too low if self.Securities[stock].Price < self.mean.Current.Value - 2.0 * self.atr.Current.Value: holding = self.Portfolio[stock].Quantity #marketTicket = self.MarketOrder(stock, holding) limitTicket = self.LimitOrder(stock, -self.Portfolio[stock].Quantity, self.Securities[stock].Price + self.atr.Current.Value) stopTicket = self.StopMarketOrder(stock, -self.Portfolio[stock].Quantity, self.Securities[stock].Price - self.atr.Current.Value) # Mean reversion when price is too high if self.Securities[stock].Price > self.mean.Current.Value + 2.0 * self.atr.Current.Value: holding = self.Portfolio[stock].Quantity #marketTicket = self.MarketOrder(stock, -holding) limitTicket = self.LimitOrder(stock, self.Portfolio[stock].Quantity, self.Securities[stock].Price + self.atr.Current.Value) stopTicket = self.StopMarketOrder(stock, self.Portfolio[stock].Quantity, self.Securities[stock].Price + self.atr.Current.Value) def TradeOptions(self, symbol): equity = self.AddEquity(symbol, Resolution.Minute) option = self.AddOption(symbol, Resolution.Minute) self.symbol = option.Symbol equity.SetDataNormalizationMode(DataNormalizationMode.Raw) option.SetFilter(-3, +3, timedelta(0), timedelta(60)) # use the underlying equity as the benchmark self.SetBenchmark(equity.Symbol) self.call = symbol # Initialize the call contract if slice.OptionChains.Count == 0: return for i in slice.OptionChains: if i.Key != self.symbol: continue chain = i.Value call = [x for x in chain if x.Right == 0] # filter the call options contracts # sorted the contracts according to their expiration dates and choose the ATM options contracts = sorted(sorted(call, key = lambda x: x.Expiry, reverse=True), key = lambda x: abs(chain.Underlying.Price - x.Strike)) if len(contracts) == 0: return contract = contracts[0] self.call = contract.Symbol self.Sell(symbol, 1) # short the call options if self.Portfolio[symbol].Quantity == 0: self.Buy(symbol, 100) # buy 100 the underlying stock self.Log("The stock price at time 0 S(0): {}".format(self.Securities[symbol].Price)) def MarketOpen(self): return self.Time.hour != 0 and self.Time.minute == 1
from System import * from QuantConnect import * from QuantConnect.Indicators import * from QuantConnect.Data.Consolidators import * from QuantConnect.Data.UniverseSelection import * from QuantConnect.Orders.Fees import ConstantFeeModel from QuantConnect.Algorithm import * from QuantConnect.Algorithm.Framework.Alphas import * from QuantConnect.Algorithm.Framework.Portfolio import EqualWeightingPortfolioConstructionModel, NullPortfolioConstructionModel from QuantConnect.Algorithm.Framework.Risk import MaximumDrawdownPercentPortfolio, MaximumUnrealizedProfitPercentPerSecurity, MaximumDrawdownPercentPerSecurity from Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel from datetime import datetime, timedelta import pandas as pd import numpy as np from scipy.stats import hmean, gmean import re from math import ceil from itertools import chain from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import train_test_split from DeltaModel import DeltaModel from VixFix import VixFix from UniverseSelectionHelperFunctions import SelectionData from CompositeTradeSignals import CompositeTradeSignals from SequoiaAlphaModel import SequoiaAlphaModel from IchimokuCloudCrossOverAlphaModel import IchimokuCloudCrossOverAlphaModel from trading_algorithm import TradingAlgorithm_main class EnhancedShortTermMeanReversionAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2010, 1, 1) #Set Start Date #self.SetEndDate(2016, 1, 1) #Set Start Date self.SetCash(10000) #Set Strategy Cash self.SetWarmUp(timedelta(minutes=1)) # Set Algorithm to 21 days #self.SetBrokerageModel(BrokerageName.AlphaStreams) self.UniverseSettings.Resolution = Resolution.Daily self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction) #self.AddUniverse("Weekly", lambda dt: self.weekly_adds if dt.day % 7 == 0 else []) # rebalance the universe selection once a month self.rebalance_flag = 0 # make sure to run the universe selection at the start of the algorithm even it's not the month start self.first_month_trade_flag = 1 self.trade_flag = 0 # Number of quantiles for sorting returns for mean reversion self.nq = 5 # Number of quantiles for sorting volatility over five-day mean reversion period self.nq_vol = 3 # the symbol list after the coarse and fine universe selection self.universe = None self.NumberOfSymbolsFine = 500 self.NumberOfSymbolsCoarse = 150 self.dollarVolumeBySymbol = {} # setup state storage in initialize method self.stateData = { }; # Benchmark self.AddEquity("SPY", Resolution.Daily) self.SetBenchmark("SPY") # SPY HIGH self.correction_flag = 0 self.SPY_avg_delta = DeltaModel(self.History(self.Symbol('SPY'), 25, Resolution.Daily), self.Symbol('SPY')).run() self.SPY_vix_data = VixFix(self.History(self.Symbol('SPY'), 100, Resolution.Daily), self.Symbol('SPY')).run() self.SPY_mom = self.MOM('SPY', 10, Resolution.Daily).Current.Value self.Schedule.On(self.DateRules.MonthStart("SPY"), self.TimeRules.At(0, 0), Action(self.monthly_rebalance)) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 304), Action(self.get_prices)) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 302), Action(self.daily_rebalance)) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 301), Action(self.short)) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 300), Action(self.long)) def monthly_rebalance(self): # rebalance the universe every month self.rebalance_flag = 1 def CoarseSelectionFunction(self, coarse): def aggregate_mean(x): x = np.asarray(x, dtype=np.float32) x = x[x >= 0] cutoff = np.mean(x, dtype=np.float64) return cutoff if self.rebalance_flag or self.first_month_trade_flag: # drop stocks which have no fundamental data or have too low prices selected = [x for x in coarse if (x.HasFundamentalData) and (20 <= float(x.Price) <= 300) and (float(x.Volume) >= 2000000)] # rank the stocks by dollar volume and choose the top 150 # <= 300 avg_dollar_volume = aggregate_mean([x.DollarVolume for x in coarse]) filtered = sorted(selected, key=lambda x: x.DollarVolume > avg_dollar_volume, reverse=True)[:self.NumberOfSymbolsCoarse] self.dollarVolumeBySymbol = {i.Symbol: i.DollarVolume for i in filtered} return [x.Symbol for x in filtered] else: return self.universe def FineSelectionFunction(self, fine): def aggregate_mean(x): x = np.asarray(x, dtype=np.float32) x = x[x >= 0] cutoff = np.mean(x, dtype=np.float64) return cutoff def upper_groups(x): x = np.asarray(x, dtype=np.float32) x = x[x >= 0] cutoff = np.mean(x, dtype=np.float64) + (np.std(x, dtype=np.float64) *2) return cutoff if self.rebalance_flag or self.first_month_trade_flag: # filter the stocks which have positive EV To EBITDA filtered_fine = [x for x in fine if x.ValuationRatios.EVToEBITDA > 0 and x.ValuationRatios.PriceChange1M > 0 and x.CompanyReference.CountryId == "USA" and (x.CompanyReference.PrimaryExchangeID == "NYS" or x.CompanyReference.PrimaryExchangeID == "NAS") and x.CompanyReference.IndustryTemplateCode == "N" and x.ValuationRatios.PERatio > 20 and x.ValuationRatios.FirstYearEstimatedEPSGrowth > 0.2 and x.EarningReports.BasicAverageShares.ThreeMonths * x.EarningReports.BasicEPS.TwelveMonths * x.ValuationRatios.PERatio > 5e8 and (x.Time - x.SecurityReference.IPODate).days > 180 and x.ValuationRatios.PEGRatio > 2 and x.FinancialStatements.CashFlowStatement.FreeCashFlow.ThreeMonths > 10] if len(filtered_fine) <= 0: symbol_marketcap = [x.EarningReports.BasicAverageShares.ThreeMonths * x.EarningReports.BasicEPS.TwelveMonths * x.ValuationRatios.PERatio for x in fine] marketcap_mean = upper_groups([x for x in symbol_marketcap]) initial_filter = [x for x in fine if x.ValuationRatios.EVToEBITDA > 0 and x.ValuationRatios.PriceChange1M > 0 and x.EarningReports.BasicAverageShares.ThreeMonths * x.EarningReports.BasicEPS.TwelveMonths * x.ValuationRatios.PERatio > marketcap_mean and (x.Time - x.SecurityReference.IPODate).days > 180 and x.CompanyReference.CountryId == "USA" and (x.CompanyReference.PrimaryExchangeID == "NYS" or x.CompanyReference.PrimaryExchangeID == "NAS") and x.CompanyReference.IndustryTemplateCode == "N"] PERatio_mean = upper_groups([float(x.ValuationRatios.PERatio) for x in initial_filter]) PEGRatio_mean = upper_groups([float(x.ValuationRatios.PEGRatio) for x in initial_filter]) FreeCashFlow_mean = upper_groups([float(x.FinancialStatements.CashFlowStatement.FreeCashFlow.ThreeMonths) for x in initial_filter]) FirstYREPSGrowth_mean = upper_groups([float(x.ValuationRatios.FirstYearEstimatedEPSGrowth) for x in initial_filter]) second_filter = sorted(initial_filter, key = lambda x: x.ValuationRatios.PERatio > PERatio_mean, reverse = True)#[:int(len(initial_filter)/2)] third_filter = sorted(initial_filter, key = lambda x: x.ValuationRatios.PEGRatio >= PEGRatio_mean, reverse = True)#[:int(len(initial_filter)/1.5)] fourth_filter = sorted(initial_filter, key = lambda x: x.FinancialStatements.CashFlowStatement.FreeCashFlow.ThreeMonths >= FreeCashFlow_mean, reverse = True)#[:int(len(initial_filter)/1.5)] fifth_filter = sorted(initial_filter, key = lambda x: x.ValuationRatios.FirstYearEstimatedEPSGrowth >= FirstYREPSGrowth_mean, reverse = True)#[:int(len(initial_filter)/1.5)] filtered_fine = list(set.intersection(*map(set, [initial_filter, second_filter, third_filter, fourth_filter, fifth_filter]))) count = len(filtered_fine) if count == 0: return [] myDict = dict() percent = float(self.NumberOfSymbolsFine / count) # select stocks with top dollar volume in every single sector for key in ["N"]: value = [x for x in filtered_fine if x.CompanyReference.IndustryTemplateCode == key] value = sorted(value, key=lambda x: self.dollarVolumeBySymbol[x.Symbol], reverse = True) myDict[key] = value[:ceil(len(value) * percent)] topFine = list(chain.from_iterable(myDict.values()))[:self.NumberOfSymbolsFine] self.universe = [f.Symbol for f in topFine] self.rebalance_flag = 0 self.first_month_trade_flag = 0 self.trade_flag = 1 return self.universe def OnData(self, data): pass def short(self): if self.universe is None: return if (self.SPY_avg_delta > -5.5): SPY_Velocity = 0 self.long_leverage = 0 self.short_leverage = 0 # request the history of benchmark pri = self.History(["SPY"], 75, Resolution.Daily) pos_one = (pri.loc["SPY"]['close'][-1]) pos_six = (pri.loc["SPY"]['close'][-75:].mean()) # calculate velocity of the benchmark velocity_stop = (pos_one - pos_six)/100.0 SPY_Velocity = velocity_stop if SPY_Velocity > 0.0: self.long_leverage = 1.8 self.short_leverage = -0.0 else: self.long_leverage = 1.1 self.short_leverage = -0.7 for symbol in self.shorts: if len(self.shorts) + self.existing_shorts == 0: return self.AddEquity(symbol, Resolution.Daily) self.SetHoldings(symbol, 0.8*(self.short_leverage/(len(self.shorts) + self.existing_shorts))) def long(self): if self.universe is None: return if (self.SPY_avg_delta > -5.5): for symbol in self.longs: if len(self.longs) + self.existing_longs == 0: return self.AddEquity(symbol, Resolution.Daily) self.SetHoldings(symbol, (self.long_leverage/(len(self.longs) + self.existing_longs))) def get_prices(self): if self.universe is None: return # Get the last 25 days of prices for every stock in our universe prices = {} hist = self.History(self.universe, 25, Resolution.Daily) for i in self.universe: if str(i) in hist.index.levels[0]: if self.technical_setup(i): prices[i.Value] = hist.loc[str(i)]['close'] df_prices = pd.DataFrame(prices, columns = prices.keys()) # calculate the daily log return daily_rets = np.log(df_prices/df_prices.shift(1)) # calculate the latest return but skip the most recent price rets = (df_prices.iloc[-2] - df_prices.iloc[0]) / df_prices.iloc[0] # standard deviation of the daily return stdevs = daily_rets.std(axis = 0) try: self.ret_qt = pd.qcut(rets, 5, labels=False) + 1 self.stdev_qt = pd.qcut(stdevs, 3, labels=False) + 1 except: self.ret_qt = pd.qcut(rets, 5, labels=False, duplicates='drop') + 1 self.stdev_qt = pd.qcut(stdevs, 3, labels=False, duplicates='drop') + 1 self.longs = list((self.ret_qt[self.ret_qt == 1].index) & (self.stdev_qt[self.stdev_qt < 3].index)) self.shorts = list((self.ret_qt[self.ret_qt == self.nq].index) & (self.stdev_qt[self.stdev_qt < 3].index)) def daily_rebalance(self): # rebalance the position in portfolio every day if self.universe is None: return if self.longs is None: return if self.shorts is None: return self.existing_longs = 0 self.existing_shorts = 0 if (self.SPY_avg_delta <= - 5.5 and self.SPY_vix_data == 'No') or (self.SPY_mom < 0.0): if self.correction_flag == 0: for symbol in self.Portfolio.Invested: self.Liquidate(symbol) self.correction_flag = 1 elif self.correction_flag == 1: return elif (self.SPY_avg_delta <= -5.5 and self.SPY_vix_data == 'Yes'): if self.correction_flag == 1: self.monthly_rebalance self.daily_rebalance self.correction_flag = 0 return elif self.correction_flag == 0: pass elif (self.SPY_avg_delta > -5.5) or (self.SPY_avg_delta <= -5.5 and self.SPY_vix_data == 'Yes') or (self.SPY_mom >= 0.5): for symbol in self.Portfolio.Keys: if (symbol.Value != 'SPY') and (symbol.Value in self.ret_qt.index) and (self.Portfolio[symbol].IsLong): current_quantile = self.ret_qt.loc[symbol.Value] avg_delta = DeltaModel(self.History(symbol, 25, Resolution.Daily), symbol).run() vix_data = VixFix(self.History(symbol, 50, Resolution.Daily), symbol).run() if avg_delta > -5.5 or (avg_delta <= -5.5 and vix_data == 'Yes'): def ttm_squeeze(symbol): #TTM Squeeze sma_twenty = self.SMA(symbol, 20, Resolution.Daily).Current.Value std = self.STD(symbol, 20, Resolution.Daily).Current.Value lower_band = sma_twenty - 2*std upper_band = sma_twenty + 2*std atr = self.ATR(symbol, 20, Resolution.Daily).Current.Value lower_keltner = sma_twenty - 2*atr upper_keltner = sma_twenty + 2*atr return (lower_band > lower_keltner) and (upper_band < upper_keltner) def ema_squeeze(symbol): # create a 8 day exponential moving average fastest = self.EMA(symbol, 8, Resolution.Daily).Current.Value # create a 14 day exponential moving average faster = self.EMA(symbol, 14, Resolution.Daily).Current.Value # create a 21 day exponential moving average fast = self.EMA(symbol, 21, Resolution.Daily).Current.Value # create a 34 day exponential moving average slow = self.EMA(symbol, 34, Resolution.Daily).Current.Value # create a 50 day exponential moving average slower = self.EMA(symbol, 55, Resolution.Daily).Current.Value # creat a 200 day simple moving average slowest = self.SMA(symbol, 200, Resolution.Daily).Current.Value # define a small tolerance on our checks to avoid bouncing tolerance = 0.025 condition_one = ((fastest * (1-tolerance)) >= (faster * (1+tolerance))) condition_two = ((faster * (1-tolerance)) >= (fast * (1+tolerance))) condition_three = ((fast * (1-tolerance)) >= (slow * (1+tolerance))) condition_four = ((slow * (1-tolerance)) >= (slower * (1+tolerance))) condition_five = (slower > slowest) conditions = [condition_one, condition_two, condition_three, condition_four, condition_five] count = 0 for condition in conditions: if condition is True: count += 1 return count def go_time_indicators(symbol): # Momentum & Volume & Trend # RSI <= 30 = buy rsi = self.RSI(symbol, 14, MovingAverageType.Simple, Resolution.Daily).Current.Value # William's %R <= -20 = buy wilr = self.WILR(symbol, 14, Resolution.Daily).Current.Value # MACD current < signal = buy macd = self.MACD(symbol, 12, 26, 9, Resolution.Daily) macd_current = macd.Current.Value macd_signal = macd.Signal.Current.Value macd_fast = macd.Fast.Current.Value macd_signal_delta = (macd_current - macd_signal)/(macd_fast) tolerance = 0.0025 condition_one = (rsi < 30) condition_two = (wilr < -20) condition_three = (macd_signal_delta > tolerance) return condition_one or condition_two or condition_three # create a 21 day exponential moving average self.slow = self.EMA(symbol, 21, Resolution.Daily).Current.Value # creat a 55 day simple moving average self.slower = self.SMA(symbol, 55, Resolution.Daily).Current.Value if (self.slow > self.slower): if ((ttm_squeeze(symbol) and go_time_indicators(symbol)) and self.SPY_mom > 0.5): self.TradeOptions(symbol) self.update_ticket(symbol) if ttm_squeeze(symbol) or (ema_squeeze(symbol) >= 3) or go_time_indicators(symbol): if self.Portfolio[symbol].Quantity > 0: if (current_quantile == 1) and (symbol not in self.longs): self.existing_longs += 2 self.update_ticket(symbol) elif (current_quantile > 1) and (symbol not in self.shorts): self.Liquidate(symbol) self.longs.remove(symbol) self.shorts.append(symbol) elif self.Portfolio[symbol].Quantity < 0: if (current_quantile == self.nq) and (symbol not in self.shorts): self.existing_shorts += 1 self.update_ticket(symbol) elif (current_quantile < self.nq) and (symbol not in self.longs): self.Liquidate(symbol) # else: # self.update_ticket(symbol) elif avg_delta <= -5.5: try: self.Liquidate(symbol) self.universe.remove(symbol) self.longs.remove(symbol) self.shorts.remove(symbol) except: pass self.AddEquity('SPY', Resolution.Daily) self.SPY_avg_delta = DeltaModel(self.History(self.Symbol('SPY'), 25, Resolution.Daily), self.Symbol('SPY')).run() self.SPY_vix_data = VixFix(self.History(self.Symbol('SPY'), 100, Resolution.Daily), self.Symbol('SPY')).run() self.SPY_mom = self.MOM('SPY', 10, Resolution.Daily).Current.Value self.take_profit() def OnOrderEvent(self, orderEvent): if orderEvent.Status == OrderStatus.Invalid: self.Log(str(orderEvent)) if orderEvent.Status == OrderStatus.Filled: order = self.Transactions.GetOrderById(orderEvent.OrderId) if order.Tag == 'pt': # If hit profit target, update stop order quantity updateSettings = UpdateOrderFields() updateSettings.Quantity = self.stops[orderEvent.Symbol].Quantity - orderEvent.Quantity self.stops[orderEvent.Symbol].Update(updateSettings) elif order.Tag == 'sl': # If hit stop loss, cancel profit target orders self.Transactions.CancelOpenOrders(orderEvent.Symbol, "Hit stop price") def technical_setup(self, i): x = re.sub('[(){}<>| ]', '', str(i)) self.AddEquity(x, Resolution.Daily) self.History(self.Symbol(x), 200, Resolution.Daily) if self.EMA(x, 50, Resolution.Daily).Current.Value >= self.EMA(x, 200, Resolution.Daily).Current.Value: if self.RSI(x, 14, Resolution.Daily).Current.Value < 30: return x def take_profit(self): if self.Portfolio.TotalUnrealizedProfit > (0.2 * self.Portfolio.TotalPortfolioValue): stocks_invested = [x.Key for x in self.Portfolio if x.Value.Invested] stocks_invested_by_profit = sorted(stocks_invested, key=lambda x: self.Portfolio[x].UnrealizedProfit, reverse=True) for stock in stocks_invested_by_profit: holding = self.Portfolio[stock].Quantity avg_price = self.Portfolio[stock].AveragePrice holdings_profit = self.Portfolio[stock].UnrealizedProfit holdings_cost = avg_price * holding if stock in self.shorts: if holdings_profit >= holdings_cost * 1.5: self.MarketOrder(stock, -holding) elif stock in self.longs: half_holding = int(holding/2.0) if holdings_profit >= holdings_cost * 1.5: self.MarketOrder(stock, -half_holding) elif self.longs and self.shorts: stocks_invested = [x.Key for x in self.Portfolio if x.Value.Invested] # liquidate stocks not in the trading list for stock in stocks_invested: holding = self.Portfolio[stock].Quantity avg_price = self.Portfolio[stock].AveragePrice holdings_profit = self.Portfolio[stock].UnrealizedProfit holdings_cost = avg_price * holding if holdings_profit > holdings_cost * 1.5: self.Liquidate(stock) elif self.SPY_mom <= 0.0: for stock in self.Portfolio: try: if stock.Value.Invested: self.Liquidate(stock) except: pass def update_ticket(self, stock): """ Uses mean reversion to update stop loss and limit orders. """ self.mean = self.EMA(stock, 20, Resolution.Daily) self.atr = self.ATR(stock, 20, Resolution.Daily) # Mean reversion when price is too low if self.Securities[stock].Price < self.mean.Current.Value - 2.0 * self.atr.Current.Value: holding = self.Portfolio[stock].Quantity marketTicket = self.MarketOrder(stock, quantity) limitTicket = self.LimitOrder(stock, -self.Portfolio[stock].Quantity, self.Securities[stock].Price + self.atr.Current.Value) stopTicket = self.StopMarketOrder(stock, -self.Portfolio[stock].Quantity, self.Securities[stock].Price - self.atr.Current.Value) # Mean reversion when price is too high if self.Securities[stock].Price > self.mean.Current.Value + 2.0 * self.atr.Current.Value: holding = self.Portfolio[stock].Quantity marketTicket = self.MarketOrder(stock, -holding) limitTicket = self.LimitOrder(stock, self.Portfolio[stock].Quantity, self.Securities[stock].Price + self.atr.Current.Value) stopTicket = self.StopMarketOrder(stock, self.Portfolio[stock].Quantity, self.Securities[stock].Price + self.atr.Current.Value) def TradeOptions(self, symbol): equity = self.AddEquity(symbol, Resolution.Minute) option = self.AddOption(symbol, Resolution.Minute) self.symbol = option.Symbol equity.SetDataNormalizationMode(DataNormalizationMode.Raw) option.SetFilter(-2, +2, timedelta(0), timedelta(60)) # use the underlying equity as the benchmark self.SetBenchmark(equity.Symbol) self.call = symbol # Initialize the call contract if slice.OptionChains.Count == 0: return for i in slice.OptionChains: if i.Key != self.symbol: continue chain = i.Value call = [x for x in chain if x.Right == 0] # filter the call options contracts # sorted the contracts according to their expiration dates and choose the ATM options contracts = sorted(sorted(call, key = lambda x: x.Expiry, reverse=True), key = lambda x: abs(chain.Underlying.Price - x.Strike)) if len(contracts) == 0: return contract = contracts[0] self.call = contract.Symbol if self.Securities[self.call].Price < (0.01 * self.Portfolio.TotalPortfolioValue): num_contracts = int((self.Portfolio.TotalPortfolioValue * 0.01)/self.Securities[self.call].Price) #if num_contracts > 0: self.Sell(symbol, num_contracts) # short the call options if self.Portfolio[symbol].Quantity == 0: self.Buy(symbol, 100) # buy 100 the underlying stock self.Log("The stock price at time 0 S(0): {}".format(self.Securities[symbol].Price)) def MarketOpen(self): return self.Time.hour != 0 and self.Time.minute == 1
from System import * from QuantConnect import * from QuantConnect.Data.Consolidators import * from QuantConnect.Data.Market import * from QuantConnect.Orders import OrderStatus from QuantConnect.Algorithm import QCAlgorithm from QuantConnect.Indicators import * import numpy as np from datetime import timedelta, datetime ### <summary> ### Example structure for structuring an algorithm with indicator and consolidator data for many tickers. ### </summary> ### <meta name="tag" content="consolidating data" /> ### <meta name="tag" content="indicators" /> ### <meta name="tag" content="using data" /> ### <meta name="tag" content="strategy example" /> class MultipleSymbolConsolidationAlgorithm(QCAlgorithm): # Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized. def Initialize(self): # This is the period of bars we'll be creating BarPeriod = TimeSpan.FromMinutes(10) # This is the period of our sma indicators SimpleMovingAveragePeriod = 10 # This is the number of consolidated bars we'll hold in symbol data for reference RollingWindowSize = 10 # Holds all of our data keyed by each symbol self.Data = {} # Contains all of our equity symbols EquitySymbols = ["AAPL","SPY","IBM"] self.SetStartDate(2014, 12, 1) self.SetEndDate(2015, 2, 1) # initialize our equity data for symbol in EquitySymbols: equity = self.AddEquity(symbol) self.Data[symbol] = SymbolData(equity.Symbol, BarPeriod, RollingWindowSize) # loop through all our symbols and request data subscriptions and initialize indicator for symbol, symbolData in self.Data.items(): # define the indicator symbolData.SMA = SimpleMovingAverage(self.CreateIndicatorName(symbol, "SMA" + str(SimpleMovingAveragePeriod), Resolution.Minute), SimpleMovingAveragePeriod) # define a consolidator to consolidate data for this symbol on the requested period consolidator = TradeBarConsolidator(BarPeriod) if symbolData.Symbol.SecurityType == SecurityType.Equity else QuoteBarConsolidator(BarPeriod) # write up our consolidator to update the indicator consolidator.DataConsolidated += self.OnDataConsolidated # we need to add this consolidator so it gets auto updates self.SubscriptionManager.AddConsolidator(symbolData.Symbol, consolidator) def OnDataConsolidated(self, sender, bar): self.Data[bar.Symbol.Value].SMA.Update(bar.Time, bar.Close) self.Data[bar.Symbol.Value].Bars.Add(bar) # OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here. # Argument "data": Slice object, dictionary object with your stock data def OnData(self,data): # loop through each symbol in our structure for symbol in self.Data.keys(): symbolData = self.Data[symbol] # this check proves that this symbol was JUST updated prior to this OnData function being called if symbolData.IsReady() and symbolData.WasJustUpdated(self.Time): if not self.Portfolio[symbol].Invested: self.MarketOrder(symbol, 1) # End of a trading day event handler. This method is called at the end of the algorithm day (or multiple times if trading multiple assets). # Method is called 10 minutes before closing to allow user to close out position. def OnEndOfDay(self): i = 0 for symbol in sorted(self.Data.keys()): symbolData = self.Data[symbol] # we have too many symbols to plot them all, so plot every other i += 1 if symbolData.IsReady() and i%2 == 0: self.Plot(symbol, symbol, symbolData.SMA.Current.Value) class SymbolData(object): def __init__(self, symbol, barPeriod, windowSize): self.Symbol = symbol # The period used when population the Bars rolling window self.BarPeriod = barPeriod # A rolling window of data, data needs to be pumped into Bars by using Bars.Update( tradeBar ) and can be accessed like: # mySymbolData.Bars[0] - most first recent piece of data # mySymbolData.Bars[5] - the sixth most recent piece of data (zero based indexing) self.Bars = RollingWindow[IBaseDataBar](windowSize) # The simple moving average indicator for our symbol self.SMA = None # Returns true if all the data in this instance is ready (indicators, rolling windows, ect...) def IsReady(self): return self.Bars.IsReady and self.SMA.IsReady # Returns true if the most recent trade bar time matches the current time minus the bar's period, this # indicates that update was just called on this instance def WasJustUpdated(self, current): return self.Bars.Count > 0 and self.Bars[0].Time == current - self.BarPeriod
# Your New Python File
# Your New Python File
# Your New Python File
import pandas as pd import numpy as np class SpyAnalysis: """ Bullish Count > 3. Max 5 Bearish Count > 3. Max 8 """ def __init__(self, symbol, *args, **kwargs): super(SpyAnalysis, self).__init__() self.symbol = symbol self.count = 0 self.tolerance = 0.00015 # EMA Cross fastest = self.EMA(self.symbol, 14, Resolution.Daily).Current.Value fast = self.EMA(self.symbol, 21, Resolution.Daily).Current.Value slow = self.EMA(self.symbol, 50, Resolution.Daily).Current.Value base = self.SMA(self.symbol, 200, Resolution.Daily).Current.Value if fastest > fast * (1 + self.tolerance): self.count += 1 if fast > slow * (1 + self.tolerance): self.count += 1 if slow > base * (1 + self.tolerance): self.count += 1 elif fastest < fast * (1 + self.tolerance): self.count -= 1 if fast < slow * (1 + self.tolerance): self.count -= 1 if slow < base * (1 + self.tolerance): self.count -= 1 # RSI and RSI Crossover for period in [14, 21, 50, 200]: rsi = self.RSI(self.symbol, period, Resolution.Daily).Current.Value rsiAvg = IndicatorExtensions.SMA(rsi, period).Current.Value if rsi < 25: if period == 200: self.count += 3 elif period in [21, 50]: self.count += 2 else: self.count += 1 elif (25 < rsi) and (rsi < 50): if period == 200: self.count += 3 elif period in [21, 50]: self.count += 2 else: self.count += 1 elif (50 < rsi) and (rsi < 75): if period == 200: self.count -= 2 elif period in [21, 50]: self.count -= 2 else: self.count -= 1 elif rsi > 80: if period == 200: self.count -= 3 elif period in [21, 50]: self.count -= 2 else: self.count -= 1 if rsi > rsiAvg: if period == 200: self.count += 3 elif period in [21, 50]: self.count += 2 else: self.count += 1 elif rsiAvg > rsi: if period == 200: self.count -= 3 elif period in [21, 50]: self.count -= 2 else: self.count -= 1 # Momentum for period in [14, 21, 50, 200]: mom = self.MOM(self.symbol, period, Resolution.Daily).Current.Value mean = self.SMA(self.symbol, period, Resolution.Daily).Current.Value if mom > mean: if period == 200: self.count += 3 elif period in [21, 50]: self.count += 2 else: self.count += 1 elif mean > mom: if period == 200: self.count -= 3 elif period in [21, 50]: self.count -= 2 else: self.count -= 1 # Ultimate Oscillator ultosc = self.ULTOSC(self.symbol, 7, 14, 28, Resolution.Daily).Current.Value if ultosc < 25: self.count += 3 elif (25 < ultosc) and (ultosc < 50): self.count += 1 elif (25 < ultosc) and (ultosc < 50): self.count -= 1 elif ultosc > 80: self.count -= 3 return self.count
# Your New Python File
# Your New Python File
import os, sys, copy import numpy as np class LongTermAlgos: """ This function deploys and employs various trading strategies to arrive at the best decision to buy/sell/hold a stock. """ def __init__(self, data, *args, **kwargs): """ :param data: :param args: :param kwargs: """ super(LongTermAlgos, self).__init__() self.data = data self.algos = {} # import libraries from MeanReversion.main import MeanReversion from StatisticalArbitrage.main import StatsArbitrage from CrowdSourcing.main import CrowdSource from MachineLearning.main import MachineLearning from Momentum.main import Momentum from TrendFollowing.main import TrendFollowing from MarketMaking.main import MarketMaking from SentimentAnalysis.main import Sentiment self.algos['MeanReversion'] = MeanReversion self.algos['StatsticalArbitrage'] = StatsticalArbitrage self.algos['CrowdSourcing'] = CrowdSourcing self.algos['MachineLearning'] = MachineLearning self.algos['Momentum'] = Momentum self.algos['TrendFollowing'] = TrendFollowing self.algos['MarketMaking'] = MarketMaking self.algos['SentimentAnalysis'] = SentimentAnalysis def run(self): """ :return: """ for algo in self.algos.keys(): trading_strategy = self.algos[algo](data = self.data) results = trading_strategy.run() # Your New Python File