diff --git a/requirements.txt b/requirements.txt index 5621f54..98ab2a2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,8 @@ flask gunicorn -quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@023641c88506c732624a7329e48b51b9dbbe3c2a -us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@7d35772d1125b534d0bcca557cb6dbaf28914719 -hk-equity-strategies @ git+https://github.com/QuantStrategyLab/HkEquityStrategies.git@2e0075004239e7ede7ba256763a3441d4ec4ca73 +quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@c6e221f71d7be4d8b1c8c94ce05452c3116e0c10 +us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@2b02dc947d99a593d2bbae4833f0eeeffc2eecd2 +hk-equity-strategies @ git+https://github.com/QuantStrategyLab/HkEquityStrategies.git@5f739744b97d5a9f8981e7ae649c71a0a8ef10fa pandas numpy requests diff --git a/runtime_config_support.py b/runtime_config_support.py index c7fdb8c..566202d 100644 --- a/runtime_config_support.py +++ b/runtime_config_support.py @@ -17,6 +17,31 @@ RuntimeTarget, resolve_runtime_target_from_env, ) +try: + from quant_platform_kit.common.broker_costs import ( + BrokerCostProfile, + minimum_economic_order_notional_usd, + ) +except ImportError: # pragma: no cover - compatibility with older pinned shared wheels + @dataclass(frozen=True) + class BrokerCostProfile: + fixed_order_fee_usd: float = 0.0 + minimum_order_fee_usd: float = 0.0 + max_fixed_fee_bps: float = 100.0 + explicit_min_order_notional_usd: float = 0.0 + + def minimum_economic_order_notional_usd(profile: BrokerCostProfile | None) -> float: + if profile is None: + return 0.0 + explicit_floor = max(0.0, float(profile.explicit_min_order_notional_usd or 0.0)) + fee_floor = max( + max(0.0, float(profile.fixed_order_fee_usd or 0.0)), + max(0.0, float(profile.minimum_order_fee_usd or 0.0)), + ) + max_fee_bps = max(0.0, float(profile.max_fixed_fee_bps or 0.0)) + if fee_floor <= 0.0 or max_fee_bps <= 0.0: + return explicit_floor + return max(explicit_floor, fee_floor / (max_fee_bps / 10_000.0)) from strategy_registry import ( IBKR_PLATFORM, STRATEGY_CATALOG, @@ -39,6 +64,14 @@ HK_MARKET_TIMEZONE = "Asia/Hong_Kong" DEFAULT_RESERVED_CASH_FLOOR_USD = 0.0 DEFAULT_SAFE_HAVEN_CASH_SUBSTITUTE_THRESHOLD_USD = 1000.0 +DEFAULT_IBKR_MIN_ORDER_FEE_USD = 0.35 +DEFAULT_IBKR_MAX_FIXED_FEE_BPS = 50.0 +DEFAULT_IBKR_MIN_ORDER_NOTIONAL_USD = minimum_economic_order_notional_usd( + BrokerCostProfile( + minimum_order_fee_usd=DEFAULT_IBKR_MIN_ORDER_FEE_USD, + max_fixed_fee_bps=DEFAULT_IBKR_MAX_FIXED_FEE_BPS, + ) +) EXECUTION_BACKEND_GATEWAY = "gateway" EXECUTION_BACKEND_QUANTCONNECT = "quantconnect" SUPPORTED_EXECUTION_BACKENDS = frozenset( @@ -135,7 +168,7 @@ class PlatformRuntimeSettings: market_exchange: str = DEFAULT_MARKET_EXCHANGE market_timezone: str = DEFAULT_MARKET_TIMEZONE quantity_step: float = 1.0 - min_order_notional: float = 50.0 + min_order_notional: float = DEFAULT_IBKR_MIN_ORDER_NOTIONAL_USD reserved_cash_floor_usd: float = DEFAULT_RESERVED_CASH_FLOOR_USD reserved_cash_ratio: float | None = None safe_haven_cash_substitute_threshold_usd: float = DEFAULT_SAFE_HAVEN_CASH_SUBSTITUTE_THRESHOLD_USD @@ -322,7 +355,7 @@ def load_platform_runtime_settings( min_order_notional=resolve_float_env( os.environ, "IBKR_MIN_ORDER_NOTIONAL_USD", - default=50.0, + default=DEFAULT_IBKR_MIN_ORDER_NOTIONAL_USD, ), reserved_cash_floor_usd=resolve_non_negative_float_env( "IBKR_MIN_RESERVED_CASH_USD", diff --git a/tests/test_runtime_config_support.py b/tests/test_runtime_config_support.py index 5763122..9ccb8fa 100644 --- a/tests/test_runtime_config_support.py +++ b/tests/test_runtime_config_support.py @@ -176,7 +176,7 @@ def test_load_platform_runtime_settings_uses_minimal_group_config(monkeypatch): assert settings.market_exchange == DEFAULT_MARKET_EXCHANGE assert settings.market_timezone == DEFAULT_MARKET_TIMEZONE assert settings.quantity_step == 1.0 - assert settings.min_order_notional == 50.0 + assert settings.min_order_notional == 70.0 assert settings.reserved_cash_floor_usd == DEFAULT_RESERVED_CASH_FLOOR_USD assert settings.reserved_cash_ratio is None assert settings.safe_haven_cash_substitute_threshold_usd == DEFAULT_SAFE_HAVEN_CASH_SUBSTITUTE_THRESHOLD_USD