Skip to content
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
7 changes: 7 additions & 0 deletions .github/workflows/sync-cloud-run-env.yml
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ jobs:
INCOME_THRESHOLD_USD: ${{ vars.INCOME_THRESHOLD_USD }}
QQQI_INCOME_RATIO: ${{ vars.QQQI_INCOME_RATIO }}
INCOME_LAYER_ENABLED: ${{ vars.INCOME_LAYER_ENABLED }}
INCOME_LAYER_START_USD: ${{ vars.INCOME_LAYER_START_USD }}
INCOME_LAYER_MAX_RATIO: ${{ vars.INCOME_LAYER_MAX_RATIO }}
RUNTIME_TARGET_ENABLED: ${{ vars.RUNTIME_TARGET_ENABLED }}
NOTIFY_LANG: ${{ vars.NOTIFY_LANG }}
Expand Down Expand Up @@ -942,6 +943,12 @@ jobs:
remove_env_vars+=("INCOME_LAYER_ENABLED")
fi

if [ -n "${INCOME_LAYER_START_USD:-}" ]; then
env_pairs+=("INCOME_LAYER_START_USD=${INCOME_LAYER_START_USD}")
else
remove_env_vars+=("INCOME_LAYER_START_USD")
fi

if [ -n "${INCOME_LAYER_MAX_RATIO:-}" ]; then
env_pairs+=("INCOME_LAYER_MAX_RATIO=${INCOME_LAYER_MAX_RATIO}")
else
Expand Down
13 changes: 13 additions & 0 deletions runtime_config_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ class PlatformRuntimeSettings:
income_threshold_usd: float | None = None
qqqi_income_ratio: float | None = None
income_layer_enabled: bool | None = None
income_layer_start_usd: float | None = None
income_layer_max_ratio: float | None = None
runtime_execution_window_trading_days: int | None = None
feature_snapshot_path: str | None = None
Expand Down Expand Up @@ -288,6 +289,7 @@ def load_platform_runtime_settings(
income_threshold_usd=resolve_optional_float_env(os.environ, "INCOME_THRESHOLD_USD"),
qqqi_income_ratio=_qqqi_income_ratio_env(),
income_layer_enabled=_optional_bool_env("INCOME_LAYER_ENABLED"),
income_layer_start_usd=_optional_non_negative_float_env("INCOME_LAYER_START_USD"),
income_layer_max_ratio=_optional_ratio_env("INCOME_LAYER_MAX_RATIO"),
runtime_execution_window_trading_days=_runtime_execution_window_trading_days_env(
strategy_definition.profile
Expand Down Expand Up @@ -400,6 +402,17 @@ def _optional_ratio_env(name: str) -> float | None:
return value


def _optional_non_negative_float_env(name: str) -> float | None:
value = resolve_optional_float_env(os.environ, name)
if value is None:
return None
if not math.isfinite(value):
raise ValueError(f"{name} must be finite, got {value}")
if value < 0:
raise ValueError(f"{name} must be non-negative, got {value}")
return float(value)


def _resolve_non_negative_float_env(name: str, *, default: float) -> float:
value = resolve_optional_float_env(os.environ, name)
if value is None:
Expand Down
3 changes: 3 additions & 0 deletions strategy_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,12 @@ def _default_runtime_settings(profile: str, display_name: str) -> PlatformRuntim
def _build_runtime_overrides(profile: str, runtime_settings: PlatformRuntimeSettings) -> dict[str, Any]:
overrides: dict[str, Any] = {}
income_layer_enabled = getattr(runtime_settings, "income_layer_enabled", None)
income_layer_start_usd = getattr(runtime_settings, "income_layer_start_usd", None)
income_layer_max_ratio = getattr(runtime_settings, "income_layer_max_ratio", None)
if income_layer_enabled is not None:
overrides["income_layer_enabled"] = income_layer_enabled
if income_layer_start_usd is not None:
overrides["income_layer_start_usd"] = income_layer_start_usd
Comment on lines +174 to +175

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scope income layer start overrides to supported profiles

When INCOME_LAYER_START_USD is set for any deployment whose selected profile is not soxl_soxx_trend_income (for example tqqq_growth_income), this unconditional override is merged into the runtime config and then the vendored evaluate_tqqq_growth_income passes it through as **config to build_rebalance_plan, whose signature has no income_layer_start_usd parameter. That makes runtime evaluation fail with an unexpected-keyword TypeError instead of producing a rebalance plan, so this override should be gated to the profile(s) that actually accept it.

Useful? React with 👍 / 👎.

if income_layer_max_ratio is not None:
overrides["income_layer_max_ratio"] = income_layer_max_ratio
if profile == "tqqq_growth_income":
Expand Down
14 changes: 14 additions & 0 deletions tests/test_runtime_config_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,7 @@ def test_income_layer_overrides_are_loaded_from_env(self):
"INCOME_THRESHOLD_USD": "100000",
"QQQI_INCOME_RATIO": "0.5",
"INCOME_LAYER_ENABLED": "false",
"INCOME_LAYER_START_USD": "250000",
"INCOME_LAYER_MAX_RATIO": "0.25",
},
clear=True,
Expand All @@ -492,6 +493,7 @@ def test_income_layer_overrides_are_loaded_from_env(self):
self.assertEqual(settings.income_threshold_usd, 100000.0)
self.assertEqual(settings.qqqi_income_ratio, 0.5)
self.assertFalse(settings.income_layer_enabled)
self.assertEqual(settings.income_layer_start_usd, 250000.0)
self.assertEqual(settings.income_layer_max_ratio, 0.25)

def test_tech_runtime_execution_window_override_rejects_research_only_profile(self):
Expand Down Expand Up @@ -532,6 +534,18 @@ def test_rejects_invalid_income_layer_max_ratio(self):
with self.assertRaisesRegex(ValueError, "INCOME_LAYER_MAX_RATIO"):
load_platform_runtime_settings(project_id_resolver=lambda: "project-1")

def test_rejects_invalid_income_layer_start_usd(self):
with patch.dict(
os.environ,
{
"RUNTIME_TARGET_JSON": runtime_target_json("tqqq_growth_income"),
"INCOME_LAYER_START_USD": "-1",
},
clear=True,
):
with self.assertRaisesRegex(ValueError, "INCOME_LAYER_START_USD"):
load_platform_runtime_settings(project_id_resolver=lambda: "project-1")

def test_rejects_human_readable_alias(self):
with patch.dict(
os.environ,
Expand Down
5 changes: 5 additions & 0 deletions tests/test_strategy_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ def _build_runtime_settings(
income_threshold_usd: float | None = None,
qqqi_income_ratio: float | None = None,
income_layer_enabled: bool | None = None,
income_layer_start_usd: float | None = None,
income_layer_max_ratio: float | None = None,
runtime_execution_window_trading_days: int | None = None,
) -> PlatformRuntimeSettings:
Expand All @@ -121,6 +122,7 @@ def _build_runtime_settings(
income_threshold_usd=income_threshold_usd,
qqqi_income_ratio=qqqi_income_ratio,
income_layer_enabled=income_layer_enabled,
income_layer_start_usd=income_layer_start_usd,
income_layer_max_ratio=income_layer_max_ratio,
runtime_execution_window_trading_days=runtime_execution_window_trading_days,
feature_snapshot_path=feature_snapshot_path,
Expand Down Expand Up @@ -262,17 +264,20 @@ def test_load_strategy_runtime_applies_tqqq_income_overrides_from_settings(self)
income_threshold_usd=100000.0,
qqqi_income_ratio=0.5,
income_layer_enabled=False,
income_layer_start_usd=250000.0,
income_layer_max_ratio=0.25,
),
)

self.assertEqual(runtime.runtime_overrides["income_threshold_usd"], 100000.0)
self.assertEqual(runtime.runtime_overrides["qqqi_income_ratio"], 0.5)
self.assertFalse(runtime.runtime_overrides["income_layer_enabled"])
self.assertEqual(runtime.runtime_overrides["income_layer_start_usd"], 250000.0)
self.assertEqual(runtime.runtime_overrides["income_layer_max_ratio"], 0.25)
self.assertEqual(runtime.merged_runtime_config["income_threshold_usd"], 100000.0)
self.assertEqual(runtime.merged_runtime_config["qqqi_income_ratio"], 0.5)
self.assertFalse(runtime.merged_runtime_config["income_layer_enabled"])
self.assertEqual(runtime.merged_runtime_config["income_layer_start_usd"], 250000.0)
self.assertEqual(runtime.merged_runtime_config["income_layer_max_ratio"], 0.25)

def test_load_strategy_runtime_applies_tech_execution_window_overrides_from_settings(self):
Expand Down
1 change: 1 addition & 0 deletions tests/test_sync_cloud_run_env_workflow.sh
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ grep -Fq 'STRATEGY_PLUGIN_ALERT_TELEGRAM_CHAT_IDS: ${{ vars.STRATEGY_PLUGIN_ALER
grep -Fq 'STRATEGY_PLUGIN_ALERT_TELEGRAM_BOT_TOKEN_SECRET_NAME: ${{ vars.STRATEGY_PLUGIN_ALERT_TELEGRAM_BOT_TOKEN_SECRET_NAME }}' "$workflow_file"
grep -Fq 'INCOME_THRESHOLD_USD: ${{ vars.INCOME_THRESHOLD_USD }}' "$workflow_file"
grep -Fq 'QQQI_INCOME_RATIO: ${{ vars.QQQI_INCOME_RATIO }}' "$workflow_file"
grep -Fq 'INCOME_LAYER_START_USD: ${{ vars.INCOME_LAYER_START_USD }}' "$workflow_file"
grep -Fq 'LONGBRIDGE_DRY_RUN_ONLY: ${{ vars.LONGBRIDGE_DRY_RUN_ONLY }}' "$workflow_file"
grep -Fq 'RUNTIME_TARGET_JSON: ${{ vars.RUNTIME_TARGET_JSON }}' "$workflow_file"
grep -Fq 'ACCOUNT_REGION: ${{ vars.ACCOUNT_REGION || matrix.target.default_account_region }}' "$workflow_file"
Expand Down