1717import pytest
1818import decimal
1919
20+ import octobot_commons .asyncio_tools as asyncio_tools
2021import octobot_trading .constants as constants
2122import octobot_trading .enums as trading_enums
2223import octobot_trading .personal_data as trading_personal_data
2324import octobot_trading .modes .modes_util as modes_util
25+ import octobot_trading .signals as signals
2426
2527from tests .exchanges import backtesting_trader , backtesting_config , backtesting_exchange_manager , fake_backtesting
2628from 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
314344def _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