From b9d27598cf17f8401bcd36eef01cc6e1be77a98d Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Tue, 12 May 2026 03:54:50 +0800 Subject: [PATCH] Handle missing TQQQ account hash metadata --- .../strategies/tqqq_growth_income.py | 4 ++- tests/test_strategy_plans.py | 36 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/us_equity_strategies/strategies/tqqq_growth_income.py b/src/us_equity_strategies/strategies/tqqq_growth_income.py index 8ced336..7150ca3 100644 --- a/src/us_equity_strategies/strategies/tqqq_growth_income.py +++ b/src/us_equity_strategies/strategies/tqqq_growth_income.py @@ -232,6 +232,8 @@ def build_rebalance_plan( ) sell_order_symbols = ("TQQQ", unlevered_symbol, "SPYI", "QQQI", "BOXX") buy_order_symbols = ("SPYI", "QQQI", "TQQQ", unlevered_symbol) + snapshot_metadata = getattr(snapshot, "metadata", {}) or {} + account_hash = snapshot_metadata.get("account_hash") if isinstance(snapshot_metadata, dict) else None return { "strategy_symbols": strategy_symbols, @@ -240,7 +242,7 @@ def build_rebalance_plan( "buy_order_symbols": buy_order_symbols, "cash_sweep_symbol": "BOXX", "portfolio_rows": (("TQQQ", unlevered_symbol, "BOXX"), ("QQQI", "SPYI")), - "account_hash": snapshot.metadata["account_hash"], + "account_hash": account_hash, "market_values": market_values, "quantities": quantities, "total_equity": total_equity, diff --git a/tests/test_strategy_plans.py b/tests/test_strategy_plans.py index f265b27..32e272e 100644 --- a/tests/test_strategy_plans.py +++ b/tests/test_strategy_plans.py @@ -56,6 +56,7 @@ def test_tqqq_growth_income_exposes_live_dual_drive_metadata(self): self.assertEqual(plan["sell_order_symbols"], ("TQQQ", "QQQ", "SPYI", "QQQI", "BOXX")) self.assertEqual(plan["buy_order_symbols"], ("SPYI", "QQQI", "TQQQ", "QQQ")) self.assertEqual(plan["portfolio_rows"], (("TQQQ", "QQQ", "BOXX"), ("QQQI", "SPYI"))) + self.assertEqual(plan["account_hash"], "acct-1") self.assertEqual(plan["allocation_mode"], "fixed_qqq_tqqq_pullback") self.assertAlmostEqual(plan["target_values"]["TQQQ"], 150000.0 * 0.45) self.assertAlmostEqual(plan["target_values"]["QQQ"], 150000.0 * 0.45) @@ -80,6 +81,41 @@ def test_tqqq_growth_income_exposes_live_dual_drive_metadata(self): self.assertEqual(plan["notification_context"]["portfolio"]["reserved_cash"], 15000.0) self.assertEqual(plan["notification_context"]["portfolio"]["investable_cash"], 5000.0) + def test_tqqq_growth_income_accepts_portfolio_without_account_hash(self): + _skip_if_missing_numeric_stack() + from us_equity_strategies.strategies.tqqq_growth_income import ( + build_rebalance_plan as build_tqqq_plan, + ) + + qqq_history = [ + {"close": 100.0 + index * 0.5, "high": 101.0 + index * 0.5, "low": 99.0 + index * 0.5} + for index in range(260) + ] + snapshot = SimpleNamespace( + positions=[SimpleNamespace(symbol="BOXX", market_value=150000.0, quantity=1000)], + total_equity=150000.0, + buying_power=20000.0, + metadata={"account_ids": ["U18308207"]}, + ) + + plan = build_tqqq_plan( + qqq_history, + snapshot, + signal_text_fn=lambda icon: icon, + translator=_translator, + income_threshold_usd=1_000_000_000.0, + qqqi_income_ratio=0.5, + cash_reserve_ratio=0.03, + rebalance_threshold_ratio=0.01, + dual_drive_qqq_weight=0.45, + dual_drive_tqqq_weight=0.45, + dual_drive_cash_reserve_ratio=0.10, + ) + + self.assertIsNone(plan["account_hash"]) + self.assertEqual(plan["sell_order_symbols"], ("TQQQ", "QQQ", "SPYI", "QQQI", "BOXX")) + self.assertEqual(plan["notification_context"]["portfolio"]["raw_buying_power"], 20000.0) + def test_tqqq_growth_income_can_trade_qqqm_while_using_qqq_signal(self): _skip_if_missing_numeric_stack() from us_equity_strategies.strategies.tqqq_growth_income import (