diff --git a/docs/trade-object.md b/docs/trade-object.md index 4962d6b30..7434b826d 100644 --- a/docs/trade-object.md +++ b/docs/trade-object.md @@ -18,7 +18,7 @@ The following attributes / properties are available for each individual trade - | `open_rate` | float | Rate this trade was entered at (Avg. entry rate in case of trade-adjustments). | | `close_rate` | float | Close rate - only set when is_open = False. | | `stake_amount` | float | Amount in Stake (or Quote) currency. | -| `amount` | float | Amount in Asset / Base currency that is currently owned. | +| `amount` | float | Amount in Asset / Base currency that is currently owned. Will be 0.0 until the initial order fills. | | `open_date` | datetime | Timestamp when trade was opened **use `open_date_utc` instead** | | `open_date_utc` | datetime | Timestamp when trade was opened - in UTC. | | `close_date` | datetime | Timestamp when trade was closed **use `close_date_utc` instead** | diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 0bc11e0fd..fe270f670 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -987,7 +987,7 @@ class FreqtradeBot(LoggingMixin): base_currency=base_currency, stake_currency=self.config["stake_currency"], stake_amount=stake_amount, - amount=amount, + amount=0, is_open=True, amount_requested=amount_requested, fee_open=fee, diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 71adfbb4b..20116f670 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -1099,7 +1099,7 @@ class Backtesting: open_rate_requested=propose_rate, open_date=current_time, stake_amount=stake_amount, - amount=amount, + amount=0, amount_requested=amount, fee_open=self.fee, fee_close=self.fee, diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py index e276d547d..698e9721c 100644 --- a/freqtrade/persistence/trade_model.py +++ b/freqtrade/persistence/trade_model.py @@ -1161,10 +1161,7 @@ class LocalTrade: else: open_trade_value = self._calc_open_trade_value(amount, open_rate) - short_close_zero = self.is_short and close_trade_value == 0.0 - long_close_zero = not self.is_short and open_trade_value == 0.0 - - if short_close_zero or long_close_zero: + if open_trade_value == 0.0: return 0.0 else: if self.is_short: diff --git a/freqtrade/wallets.py b/freqtrade/wallets.py index f888ef92e..e67bdd79e 100644 --- a/freqtrade/wallets.py +++ b/freqtrade/wallets.py @@ -99,11 +99,21 @@ class Wallets: used_stake = 0.0 if self._config.get("trading_mode", "spot") != TradingMode.FUTURES: - current_stake = self.start_cap + tot_profit - tot_in_trades - total_stake = current_stake for trade in open_trades: curr = self._exchange.get_pair_base_currency(trade.pair) - _wallets[curr] = Wallet(curr, trade.amount, 0, trade.amount) + used_stake += sum( + o.stake_amount for o in trade.open_orders if o.ft_order_side == trade.entry_side + ) + pending = sum( + o.amount + for o in trade.open_orders + if o.amount and o.ft_order_side == trade.exit_side + ) + + _wallets[curr] = Wallet(curr, trade.amount - pending, pending, trade.amount) + + current_stake = self.start_cap + tot_profit - tot_in_trades + total_stake = current_stake + used_stake else: tot_in_trades = 0 for position in open_trades: diff --git a/tests/freqtradebot/test_freqtradebot.py b/tests/freqtradebot/test_freqtradebot.py index 8b24e5d70..8587e7f9d 100644 --- a/tests/freqtradebot/test_freqtradebot.py +++ b/tests/freqtradebot/test_freqtradebot.py @@ -689,13 +689,29 @@ def test_process_trade_creation( assert trade.open_date is not None assert trade.exchange == "binance" assert trade.open_rate == ticker_usdt.return_value[ticker_side] - assert pytest.approx(trade.amount) == 60 / ticker_usdt.return_value[ticker_side] + # Trade opens with 0 amount. Only trade filling will set the amount + assert pytest.approx(trade.amount) == 0 + assert pytest.approx(trade.amount_requested) == 60 / ticker_usdt.return_value[ticker_side] assert log_has( f'{"Short" if is_short else "Long"} signal found: about create a new trade for ETH/USDT ' "with stake_amount: 60.0 ...", caplog, ) + mocker.patch("freqtrade.freqtradebot.FreqtradeBot._check_and_execute_exit") + + # Fill trade. + freqtrade.process() + trades = Trade.get_open_trades() + assert len(trades) == 1 + trade = trades[0] + assert trade is not None + assert trade.is_open + assert trade.open_date is not None + assert trade.exchange == "binance" + assert trade.open_rate == limit_order[entry_side(is_short)]["price"] + # Filled trade has amount set to filled order amount + assert pytest.approx(trade.amount) == limit_order[entry_side(is_short)]["filled"] def test_process_exchange_failures(default_conf_usdt, ticker_usdt, mocker) -> None: @@ -1684,7 +1700,7 @@ def test_handle_trade_roi( create_order=MagicMock( side_effect=[ open_order, - {"id": 1234553382}, + {"id": 1234553382, "amount": open_order["amount"]}, ] ), get_fee=fee, diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index feb7f9f1a..dd8c1bb9a 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -149,7 +149,10 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: # Different from "filled" response: response_unfilled.update( { - "amount": 91.07468124, + "amount": 0.0, + "open_trade_value": 0.0, + "stoploss_entry_dist": 0.0, + "stoploss_entry_dist_ratio": 0.0, "profit_ratio": 0.0, "profit_pct": 0.0, "profit_abs": 0.0, @@ -762,7 +765,7 @@ def test_rpc_force_exit(default_conf, ticker, fee, mocker) -> None: freqtradebot.enter_positions() # make an limit-buy open trade trade = Trade.session.scalars(select(Trade).filter(Trade.id == "3")).first() - filled_amount = trade.amount / 2 + filled_amount = trade.amount_requested / 2 # Fetch order - it's open first, and closed after cancel_order is called. mocker.patch( f"{EXMS}.fetch_order", @@ -799,7 +802,7 @@ def test_rpc_force_exit(default_conf, ticker, fee, mocker) -> None: cancel_order_mock.reset_mock() trade = Trade.session.scalars(select(Trade).filter(Trade.id == "3")).first() - amount = trade.amount + amount = trade.amount_requested # make an limit-sell open order trade mocker.patch( f"{EXMS}.fetch_order", @@ -832,7 +835,7 @@ def test_rpc_force_exit(default_conf, ticker, fee, mocker) -> None: assert cancel_order_mock.call_count == 0 trade = Trade.session.scalars(select(Trade).filter(Trade.id == "4")).first() - amount = trade.amount + amount = trade.amount_requested # make an limit-buy open trade, if there is no 'filled', don't sell it mocker.patch( f"{EXMS}.fetch_order", diff --git a/tests/test_wallets.py b/tests/test_wallets.py index a2aebeea4..ef3129b88 100644 --- a/tests/test_wallets.py +++ b/tests/test_wallets.py @@ -365,13 +365,18 @@ def test_sync_wallet_dry(mocker, default_conf_usdt, fee): assert bal["NEO"].total == 10 assert bal["XRP"].total == 10 assert bal["LTC"].total == 2 - assert bal["USDT"].total == 922.74 + usdt_bal = bal["USDT"] + assert usdt_bal.free == 922.74 + assert usdt_bal.total == 942.74 + assert usdt_bal.used == 20.0 + # sum of used and free should be total. + assert usdt_bal.total == usdt_bal.free + usdt_bal.used assert freqtrade.wallets.get_starting_balance() == default_conf_usdt["dry_run_wallet"] total = freqtrade.wallets.get_total("LTC") free = freqtrade.wallets.get_free("LTC") used = freqtrade.wallets.get_used("LTC") - assert free != 0 + assert used != 0 assert free + used == total