55FRACTIONAL_SHARE_EXECUTION_CAPABILITY = "fractional_share_execution"
66FRACTIONAL_SHARE_EXECUTION_SKIP_REASON = "fractional_share_execution_required"
77
8+ # When a platform does NOT natively support fractional shares, DCA / notional
9+ # strategies can still run by converting each notional buy into a minimum
10+ # 1-share (US) or 1-lot (HK) order. This compat mode is signalled via
11+ # ``notional_buy_compat_mode_enabled()`` so the execution layer knows to
12+ # floor-up notional amounts instead of placing true fractional orders.
13+ NOTIONAL_TO_WHOLE_SHARE_COMPAT_SKIP_REASON = "notional_to_whole_share_compat"
14+
815
916def definition_requires_fractional_share_execution (definition : StrategyDefinition ) -> bool :
1017 return FRACTIONAL_SHARE_EXECUTION_CAPABILITY in frozenset (definition .compatible_capabilities )
@@ -20,6 +27,15 @@ def fractional_share_execution_unsupported_reason(
2027 strategy_catalog : StrategyCatalog ,
2128 capability_matrix : PlatformCapabilityMatrix ,
2229) -> str | None :
30+ """Return a reason string if *profile* requires fractional shares but the
31+ platform does **not** support them natively.
32+
33+ When the platform lacks ``fractional_share_execution`` but a strategy
34+ still needs to place notional orders, callers should check
35+ ``notional_buy_compat_mode_enabled()`` — if that returns ``True`` the
36+ execution layer should convert each notional buy into a minimum
37+ whole-share (or whole-lot) order instead of skipping the strategy.
38+ """
2339 normalized_profile = normalize_profile_name (profile )
2440 definition = strategy_catalog .definitions .get (normalized_profile )
2541 if definition is None :
@@ -28,3 +44,25 @@ def fractional_share_execution_unsupported_reason(
2844 if not platform_supports_fractional_share_execution (capability_matrix = capability_matrix ):
2945 return FRACTIONAL_SHARE_EXECUTION_SKIP_REASON
3046 return None
47+
48+
49+ def notional_buy_compat_mode_enabled (
50+ profile : str ,
51+ * ,
52+ strategy_catalog : StrategyCatalog ,
53+ capability_matrix : PlatformCapabilityMatrix ,
54+ ) -> bool :
55+ """Return ``True`` when *profile* requires fractional execution but the
56+ platform does **not** support it natively.
57+
58+ In compat mode the execution layer should convert each notional buy
59+ intent into a minimum 1‑share (US) or 1‑lot (HK) order, falling back
60+ to skipping the order if the notional amount is smaller than one unit.
61+ """
62+ normalized_profile = normalize_profile_name (profile )
63+ definition = strategy_catalog .definitions .get (normalized_profile )
64+ if definition is None :
65+ return False
66+ if not definition_requires_fractional_share_execution (definition ):
67+ return False
68+ return not platform_supports_fractional_share_execution (capability_matrix = capability_matrix )
0 commit comments