I have a major bug which makes strange orders which logically don’t make sense.
Hoply someone have a idea or solution how to get the logic working.
The 2. problem is: SL of 5% sometime is 6-8% or more. TP level are limit but sometime few % different.
But Let’s focus first on the basic logic.
Note: The problem just show up sometimes coins like AAVE and more (see the results)
I tried different timeframes. Like, 4h, 1D etc. Problem is the same.
Datasets are clean. No duplicated, Null or Nan values.
I use multiple assets!
Strategy Code:
class Strategy(bt.Strategy):
params = (
('rsi_period', 14),
('crsi_period', 44),
("buffer", 0.05),
("threshold", 0.025),
)
def __init__(self):
self.broker.set_coc(False) #Cheat on Close
self.rsi = {}
self.crsi = {}
self.returns = []
self.trades = dict()
for i, d in enumerate(self.datas):
self.rsi[d] = bt.ind.RSI(d.close, period=self.params.rsi_period)
self.crsi[d] = CycledRSI(d.close)
self.trades[d] = dict()
self.trades[d] = {"stage": 0, "units" : 0.0, "pos_price" : 0.0}
def log(self, txt, dt=None):
""" Logging function fot this strategy"""
dt = dt or self.data.datetime[0]
if isinstance(dt, float):
dt = bt.num2date(dt)
print("%s, %s" % (dt.date(), txt))
def notify_order(self, order):
""" Triggered upon changes to orders. """
# Suppress notification if it is just a submitted order.
if order.status == order.Submitted:
return
if order.status == order.Margin:
return
# Check if an order has been completed
if order.status in [order.Completed]:
self.log(
f"{order.data._name:<6} {('BUY' if order.isbuy() else 'SELL'):<5} "
#f"EXECUTED for: {dn} "
f"Price: {order.executed.price:6.4f} "
f"Cost: {order.executed.value:6.2f} "
f"Comm: {order.executed.comm:4.2f} "
f"Size: {order.created.size:9.4f} "
)
def next(self):
for i, d in enumerate(self.datas):
if len(d) > self.params.crsi_period: # Ensure enough data points for RSI calculation
if self.getposition(d).size == 0:
crsi_value = self.crsi[d][0]
upper_band = self.crsi[d].lines.upper_band[0]
crsi_prev_value = self.crsi[d][-1]
upper_band_prev = self.crsi[d].lines.upper_band[-1]
#if crsi_value > upper_band and crsi_prev_value <= upper_band_prev:
if self.crsi[d][0] > self.crsi[d].lines.upper_band[0]:
self.trades[d]["stage"] = 1
self.trades[d]["pos_price"] = d.close[0] # don't needed anymore
self.trades[d]["units"] = d.target / d.close[0] #d.target defined in USD from __name__ == "__main__"
self.buy(data=d, price=d.close[0], size=self.trades[d]["units"], exectype=bt.Order.Limit) #buy position
if self.getposition(d).size > 0:
#Multiple Take Profit levels
pos_price = self.getposition(d).price
pos_units = self.trades[d]["units"]
tp_level4 = pos_price * (1 + 1.00)
tp_level3 = pos_price * (1 + 0.75)
tp_level2 = pos_price * (1 + 0.50)
tp_level1 = pos_price * (1 + 0.25)
sl_level = pos_price * (1 - 0.05)
if self.trades[d]["stage"] >= 2:
sl_level = pos_price * (1 + 0.05)
#Stop Loss
# Idea: Step up with SL on every level
if d.low[0] <= sl_level:
self.sell(data=d, price=sl_level, size=self.getposition(d).size, exectype=bt.Order.Market)
self.trades[d]["stage"] = 0
self.returns.append((self.datas[0].datetime.datetime(0), self.broker.getvalue())) #store timestamp and return for results
#Take Profit
# Idea: TTP
# Note: I tried also d.close[0] >= tp_level
if self.trades[d]["stage"] == 1 and d.high[0] >= tp_level1:
self.sell(data=d, price=tp_level1, size=pos_units*0.25, exectype=bt.Order.Limit)
self.trades[d]["stage"] = 2
if self.trades[d]["stage"] == 2 and d.high[0] >= tp_level2:
self.sell(data=d, price=tp_level2, size=pos_units*0.25, exectype=bt.Order.Limit)
self.trades[d]["stage"] = 3
if self.trades[d]["stage"] == 3 and d.high[0] >= tp_level3:
self.sell(data=d, price=tp_level3, size=pos_units*0.25, exectype=bt.Order.Limit)
self.trades[d]["stage"] = 4
if self.trades[d]["stage"] == 4 and d.high[0] >= tp_level4:
self.sell(data=d, price=tp_level4, size=self.getposition(d).size, exectype=bt.Order.Limit) #use current position size to make sure no micro values are left
#self.returns.append((self.datas[0].datetime.datetime(0), self.broker.getvalue())) #store timestamp and return for results
self.trades[d]["stage"] = 0
if __name__ == "__main__":
files = os.listdir('data/')
cerebro = bt.Cerebro()
#capital is ordersize * nr of assets *10
#captial = 10_000
ordersize = 100
timeframe = '1D'
com_perc = 0.08
starttime = "2023-01-01"
assets = files[1:2] #filtered
for file in assets:
full_path = os.path.join('data/', file)
symbol = file.split('-')[0]
ticker = symbol+'-USDT'
df = pd.read_csv(full_path)
# drop columns
df.drop(columns=['close_time', 'quote_asset_volume', 'number_of_trades', 'taker_buy_base_asset_volume', 'taker_buy_quote_asset_volume', 'ignore'], inplace=True)
# Format timestamp
df['open_time'] = pd.to_datetime(df['open_time'], unit='ms')
df["openinterest"] = 0
# time filter
df = df[df.open_time >= starttime]
# Resample
df.set_index('open_time', inplace=True)
df = df.resample(timeframe).mean().ffill()
data = bt.feeds.PandasData(
dataname=df,
name=ticker,
datetime=None,
open=0,
high=1,
low=2,
close=3,
volume=4,
openinterest=5,
)
data.target = ordersize
cerebro.adddata(data, name=ticker)
cerebro.addstrategy(Strategy)
cerebro.broker.setcash(ordersize*len(assets*10)) # Set initial cash
cerebro.broker.setcommission(commission=(com_perc/100)) # Set commission
# Analyzer
#cerebro.addanalyzer(bt.analyzers.Returns, _name='returns')
# Execute
results = cerebro.run()
# Results
strategy = results[0]
#cerebro.addwriter(bt.WriterFile, csv=True, out='log.csv')
plt.rcParams['figure.figsize'] = [15, 10]
plt.rcParams.update({'font.size': 12})
cerebro.plot(iplot = False, )
Results of Orders:
2023-06-26, AAVE-USDT BUY Price: 65.0436 Cost: 94.06 Comm: 0.08 Size: 1.4461
2023-06-29, AAVE-USDT SELL Price: 61.7486 Cost: 94.06 Comm: 0.07 Size: -1.4461
2023-07-06, AAVE-USDT BUY Price: 73.8691 Cost: 97.24 Comm: 0.08 Size: 1.3163
2023-07-06, AAVE-USDT BUY Price: 73.8691 Cost: 96.81 Comm: 0.08 Size: 1.3105
2023-07-10, AAVE-USDT BUY Price: 70.3935 Cost: 98.71 Comm: 0.08 Size: 1.4023
2023-08-01, AAVE-USDT BUY Price: 63.9554 Cost: 95.79 Comm: 0.08 Size: 1.4978
2023-08-01, AAVE-USDT BUY Price: 63.9554 Cost: 94.38 Comm: 0.08 Size: 1.4757
2023-08-01, AAVE-USDT BUY Price: 63.9554 Cost: 92.25 Comm: 0.07 Size: 1.4425
2023-08-02, AAVE-USDT SELL Price: 64.3892 Cost: 575.18 Comm: 0.44 Size: -8.4451
2023-10-03, AAVE-USDT BUY Price: 67.5518 Cost: 97.80 Comm: 0.08 Size: 1.4477
2023-10-03, AAVE-USDT BUY Price: 67.5518 Cost: 96.28 Comm: 0.08 Size: 1.4253
2023-10-04, AAVE-USDT BUY Price: 64.9367 Cost: 98.43 Comm: 0.08 Size: 1.5157
2023-10-04, AAVE-USDT BUY Price: 64.9367 Cost: 96.50 Comm: 0.08 Size: 1.4860
2023-10-13, AAVE-USDT SELL Price: 63.4378 Cost: 389.00 Comm: 0.30 Size: -5.8748
2023-10-25, AAVE-USDT BUY Price: 83.4728 Cost: 98.56 Comm: 0.08 Size: 1.1808
2023-10-26, AAVE-USDT BUY Price: 82.0772 Cost: 98.98 Comm: 0.08 Size: 1.2060
2023-10-28, AAVE-USDT SELL Price: 80.1142 Cost: 197.54 Comm: 0.15 Size: -2.3867
2023-10-31, AAVE-USDT BUY Price: 81.9492 Cost: 98.84 Comm: 0.08 Size: 1.2061
2023-10-31, AAVE-USDT BUY Price: 81.9492 Cost: 97.20 Comm: 0.08 Size: 1.1861
2023-12-15, AAVE-USDT SELL Price: 110.0129 Cost: 24.30 Comm: 0.03 Size: -0.2965
2024-02-02, AAVE-USDT SELL Price: 84.2158 Cost: 171.74 Comm: 0.14 Size: -2.0957
2024-03-01, AAVE-USDT BUY Price: 108.7384 Cost: 100.00 Comm: 0.08 Size: 0.9196
2024-03-14, AAVE-USDT SELL Price: 139.5675 Cost: 25.00 Comm: 0.03 Size: -0.2299
2024-03-20, AAVE-USDT SELL Price: 113.2177 Cost: 75.00 Comm: 0.06 Size: -0.6897
2024-04-13, AAVE-USDT BUY Price: 93.1812 Cost: 90.24 Comm: 0.07 Size: 0.9684
2024-04-13, AAVE-USDT BUY Price: 93.1812 Cost: 88.69 Comm: 0.07 Size: 0.9518
2024-04-15, AAVE-USDT SELL Price: 86.8857 Cost: 178.92 Comm: 0.13 Size: -1.9202
enter image description here
Look at the multiple buy orders which not make sense because the position is already open. Logically it can’t open a new one.
Thanks to everybody that have a look!
Dev-Joe is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.