Skip to content

Commit a130ed8

Browse files
committed
[TradingMode] fix convertor with simulated liimit order
1 parent 7ba0998 commit a130ed8

File tree

2 files changed

+45
-6
lines changed

2 files changed

+45
-6
lines changed

octobot_trading/modes/modes_util.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,11 @@ async def convert_asset_to_target_asset(
142142
quantity=order_quantity,
143143
price=order_price
144144
)
145-
created_orders.append(await trading_mode.create_order(order))
145+
initialized_order = await trading_mode.create_order(order)
146+
if isinstance(initialized_order, trading_personal_data.LimitOrder) and initialized_order.simulated:
147+
# on simulator, this order should be instantly filled now as its price is meant to be instantly filled
148+
await initialized_order.on_fill()
149+
created_orders.append(initialized_order)
146150
return created_orders
147151

148152

tests/modes/test_modes_util.py

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
import pytest
1818
import decimal
1919

20+
import octobot_commons.asyncio_tools as asyncio_tools
2021
import octobot_trading.constants as constants
2122
import octobot_trading.enums as trading_enums
2223
import octobot_trading.personal_data as trading_personal_data
2324
import octobot_trading.modes.modes_util as modes_util
25+
import octobot_trading.signals as signals
2426

2527
from tests.exchanges import backtesting_trader, backtesting_config, backtesting_exchange_manager, fake_backtesting
2628
from tests import event_loop
@@ -130,6 +132,10 @@ async def test_convert_asset_to_target_asset(backtesting_trader):
130132
assert order.symbol == "BTC/USDT"
131133
assert order.origin_quantity == decimal.Decimal(10)
132134
assert order.created_last_price == decimal.Decimal(30000)
135+
# market order is instantly filled
136+
assert order.is_filled()
137+
assert order.filled_price == order.origin_price
138+
assert order.filled_quantity > constants.ZERO
133139
trading_mode.create_order.assert_called_once()
134140
trading_mode.create_order.reset_mock()
135141
is_market_open_for_order_type_mock.assert_called_once_with(
@@ -142,8 +148,9 @@ async def test_convert_asset_to_target_asset(backtesting_trader):
142148
order = orders[0]
143149
assert order.order_type == trading_enums.TraderOrderType.BUY_MARKET
144150
assert order.symbol == "BTC/USDT"
145-
assert order.origin_quantity == decimal.Decimal("0.03333333")
151+
assert order.origin_quantity == decimal.Decimal("10.03333333")
146152
assert order.created_last_price == decimal.Decimal(30000)
153+
assert order.is_filled()
147154
trading_mode.create_order.assert_called_once()
148155
trading_mode.create_order.reset_mock()
149156

@@ -153,33 +160,39 @@ async def test_convert_asset_to_target_asset(backtesting_trader):
153160
trading_enums.ExchangeConstantsTickersColumns.CLOSE.value: decimal.Decimal(1500)
154161
}
155162

163+
# reset portfolio
164+
portfolio["USDT"] = trading_personal_data.SpotAsset("USDT", decimal.Decimal(1000), decimal.Decimal(1000))
156165
orders = await modes_util.convert_asset_to_target_asset(trading_mode, "USDT", "ETH", tickers)
157166
assert len(orders) == 1
158167
order = orders[0]
159168
assert order.order_type == trading_enums.TraderOrderType.BUY_MARKET
160169
assert order.symbol == "ETH/USDT"
161170
assert order.origin_quantity == decimal.Decimal("0.66666666")
162171
assert order.created_last_price == decimal.Decimal(1500)
172+
assert order.is_filled()
163173
trading_mode.create_order.assert_called_once()
164174
trading_mode.create_order.reset_mock()
165175

176+
portfolio["ETH"] = trading_personal_data.SpotAsset("ETH", constants.ZERO, constants.ZERO)
166177
orders = await modes_util.convert_asset_to_target_asset(trading_mode, "ETH", "USDT", tickers)
167178
# no ETH in portfolio
168179
assert orders == []
169180
trading_mode.create_order.assert_not_called()
170181

171-
portfolio["ETH"] = trading_personal_data.Asset("ETH", constants.ONE, constants.ONE)
182+
portfolio["ETH"] = trading_personal_data.SpotAsset("ETH", constants.ONE, constants.ONE)
172183
orders = await modes_util.convert_asset_to_target_asset(trading_mode, "ETH", "USDT", tickers)
173184
assert len(orders) == 1
174185
order = orders[0]
175186
assert order.order_type == trading_enums.TraderOrderType.SELL_MARKET
176187
assert order.symbol == "ETH/USDT"
177188
assert order.origin_quantity == decimal.Decimal("1")
178189
assert order.created_last_price == decimal.Decimal(1500)
190+
assert order.is_filled()
179191
trading_mode.create_order.assert_called_once()
180192
trading_mode.create_order.reset_mock()
181193

182194
# with amount param
195+
portfolio["ETH"] = trading_personal_data.SpotAsset("ETH", decimal.Decimal("2"), decimal.Decimal("2"))
183196
orders = await modes_util.convert_asset_to_target_asset(
184197
trading_mode, "ETH", "USDT", tickers, asset_amount=decimal.Decimal(2)
185198
)
@@ -189,6 +202,7 @@ async def test_convert_asset_to_target_asset(backtesting_trader):
189202
assert order.symbol == "ETH/USDT"
190203
assert order.origin_quantity == decimal.Decimal(2)
191204
assert order.created_last_price == decimal.Decimal(1500)
205+
assert order.is_filled()
192206
trading_mode.create_order.assert_called_once()
193207
trading_mode.create_order.reset_mock()
194208

@@ -201,6 +215,7 @@ async def test_convert_asset_to_target_asset(backtesting_trader):
201215
assert order.symbol == "ETH/USDT"
202216
assert order.origin_quantity == decimal.Decimal("0.3")
203217
assert order.created_last_price == decimal.Decimal(1500)
218+
assert order.is_filled()
204219
trading_mode.create_order.assert_called_once()
205220
trading_mode.create_order.reset_mock()
206221

@@ -213,17 +228,20 @@ async def test_convert_asset_to_target_asset(backtesting_trader):
213228
assert order.symbol == "ETH/USDT"
214229
assert order.origin_quantity == decimal.Decimal("0.66666666")
215230
assert order.created_last_price == decimal.Decimal(1500)
231+
assert order.is_filled()
216232
trading_mode.create_order.assert_called_once()
217233
trading_mode.create_order.reset_mock()
218234

219235
# with fees paid in quote
220236
fees = {
221237
trading_enums.FeePropertyColumns.COST.value: "2",
222238
trading_enums.FeePropertyColumns.CURRENCY.value: "USDT",
239+
trading_enums.FeePropertyColumns.IS_FROM_EXCHANGE.value: True,
223240
}
224241
with mock.patch.object(exchange_manager.exchange, "get_trade_fee", mock.Mock(return_value=fees)) \
225242
as get_trade_fee_mock:
226243
# cast 1: enough funds in pf to cover fees
244+
portfolio["USDT"] = trading_personal_data.SpotAsset("USDT", decimal.Decimal(1000), decimal.Decimal(1000))
227245
orders = await modes_util.convert_asset_to_target_asset(
228246
trading_mode, "USDT", "ETH", tickers, asset_amount=decimal.Decimal(450)
229247
)
@@ -233,12 +251,14 @@ async def test_convert_asset_to_target_asset(backtesting_trader):
233251
assert order.symbol == "ETH/USDT"
234252
assert order.origin_quantity == decimal.Decimal("0.3")
235253
assert order.created_last_price == decimal.Decimal(1500)
236-
get_trade_fee_mock.assert_called_once()
254+
assert order.is_filled()
255+
assert get_trade_fee_mock.call_count == 4
237256
get_trade_fee_mock.reset_mock()
238257
trading_mode.create_order.assert_called_once()
239258
trading_mode.create_order.reset_mock()
240259

241260
# cast 2: reduce amount to cover fees
261+
portfolio["USDT"] = trading_personal_data.SpotAsset("USDT", decimal.Decimal(1000), decimal.Decimal(1000))
242262
orders = await modes_util.convert_asset_to_target_asset(
243263
trading_mode, "USDT", "ETH", tickers, asset_amount=decimal.Decimal(1000)
244264
)
@@ -248,7 +268,8 @@ async def test_convert_asset_to_target_asset(backtesting_trader):
248268
assert order.symbol == "ETH/USDT"
249269
assert order.origin_quantity == decimal.Decimal("0.66400000") # lower than 0.66666666 when fees is 0 USDT
250270
assert order.created_last_price == decimal.Decimal(1500)
251-
get_trade_fee_mock.assert_called_once()
271+
assert order.is_filled()
272+
assert get_trade_fee_mock.call_count == 4
252273
trading_mode.create_order.assert_called_once()
253274
trading_mode.create_order.reset_mock()
254275

@@ -264,6 +285,7 @@ def _is_market_open_for_order_type(symbol: str, order_type: trading_enums.Trader
264285
mock.Mock(side_effect=_is_market_open_for_order_type)
265286
) as is_market_open_for_order_type_mock:
266287
# cast 1: buying
288+
portfolio["USDT"] = trading_personal_data.SpotAsset("USDT", decimal.Decimal(1000), decimal.Decimal(1000))
267289
orders = await modes_util.convert_asset_to_target_asset(
268290
trading_mode, "USDT", "ETH", tickers, asset_amount=decimal.Decimal(450)
269291
)
@@ -278,6 +300,10 @@ def _is_market_open_for_order_type(symbol: str, order_type: trading_enums.Trader
278300
)
279301
assert order.origin_price == adapted_price
280302
assert order.created_last_price == adapted_price
303+
# limit order is designed to be instantly filled, on simulator it should be filled
304+
assert order.is_filled()
305+
assert order.filled_price == order.origin_price
306+
assert order.filled_quantity > constants.ZERO
281307
assert is_market_open_for_order_type_mock.call_count == 2
282308
assert is_market_open_for_order_type_mock.mock_calls[0].args == \
283309
("ETH/USDT", trading_enums.TraderOrderType.BUY_MARKET)
@@ -288,6 +314,7 @@ def _is_market_open_for_order_type(symbol: str, order_type: trading_enums.Trader
288314
trading_mode.create_order.reset_mock()
289315

290316
# cast 2: selling
317+
portfolio["USDT"] = trading_personal_data.SpotAsset("USDT", decimal.Decimal(2), decimal.Decimal(2))
291318
orders = await modes_util.convert_asset_to_target_asset(
292319
trading_mode, "ETH", "USDT", tickers, asset_amount=decimal.Decimal(2)
293320
)
@@ -301,6 +328,9 @@ def _is_market_open_for_order_type(symbol: str, order_type: trading_enums.Trader
301328
)
302329
assert order.origin_price == adapted_price
303330
assert order.created_last_price == adapted_price
331+
assert order.is_filled()
332+
assert order.filled_price == order.origin_price
333+
assert order.filled_quantity > constants.ZERO
304334
assert is_market_open_for_order_type_mock.call_count == 2
305335
assert is_market_open_for_order_type_mock.mock_calls[0].args == \
306336
("ETH/USDT", trading_enums.TraderOrderType.SELL_MARKET)
@@ -312,7 +342,12 @@ def _is_market_open_for_order_type(symbol: str, order_type: trading_enums.Trader
312342

313343

314344
def _get_trading_mode(exchange_manager):
345+
async def _create_order(order):
346+
return await signals.create_order(
347+
exchange_manager, False, order
348+
)
349+
315350
return mock.Mock(
316351
exchange_manager=exchange_manager,
317-
create_order=mock.AsyncMock(side_effect=lambda x: x)
352+
create_order=mock.AsyncMock(side_effect=_create_order)
318353
)

0 commit comments

Comments
 (0)