I wrote this but instead of buying when stocks are oversold and selling when stocks are over bought, the algo buys when stocks are overbought and price is > sma and sell when stocks are oversold and price is < sma. I used tech stocks. It was an error at first but then I rationalized it by thinking that stocks are traded algorithmically so much that stocks have a little more volatility that can be taken advantage at highs and lows. It was fun to write
# region imports
from AlgorithmImports import *
# endregion
class AlertMagentaParrot(QCAlgorithm):
def Initialize(self) -> None:
self.SetStartDate(2017, 1, 1)
self.SetEndDate(2020, 12, 31)
self.SetCash(50000)
# Add symbols
self.docu_symbol = self.AddEquity("DOCU", Resolution.Daily).Symbol
self.amd_symbol = self.AddEquity("AMD", Resolution.Daily).Symbol
self.nflx_symbol = self.AddEquity("NFLX", Resolution.Daily).Symbol
self.tsla_symbol = self.AddEquity("TSLA", Resolution.Daily).Symbol
self.goog_symbol = self.AddEquity("GOOG", Resolution.Daily).Symbol
# RSI for DOCU
self.docu_rsi = self.RSI(self.docu_symbol, 3)
self.docu_periods_above_70 = 0
self.docu_periods_below_30 = 0
# RSI for AMD
self.amd_rsi = self.RSI(self.amd_symbol, 3)
self.amd_periods_above_70 = 0
self.amd_periods_below_30 = 0
# RSI for nflx
self.nflx_rsi = self.RSI(self.nflx_symbol, 3)
self.nflx_periods_above_70 = 0
self.nflx_periods_below_30 = 0
# RSI for tsla
self.tsla_rsi = self.RSI(self.tsla_symbol, 3)
self.tsla_periods_above_70 = 0
self.tsla_periods_below_30 = 0
# RSI for GOOG
self.goog_rsi = self.RSI(self.goog_symbol, 3)
self.goog_periods_above_70 = 0
self.goog_periods_below_30 = 0
self.sma_period = 21
self.docu_sma_symbol = self.docu_symbol # SMA based on DOCU, you can change this if needed
self.amd_sma_symbol = self.amd_symbol # SMA based on amd
self.nflx_sma_symbol = self.nflx_symbol # SMA based on nflx
self.tsla_sma_symbol = self.tsla_symbol # sma based on tsla
self.goog_sma_symbol = self.goog_symbol # sma based on goog
# SMA runs every day after market open
self.Schedule.On(self.DateRules.EveryDay(self.docu_sma_symbol),
self.TimeRules.AfterMarketOpen(self.docu_sma_symbol, 1),
self.Rebalance)
self.Schedule.On(self.DateRules.EveryDay(self.amd_sma_symbol),
self.TimeRules.AfterMarketOpen(self.amd_sma_symbol, 1),
self.Rebalance)
self.Schedule.On(self.DateRules.EveryDay(self.nflx_sma_symbol),
self.TimeRules.AfterMarketOpen(self.nflx_sma_symbol, 1),
self.Rebalance)
self.Schedule.On(self.DateRules.EveryDay(self.tsla_sma_symbol),
self.TimeRules.AfterMarketOpen(self.tsla_sma_symbol, 1),
self.Rebalance)
self.Schedule.On(self.DateRules.EveryDay(self.goog_sma_symbol),
self.TimeRules.AfterMarketOpen(self.goog_sma_symbol, 1),
self.Rebalance)
def OnData(self, slice: Slice) -> None:
# Check if there is enough data to compute SMA
if self.IsWarmingUp:
return
# Check if the symbols are in the slice
if self.docu_sma_symbol not in slice.Keys or self.docu_symbol not in slice.Keys:
return
if self.amd_sma_symbol not in slice.Keys or self.amd_symbol not in slice.Keys:
return
if self.nflx_sma_symbol not in slice.Keys or self.nflx_symbol not in slice.Keys:
return
if self.tsla_sma_symbol not in slice.Keys or self.tsla_symbol not in slice.Keys:
return
if self.goog_sma_symbol not in slice.Keys or self.goog_symbol not in slice.Keys:
return
# Access historical data for SMA calculation
docu_history = self.History(self.docu_symbol, self.sma_period, Resolution.Daily)
amd_history = self.History(self.amd_symbol, self.sma_period, Resolution.Daily)
nflx_history = self.History(self.nflx_symbol, self.sma_period, Resolution.Daily)
tsla_history = self.History(self.tsla_symbol, self.sma_period, Resolution.Daily)
goog_history = self.History(self.goog_symbol, self.sma_period, Resolution.Daily)
# Calculate SMA for DOCU
if not docu_history.empty:
docu_sma = docu_history["close"].mean()
docu_current_price = slice.Bars[self.docu_symbol].Close
# Buying and Selling Logic for DOCU (similar logic can be applied to amd)
if self.docu_rsi.Current.Value > 70:
self.docu_periods_above_70 += 1
if self.docu_periods_above_70 == 2 and docu_current_price > docu_sma:
self.LimitOrder("DOCU", 100, docu_current_price)
else:
self.docu_periods_above_70 = 0
if self.docu_rsi.Current.Value < 30:
self.docu_periods_below_30 += 1
if self.docu_periods_below_30 == 2 and docu_current_price < docu_sma:
if self.Portfolio[self.docu_symbol].Quantity >= 100:
self.LimitOrder("DOCU", -100, docu_current_price)
else:
self.docu_periods_below_30 = 0
# Calculate SMA for amd (similar logic can be applied to DOCU)
if not amd_history.empty:
amd_sma = amd_history["close"].mean()
amd_current_price = slice.Bars[self.amd_symbol].Close
# Buying and Selling Logic for amd
if self.amd_rsi.Current.Value > 70:
self.amd_periods_above_70 += 1
if self.amd_periods_above_70 == 2 and amd_current_price > amd_sma:
self.LimitOrder("AMD", 40, amd_current_price)
else:
self.amd_periods_above_70 = 0
if self.amd_rsi.Current.Value < 30:
self.amd_periods_below_30 += 1
if self.amd_periods_below_30 == 2 and amd_current_price < amd_sma:
if self.Portfolio[self.amd_symbol].Quantity >= 40:
self.LimitOrder("AMD", -40, amd_current_price)
else:
self.amd_periods_below_30 = 0
# Calculate SMA for nflx(similar logic can be applied to nflx)
if not nflx_history.empty:
nflx_sma = nflx_history["close"].mean()
nflx_current_price = slice.Bars[self.nflx_symbol].Close
# Buying and Selling Logic for amd
if self.nflx_rsi.Current.Value > 70:
self.nflx_periods_above_70 += 1
if self.nflx_periods_above_70 == 2 and nflx_current_price > nflx_sma:
self.LimitOrder("nflx", 10, nflx_current_price)
else:
self.nflx_periods_above_70 = 0
if self.nflx_rsi.Current.Value < 30:
self.nflx_periods_below_30 += 1
if self.nflx_periods_below_30 == 2 and nflx_current_price < nflx_sma:
if self.Portfolio[self.nflx_symbol].Quantity >= 10:
self.LimitOrder("nflx", -10, nflx_current_price)
else:
self.nflx_periods_below_30 = 0
# Calculate SMA for tsla
if not tsla_history.empty:
tsla_sma = tsla_history["close"].mean()
tsla_current_price = slice.Bars[self.tsla_symbol].Close
# Buying and Selling Logic for tsla (similar logic can be applied to amd)
if self.tsla_rsi.Current.Value > 70:
self.tsla_periods_above_70 += 1
if self.tsla_periods_above_70 == 2 and tsla_current_price > tsla_sma:
self.LimitOrder("tsla", 21, tsla_current_price)
else:
self.tsla_periods_above_70 = 0
if self.tsla_rsi.Current.Value < 30:
self.tsla_periods_below_30 += 1
if self.tsla_periods_below_30 == 2 and tsla_current_price < tsla_sma:
if self.Portfolio[self.tsla_symbol].Quantity >= 21:
self.LimitOrder("tsla", -21, tsla_current_price)
else:
self.tsla_periods_below_30 = 0
# Calculate SMA for GOOG
if not goog_history.empty:
goog_sma = goog_history["close"].mean()
goog_current_price = slice.Bars[self.goog_symbol].Close
# Buying and Selling Logic for goog (similar logic can be applied to amd)
if self.goog_rsi.Current.Value > 70:
self.goog_periods_above_70 += 1
if self.goog_periods_above_70 == 2 and goog_current_price > goog_sma:
self.LimitOrder("GOOG", 65, goog_current_price)
else:
self.goog_periods_above_70 = 0
if self.goog_rsi.Current.Value < 30:
self.goog_periods_below_30 += 1
if self.goog_periods_below_30 == 2 and goog_current_price < goog_sma:
if self.Portfolio[self.goog_symbol].Quantity >= 65:
self.LimitOrder("GOOG", -65, goog_current_price)
else:
self.goog_periods_below_30 = 0
def Rebalance(self):
# Rebalance logic goes here
pass
if self.docu_rsi.IsReady:
# The current value of self.rsi is represented by self.rsi.Current.Value
self.Plot("RelativeStrengthIndex", "rsi", self.docu_rsi.Current.Value)
# Plot all attributes of self.rsi
self.Plot("RelativeStrengthIndex", "averagegain", self.docu_rsi.AverageGain.Current.Value)
self.Plot("RelativeStrengthIndex", "averageloss", self.docu_rsi.AverageLoss.Current.Value)
if self.amd_rsi.IsReady:
# The current value of self.rsi is represented by self.rsi.Current.Value
self.Plot("RelativeStrengthIndex", "rsi", self.amd_rsi.Current.Value)
# Plot all attributes of self.rsi
self.Plot("RelativeStrengthIndex", "averagegain", self.amd_rsi.AverageGain.Current.Value)
self.Plot("RelativeStrengthIndex", "averageloss", self.amd_rsi.AverageLoss.Current.Value)
if self.nflx_rsi.IsReady:
# The current value of self.rsi is represented by self.rsi.Current.Value
self.Plot("RelativeStrengthIndex", "rsi", self.nflx_rsi.Current.Value)
# Plot all attributes of self.rsi
self.Plot("RelativeStrengthIndex", "averagegain", self.nflx_rsi.AverageGain.Current.Value)
self.Plot("RelativeStrengthIndex", "averageloss", self.nflx_rsi.AverageLoss.Current.Value)
if self.tsla_rsi.IsReady:
# The current value of self.rsi is represented by self.rsi.Current.Value
self.Plot("RelativeStrengthIndex", "rsi", self.tsla_rsi.Current.Value)
# Plot all attributes of self.rsi
self.Plot("RelativeStrengthIndex", "averagegain", self.tsla_rsi.AverageGain.Current.Value)
self.Plot("RelativeStrengthIndex", "averageloss", self.tsla_rsi.AverageLoss.Current.Value)
if self.goog_rsi.IsReady:
# The current value of self.rsi is represented by self.rsi.Current.Value
self.Plot("RelativeStrengthIndex", "rsi", self.goog_rsi.Current.Value)
# Plot all attributes of self.rsi
self.Plot("RelativeStrengthIndex", "averagegain", self.goog_rsi.AverageGain.Current.Value)
self.Plot("RelativeStrengthIndex", "averageloss", self.goog_rsi.AverageLoss.Current.Value)
Alex mindustry
nice work. lots of code for the repeated logic.
i suggest to use dictionary to store the variables… or maybe a custom class to handle the logic.
this way, u can more easily repeat the analysis for other symbols.
Evan Kirtz
i made my sma-periods, rsi-periods, buying-periods, and selling-periods into parameters stored in config.json. I reran and received the same output for a single backtest which is what i expected. But when i optimized I ran the parameters and none of the output changed for any backtest (after 1000 i quit), which made me suspicious so i am going to try Composer. I am not much of a coder to begin with i like the granularity that Quant Connect offered though.
Many Thanks
Evan
Evan Kirtz
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!