Skip to content

Commit 5e19ad7

Browse files
committed
fix(Strategy): range bot has maximum orders to place at one time
only the nearest MAX_ORDERS number of orders will be placed, to comply with exchange limits
1 parent 2cb4394 commit 5e19ad7

File tree

2 files changed

+45
-8
lines changed

2 files changed

+45
-8
lines changed

src/cryptobots/config/range.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ MODULE: 'range'
33
CLASS: 'Range'
44
INTERVAL: '10s'
55
PARAMETERS:
6-
lower_price: 0.34
7-
upper_price: 0.4
8-
no_levels: 20
9-
max_position: 50
10-
WATCHLIST: ['POLYX/USDT:USDT']
6+
lower_price: 50
7+
upper_price: 60
8+
no_levels: 40
9+
max_position: 20
10+
WATCHLIST: ['AVAX/USDT:USDT']
1111
INCLUDE: ["INTERVAL"] # used when displaying params in CLI, exclude others
1212
BACKTEST_READY: False

src/cryptobots/strategies/range.py

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ class Range(Strategy):
3434
used to size the orders per grid level.
3535
"""
3636

37+
MAX_ORDERS = 16
38+
3739
def __init__(
3840
self,
3941
parameters: dict,
@@ -159,7 +161,6 @@ def check_orders(self, new_orders: list[Order]):
159161
else:
160162
# This order is in the target - mark as existing
161163
# by removing from missing_orders dict
162-
# TODO - check size?
163164
grid_no = target_prices.index(Decimal(str(price))) + 1
164165
missing_orders[direction].pop(grid_no)
165166

@@ -169,8 +170,41 @@ def check_orders(self, new_orders: list[Order]):
169170
sell_levels_filled = -min(0, np.ceil(net_position / self.order_size))
170171
grid_levels_filled = {1: buy_levels_filled, -1: sell_levels_filled}
171172

172-
# Create orders for missing levels
173+
# Set limits on orders to be placed - only place up to MAX_ORDERS orders at a time.
174+
# This means if more than MAX_ORDERS levels are specified for the grid, only the
175+
# closest MAX_ORDERS should be placed.
173176
mid_price = Decimal(str(self.exchange.get_orderbook(self.instrument).midprice))
177+
178+
# Calculate distance of midprice from each level of buy and sell prices
179+
distances = {
180+
d: {l: abs(mid_price - p) for l, p in ps.items()}
181+
for d, ps in self.target_order_prices.items()
182+
}
183+
min_d = {d: {min(v, key=v.get): min(v.values())} for d, v in distances.items()}
184+
dir_mins = {d: min(v.values()) for d, v in min_d.items()}
185+
186+
# Determine order direction mid price is closest too
187+
direction = min(dir_mins, key=dir_mins.get)
188+
189+
# Determine nearest level matching the direction
190+
nearest_level = min_d[direction].popitem()[0]
191+
192+
# Calculate allowable grid ranges for each direction
193+
levels_per_side = int(np.ceil(self.MAX_ORDERS / 2))
194+
allowed_levels = {
195+
direction: [
196+
i
197+
for i in range(
198+
nearest_level - levels_per_side, nearest_level + levels_per_side
199+
)
200+
if i > 0
201+
]
202+
}
203+
allowed_levels[-direction] = [
204+
i for i in range(1, self.MAX_ORDERS - len(allowed_levels[direction]) + 1)
205+
]
206+
207+
# Create orders for missing levels
174208
for direction, orders in missing_orders.items():
175209
for grid_no, order_price in orders.items():
176210
# Check order price against current mid price
@@ -179,8 +213,11 @@ def check_orders(self, new_orders: list[Order]):
179213
# Check current position to prevent replacing an order already filled
180214
level_not_filled = grid_no > grid_levels_filled[direction]
181215

216+
# Check grid_no is in the allowed levels
217+
place_level = grid_no in allowed_levels[direction]
218+
182219
# Check both conditions
183-
if price_valid and level_not_filled:
220+
if price_valid and level_not_filled and place_level:
184221
# Proceed with order
185222
o = Order(
186223
instrument=self.instrument,

0 commit comments

Comments
 (0)