Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
Back to tool/Back to issues
#13604·freqtrade

Wallets._update_live drops cross-margin positions: collateral==0 filter runs before the initialMargin fallback

Author: shlomixxxCreated Sep 24, 2026Updated Sep 25, 2026
LabelsAI Slop

Describe your environment

  • Operating system: macOS 15.2 (arm64)
  • Python Version: 3.12.3
  • CCXT version: 4.5.82
  • Freqtrade Version: 2026.8

Describe the problem

In Wallets._update_live, open positions are discarded when ccxt reports collateral == 0.0 — but the very next lines already know how to substitute that value with initialMargin. The filter runs before the fallback:

python
# freqtrade/wallets.py
204:  if position["side"] is None or position["collateral"] == 0.0:
205:      continue
...
208:  collateral = safe_value_fallback(position, "initialMargin", "collateral", 0.0)

On Binance USDⓈ-M cross margin, liquidationPrice is frequently None. ccxt derives collateral from liquidationPrice, so it returns 0.0 for those positions — while initialMargin is present and correct.

The result is that genuinely open positions are silently dropped from Wallets._positions.

Observed data

Captured via exchange.fetch_positions() — the same call _update_live makes — against a live Binance USDⓈ-M cross-margin account:

symbol side collateral initialMargin liquidationPrice notional
PAIR_A/USDT:USDT long 0.0 3.815 None 76.30
PAIR_B/USDT:USDT long 0.0 40.878 None 163.51

(Pair names replaced with placeholders. All numeric values are the real captured data, unmodified.)

Both positions are open and carry real margin. Both are skipped at line 204. A third position in the same account, which did have a liquidationPrice, reported a non-zero collateral and was kept — so the behaviour is per-position, not per-account.

Impact

1. Wallets._positions is incomplete in live cross-margin. Consumers such as get_collateral(), _check_exit_amount() and the /balance RPC see only the subset of positions for which ccxt happened to derive a collateral value.

2. Wallet.total for the stake currency is wrong on exchanges with balance_includes_unrealized_pnl: True (Binance futures). _strip_unrealized_pnl sums unrealized PnL over the already filtered dict:

python
238:  upnl = sum(pos.unrealized_pnl for pos in positions.values())
241:  wallets[...] = stake_wallet._replace(total=stake_wallet.total - upnl)

Positions dropped at line 204 contribute no unrealized_pnl, so only part of the account's unrealized PnL is stripped. In the account above both open positions were skipped, so 100% of the account's unrealized PnL was excluded from the correction — total was left uncorrected by -75.99 USDT at the moment of capture, and get_total() reported that much more equity than the account actually had.

Suggested fix

Apply the initialMargin fallback before the emptiness test, so that a position is only skipped when it genuinely has no margin:

python
for position in positions:
    symbol = position["symbol"]
    collateral = safe_value_fallback(position, "initialMargin", "collateral", 0.0)
    if position["side"] is None or collateral == 0.0:
        continue
    ...

This keeps the existing intent ("skip positions that are not open") while no longer relying on a field that Binance does not always allow ccxt to derive.

Steps to reproduce

  1. Hold an open position on Binance USDⓈ-M in cross margin mode for which the exchange returns no per-position liquidation price.
  2. Call exchange.fetch_positions() and inspect collateral vs initialMargin.
  3. Observe that Wallets._positions omits the position.

Source: freqtrade/freqtrade

View original on GitHubView discussion on GitHub