| 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 learningimport 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_percentfrom 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 == 1from 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 = Nonefrom 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 == 1import 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] > 30from 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 == 1from 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 == 1from 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 cloudfrom 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 Filefrom 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 == 1from 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 == 1from 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