@@ -45,13 +45,16 @@ class PlatformRuntimeSettings:
4545 run_strategy_on_http : bool
4646 live_order_ack : bool
4747 max_order_notional_usd : float | None
48+ runtime_target_enabled : bool = True
4849 reserved_cash_floor_usd : float = DEFAULT_RESERVED_CASH_FLOOR_USD
4950 reserved_cash_ratio : float = DEFAULT_RESERVED_CASH_RATIO
5051 persist_strategy_runs : bool = False
5152 safe_haven_cash_substitute_threshold_usd : float = DEFAULT_SAFE_HAVEN_CASH_SUBSTITUTE_THRESHOLD_USD
5253 debug_position_snapshot : bool = False
5354 income_threshold_usd : float | None = None
5455 qqqi_income_ratio : float | None = None
56+ income_layer_enabled : bool | None = None
57+ income_layer_max_ratio : float | None = None
5558 runtime_execution_window_trading_days : int | None = None
5659 feature_snapshot_path : str | None = None
5760 feature_snapshot_manifest_path : str | None = None
@@ -140,6 +143,7 @@ def load_platform_runtime_settings(
140143 tg_token = os .getenv ("TELEGRAM_TOKEN" ),
141144 tg_chat_id = os .getenv ("GLOBAL_TELEGRAM_CHAT_ID" ),
142145 dry_run_only = dry_run_only ,
146+ runtime_target_enabled = _runtime_target_enabled_env (),
143147 live_trading_enabled = resolve_bool_value (os .getenv ("FIRSTRADE_ENABLE_LIVE_TRADING" )),
144148 run_strategy_on_http = resolve_bool_value (os .getenv ("FIRSTRADE_RUN_STRATEGY_ON_HTTP" )),
145149 live_order_ack = resolve_bool_value (os .getenv ("FIRSTRADE_LIVE_ORDER_ACK" )),
@@ -164,6 +168,8 @@ def load_platform_runtime_settings(
164168 debug_position_snapshot = resolve_bool_value (os .getenv ("FIRSTRADE_DEBUG_POSITION_SNAPSHOT" )),
165169 income_threshold_usd = resolve_optional_float_env (os .environ , "INCOME_THRESHOLD_USD" ),
166170 qqqi_income_ratio = _qqqi_income_ratio_env (),
171+ income_layer_enabled = _optional_bool_env ("INCOME_LAYER_ENABLED" ),
172+ income_layer_max_ratio = _optional_ratio_env ("INCOME_LAYER_MAX_RATIO" ),
167173 runtime_execution_window_trading_days = _runtime_execution_window_trading_days_env (
168174 strategy_definition .profile
169175 ),
@@ -267,6 +273,34 @@ def _qqqi_income_ratio_env() -> float | None:
267273 return value
268274
269275
276+ def _optional_bool_env (name : str ) -> bool | None :
277+ raw_value = os .getenv (name )
278+ if raw_value is None or str (raw_value ).strip () == "" :
279+ return None
280+ value = str (raw_value ).strip ().lower ()
281+ if value in {"1" , "true" , "yes" , "y" , "on" }:
282+ return True
283+ if value in {"0" , "false" , "no" , "n" , "off" }:
284+ return False
285+ raise ValueError (f"{ name } must be boolean, got { raw_value !r} " )
286+
287+
288+ def _runtime_target_enabled_env () -> bool :
289+ value = _optional_bool_env ("RUNTIME_TARGET_ENABLED" )
290+ return True if value is None else value
291+
292+
293+ def _optional_ratio_env (name : str ) -> float | None :
294+ value = resolve_optional_float_env (os .environ , name )
295+ if value is None :
296+ return None
297+ if not math .isfinite (value ):
298+ raise ValueError (f"{ name } must be finite, got { value } " )
299+ if not (0.0 <= value <= 1.0 ):
300+ raise ValueError (f"{ name } must be in [0,1], got { value } " )
301+ return value
302+
303+
270304def _resolve_non_negative_float_env (name : str , * , default : float ) -> float :
271305 value = resolve_optional_float_env (os .environ , name )
272306 if value is None :
0 commit comments