Skip to content

Commit f4496da

Browse files
committed
Add allocation intent metadata for IBKR
1 parent 48932a9 commit f4496da

3 files changed

Lines changed: 34 additions & 4 deletions

File tree

decision_mapper.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
from collections.abc import Mapping
44
from typing import Any
55

6-
from quant_platform_kit.strategy_contracts import StrategyDecision
6+
from quant_platform_kit.strategy_contracts import (
7+
StrategyDecision,
8+
build_allocation_intent,
9+
build_allocation_payload,
10+
)
711

812

913
_EMERGENCY_FLAGS = frozenset({"emergency", "hard_defense"})
@@ -25,10 +29,15 @@ def _derive_target_weights(decision: StrategyDecision) -> dict[str, float]:
2529
def _derive_managed_symbols(
2630
decision: StrategyDecision,
2731
runtime_metadata: Mapping[str, Any],
32+
*,
33+
allocation_payload: Mapping[str, Any] | None = None,
2834
) -> tuple[str, ...]:
2935
explicit = runtime_metadata.get("managed_symbols")
3036
if explicit:
3137
return tuple(str(symbol) for symbol in explicit)
38+
allocation_symbols = allocation_payload.get("strategy_symbols") if allocation_payload else None
39+
if allocation_symbols:
40+
return tuple(str(symbol) for symbol in allocation_symbols)
3241
return tuple(position.symbol for position in decision.positions)
3342

3443

@@ -93,18 +102,33 @@ def map_strategy_decision(
93102
risk_flags = tuple(str(flag) for flag in decision.risk_flags)
94103
no_execute = bool(_NO_EXECUTE_FLAGS & set(risk_flags))
95104
target_weights = None if no_execute else _derive_target_weights(decision)
105+
allocation_payload = None
106+
if not no_execute and decision.positions:
107+
allocation_payload = build_allocation_payload(
108+
build_allocation_intent(
109+
decision,
110+
strategy_profile=strategy_profile,
111+
strategy_symbols_order="risk_safe_income",
112+
)
113+
)
96114
signal_desc = _derive_signal_description(decision, runtime_metadata)
97115
status_desc = _derive_status_description(decision, runtime_metadata)
98116
is_emergency = bool(_EMERGENCY_FLAGS & set(risk_flags))
99117

100118
metadata: dict[str, Any] = {**runtime_metadata, **diagnostics}
101119
metadata.setdefault("strategy_profile", strategy_profile)
102120
metadata.setdefault("status_icon", "🐤")
103-
metadata.setdefault("managed_symbols", _derive_managed_symbols(decision, runtime_metadata))
121+
metadata.setdefault(
122+
"managed_symbols",
123+
_derive_managed_symbols(decision, runtime_metadata, allocation_payload=allocation_payload),
124+
)
104125
safe_haven_symbol = _derive_safe_haven_symbol(decision, runtime_metadata)
105126
if safe_haven_symbol:
106127
metadata.setdefault("safe_haven_symbol", safe_haven_symbol)
107128
metadata.setdefault("risk_flags", risk_flags)
108129
metadata.setdefault("actionable", not no_execute)
130+
if allocation_payload:
131+
metadata.setdefault("target_mode", allocation_payload["target_mode"])
132+
metadata.setdefault("allocation", allocation_payload)
109133

110134
return target_weights, signal_desc, is_emergency, status_desc, metadata

requirements.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
flask
22
gunicorn
3-
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@v0.7.5 # quote fallback hotfix
4-
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@v0.7.5
3+
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@v0.7.7
4+
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@v0.7.7
55
pandas
66
numpy
77
requests

tests/test_decision_mapper.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ def test_map_strategy_decision_maps_weight_positions_and_safe_haven():
2828
assert metadata["safe_haven_symbol"] == "BOXX"
2929
assert metadata["managed_symbols"] == ("AAA", "BOXX")
3030
assert metadata["status_icon"] == "🧲"
31+
assert metadata["target_mode"] == "weight"
32+
assert metadata["allocation"]["target_mode"] == "weight"
33+
assert metadata["allocation"]["strategy_symbols"] == ("AAA", "BOXX")
34+
assert metadata["allocation"]["targets"] == {"AAA": 0.6, "BOXX": 0.4}
35+
assert metadata["allocation"]["positions"][1]["role"] == "safe_haven"
3136

3237

3338
def test_map_strategy_decision_returns_noop_when_flagged_no_execute():
@@ -55,3 +60,4 @@ def test_map_strategy_decision_returns_noop_when_flagged_no_execute():
5560
assert status_desc == "fail_closed | reason=feature_snapshot_path_missing"
5661
assert metadata["actionable"] is False
5762
assert metadata["snapshot_guard_decision"] == "fail_closed"
63+
assert "allocation" not in metadata

0 commit comments

Comments
 (0)