Closed
Description
Expected Behavior
I calculated support & resistance and then created a function ("SIGNAL") that returns a list of signals, when I created the indicator "self.signal" on the "init" function, I passed the "SIGNAL" function. But when I tried to get the stats of the backtest, i get the next exception: "ValueError: attempt to get argmax of an empty sequence".
Actual Behavior
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
/tmp/ipykernel_6680/2172180610.py in <module>
5 # --------------------------------Stats--------------------------------
6 bt = Backtest(df1, S_R_RSI, cash=1000000, margin=1/1, commission = 0.0006, trade_on_close=True, hedging=True)
----> 7 stat = bt.run()
8 print(stat)
9 # print(f'Ret/DD Ratio: {retDD_ratio(stat)}')
~/miniconda3/envs/TRADING/lib/python3.9/site-packages/backtesting/backtesting.py in run(self, **kwargs)
1137 strategy: Strategy = self._strategy(broker, data, kwargs)
1138
-> 1139 strategy.init()
1140 data._update() # Strategy.init might have changed/added to data.df
1141
/tmp/ipykernel_6680/1588484572.py in init(self)
25 close = self.data.Close
26
---> 27 self.signal = self.I(lambda: SIGNAL)
28 self.rsi = self.I(lambda: ta.rsi(self.data.Close.s, length=self.rsi_window))
29
~/miniconda3/envs/TRADING/lib/python3.9/site-packages/backtesting/backtesting.py in I(self, func, name, plot, overlay, color, scatter, *args, **kwargs)
137
138 # Optionally flip the array if the user returned e.g. `df.values`
--> 139 if is_arraylike and np.argmax(value.shape) == 0:
140 value = value.T
141
~/miniconda3/envs/TRADING/lib/python3.9/site-packages/numpy/core/overrides.py in argmax(*args, **kwargs)
~/miniconda3/envs/TRADING/lib/python3.9/site-packages/numpy/core/fromnumeric.py in argmax(a, axis, out, keepdims)
1214 """
1215 kwds = {'keepdims': keepdims} if keepdims is not np._NoValue else {}
-> 1216 return _wrapfunc(a, 'argmax', axis=axis, out=out, **kwds)
1217
1218
~/miniconda3/envs/TRADING/lib/python3.9/site-packages/numpy/core/fromnumeric.py in _wrapfunc(obj, method, *args, **kwds)
52 bound = getattr(obj, method, None)
53 if bound is None:
---> 54 return _wrapit(obj, method, *args, **kwds)
55
56 try:
~/miniconda3/envs/TRADING/lib/python3.9/site-packages/numpy/core/fromnumeric.py in _wrapit(obj, method, *args, **kwds)
41 except AttributeError:
42 wrap = None
---> 43 result = getattr(asarray(obj), method)(*args, **kwds)
44 if wrap:
45 if not isinstance(result, mu.ndarray):
ValueError: attempt to get argmax of an empty sequence
Steps to Reproduce
import warnings
warnings.simplefilter(action='ignore', category=RuntimeWarning)
warnings.simplefilter(action='ignore', category=FutureWarning)
import pandas_ta as ta
import pandas as pd
from binance.client import Client
from dotenv import load_dotenv
import os
from datetime import datetime, date, timedelta
import matplotlib.pyplot as plt
from backtesting import Strategy, Backtest
import numpy as np
from binance.enums import HistoricalKlinesType
import pandas_montecarlo
# Data
load_dotenv()
public = os.getenv('PUBLIC1')
private = os.getenv('SECRET1')
def save_data(symbol,interval,start_date,end_date):
client = Client(public,private)
history = client.get_historical_klines_generator(symbol = symbol, interval = interval, start_str = start_date, end_str=end_date,klines_type=HistoricalKlinesType.FUTURES)
df = pd.DataFrame(data = history)
clean_df = df.drop(columns=[0,7,8,9,10,11])
clean_df.columns = ['Open','High','Low','Close','Volume','time']
clean_df['time'] = pd.to_datetime(clean_df['time'], unit='ms')
dict_cols = {'Open':float, 'High':float, 'Low':float, 'Close':float,'Volume':float}
newdf = clean_df.astype(dict_cols)
newdf.set_index('time', inplace=True)
return newdf
df = save_data(symbol='BTCUSDT',interval='1h',start_date='31 Jan, 2020',end_date='17 Oct, 2022')
df1 = save_data(symbol='BTCUSDT',interval='1h',start_date='31 Jan, 2020',end_date='17 Feb, 2022')
# S&R and signal calculation
def support(data, l, n1, n2): #n1 n2 before and after candle l
for i in range(l - n1 + 1, l + 1):
if(data.Low[i] > data.Low[i - 1]):
return 0
for i in range(l + 1, l + n2 + 1):
if(data.Low[i] < data.Low[i - 1]):
return 0
return 1
def resistance(data, l, n1, n2): #n1 n2 before and after candle l
for i in range(l - n1 + 1, l + 1):
if(data.High[i] < data.High[i - 1]):
return 0
for i in range(l + 1, l + n2 + 1):
if(data.High[i] > data.High[i - 1]):
return 0
return 1
def closeResistance(l,levels,lim):
if len(levels)==0:
return 0
c1 = abs(df.High[l]-min(levels, key=lambda x:abs(x-df.High[l])))<=lim
c2 = abs(max(df.Open[l],df.Close[l])-min(levels, key=lambda x:abs(x-df.High[l])))<=lim
c3 = min(df.Open[l],df.Close[l])<min(levels, key=lambda x:abs(x-df.High[l]))
c4 = df.Low[l]<min(levels, key=lambda x:abs(x-df.High[l]))
if( (c1 or c2) and c3 and c4 ):
return 1
else:
return 0
def closeSupport(l,levels,lim):
if len(levels)==0:
return 0
c1 = abs(df.Low[l]-min(levels, key=lambda x:abs(x-df.Low[l])))<=lim
c2 = abs(min(df.Open[l],df.Close[l])-min(levels, key=lambda x:abs(x-df.Low[l])))<=lim
c3 = max(df.Open[l],df.Close[l])>min(levels, key=lambda x:abs(x-df.Low[l]))
c4 = df.High[l]>min(levels, key=lambda x:abs(x-df.Low[l]))
if( (c1 or c2) and c3 and c4 ):
return 1
else:
return 0
n1 = 2
n2 = 2
backcandles = 30
close = df.Close[-1]
signal = [0] * len(df)
for row in range(backcandles, len(df)-n2):
ss = []
rr = []
for subrow in range(row-backcandles+n1, row+1):
if support(df, subrow, n1, n2):
ss.append(df.Low[subrow])
if resistance(df, subrow, n1, n2):
rr.append(df.High[subrow])
if close <= closeSupport(row,ss,0.1):
signal[row] = 1
elif close >= closeResistance(row,rr,0.1):
signal[row] = 2
else:
signal[row] = 0
df['signal'] = signal
df
def SIGNAL():
return df.signal
# Strategy
class S_R_RSI(Strategy):
riskpct = 0.01
rsi_window = 14
ob = 70
os = 30
sl_buy = 0.97
sl_sell = 1.03
tp_buy = 1.005
tp_sell = 0.995
n1 = 2
n2 = 2
def init(self):
super().init()
high = self.data.High
low = self.data.Low
close = self.data.Close
self.signal = self.I(lambda: SIGNAL)
self.rsi = self.I(lambda: ta.rsi(self.data.Close.s, length=self.rsi_window))
def next(self):
super().next()
price = self.data.Close[-1]
if self.position:
self.position.close()
if self.signal == 1 and self.rsi < self.os:
self.position.close()
self.buy(sl = self.sl_buy * price, tp = self.tp_buy * price, size = self.riskpct)
elif self.signal == 2 and self.rsi > self.ob:
self.position.close()
self.sell(sl = self.sl_sell*price, tp = self.tp_sell * price, size=self.riskpct)
# Results
bt = Backtest(df1, S_R_RSI, cash=1000000, margin=1/1, commission = 0.0006, trade_on_close=True, hedging=True)
stat = bt.run()
print(stat)
Additional info
- Backtesting version: 0.0.3