diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2c0a0c2ef..bd3e9330f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,10 +24,10 @@ jobs: python-version: ["3.8", "3.9", "3.10"] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} @@ -119,10 +119,10 @@ jobs: python-version: ["3.8", "3.9", "3.10"] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} @@ -211,10 +211,10 @@ jobs: python-version: ["3.8", "3.9", "3.10"] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} @@ -263,14 +263,14 @@ jobs: docs_check: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Documentation syntax run: | ./tests/test_docs.sh - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: 3.8 @@ -326,10 +326,10 @@ jobs: if: (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'release') && github.repository == 'freqtrade/freqtrade' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: 3.8 @@ -406,7 +406,7 @@ jobs: if: (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'release') && github.repository == 'freqtrade/freqtrade' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Extract branch name shell: bash diff --git a/.github/workflows/docker_update_readme.yml b/.github/workflows/docker_update_readme.yml index 95a6e82e2..ebb773ad7 100644 --- a/.github/workflows/docker_update_readme.yml +++ b/.github/workflows/docker_update_readme.yml @@ -8,7 +8,7 @@ jobs: dockerHubDescription: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - name: Docker Hub Description uses: peter-evans/dockerhub-description@v2.4.3 env: diff --git a/config_examples/config_full.example.json b/config_examples/config_full.example.json index 1fb2817b8..f48086975 100644 --- a/config_examples/config_full.example.json +++ b/config_examples/config_full.example.json @@ -58,7 +58,8 @@ "forcebuy": "market", "stoploss": "market", "stoploss_on_exchange": false, - "stoploss_on_exchange_interval": 60 + "stoploss_on_exchange_interval": 60, + "stoploss_on_exchange_limit_ratio": 0.99 }, "order_time_in_force": { "entry": "gtc", diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index 839485629..0ca0e4b63 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,4 +1,4 @@ mkdocs==1.2.3 -mkdocs-material==8.2.3 +mkdocs-material==8.2.5 mdx_truly_sane_lists==1.2 pymdown-extensions==9.2 diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 4f3f723c0..cbc6be11a 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -507,7 +507,6 @@ class FreqtradeBot(LoggingMixin): If the strategy triggers the adjustment, a new order gets issued. Once that completes, the existing trade is modified to match new data. """ - # TODO-lev: Check what changes are necessary for DCA in relation to shorts. if self.strategy.max_entry_position_adjustment > -1: count_of_buys = trade.nr_of_successful_entries if count_of_buys > self.strategy.max_entry_position_adjustment: @@ -1077,7 +1076,9 @@ class FreqtradeBot(LoggingMixin): :param order: Current on exchange stoploss order :return: None """ - if self.exchange.stoploss_adjust(trade.stop_loss, order, side=trade.exit_side): + stoploss_norm = self.exchange.price_to_precision(trade.pair, trade.stop_loss) + + if self.exchange.stoploss_adjust(stoploss_norm, order, side=trade.exit_side): # we check if the update is necessary update_beat = self.strategy.order_types.get('stoploss_on_exchange_interval', 60) if (datetime.utcnow() - trade.stoploss_last_update).total_seconds() >= update_beat: diff --git a/freqtrade/rpc/fiat_convert.py b/freqtrade/rpc/fiat_convert.py index 82a6a4778..70f3647b6 100644 --- a/freqtrade/rpc/fiat_convert.py +++ b/freqtrade/rpc/fiat_convert.py @@ -23,6 +23,7 @@ coingecko_mapping = { 'eth': 'ethereum', 'bnb': 'binancecoin', 'sol': 'solana', + 'usdt': 'tether', } diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index a77f6f69c..f47bc2668 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -586,6 +586,9 @@ class RPC: if coin == stake_currency: rate = 1.0 est_stake = balance.total + if self._config.get('trading_mode', TradingMode.SPOT) != TradingMode.SPOT: + # in Futures, "total" includes the locked stake, and therefore all positions + est_stake = balance.free else: try: pair = self._freqtrade.exchange.get_valid_pair_combination(coin, stake_currency) @@ -614,6 +617,7 @@ class RPC: symbol: str position: PositionWallet for symbol, position in self._freqtrade.wallets.get_all_positions().items(): + total += position.collateral currencies.append({ 'currency': symbol, diff --git a/freqtrade/wallets.py b/freqtrade/wallets.py index 7a46f0397..d93689a0e 100644 --- a/freqtrade/wallets.py +++ b/freqtrade/wallets.py @@ -104,16 +104,16 @@ class Wallets: size = position.amount collateral = position.stake_amount leverage = position.leverage - tot_in_trades -= collateral + tot_in_trades += collateral _positions[position.pair] = PositionWallet( position.pair, position=size, leverage=leverage, collateral=collateral, side=position.trade_direction ) - current_stake = self.start_cap + tot_profit + current_stake = self.start_cap + tot_profit - tot_in_trades used_stake = tot_in_trades - total_stake = current_stake - tot_in_trades + total_stake = current_stake + tot_in_trades _wallets[self._config['stake_currency']] = Wallet( currency=self._config['stake_currency'], diff --git a/requirements-dev.txt b/requirements-dev.txt index 9fc8a18ad..5c94ac7c8 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -8,7 +8,7 @@ flake8==4.0.1 flake8-tidy-imports==4.6.0 mypy==0.931 pytest==7.0.1 -pytest-asyncio==0.18.1 +pytest-asyncio==0.18.2 pytest-cov==3.0.0 pytest-mock==3.7.0 pytest-random-order==1.0.4 @@ -20,7 +20,7 @@ time-machine==2.6.0 nbconvert==6.4.2 # mypy types -types-cachetools==4.2.9 +types-cachetools==4.2.10 types-filelock==3.2.5 types-requests==2.27.11 types-tabulate==0.8.5 diff --git a/requirements.txt b/requirements.txt index 802741e34..b523defb2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,11 +2,11 @@ numpy==1.22.2 pandas==1.4.1 pandas-ta==0.3.14b -ccxt==1.74.63 +ccxt==1.75.12 # Pin cryptography for now due to rust build errors with piwheels cryptography==36.0.1 aiohttp==3.8.1 -SQLAlchemy==1.4.31 +SQLAlchemy==1.4.32 python-telegram-bot==13.11 arrow==1.2.2 cachetools==4.2.2 @@ -31,7 +31,7 @@ python-rapidjson==1.6 sdnotify==0.3.2 # API Server -fastapi==0.74.1 +fastapi==0.75.0 uvicorn==0.17.5 pyjwt==2.3.0 aiofiles==0.8.0 diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 7e34506d6..11fb5e0a1 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -650,8 +650,8 @@ def test_rpc_balance_handle(default_conf, mocker, tickers): rpc._fiat_converter = CryptoToFiatConverter() result = rpc._rpc_balance(default_conf['stake_currency'], default_conf['fiat_display_currency']) - assert prec_satoshi(result['total'], 12.309096315) - assert prec_satoshi(result['value'], 184636.44472997) + assert prec_satoshi(result['total'], 30.309096315) + assert prec_satoshi(result['value'], 454636.44472997) assert tickers.call_count == 1 assert tickers.call_args_list[0][1]['cached'] is True assert 'USD' == result['symbol'] @@ -661,7 +661,7 @@ def test_rpc_balance_handle(default_conf, mocker, tickers): 'free': 10.0, 'balance': 12.0, 'used': 2.0, - 'est_stake': 12.0, + 'est_stake': 10.0, # In futures mode, "free" is used here. 'stake': 'BTC', 'is_position': False, 'leverage': 1.0, @@ -706,7 +706,6 @@ def test_rpc_balance_handle(default_conf, mocker, tickers): 'side': 'short', } ] - assert result['total'] == 12.309096315331816 def test_rpc_start(mocker, default_conf) -> None: diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 33e44eed9..d115eddc9 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1508,7 +1508,34 @@ def test_handle_stoploss_on_exchange_trailing_error( assert log_has_re(r"Could not create trailing stoploss order for pair ETH/USDT\..*", caplog) +def test_stoploss_on_exchange_price_rounding( + mocker, default_conf_usdt, fee, open_trade_usdt) -> None: + patch_RPCManager(mocker) + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + get_fee=fee, + ) + price_mock = MagicMock(side_effect=lambda p, s: int(s)) + stoploss_mock = MagicMock(return_value={'id': '13434334'}) + adjust_mock = MagicMock(return_value=False) + mocker.patch.multiple( + 'freqtrade.exchange.Binance', + stoploss=stoploss_mock, + stoploss_adjust=adjust_mock, + price_to_precision=price_mock, + ) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) + open_trade_usdt.stoploss_order_id = '13434334' + open_trade_usdt.stop_loss = 222.55 + + freqtrade.handle_trailing_stoploss_on_exchange(open_trade_usdt, {}) + assert price_mock.call_count == 1 + assert adjust_mock.call_count == 1 + assert adjust_mock.call_args_list[0][0][0] == 222 + + @pytest.mark.parametrize("is_short", [False, True]) +@pytest.mark.usefixtures("init_persistence") def test_handle_stoploss_on_exchange_custom_stop( mocker, default_conf_usdt, fee, is_short, limit_order ) -> None: diff --git a/tests/test_wallets.py b/tests/test_wallets.py index e7b804a0b..73a34bbae 100644 --- a/tests/test_wallets.py +++ b/tests/test_wallets.py @@ -356,3 +356,9 @@ def test_sync_wallet_futures_dry(mocker, default_conf, fee): positions['ETC/BTC'].side == 'long' positions['XRP/BTC'].side == 'long' positions['LTC/BTC'].side == 'short' + + assert freqtrade.wallets.get_starting_balance() == default_conf['dry_run_wallet'] + total = freqtrade.wallets.get_total('BTC') + free = freqtrade.wallets.get_free('BTC') + used = freqtrade.wallets.get_used('BTC') + assert free + used == total