from __future__ import annotations from decimal import Decimal from decimal import ROUND_HALF_EVEN from typing import Any CONVICTION_MIN = 0.5 CONVICTION_MAX = 9.0 EXCHANGE_LEV_MIN = 1 EXCHANGE_LEV_MAX = 3 LEVERAGE_MAPPING_RULE = "round_half_even_linear_0.5_to_9.0_to_1_to_exchange_cap" def _clamp_exchange_bounds(exchange_min: Any, exchange_max: Any) -> tuple[int, int]: lower = int(Decimal(str(exchange_min))) upper = int(Decimal(str(exchange_max))) if lower < 1: lower = 1 if upper < lower: upper = lower return lower, upper def normalize_bingx_leverage_value( leverage: Any, *, exchange_min: Any = EXCHANGE_LEV_MIN, exchange_max: Any = EXCHANGE_LEV_MAX, ) -> int: """ BingX exchange leverage is integer-only and conservatively capped. """ lower, upper = _clamp_exchange_bounds(exchange_min, exchange_max) desired = int( Decimal(str(leverage)).quantize(Decimal("1"), rounding=ROUND_HALF_EVEN) ) if desired < lower: return lower if desired > upper: return upper return desired def map_internal_conviction_to_exchange_leverage_target( leverage: Any, *, exchange_min: Any = EXCHANGE_LEV_MIN, exchange_max: Any = EXCHANGE_LEV_MAX, ) -> float: """ Map engine conviction/sizing into BingX exchange leverage. The engine retains the fractional conviction signal internally. The exchange receives a leverage target derived from that signal. """ lower, upper = _clamp_exchange_bounds(exchange_min, exchange_max) internal = float(Decimal(str(leverage))) internal = max(CONVICTION_MIN, min(CONVICTION_MAX, internal)) return float(lower + ( (internal - CONVICTION_MIN) / (CONVICTION_MAX - CONVICTION_MIN) ) * (upper - lower)) def map_internal_conviction_to_exchange_leverage( leverage: Any, *, exchange_min: Any = EXCHANGE_LEV_MIN, exchange_max: Any = EXCHANGE_LEV_MAX, ) -> int: """ Backwards-compatible integer exchange leverage mapper. """ target = map_internal_conviction_to_exchange_leverage_target( leverage, exchange_min=exchange_min, exchange_max=exchange_max, ) return normalize_bingx_leverage_value( target, exchange_min=exchange_min, exchange_max=exchange_max, )