Skip to content

Commit a6a725e

Browse files
author
Sai Asish Y
authored
fix(strategy_v2): accept positional key/value in DiscoveryContext.set_metadata (#209)
Signed-off-by: Sai Asish Y <say.apm35@gmail.com>
1 parent 241446e commit a6a725e

3 files changed

Lines changed: 28 additions & 3 deletions

File tree

backend_api_python/app/services/ai_generation_contracts.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
- Read run-supplied values only inside executable handlers or scheduled callbacks with `context.params.get("name", same_default)`. The discovery context used by `initialize(context)` has no `params`; never read `context.params` in `initialize`.
3333
- Parameters may control signal periods, thresholds, target weights, stops, take profit, trailing protection, cooldowns, and bounded layer counts.
3434
- Do not disguise universe, symbol, market type, frequency, leverage permission, initial capital, date range, commission, or slippage as ordinary strategy parameters.
35-
- Use `context.set_metadata(...)` in `initialize` for stable descriptive metadata such as direction mode and strategy family. Metadata is not a substitute for executable risk logic.
35+
- Use `context.set_metadata(...)` in `initialize` for stable descriptive metadata such as direction mode and strategy family, passing keyword arguments such as `context.set_metadata(direction_mode="long_only", strategy_family="trend")`. Metadata is not a substitute for executable risk logic.
3636
3737
## Data and factors
3838
- Historical-bar signatures are exact: `get_history(count, frequency=None, field=None, security_list=None)` and `data.history(symbols, count, fields=None)`.

backend_api_python/app/services/strategy_v2/contract.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import hashlib
77
from dataclasses import dataclass
88
from types import SimpleNamespace
9-
from typing import Any, Callable, Iterable
9+
from typing import Any, Callable, Iterable, Mapping
1010

1111
from app.utils.safe_exec import build_safe_builtins, safe_exec_with_validation
1212
from app.services.factors import FactorError, get_factor
@@ -150,7 +150,15 @@ def allow_leverage(self, max_leverage: object = 1) -> None:
150150
self.leverage_allowed = value > 1.0
151151
self.max_leverage = value
152152

153-
def set_metadata(self, **values: Any) -> None:
153+
def set_metadata(self, *args: Any, **values: Any) -> None:
154+
if len(args) == 1 and isinstance(args[0], Mapping):
155+
self.metadata.update(args[0])
156+
elif len(args) == 2:
157+
self.metadata[str(args[0])] = args[1]
158+
elif args:
159+
raise TypeError(
160+
"set_metadata expects keyword arguments, a single key/value pair, or a mapping"
161+
)
154162
self.metadata.update(values)
155163

156164

backend_api_python/tests/test_strategy_v2_contract.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,23 @@ def handle_data(context, data):
405405
assert manifest.metadata()["directionMode"] == "both"
406406

407407

408+
def test_set_metadata_accepts_positional_key_value_pair():
409+
code = """
410+
def initialize(context):
411+
context.set_universe(["Crypto:BTC/USDT@okx:swap"])
412+
context.subscribe(frequency="1h")
413+
context.set_metadata("direction_mode", "long_only")
414+
context.set_metadata({"strategy_family": "trend"})
415+
416+
def handle_data(context, data):
417+
pass
418+
"""
419+
manifest = compile_strategy_v2(code).manifest
420+
421+
assert manifest.direction_mode == "long_only"
422+
assert manifest.metadata()["directionMode"] == "long_only"
423+
424+
408425
@pytest.mark.parametrize(
409426
"strategy_body,expected",
410427
[

0 commit comments

Comments
 (0)