diff --git a/Meta/Keywords/scripting_library/orders/position_size/amount.py b/Meta/Keywords/scripting_library/orders/position_size/amount.py index 7e6046113..6ed78d3f6 100644 --- a/Meta/Keywords/scripting_library/orders/position_size/amount.py +++ b/Meta/Keywords/scripting_library/orders/position_size/amount.py @@ -31,28 +31,15 @@ async def get_amount( unknown_portfolio_on_creation=False, target_price=None ): - try: - amount_value = await script_keywords.get_amount_from_input_amount( - context=context, - input_amount=input_amount, - side=side, - reduce_only=reduce_only, - is_stop_order=is_stop_order, - use_total_holding=use_total_holding, - target_price=target_price - ) - except NotImplementedError: - amount_type, amount_value = script_keywords.parse_quantity(input_amount) - if amount_type is script_keywords.QuantityType.POSITION_PERCENT: # todo handle existing open short position - amount_value = \ - exchange_private_data.open_position_size(context, side, - amount_type=commons_constants.PORTFOLIO_AVAILABLE) \ - * amount_value / 100 - else: - raise trading_errors.InvalidArgumentError("make sure to use a supported syntax for amount") - return await script_keywords.adapt_amount_to_holdings(context, amount_value, side, - use_total_holding, reduce_only, is_stop_order, - target_price=target_price) + amount_value = await script_keywords.get_amount_from_input_amount( + context=context, + input_amount=input_amount, + side=side, + reduce_only=reduce_only, + is_stop_order=is_stop_order, + use_total_holding=use_total_holding, + target_price=target_price + ) if unknown_portfolio_on_creation: # no way to check if the amount is valid when creating order _, amount_value = script_keywords.parse_quantity(input_amount) diff --git a/Meta/Keywords/scripting_library/tests/orders/order_types/test_multiple_orders_creation.py b/Meta/Keywords/scripting_library/tests/orders/order_types/test_multiple_orders_creation.py index 33f7199d2..e26422622 100644 --- a/Meta/Keywords/scripting_library/tests/orders/order_types/test_multiple_orders_creation.py +++ b/Meta/Keywords/scripting_library/tests/orders/order_types/test_multiple_orders_creation.py @@ -24,6 +24,7 @@ import octobot_trading.personal_data.orders.order_util as order_util import octobot_trading.api as api import octobot_trading.errors as errors +import octobot_trading.enums as trading_enums import octobot_trading.constants as trading_constants import tentacles.Meta.Keywords.scripting_library as scripting_library @@ -102,6 +103,13 @@ async def test_orders_with_invalid_values(mock_context, skip_if_octobot_trading_ @pytest.mark.parametrize("backtesting_config", ["USDT"], indirect=["backtesting_config"]) async def test_orders_amount_then_position_sequence(mock_context): initial_usdt_holdings, btc_price = await _usdt_trading_context(mock_context) + mock_context.exchange_manager.is_future = True + api.load_pair_contract( + mock_context.exchange_manager, + api.create_default_future_contract( + mock_context.symbol, decimal.Decimal(1), trading_enums.FutureContractType.LINEAR_PERPETUAL + ).to_dict() + ) if os.getenv('CYTHON_IGNORE'): return @@ -193,7 +201,7 @@ async def test_concurrent_orders(mock_context): # create 3 sell orders (at price = 500 + 10 = 510) # that would end up selling more than what we have if not executed sequentially - # 1st order is 80% of position, second is 80% of the remaining 20% and so on + # 1st order is 80% of available btc, second is 80% of the remaining 20% and so on orders = [] async def create_order(amount): @@ -207,13 +215,13 @@ async def create_order(amount): ) await asyncio.gather( *( - create_order("80%p") + create_order("80%a") for _ in range(3) ) ) initial_btc_holdings = btc_val - btc_val = initial_btc_holdings * decimal.Decimal("0.2") ** 3 # 0.16 + btc_val = initial_btc_holdings * (decimal.Decimal("0.2") ** 3) usdt_val = usdt_val + (initial_btc_holdings - btc_val) * (btc_price + 10) # 50118.40 await _fill_and_check(mock_context, btc_val, usdt_val, orders, orders_count=3) diff --git a/Meta/Keywords/scripting_library/tests/orders/position_size/test_amount.py b/Meta/Keywords/scripting_library/tests/orders/position_size/test_amount.py deleted file mode 100644 index f79a4fc4e..000000000 --- a/Meta/Keywords/scripting_library/tests/orders/position_size/test_amount.py +++ /dev/null @@ -1,77 +0,0 @@ -# Drakkar-Software OctoBot-Trading -# Copyright (c) Drakkar-Software, All rights reserved. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 3.0 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library. -import pytest -import mock -import decimal - -import octobot_trading.constants as constants -import octobot_trading.errors as errors -import octobot_trading.modes.script_keywords as script_keywords -import octobot_trading.modes.script_keywords.dsl as dsl -import octobot_trading.modes.script_keywords.basic_keywords.account_balance as account_balance -import tentacles.Meta.Keywords.scripting_library.orders.position_size.amount as amount -import tentacles.Meta.Keywords.scripting_library.data.reading.exchange_private_data as exchange_private_data -import octobot_commons.constants as commons_constants - -from tentacles.Meta.Keywords.scripting_library.tests import event_loop, null_context - -# All test coroutines will be treated as marked. -pytestmark = pytest.mark.asyncio - - -async def test_get_amount(null_context): - with pytest.raises(errors.InvalidArgumentError): - await amount.get_amount(null_context, "-1") - - with pytest.raises(errors.InvalidArgumentError): - await amount.get_amount(null_context, "1sdsqdq") - - with mock.patch.object(account_balance, "adapt_amount_to_holdings", - mock.AsyncMock(return_value=decimal.Decimal(1))) as adapt_amount_to_holdings_mock, \ - mock.patch.object(script_keywords, "adapt_amount_to_holdings", - mock.AsyncMock(return_value=decimal.Decimal(1))) \ - as script_keywords_adapt_amount_to_holdings_mock: - with mock.patch.object(dsl, "parse_quantity", - mock.Mock(return_value=(script_keywords.QuantityType.DELTA_BASE, decimal.Decimal(2)))) \ - as parse_quantity_mock: - assert await amount.get_amount(null_context, "1", "buy", target_price=constants.ONE) == decimal.Decimal(1) - adapt_amount_to_holdings_mock.assert_called_once_with(null_context, decimal.Decimal(2), "buy", - False, True, False, target_price=constants.ONE, - orders_to_be_ignored=None) - parse_quantity_mock.assert_called_once_with("1") - adapt_amount_to_holdings_mock.reset_mock() - - with mock.patch.object(dsl, "parse_quantity", - mock.Mock( - return_value=(script_keywords.QuantityType.POSITION_PERCENT, decimal.Decimal(75)))) \ - as dsl_parse_quantity_mock, \ - mock.patch.object(script_keywords, "parse_quantity", - mock.Mock( - return_value=( - script_keywords.QuantityType.POSITION_PERCENT, decimal.Decimal(75)))) \ - as parse_quantity_mock, \ - mock.patch.object(exchange_private_data, "open_position_size", - mock.Mock(return_value=decimal.Decimal(2))) \ - as open_position_size_mock: - assert await amount.get_amount(null_context, "50", "buy") == decimal.Decimal(1) - script_keywords_adapt_amount_to_holdings_mock.assert_called_once_with( - null_context, decimal.Decimal("1.5"), "buy", False, True, False, target_price=None - ) - dsl_parse_quantity_mock.assert_called_once_with("50") - parse_quantity_mock.assert_called_once_with("50") - open_position_size_mock.assert_called_once_with(null_context, "buy", - amount_type=commons_constants.PORTFOLIO_AVAILABLE) - script_keywords_adapt_amount_to_holdings_mock.reset_mock()