|
35 | 35 | import octobot_trading.api as trading_api |
36 | 36 | import octobot_trading.exchange_channel as exchanges_channel |
37 | 37 | import octobot_trading.exchanges as exchanges |
| 38 | +import octobot_trading.exchange_data as trading_exchange_data |
38 | 39 | import octobot_trading.personal_data as trading_personal_data |
39 | 40 | import octobot_trading.enums as trading_enums |
40 | 41 | import octobot_trading.constants as trading_constants |
@@ -66,6 +67,18 @@ async def tools(): |
66 | 67 | await _stop(trader.exchange_manager) |
67 | 68 |
|
68 | 69 |
|
| 70 | +@pytest_asyncio.fixture |
| 71 | +async def futures_tools(): |
| 72 | + trader = None |
| 73 | + try: |
| 74 | + tentacles_manager_api.reload_tentacle_info() |
| 75 | + mode, trader = await _get_futures_tools() |
| 76 | + yield mode, trader |
| 77 | + finally: |
| 78 | + if trader: |
| 79 | + await _stop(trader.exchange_manager) |
| 80 | + |
| 81 | + |
69 | 82 | async def test_run_independent_backtestings_with_memory_check(): |
70 | 83 | """ |
71 | 84 | Should always be called first here to avoid other tests' related memory check issues |
@@ -1002,6 +1015,60 @@ async def _create_order(order, **kwargs): |
1002 | 1015 | assert total_cost <= decimal.Decimal("225.98463886") # took fees into account |
1003 | 1016 |
|
1004 | 1017 |
|
| 1018 | +async def test_create_new_buy_orders_futures_trading(futures_tools): |
| 1019 | + update = {} |
| 1020 | + mode, producer, consumer, trader = await _init_mode(futures_tools, _get_config(futures_tools, update)) |
| 1021 | + mode.use_secondary_entry_orders = True |
| 1022 | + mode.secondary_entry_orders_count = 3 |
| 1023 | + mode.secondary_entry_orders_amount = "8%t" |
| 1024 | + mode.use_market_entry_orders = False |
| 1025 | + mode.cancel_open_orders_at_each_entry = False |
| 1026 | + mode.trading_config[trading_constants.CONFIG_BUY_ORDER_AMOUNT] = "8%t" |
| 1027 | + |
| 1028 | + mode.exchange_manager.exchange_config.traded_symbols = [ |
| 1029 | + commons_symbols.parse_symbol("BTC/USDT:USDT") |
| 1030 | + ] |
| 1031 | + portfolio = trader.exchange_manager.exchange_personal_data.portfolio_manager.portfolio.portfolio |
| 1032 | + portfolio["USDT"].available = decimal.Decimal("200") |
| 1033 | + portfolio["USDT"].total = decimal.Decimal("200") |
| 1034 | + portfolio.pop("USD", None) |
| 1035 | + portfolio.pop("BTC", None) |
| 1036 | + |
| 1037 | + trading_api.force_set_mark_price(trader.exchange_manager, "BTC/USDT:USDT", 11.0096) |
| 1038 | + converter = trader.exchange_manager.exchange_personal_data.portfolio_manager.portfolio_value_holder.value_converter |
| 1039 | + converter.update_last_price("BTC/USDT:USDT", decimal.Decimal("11.0096")) |
| 1040 | + |
| 1041 | + |
| 1042 | + def _get_fees_currency(base, quote, order_type): |
| 1043 | + # force quote fees |
| 1044 | + return quote |
| 1045 | + |
| 1046 | + def _read_fees_from_config(fees): |
| 1047 | + # use 0.2% fees |
| 1048 | + fees[trading_enums.ExchangeConstantsMarketPropertyColumns.MAKER.value] = 0.002 |
| 1049 | + fees[trading_enums.ExchangeConstantsMarketPropertyColumns.TAKER.value] = 0.002 |
| 1050 | + fees[trading_enums.ExchangeConstantsMarketPropertyColumns.FEE.value] = 0.002 |
| 1051 | + |
| 1052 | + async def _create_order(order, **kwargs): |
| 1053 | + await order.initialize(is_from_exchange_data=True, enable_associated_orders_creation=False) |
| 1054 | + return order |
| 1055 | + |
| 1056 | + with mock.patch.object( |
| 1057 | + mode, "create_order", mock.AsyncMock(side_effect=_create_order) |
| 1058 | + ) as create_order_mock, mock.patch.object( |
| 1059 | + trader.exchange_manager.exchange.connector, "_get_fees_currency", |
| 1060 | + mock.Mock(side_effect=_get_fees_currency) |
| 1061 | + ) as _get_fees_currency_mock, mock.patch.object( |
| 1062 | + trader.exchange_manager.exchange.connector, "_read_fees_from_config", |
| 1063 | + mock.Mock(side_effect=_read_fees_from_config) |
| 1064 | + ) as _get_fees_currency_mock: |
| 1065 | + orders = await consumer.create_new_orders("BTC/USDT:USDT", None, trading_enums.EvaluatorStates.LONG.value) |
| 1066 | + assert orders |
| 1067 | + assert len(orders) == 4 |
| 1068 | + total_cost = sum(order.total_cost for order in orders) |
| 1069 | + assert round(total_cost) == decimal.Decimal("56") |
| 1070 | + |
| 1071 | + |
1005 | 1072 | async def test_single_exchange_process_optimize_initial_portfolio(tools): |
1006 | 1073 | update = {} |
1007 | 1074 | mode, producer, consumer, trader = await _init_mode(tools, _get_config(tools, update)) |
@@ -1203,6 +1270,55 @@ async def _get_tools(symbol="BTC/USDT"): |
1203 | 1270 | return mode, trader |
1204 | 1271 |
|
1205 | 1272 |
|
| 1273 | +async def _get_futures_tools(symbol="BTC/USDT:USDT"): |
| 1274 | + config = test_config.load_test_config() |
| 1275 | + config[commons_constants.CONFIG_SIMULATOR][commons_constants.CONFIG_STARTING_PORTFOLIO]["USDT"] = 2000 |
| 1276 | + exchange_manager = test_exchanges.get_test_exchange_manager(config, "binance") |
| 1277 | + exchange_manager.tentacles_setup_config = test_utils_config.get_tentacles_setup_config() |
| 1278 | + |
| 1279 | + # use backtesting not to spam exchanges apis |
| 1280 | + exchange_manager.is_spot_only = False |
| 1281 | + exchange_manager.is_future = True |
| 1282 | + exchange_manager.is_simulated = True |
| 1283 | + exchange_manager.is_backtesting = True |
| 1284 | + exchange_manager.use_cached_markets = False |
| 1285 | + backtesting = await backtesting_api.initialize_backtesting( |
| 1286 | + config, |
| 1287 | + exchange_ids=[exchange_manager.id], |
| 1288 | + matrix_id=None, |
| 1289 | + data_files=[os.path.join(test_config.TEST_CONFIG_FOLDER, |
| 1290 | + "AbstractExchangeHistoryCollector_1586017993.616272.data")]) |
| 1291 | + exchange_manager.exchange = exchanges.ExchangeSimulator( |
| 1292 | + exchange_manager.config, exchange_manager, backtesting |
| 1293 | + ) |
| 1294 | + await exchange_manager.exchange.initialize() |
| 1295 | + for exchange_channel_class_type in [exchanges_channel.ExchangeChannel, exchanges_channel.TimeFrameExchangeChannel]: |
| 1296 | + await channel_util.create_all_subclasses_channel(exchange_channel_class_type, exchanges_channel.set_chan, |
| 1297 | + exchange_manager=exchange_manager) |
| 1298 | + contract = trading_exchange_data.FutureContract( |
| 1299 | + pair=symbol, |
| 1300 | + margin_type=trading_enums.MarginType.ISOLATED, |
| 1301 | + contract_type=trading_enums.FutureContractType.LINEAR_PERPETUAL, |
| 1302 | + current_leverage=trading_constants.ONE, |
| 1303 | + maximum_leverage=trading_constants.ONE_HUNDRED |
| 1304 | + ) |
| 1305 | + exchange_manager.exchange.set_pair_future_contract(symbol, contract) |
| 1306 | + trader = exchanges.TraderSimulator(config, exchange_manager) |
| 1307 | + await trader.initialize() |
| 1308 | + |
| 1309 | + mode = Mode.DCATradingMode(config, exchange_manager) |
| 1310 | + mode.symbol = None if mode.get_is_symbol_wildcard() else symbol |
| 1311 | + # trading mode is not initialized: to be initialized with the required config in tests |
| 1312 | + |
| 1313 | + # add mode to exchange manager so that it can be stopped and freed from memory |
| 1314 | + exchange_manager.trading_modes.append(mode) |
| 1315 | + |
| 1316 | + # set BTC/USDT price at 1000 USDT |
| 1317 | + trading_api.force_set_mark_price(exchange_manager, symbol, 1000) |
| 1318 | + |
| 1319 | + return mode, trader |
| 1320 | + |
| 1321 | + |
1206 | 1322 | async def _init_mode(tools, config): |
1207 | 1323 | mode, trader = tools |
1208 | 1324 | await mode.initialize(trading_config=config) |
|
0 commit comments