|
1 | 1 | from types import SimpleNamespace |
2 | 2 |
|
| 3 | +import pytest |
| 4 | + |
3 | 5 | from application.execution_service import check_order_submitted, execute_rebalance, get_available_buying_power |
4 | 6 | from notifications.telegram import build_translator |
5 | 7 | from quant_platform_kit.common.models import OrderIntent |
@@ -159,6 +161,7 @@ def fake_fetch_quote_snapshots(_ib, symbols): |
159 | 161 | fetch_quote_snapshots=fake_fetch_quote_snapshots, |
160 | 162 | submit_order_intent=fake_submit_order_intent, |
161 | 163 | order_intent_cls=OrderIntent, |
| 164 | + acquire_execution_claim=lambda: True, |
162 | 165 | translator=translate, |
163 | 166 | strategy_symbols=["VOO", "BIL"], |
164 | 167 | strategy_profile="tech_communication_pullback_enhancement", |
@@ -188,6 +191,30 @@ def fake_fetch_quote_snapshots(_ib, symbols): |
188 | 191 | assert any(log.startswith("buy VOO") for log in trade_logs) |
189 | 192 |
|
190 | 193 |
|
| 194 | +@pytest.mark.parametrize("claim", [None, lambda: False]) |
| 195 | +def test_execute_rebalance_cannot_submit_without_successful_claim_callback(tmp_path, claim): |
| 196 | + submitted = [] |
| 197 | + ib = SimpleNamespace( |
| 198 | + openTrades=lambda: [], |
| 199 | + accountValues=lambda: [SimpleNamespace(tag="AvailableFunds", currency="USD", value="1000")], |
| 200 | + ) |
| 201 | + with pytest.raises(RuntimeError, match="execution claim required"): |
| 202 | + execute_rebalance( |
| 203 | + ib, {"VOO": 0.5}, {}, {"equity": 1000.0, "buying_power": 1000.0}, |
| 204 | + fetch_quote_snapshots=lambda _ib, symbols: { |
| 205 | + symbol: SimpleNamespace(last_price=100.0) for symbol in symbols |
| 206 | + }, |
| 207 | + submit_order_intent=lambda _ib, intent: submitted.append(intent), |
| 208 | + order_intent_cls=OrderIntent, translator=translate, |
| 209 | + signal_metadata=_signal_metadata({"VOO": 0.5}, risk_symbols=("VOO",)), |
| 210 | + acquire_execution_claim=claim, dry_run_only=False, |
| 211 | + cash_reserve_ratio=0.0, rebalance_threshold_ratio=0.02, |
| 212 | + limit_buy_premium=1.0, sell_settle_delay_sec=0, |
| 213 | + execution_lock_dir=tmp_path, |
| 214 | + ) |
| 215 | + assert submitted == [] |
| 216 | + |
| 217 | + |
191 | 218 | def test_execute_rebalance_paper_admission_blocks_before_calling_the_broker(tmp_path): |
192 | 219 | class FakeIB: |
193 | 220 | def openTrades(self): |
@@ -273,6 +300,7 @@ def accountValues(self): |
273 | 300 | status="Rejected", |
274 | 301 | ), |
275 | 302 | order_intent_cls=OrderIntent, |
| 303 | + acquire_execution_claim=lambda: True, |
276 | 304 | translator=translate, |
277 | 305 | strategy_symbols=["VOO"], |
278 | 306 | strategy_profile="tech_communication_pullback_enhancement", |
@@ -324,6 +352,7 @@ def fake_submit_order_intent(_ib, intent): |
324 | 352 | }, |
325 | 353 | submit_order_intent=fake_submit_order_intent, |
326 | 354 | order_intent_cls=OrderIntent, |
| 355 | + acquire_execution_claim=lambda: True, |
327 | 356 | translator=translate, |
328 | 357 | strategy_symbols=["SOXL"], |
329 | 358 | strategy_profile="soxl_soxx_trend_income", |
@@ -649,6 +678,7 @@ def fake_submit_order_intent(_ib, intent): |
649 | 678 | }, |
650 | 679 | submit_order_intent=fake_submit_order_intent, |
651 | 680 | order_intent_cls=OrderIntent, |
| 681 | + acquire_execution_claim=lambda: True, |
652 | 682 | translator=build_translator("zh"), |
653 | 683 | strategy_symbols=["SOXL", "SOXX"], |
654 | 684 | strategy_profile="soxl_soxx_trend_income", |
@@ -708,6 +738,7 @@ def fake_submit_order_intent(_ib, intent): |
708 | 738 | }, |
709 | 739 | submit_order_intent=fake_submit_order_intent, |
710 | 740 | order_intent_cls=OrderIntent, |
| 741 | + acquire_execution_claim=lambda: True, |
711 | 742 | translator=build_translator("zh"), |
712 | 743 | strategy_symbols=["SOXL", "SOXX"], |
713 | 744 | strategy_profile="soxl_soxx_trend_income", |
@@ -770,6 +801,7 @@ def fake_submit_order_intent(_ib, intent): |
770 | 801 | }, |
771 | 802 | submit_order_intent=fake_submit_order_intent, |
772 | 803 | order_intent_cls=OrderIntent, |
| 804 | + acquire_execution_claim=lambda: True, |
773 | 805 | translator=build_translator("zh"), |
774 | 806 | strategy_symbols=["SOXL", "SOXX"], |
775 | 807 | strategy_profile="soxl_soxx_trend_income", |
@@ -1000,6 +1032,7 @@ def fake_submit_order_intent(_ib, intent): |
1000 | 1032 | }, |
1001 | 1033 | submit_order_intent=fake_submit_order_intent, |
1002 | 1034 | order_intent_cls=OrderIntent, |
| 1035 | + acquire_execution_claim=lambda: True, |
1003 | 1036 | translator=translate, |
1004 | 1037 | strategy_symbols=["TQQQ"], |
1005 | 1038 | strategy_profile="tqqq_growth_income", |
@@ -1078,6 +1111,7 @@ def fake_submit_order_intent(_ib, intent): |
1078 | 1111 | fetch_quote_snapshots=lambda *_args, **_kwargs: {"VOO": SimpleNamespace(last_price=165.85)}, |
1079 | 1112 | submit_order_intent=fake_submit_order_intent, |
1080 | 1113 | order_intent_cls=OrderIntent, |
| 1114 | + acquire_execution_claim=lambda: True, |
1081 | 1115 | translator=translate, |
1082 | 1116 | strategy_symbols=["VOO"], |
1083 | 1117 | strategy_profile="global_etf_rotation", |
@@ -1129,6 +1163,7 @@ def fake_submit_order_intent(_ib, intent): |
1129 | 1163 | }, |
1130 | 1164 | submit_order_intent=fake_submit_order_intent, |
1131 | 1165 | order_intent_cls=OrderIntent, |
| 1166 | + acquire_execution_claim=lambda: True, |
1132 | 1167 | translator=translate, |
1133 | 1168 | strategy_symbols=["TQQQ", "QQQM"], |
1134 | 1169 | strategy_profile="tqqq_growth_income", |
@@ -1457,6 +1492,7 @@ def fake_fetch_quote_snapshots(_ib, symbols): |
1457 | 1492 | fetch_quote_snapshots=fake_fetch_quote_snapshots, |
1458 | 1493 | submit_order_intent=lambda *_args, **_kwargs: SimpleNamespace(broker_order_id="1", status="Submitted"), |
1459 | 1494 | order_intent_cls=OrderIntent, |
| 1495 | + acquire_execution_claim=lambda: True, |
1460 | 1496 | translator=translate, |
1461 | 1497 | strategy_symbols=["VOO", "BOXX"], |
1462 | 1498 | strategy_profile="tech_communication_pullback_enhancement", |
@@ -1576,6 +1612,7 @@ def fake_fetch_quote_snapshots(_ib, symbols): |
1576 | 1612 | fetch_quote_snapshots=fake_fetch_quote_snapshots, |
1577 | 1613 | submit_order_intent=lambda *_args, **_kwargs: SimpleNamespace(broker_order_id="1", status="Submitted"), |
1578 | 1614 | order_intent_cls=OrderIntent, |
| 1615 | + acquire_execution_claim=lambda: True, |
1579 | 1616 | translator=translate, |
1580 | 1617 | strategy_symbols=["VOO", "BOXX"], |
1581 | 1618 | strategy_profile="tech_communication_pullback_enhancement", |
@@ -1705,6 +1742,7 @@ def fake_submit_order_intent(_ib, intent): |
1705 | 1742 | }, |
1706 | 1743 | submit_order_intent=fake_submit_order_intent, |
1707 | 1744 | order_intent_cls=OrderIntent, |
| 1745 | + acquire_execution_claim=lambda: True, |
1708 | 1746 | translator=translate, |
1709 | 1747 | strategy_symbols=["VOO", "BOXX"], |
1710 | 1748 | strategy_profile="tech_communication_pullback_enhancement", |
@@ -1901,6 +1939,7 @@ def fake_submit_order_intent(_ib, intent): |
1901 | 1939 | fetch_quote_snapshots=lambda *_args, **_kwargs: {}, |
1902 | 1940 | submit_order_intent=fake_submit_order_intent, |
1903 | 1941 | order_intent_cls=OrderIntent, |
| 1942 | + acquire_execution_claim=lambda: True, |
1904 | 1943 | translator=translate, |
1905 | 1944 | strategy_symbols=["VOO"], |
1906 | 1945 | strategy_profile="tech_communication_pullback_enhancement", |
|
0 commit comments