Hi, i'm faced with an error: value error
ValueError : min() arg is an empty sequenceat OnDatanextResistanceLevel = greater_than_price[min(range(len(greater_than_price)) in main.py: line 69
every time i try executing this algorithm, yet when i look through the backtest logs, i see there seem to be not an empty sequence as there exist support values as well as resistance values. need some help and advice.
Here's code of the algorithm for further review.
from datetime import datetime,timedelta
import numpy as np
Macdlong = None
AboveSupport = None
BelowResistance = None
class CreativeYellowTapir(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2019, 1, 30) # Set Start Date
self.SetEndDate(2020, 12, 30)
self.SetCash(100000) # Set Strategy Cash
self.ticker = "USDCAD"
# Rolling Windows to hold bar close data keyed by symbol
self.Data = {}
#for ticker in tickers:
symbol = self.AddForex(self.ticker, Resolution.Hour, Market.Oanda).Symbol
self.Data[symbol] = SymbolData(self, symbol)
self.tolerance = 0.0025
self.toleranceR = 0.986761994
self.toleranceS = 1.004000555
self.stopLossLevel = -0.05 # stop loss percentage
self.stopProfitLevel = 0.01# stop profit percentage
self.SetWarmUp(400, Resolution.Hour)
#def MarketClose(self):
#self.SupportResistance.Reset()
def OnData(self, data):
#if self.IsWarmingUp: #Data to warm up the algo is being collected.
# return
for symbol, symbolData in self.Data.items(): #Return the dictionary's key-value pairs:
if not (data.ContainsKey(symbol) and data[symbol] is not None and symbolData.IsReady):
continue
if self.IsWarmingUp or not all([symbolData.IsReady for symbolData in self.Data.values()]):
return
MACD = symbolData.macd.Current.Value
MACDfast = symbolData.macd.Fast.Current.Value
RSI = symbolData.rsi.Current.Value
current_price = data[symbol].Close
signalDeltaPercent = (MACD - MACD)/MACDfast
supports, resistances = self.NextSupportResistance(symbolData.closeWindow)
self.Log(f"Symbol: {symbol.Value} , Supports: {supports} , Resistances: {resistances}")
#Getting the next support level
if not len(supports) > 1 and not len(resistances) > 1:
return
#Getting the next resistance level *****
greater_than_price = [y for y in resistances if y > current_price ]
nextResistanceLevel = greater_than_price[min(range(len(greater_than_price)), key=lambda i: abs(greater_than_price[i] - current_price))]
#Getting the next support level *****
less_than_price = [x for x in supports if x < current_price ]
nextSupportLevel = less_than_price[min(range(len(less_than_price)), key=lambda i: abs(less_than_price[i] - current_price))]
if self.Portfolio[symbol].Invested:
if self.isLong:
condStopProfit = (current_price - self.buyInPrice)/self.buyInPrice > self.stopProfitLevel
condStopLoss = (current_price - self.buyInPrice)/self.buyInPrice < self.stopLossLevel
if condStopProfit:
self.Liquidate(symbol)
self.Log(f"{self.Time} Long Position Stop Profit at {current_price}")
if condStopLoss:
self.Liquidate(symbol)
self.Log(f"{self.Time} Long Position Stop Loss at {current_price}")
else:
condStopProfit = (self.sellInPrice - current_price)/self.sellInPrice > self.stopProfitLevel
condStopLoss = (self.sellInPrice - current_price)/self.sellInPrice < self.stopLossLevel
if condStopProfit:
self.Liquidate(symbol)
self.Log(f"{self.Time} Short Position Stop Profit at {current_price}")
if condStopLoss:
self.Liquidate(symbol)
self.Log(f"{self.Time} Short Position Stop Loss at {current_price}")
if not self.Portfolio[symbol].Invested:
closestResistanceZone = nextResistanceLevel
closestSupportZone = nextSupportLevel
MacdLong = signalDeltaPercent > self.tolerance
AboveSupport = current_price > closestSupportZone * self.toleranceS
BelowResistance = current_price < closestResistanceZone * self.toleranceR
# tolerance = will be dependent on the minimum number of pips before a r/s level
if RSI > 50 and Macdlong and BelowResistance:
self.SetHoldings(symbol, 1)
# get buy-in price for trailing stop loss/profit
self.buyInPrice = current_price
# entered long position
self.isLong = True
self.Log(f"{self.Time} Entered Long Position at {current_price}")
if RSI < 50 and not Macdlong and AboveSupport:
self.SetHoldings(symbol, -1)
# get sell-in price for trailing stop loss/profit
self.sellInPrice = current_price
# entered short position
self.isLong = False
self.Log(f"{self.Time} Entered Short Position at {current_price}")
def NextSupportResistance(self, window, variation = 0.005, h = 3):
#price = self.Securities[self.ticker].Close
series = window
supports = []
resistances = []
maxima = []
minima = []
# finding maxima and minima by looking for hills/troughs locally
for i in range(h, series.Size-h):
if series[i] > series[i-h] and series[i] > series[i+h]:
maxima.append(series[i])
elif series[i] < series[i-h] and series[i] < series[i+h]:
minima.append(series[i])
# identifying maximas which are resistances
for m in maxima:
r = m * variation
# maxima which are near each other
commonLevel = [x for x in maxima if x > m - r and x < m + r]
# if 2 or more maxima are clustered near an area, it is a resistance
if len(commonLevel) > 1:
# we pick the highest maxima if the cluster as our resistance
level = max(commonLevel)
if level not in resistances:
resistances.append(level)
# identify minima which are supports
for l in minima:
r = l * variation
# minima which are near each other
commonLevel = [x for x in minima if x > l - r and x < l + r]
# if 2 or more minima are clustered near an area, it is a support
if len(commonLevel) > 1:
# We pick the lowest minima of the cluster as our support
level = min(commonLevel)
if level not in supports:
supports.append(level)
return supports, resistances #nextSupportLevel, nextResistanceLevel
class SymbolData:
def __init__(self, algorithm, symbol):
self.macd = MovingAverageConvergenceDivergence(12,26,9)
self.rsi = RelativeStrengthIndex(14)
self.macdWindow = RollingWindow[IndicatorDataPoint](2) #setting the Rolling Window for the fast MACD indicator, takes two values
algorithm.RegisterIndicator(symbol, self.macd, timedelta(hours=4))
self.macd.Updated += self.MacdUpdated #Updating those two values
self.rsiWindow = RollingWindow[IndicatorDataPoint](2) #setting the Rolling Window for the slow SMA indicator, takes two values
algorithm.RegisterIndicator(symbol, self.rsi, timedelta(hours=4))
self.rsi.Updated += self.RsiUpdated #Updating those two values
self.closeWindow = RollingWindow[float](200)
# Add consolidator to track rolling close prices
self.consolidator = QuoteBarConsolidator(4)
self.consolidator.DataConsolidated += self.CloseUpdated
algorithm.SubscriptionManager.AddConsolidator(symbol, self.consolidator)
def MacdUpdated(self, sender, updated):
'''Event holder to update the MACD Rolling Window values'''
if self.macd.IsReady:
self.macdWindow.Add(updated)
def RsiUpdated(self, sender, updated):
'''Event holder to update the RSI Rolling Window values'''
if self.rsi.IsReady:
self.rsiWindow.Add(updated)
def CloseUpdated(self, sender, bar):
'''Event holder to update the close Rolling Window values'''
self.closeWindow.Add(bar.Close)
@property
def IsReady(self):
return self.macd.IsReady and self.rsi.IsReady and self.closeWindow.IsReady
and here are the logs of that backtest.
I would appreciate any and all the help that i can get.
Samwel Kibet
Hey, I finally managed to get the algorithm to work, thanks in part to Jared Broad's previous contribution on Support and Resistance (Sunday Afternoon Fun algo) ;
So if any one of you wants to see how to incorporate and track Support and Resistance levels with your strategy trading forex, feel free to use my code below.
One thing i need help with is plotting the lists of supports and resistance on a chart, if you know how to do it, your insight would be greatly appreciated.
Also any input, to further the improvement of this algorithm will be appreciated.
Thank you and have a great weekend.
Varad Kabade
Hi Samwel Kibet,
We recommend creating a chart object in the initialize method and adding 2 series for support and resistances :
Then plotting the support and resistances in the OnData method:
Note that while doing these, we need to keep in mind the charting limitations. Refer to the following doc for more information.
Best,
Varad Kabade
Samwel Kibet
Thank you Varade, i will keep that in mind.
Samwel Kibet
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!