From 233850d2ef78c59a62aa54e522c867be1864c56f Mon Sep 17 00:00:00 2001 From: yakir Date: Thu, 23 May 2024 09:40:06 +0300 Subject: [PATCH 1/4] fix(order-execution): in case of 2 or more order executed on the same 1m candle. do it in order. --- jesse/modes/backtest_mode.py | 42 ++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/jesse/modes/backtest_mode.py b/jesse/modes/backtest_mode.py index bc83e832a..b9c86d378 100644 --- a/jesse/modes/backtest_mode.py +++ b/jesse/modes/backtest_mode.py @@ -401,12 +401,16 @@ def _simulate_price_change_effect(real_candle: np.ndarray, exchange: str, symbol current_temp_candle = real_candle.copy() executed_order = False + executing_orders = _get_executing_orders(exchange, symbol, real_candle) + if len(executing_orders) > 1: + executing_orders = _sort_execution_orders(executing_orders, current_temp_candle) + while True: - if len(orders) == 0: + if len(executing_orders) == 0: executed_order = False else: - for index, order in enumerate(orders): - if index == len(orders) - 1 and not order.is_active: + for index, order in enumerate(executing_orders): + if index == len(executing_orders) - 1 and not order.is_active: executed_order = False if not order.is_active: @@ -695,6 +699,9 @@ def _simulate_price_change_effect_multiple_candles( ) executing_orders = _get_executing_orders(exchange, symbol, real_candle) if len(executing_orders) > 0: + if len(executing_orders) > 1: + executing_orders = _sort_execution_orders(executing_orders, short_timeframes_candles) + for i in range(len(short_timeframes_candles)): current_temp_candle = short_timeframes_candles[i].copy() is_executed_order = False @@ -768,9 +775,32 @@ def _simulate_price_change_effect_multiple_candles( def _get_executing_orders(exchange, symbol, real_candle): orders = store.orders.get_orders(exchange, symbol) - active_orders = [order for order in orders if order.is_active] return [ order - for order in active_orders - if candle_includes_price(real_candle, order.price) + for order in orders + if order.is_active and candle_includes_price(real_candle, order.price) ] + + +def _sort_execution_orders(orders: list[Order], short_candles: np.ndarray): + sorted_orders = [] + for i in range(len(short_candles)): + included_orders = [ + order + for order in orders + if candle_includes_price(short_candles[i], order.price) + ] + if len(included_orders) == 1: + sorted_orders.append(included_orders[0]) + elif len(included_orders) > 1: + # in case that the orders are above + + # note: check the first is enough because I can assume all the orders in the same direction of the price, + # in case it doesn't than i cant really know how the price react in this 1 minute candle.. + if short_candles[i, 3] > included_orders[0].price > short_candles[i, 1]: + sorted_orders += sorted(included_orders, key=lambda o: o.price) + else: + sorted_orders += sorted(included_orders, key=lambda o: o.price, reverse=True) + if len(sorted_orders) == len(orders): + break + return sorted_orders \ No newline at end of file From 30145ee9b1d2f1f8af0525297518481e8f77e55a Mon Sep 17 00:00:00 2001 From: yakir Date: Thu, 23 May 2024 10:01:09 +0300 Subject: [PATCH 2/4] feat(backtest-mode): fix typing for python 3.8 --- jesse/modes/backtest_mode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jesse/modes/backtest_mode.py b/jesse/modes/backtest_mode.py index b9c86d378..0e5410275 100644 --- a/jesse/modes/backtest_mode.py +++ b/jesse/modes/backtest_mode.py @@ -782,7 +782,7 @@ def _get_executing_orders(exchange, symbol, real_candle): ] -def _sort_execution_orders(orders: list[Order], short_candles: np.ndarray): +def _sort_execution_orders(orders: List[Order], short_candles: np.ndarray): sorted_orders = [] for i in range(len(short_candles)): included_orders = [ From cb281b91251c43f054a1baccdda7da3c0626a840 Mon Sep 17 00:00:00 2001 From: yakir Date: Thu, 23 May 2024 23:49:43 +0300 Subject: [PATCH 3/4] feat(backtest-mode): _simulate_price_change_effect on 1m --- jesse/modes/backtest_mode.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jesse/modes/backtest_mode.py b/jesse/modes/backtest_mode.py index 0e5410275..b55f08d02 100644 --- a/jesse/modes/backtest_mode.py +++ b/jesse/modes/backtest_mode.py @@ -396,14 +396,13 @@ def _get_fixed_jumped_candle(previous_candle: np.ndarray, candle: np.ndarray) -> def _simulate_price_change_effect(real_candle: np.ndarray, exchange: str, symbol: str) -> None: - orders = store.orders.get_orders(exchange, symbol) - current_temp_candle = real_candle.copy() executed_order = False executing_orders = _get_executing_orders(exchange, symbol, real_candle) if len(executing_orders) > 1: - executing_orders = _sort_execution_orders(executing_orders, current_temp_candle) + # extend the candle shape from (6,) to (1,6) + executing_orders = _sort_execution_orders(executing_orders, current_temp_candle[None, :]) while True: if len(executing_orders) == 0: @@ -429,6 +428,7 @@ def _simulate_price_change_effect(real_candle: np.ndarray, exchange: str, symbol executed_order = True order.execute() + executing_orders = _get_executing_orders(exchange, symbol, real_candle) # break from the for loop, we'll try again inside the while # loop with the new current_temp_candle From 5c875989d48e72e8b5cacdda5b57f77e15476e40 Mon Sep 17 00:00:00 2001 From: Saleh Mir Date: Fri, 24 May 2024 13:48:47 +0330 Subject: [PATCH 4/4] feat: add TestOrdersAreSortedBeforeExecution strategy This commit adds the `TestOrdersAreSortedBeforeExecution` strategy to the `jesse/strategies` directory. This strategy is used to test the execution order of multiple orders with different prices. It checks that the order with the lowest price is executed first. --- .../__init__.py | 24 +++++++++++++++++++ tests/test_order.py | 4 ++++ 2 files changed, 28 insertions(+) create mode 100644 jesse/strategies/TestOrdersAreSortedBeforeExecution/__init__.py diff --git a/jesse/strategies/TestOrdersAreSortedBeforeExecution/__init__.py b/jesse/strategies/TestOrdersAreSortedBeforeExecution/__init__.py new file mode 100644 index 000000000..8ba04f4fb --- /dev/null +++ b/jesse/strategies/TestOrdersAreSortedBeforeExecution/__init__.py @@ -0,0 +1,24 @@ +from jesse.strategies import Strategy + + +class TestOrdersAreSortedBeforeExecution(Strategy): + def should_long(self): + return self.price == 10 + + def go_long(self): + self.buy = [ + (1, 10.2), + (1, 10.3), + (1, 10.1) + ] + + def on_open_position(self, order) -> None: + # the order with the lowest price should be executed first + assert order.price == 10.1 + + def update_position(self): + if self.price == 20: + self.liquidate() + + def should_cancel_entry(self): + return False diff --git a/tests/test_order.py b/tests/test_order.py index f5f177f84..68feaab58 100644 --- a/tests/test_order.py +++ b/tests/test_order.py @@ -11,3 +11,7 @@ def test_order_is_take_profit_property(): def test_order_value_property(): single_route_backtest('TestOrderValueProperty') + + +def test_orders_are_sorted(): + single_route_backtest('TestOrdersAreSortedBeforeExecution')