Skip to content

Commit 3df16bf

Browse files
committed
[Staggered] fix order sizing when no recent trades are available
1 parent 71af244 commit 3df16bf

File tree

2 files changed

+174
-7
lines changed

2 files changed

+174
-7
lines changed

Trading/Mode/grid_trading_mode/tests/test_grid_trading_mode.py

Lines changed: 169 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -911,7 +911,7 @@ async def test_start_after_offline_x_filled_and_price_back_should_buy_to_recreat
911911
_check_created_orders(producer, trading_api.get_open_orders(exchange_manager), 200)
912912

913913

914-
async def test_start_after_offline_x_filled_and_missing_should_recreate_sell():
914+
async def test_start_after_offline_x_filled_and_missing_should_recreate_1_sell():
915915
symbol = "BTC/USDT"
916916
async with _get_tools(symbol) as (producer, _, exchange_manager):
917917
# forced config
@@ -981,6 +981,174 @@ async def test_start_after_offline_x_filled_and_missing_should_recreate_sell():
981981
assert len([order for order in open_orders if order.side is trading_enums.TradeOrderSide.SELL]) == 6
982982
# there is now 4 buy orders
983983
assert len([order for order in open_orders if order.side is trading_enums.TradeOrderSide.BUY]) == 4
984+
# quantity is preserved
985+
assert all(
986+
decimal.Decimal("0.00028") < order.origin_quantity < decimal.Decimal("0.00029")
987+
for order in open_orders
988+
)
989+
_check_created_orders(producer, trading_api.get_open_orders(exchange_manager), initial_price)
990+
991+
992+
async def test_start_after_offline_x_filled_and_missing_should_recreate_5_sell_orders_no_recent_trade():
993+
symbol = "BTC/USDT"
994+
async with _get_tools(symbol) as (producer, _, exchange_manager):
995+
# forced config
996+
producer.buy_funds = producer.sell_funds = 0
997+
producer.allow_order_funds_redispatch = True
998+
producer.buy_orders_count = producer.sell_orders_count = 5
999+
producer.compensate_for_missed_mirror_order = True
1000+
producer.enable_trailing_down = False
1001+
producer.enable_trailing_up = True
1002+
producer.flat_increment = decimal.Decimal(100)
1003+
producer.flat_spread = decimal.Decimal(300)
1004+
producer.reinvest_profits = False
1005+
producer.sell_volume_per_order = producer.buy_volume_per_order = False
1006+
producer.starting_price = 0
1007+
producer.use_existing_orders_only = False
1008+
producer.use_fixed_volume_for_mirror_orders = False
1009+
1010+
orders_count = producer.buy_orders_count + producer.sell_orders_count
1011+
1012+
initial_price = decimal.Decimal("105278.1")
1013+
trading_api.force_set_mark_price(exchange_manager, producer.symbol, initial_price)
1014+
btc_pf = trading_api.get_portfolio_currency(exchange_manager, "BTC")
1015+
usdt_pf = trading_api.get_portfolio_currency(exchange_manager, "USDT")
1016+
btc_pf.available = decimal.Decimal("0.00141858")
1017+
btc_pf.total = decimal.Decimal("0.00141858")
1018+
usdt_pf.available = decimal.Decimal("150.505098")
1019+
usdt_pf.total = decimal.Decimal("150.505098")
1020+
1021+
await producer._ensure_staggered_orders()
1022+
await asyncio.create_task(_check_open_orders_count(exchange_manager, orders_count))
1023+
original_orders = copy.copy(trading_api.get_open_orders(exchange_manager))
1024+
assert len(original_orders) == orders_count
1025+
assert sorted([
1026+
order.origin_price for order in original_orders
1027+
]) == [
1028+
# buy orders
1029+
decimal.Decimal('104728.1'), decimal.Decimal('104828.1'), decimal.Decimal('104928.1'),
1030+
decimal.Decimal('105028.1'), decimal.Decimal('105128.1'),
1031+
# sell orders
1032+
decimal.Decimal('105428.1'), decimal.Decimal('105528.1'), decimal.Decimal('105628.1'),
1033+
decimal.Decimal('105728.1'), decimal.Decimal('105828.1')
1034+
]
1035+
1036+
# price goes down to 104720, all buy order get filled
1037+
price = decimal.Decimal("104720")
1038+
offline_filled = [order for order in original_orders if order.origin_price <= decimal.Decimal('105128.1')]
1039+
assert len(offline_filled) == 5
1040+
assert all(o.side == trading_enums.TradeOrderSide.BUY for o in offline_filled)
1041+
for order in offline_filled:
1042+
await _fill_order(order, exchange_manager, trigger_update_callback=False, producer=producer)
1043+
assert len(trading_api.get_open_orders(exchange_manager)) == orders_count - len(offline_filled)
1044+
assert btc_pf.available == decimal.Decimal("0.00143356799")
1045+
assert btc_pf.total == decimal.Decimal("0.00284915799")
1046+
assert usdt_pf.available == decimal.Decimal("0.247225519")
1047+
assert usdt_pf.total == decimal.Decimal("0.247225519")
1048+
1049+
# clear trades
1050+
exchange_manager.exchange_personal_data.trades_manager.trades.clear()
1051+
1052+
# back online: restore orders according to current price
1053+
trading_api.force_set_mark_price(exchange_manager, producer.symbol, price)
1054+
with _assert_missing_orders_count(producer, len(offline_filled)):
1055+
await producer._ensure_staggered_orders()
1056+
# create buy orders equivalent sell orders
1057+
await asyncio.create_task(_check_open_orders_count(exchange_manager, orders_count))
1058+
open_orders = trading_api.get_open_orders(exchange_manager)
1059+
# there is now 10 sell orders
1060+
assert len([order for order in open_orders if order.side is trading_enums.TradeOrderSide.SELL]) == 10
1061+
# quantity is preserved
1062+
assert all(
1063+
decimal.Decimal("0.00028") < order.origin_quantity < decimal.Decimal("0.00029")
1064+
for order in open_orders
1065+
)
1066+
# there is now 0 buy order
1067+
assert len([order for order in open_orders if order.side is trading_enums.TradeOrderSide.BUY]) == 0
1068+
_check_created_orders(producer, trading_api.get_open_orders(exchange_manager), initial_price)
1069+
1070+
assert btc_pf.available == decimal.Decimal("0.00001571799")
1071+
assert btc_pf.total == decimal.Decimal("0.00284915799")
1072+
assert usdt_pf.available == decimal.Decimal("0.247225519")
1073+
assert usdt_pf.total == decimal.Decimal("0.247225519")
1074+
1075+
1076+
async def test_start_after_offline_x_filled_and_missing_should_recreate_5_buy_orders_no_recent_trade():
1077+
symbol = "BTC/USDT"
1078+
async with _get_tools(symbol) as (producer, _, exchange_manager):
1079+
# forced config
1080+
producer.buy_funds = producer.sell_funds = 0
1081+
producer.allow_order_funds_redispatch = True
1082+
producer.buy_orders_count = producer.sell_orders_count = 5
1083+
producer.compensate_for_missed_mirror_order = True
1084+
producer.enable_trailing_down = False
1085+
producer.enable_trailing_up = True
1086+
producer.flat_increment = decimal.Decimal(100)
1087+
producer.flat_spread = decimal.Decimal(300)
1088+
producer.reinvest_profits = False
1089+
producer.sell_volume_per_order = producer.buy_volume_per_order = False
1090+
producer.starting_price = 0
1091+
producer.use_existing_orders_only = False
1092+
producer.use_fixed_volume_for_mirror_orders = False
1093+
1094+
orders_count = producer.buy_orders_count + producer.sell_orders_count
1095+
1096+
initial_price = decimal.Decimal("105278.1")
1097+
trading_api.force_set_mark_price(exchange_manager, producer.symbol, initial_price)
1098+
btc_pf = trading_api.get_portfolio_currency(exchange_manager, "BTC")
1099+
usdt_pf = trading_api.get_portfolio_currency(exchange_manager, "USDT")
1100+
btc_pf.available = decimal.Decimal("0.00141858")
1101+
btc_pf.total = decimal.Decimal("0.00141858")
1102+
usdt_pf.available = decimal.Decimal("150.505098")
1103+
usdt_pf.total = decimal.Decimal("150.505098")
1104+
1105+
await producer._ensure_staggered_orders()
1106+
await asyncio.create_task(_check_open_orders_count(exchange_manager, orders_count))
1107+
original_orders = copy.copy(trading_api.get_open_orders(exchange_manager))
1108+
assert len(original_orders) == orders_count
1109+
assert sorted([
1110+
order.origin_price for order in original_orders
1111+
]) == [
1112+
# buy orders
1113+
decimal.Decimal('104728.1'), decimal.Decimal('104828.1'), decimal.Decimal('104928.1'),
1114+
decimal.Decimal('105028.1'), decimal.Decimal('105128.1'),
1115+
# sell orders
1116+
decimal.Decimal('105428.1'), decimal.Decimal('105528.1'), decimal.Decimal('105628.1'),
1117+
decimal.Decimal('105728.1'), decimal.Decimal('105828.1')
1118+
]
1119+
1120+
# price goes up to 105838, all sell order get filled
1121+
price = decimal.Decimal("105838")
1122+
offline_filled = [order for order in original_orders if order.origin_price > decimal.Decimal('105128.1')]
1123+
assert len(offline_filled) == 5
1124+
assert all(o.side == trading_enums.TradeOrderSide.SELL for o in offline_filled)
1125+
for order in offline_filled:
1126+
await _fill_order(order, exchange_manager, trigger_update_callback=False, producer=producer)
1127+
assert len(trading_api.get_open_orders(exchange_manager)) == orders_count - len(offline_filled)
1128+
assert btc_pf.available == decimal.Decimal("0.00000299")
1129+
assert btc_pf.total == decimal.Decimal("0.00000299")
1130+
assert usdt_pf.available == decimal.Decimal("149.623458838921")
1131+
assert usdt_pf.total == decimal.Decimal("299.881331319921")
1132+
1133+
# clear trades
1134+
exchange_manager.exchange_personal_data.trades_manager.trades.clear()
1135+
1136+
# back online: restore orders according to current price
1137+
trading_api.force_set_mark_price(exchange_manager, producer.symbol, price)
1138+
with _assert_missing_orders_count(producer, len(offline_filled)):
1139+
await producer._ensure_staggered_orders()
1140+
# create buy orders equivalent sell orders
1141+
await asyncio.create_task(_check_open_orders_count(exchange_manager, orders_count))
1142+
open_orders = trading_api.get_open_orders(exchange_manager)
1143+
# there is now 0 sell order
1144+
assert len([order for order in open_orders if order.side is trading_enums.TradeOrderSide.SELL]) == 0
1145+
# there is now 10 buy orders
1146+
assert len([order for order in open_orders if order.side is trading_enums.TradeOrderSide.BUY]) == 10
1147+
# quantity is preserved
1148+
assert all(
1149+
decimal.Decimal("0.00028") < order.origin_quantity < decimal.Decimal("0.00029")
1150+
for order in open_orders
1151+
)
9841152
_check_created_orders(producer, trading_api.get_open_orders(exchange_manager), initial_price)
9851153

9861154

Trading/Mode/staggered_orders_trading_mode/staggered_orders_trading.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1502,7 +1502,7 @@ def _fill_missing_orders(
15021502
# missing order between similar orders
15031503
quantity = self._get_surrounded_missing_order_quantity(
15041504
previous_o, following_o, max_quant_per_order, decimal_missing_order_price, recent_trades,
1505-
current_price, sorted_orders
1505+
current_price, sorted_orders, side
15061506
)
15071507
orders.append(OrderData(missing_order_side, quantity,
15081508
decimal_missing_order_price, self.symbol, False))
@@ -1578,9 +1578,9 @@ def _fill_missing_orders(
15781578

15791579
def _get_surrounded_missing_order_quantity(
15801580
self, previous_order, following_order, max_quant_per_order, order_price, recent_trades,
1581-
current_price, sorted_orders
1581+
current_price, sorted_orders, side
15821582
):
1583-
selling = previous_order.side == trading_enums.TradeOrderSide.SELL
1583+
selling = side == trading_enums.TradeOrderSide.SELL
15841584
if sorted_orders:
15851585
if quantity := self._get_quantity_from_existing_orders(
15861586
order_price, sorted_orders, selling
@@ -1594,10 +1594,9 @@ def _get_surrounded_missing_order_quantity(
15941594
min(
15951595
data_util.mean([previous_order.origin_quantity, following_order.origin_quantity])
15961596
if following_order else previous_order.origin_quantity,
1597-
max_quant_per_order / order_price
1597+
(max_quant_per_order if selling else max_quant_per_order / order_price)
15981598
)
1599-
)
1600-
)
1599+
))
16011600

16021601
def _get_spread_missing_order_quantity(
16031602
self, average_order_quantity, side, i, orders_count, price, selling, limiting_amount_from_this_order,

0 commit comments

Comments
 (0)