Skip to content
This repository was archived by the owner on Feb 3, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.5.9] - 2026-01-29
### Fixed
[PositionValueHolder] Fix futures get_holdings_ratio position margin handling

## [2.5.8] - 2026-01-29
### Fixed
[OHLCVUpdater] Missing await inside _remove_unsupported_pairs
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# OctoBot-Trading [2.5.8](https://github.com/Drakkar-Software/OctoBot-Trading/blob/master/CHANGELOG.md)
# OctoBot-Trading [2.5.9](https://github.com/Drakkar-Software/OctoBot-Trading/blob/master/CHANGELOG.md)
[![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)
[![PyPI](https://img.shields.io/pypi/v/OctoBot-Trading.svg)](https://pypi.python.org/pypi/OctoBot-Trading/)
[![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)
Expand Down
2 changes: 1 addition & 1 deletion octobot_trading/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
# License along with this library.

PROJECT_NAME = "OctoBot-Trading"
VERSION = "2.5.8" # major.minor.revision
VERSION = "2.5.9" # major.minor.revision
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,13 @@ def get_holdings_ratio(
if position.is_idle():
position_value: decimal.Decimal = constants.ZERO
else:
position_value: decimal.Decimal = position.margin
# position.margin is in the settlement currency of the position
# Convert it to the reference market for proper ratio calculation
parsed_symbol = symbol_util.parse_symbol(symbol)
settlement_currency = parsed_symbol.settlement_asset or parsed_symbol.quote
position_value = self.value_converter.evaluate_value(
settlement_currency, position.margin, init_price_fetchers=False
)

if include_assets_in_open_orders:
assets_in_open_orders = self._get_total_holdings_in_open_orders(currency)
Expand Down
12 changes: 8 additions & 4 deletions tests/personal_data/portfolios/test_portfolio_value_holder.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,9 @@ async def test_get_holdings_ratio(backtesting_trader):
exchange_manager.client_symbols = [symbol]
exchange_manager.exchange_personal_data.portfolio_manager.portfolio_value_holder.\
value_converter.last_prices_by_trading_pair[symbol] = decimal.Decimal("1000")
# Also add the futures symbol price for value conversion
exchange_manager.exchange_personal_data.portfolio_manager.portfolio_value_holder.\
value_converter.last_prices_by_trading_pair["BTC/USDT:USDT"] = decimal.Decimal("1000")
exchange_manager.exchange_personal_data.portfolio_manager.portfolio_value_holder.\
portfolio_current_value = decimal.Decimal("11")
exchange_manager.exchange_personal_data.portfolio_manager.portfolio.portfolio = {}
Expand All @@ -295,7 +298,8 @@ def mock_get_or_create_position_impl(symbol, side):
mock_pos.margin = constants.ZERO
mock_pos.is_idle.return_value = True
else:
mock_pos.margin = decimal.Decimal("1")
# margin in USDT (settlement currency), which converts to 1 BTC at 1000 USDT/BTC
mock_pos.margin = decimal.Decimal("1000")
mock_pos.is_idle.return_value = False
elif symbol == "BTC/XYZ:XYZ":
mock_pos.margin = decimal.Decimal("0")
Expand Down Expand Up @@ -395,13 +399,13 @@ def mock_get_or_create_position_impl(symbol, side):
mock_pos.margin = decimal.Decimal("10") # equivalent to the 10 BTC in the spot portfolio
mock_pos.is_idle.return_value = False
if symbol == "BTC/USDT:USDT":
mock_pos.margin = decimal.Decimal("1") # equivalent to the 1000 USDT vs BTC in the spot portfolio
mock_pos.margin = decimal.Decimal("1000") # 1000 USDT margin, converts to 1 BTC at 1000 USDT/BTC
mock_pos.is_idle.return_value = False
elif symbol == "ETH/USDT:BTC":
mock_pos.margin = decimal.Decimal("100") if has_eth_position else constants.ZERO # equivalent to the 10 ETH vs BTC in the spot portfolio
mock_pos.margin = decimal.Decimal("1") if has_eth_position else constants.ZERO # 1 BTC margin (settlement is BTC)
mock_pos.is_idle.return_value = False
elif symbol == "ETH/BTC:BTC":
mock_pos.margin = decimal.Decimal("1") if has_eth_position else constants.ZERO # equivalent to the 10 ETH vs BTC in the spot portfolio
mock_pos.margin = decimal.Decimal("1") if has_eth_position else constants.ZERO # 1 BTC margin (settlement is BTC)
mock_pos.is_idle.return_value = False
else:
raise errors.ContractExistsError(f"Contract {symbol} does not exist")
Expand Down