Skip to content
This repository was archived by the owner on Feb 3, 2026. It is now read-only.

Commit 7f86f09

Browse files
committed
[Futures] Fix positions value holder margin handling
1 parent 786fb96 commit 7f86f09

File tree

5 files changed

+21
-7
lines changed

5 files changed

+21
-7
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [2.5.9] - 2026-01-29
8+
### Fixed
9+
[PositionValueHolder] Fix futures get_holdings_ratio position margin handling
10+
711
## [2.5.8] - 2026-01-29
812
### Fixed
913
[OHLCVUpdater] Missing await inside _remove_unsupported_pairs

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# OctoBot-Trading [2.5.8](https://github.com/Drakkar-Software/OctoBot-Trading/blob/master/CHANGELOG.md)
1+
# OctoBot-Trading [2.5.9](https://github.com/Drakkar-Software/OctoBot-Trading/blob/master/CHANGELOG.md)
22
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/903b6b22bceb4661b608a86fea655f69)](https://app.codacy.com/gh/Drakkar-Software/OctoBot-Trading?utm_source=github.com&utm_medium=referral&utm_content=Drakkar-Software/OctoBot-Trading&utm_campaign=Badge_Grade_Dashboard)
33
[![PyPI](https://img.shields.io/pypi/v/OctoBot-Trading.svg)](https://pypi.python.org/pypi/OctoBot-Trading/)
44
[![Coverage Status](https://coveralls.io/repos/github/Drakkar-Software/OctoBot-Trading/badge.svg?branch=master)](https://coveralls.io/github/Drakkar-Software/OctoBot-Trading?branch=master)

octobot_trading/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@
1515
# License along with this library.
1616

1717
PROJECT_NAME = "OctoBot-Trading"
18-
VERSION = "2.5.8" # major.minor.revision
18+
VERSION = "2.5.9" # major.minor.revision

octobot_trading/personal_data/portfolios/holders/futures_portfolio_value_holder.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,13 @@ def get_holdings_ratio(
5555
if position.is_idle():
5656
position_value: decimal.Decimal = constants.ZERO
5757
else:
58-
position_value: decimal.Decimal = position.margin
58+
# position.margin is in the settlement currency of the position
59+
# Convert it to the reference market for proper ratio calculation
60+
parsed_symbol = symbol_util.parse_symbol(symbol)
61+
settlement_currency = parsed_symbol.settlement_asset or parsed_symbol.quote
62+
position_value = self.value_converter.evaluate_value(
63+
settlement_currency, position.margin, init_price_fetchers=False
64+
)
5965

6066
if include_assets_in_open_orders:
6167
assets_in_open_orders = self._get_total_holdings_in_open_orders(currency)

tests/personal_data/portfolios/test_portfolio_value_holder.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,9 @@ async def test_get_holdings_ratio(backtesting_trader):
279279
exchange_manager.client_symbols = [symbol]
280280
exchange_manager.exchange_personal_data.portfolio_manager.portfolio_value_holder.\
281281
value_converter.last_prices_by_trading_pair[symbol] = decimal.Decimal("1000")
282+
# Also add the futures symbol price for value conversion
283+
exchange_manager.exchange_personal_data.portfolio_manager.portfolio_value_holder.\
284+
value_converter.last_prices_by_trading_pair["BTC/USDT:USDT"] = decimal.Decimal("1000")
282285
exchange_manager.exchange_personal_data.portfolio_manager.portfolio_value_holder.\
283286
portfolio_current_value = decimal.Decimal("11")
284287
exchange_manager.exchange_personal_data.portfolio_manager.portfolio.portfolio = {}
@@ -295,7 +298,8 @@ def mock_get_or_create_position_impl(symbol, side):
295298
mock_pos.margin = constants.ZERO
296299
mock_pos.is_idle.return_value = True
297300
else:
298-
mock_pos.margin = decimal.Decimal("1")
301+
# margin in USDT (settlement currency), which converts to 1 BTC at 1000 USDT/BTC
302+
mock_pos.margin = decimal.Decimal("1000")
299303
mock_pos.is_idle.return_value = False
300304
elif symbol == "BTC/XYZ:XYZ":
301305
mock_pos.margin = decimal.Decimal("0")
@@ -395,13 +399,13 @@ def mock_get_or_create_position_impl(symbol, side):
395399
mock_pos.margin = decimal.Decimal("10") # equivalent to the 10 BTC in the spot portfolio
396400
mock_pos.is_idle.return_value = False
397401
if symbol == "BTC/USDT:USDT":
398-
mock_pos.margin = decimal.Decimal("1") # equivalent to the 1000 USDT vs BTC in the spot portfolio
402+
mock_pos.margin = decimal.Decimal("1000") # 1000 USDT margin, converts to 1 BTC at 1000 USDT/BTC
399403
mock_pos.is_idle.return_value = False
400404
elif symbol == "ETH/USDT:BTC":
401-
mock_pos.margin = decimal.Decimal("100") if has_eth_position else constants.ZERO # equivalent to the 10 ETH vs BTC in the spot portfolio
405+
mock_pos.margin = decimal.Decimal("1") if has_eth_position else constants.ZERO # 1 BTC margin (settlement is BTC)
402406
mock_pos.is_idle.return_value = False
403407
elif symbol == "ETH/BTC:BTC":
404-
mock_pos.margin = decimal.Decimal("1") if has_eth_position else constants.ZERO # equivalent to the 10 ETH vs BTC in the spot portfolio
408+
mock_pos.margin = decimal.Decimal("1") if has_eth_position else constants.ZERO # 1 BTC margin (settlement is BTC)
405409
mock_pos.is_idle.return_value = False
406410
else:
407411
raise errors.ContractExistsError(f"Contract {symbol} does not exist")

0 commit comments

Comments
 (0)