@bitget-ai/getagent-skill 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/.claude-plugin/marketplace.json +15 -4
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/VERSION +1 -1
  4. package/package.json +1 -1
  5. package/skills/getagent/SKILL.md +169 -6
  6. package/skills/getagent/examples/btc-ema-cross-demo/backtest.yaml +4 -0
  7. package/skills/getagent/references/api/confirm.md +59 -0
  8. package/skills/getagent/references/api/index.md +9 -7
  9. package/skills/getagent/references/api/publish.md +13 -6
  10. package/skills/getagent/references/api/run.md +5 -5
  11. package/skills/getagent/references/api/upload.md +22 -4
  12. package/skills/getagent/references/backtest-engine.md +82 -152
  13. package/skills/getagent/references/package-schema.md +31 -8
  14. package/skills/getagent/references/sdk/backtest/catalog.md +17 -2
  15. package/skills/getagent/references/sdk/data/arxiv.md +8 -10
  16. package/skills/getagent/references/sdk/data/catalog.md +3 -8
  17. package/skills/getagent/references/sdk/data/commodity.md +71 -39
  18. package/skills/getagent/references/sdk/data/coverage.md +0 -3
  19. package/skills/getagent/references/sdk/data/crypto.md +1583 -402
  20. package/skills/getagent/references/sdk/data/currency.md +55 -15
  21. package/skills/getagent/references/sdk/data/derivatives.md +161 -50
  22. package/skills/getagent/references/sdk/data/economy.md +911 -259
  23. package/skills/getagent/references/sdk/data/equity.md +1719 -393
  24. package/skills/getagent/references/sdk/data/etf.md +550 -64
  25. package/skills/getagent/references/sdk/data/famafrench.md +38 -50
  26. package/skills/getagent/references/sdk/data/fixedincome.md +574 -139
  27. package/skills/getagent/references/sdk/data/imf_utils.md +0 -8
  28. package/skills/getagent/references/sdk/data/index.md +18 -32
  29. package/skills/getagent/references/sdk/data/news.md +128 -58
  30. package/skills/getagent/references/sdk/data/regulators.md +108 -43
  31. package/skills/getagent/references/sdk/data/sentiment.md +188 -34
  32. package/skills/getagent/references/sdk/data/uscongress.md +13 -21
  33. package/skills/getagent/references/sdk/data/web_search.md +3 -7
  34. package/skills/getagent/references/sdk/data/wikipedia.md +12 -18
  35. package/skills/getagent/references/sdk.md +14 -5
  36. package/skills/getagent/scripts/validate.py +109 -3
  37. package/skills/getagent/scripts/version_check.sh +13 -12
  38. package/skills/getagent/references/sdk/data/playbook-supported.md +0 -9871
@@ -13,7 +13,17 @@ This folder separates:
13
13
  1. Start with `[package-schema.md](package-schema.md)` to understand the package
14
14
  contract.
15
15
  2. Read the relevant SDK section below:
16
- - market data → `[sdk/data/playbook-supported.md](sdk/data/playbook-supported.md)`
16
+ - market data → `[sdk/data/catalog.md](sdk/data/catalog.md)` (domain index) →
17
+ domain files: `[crypto.md](sdk/data/crypto.md)`, `[equity.md](sdk/data/equity.md)`,
18
+ `[economy.md](sdk/data/economy.md)`, `[derivatives.md](sdk/data/derivatives.md)`,
19
+ `[etf.md](sdk/data/etf.md)`, `[fixedincome.md](sdk/data/fixedincome.md)`,
20
+ `[currency.md](sdk/data/currency.md)`, `[commodity.md](sdk/data/commodity.md)`,
21
+ `[sentiment.md](sdk/data/sentiment.md)`, `[index.md](sdk/data/index.md)`,
22
+ `[news.md](sdk/data/news.md)`, `[regulators.md](sdk/data/regulators.md)`,
23
+ `[uscongress.md](sdk/data/uscongress.md)`, `[arxiv.md](sdk/data/arxiv.md)`,
24
+ `[web_search.md](sdk/data/web_search.md)`, `[wikipedia.md](sdk/data/wikipedia.md)`,
25
+ `[famafrench.md](sdk/data/famafrench.md)`, `[imf_utils.md](sdk/data/imf_utils.md)`,
26
+ `[coverage.md](sdk/data/coverage.md)`
17
27
  - replay / charting → `[sdk/backtest/catalog.md](sdk/backtest/catalog.md)`
18
28
  - trading → `[sdk/trade/patterns.md](sdk/trade/patterns.md)`
19
29
  - runtime protocol / signal output → `[sdk/runtime/catalog.md](sdk/runtime/catalog.md)`
@@ -94,10 +104,9 @@ The only supported market data module.
94
104
 
95
105
  Read order:
96
106
 
97
- 1. `[sdk/data/playbook-supported.md](sdk/data/playbook-supported.md)` —
98
- full Playbook-callable `getagent.data` endpoint surface
99
- 2. `[sdk/data/catalog.md](sdk/data/catalog.md)` — full domain index
100
- 3. domain files under `[sdk/data/](sdk/data/catalog.md)` — exact signatures,
107
+ 1. `[sdk/data/catalog.md](sdk/data/catalog.md)` — full domain index with all
108
+ Playbook-callable `getagent.data` endpoints organised by domain
109
+ 2. domain files under `[sdk/data/](sdk/data/catalog.md)` — exact signatures,
101
110
  defaults, enums, and parameter notes
102
111
 
103
112
  Hard rules:
@@ -2,8 +2,10 @@
2
2
  """Playbook package Local validation script。
3
3
 
4
4
  Usage:
5
- conda activate get_agent_test
6
- python scripts/validate.py ./my-strategy/
5
+ python3 scripts/validate.py ./my-strategy/
6
+
7
+ Requires Python 3.11+. PyYAML is recommended (`pip install pyyaml`);
8
+ without it the script falls back to a weaker built-in parser.
7
9
 
8
10
  Checks:
9
11
  1. Directory structure complete(manifest.yaml, src/main.py)
@@ -52,6 +54,7 @@ MANIFEST_REQUIRED_FIELDS = [
52
54
 
53
55
  NAME_PATTERN = re.compile(r"^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$")
54
56
  BACKTEST_BAR_FIELD_PATTERN = re.compile(r"^[a-z][a-z0-9_]*$")
57
+ PUBLIC_SYMBOL_TOKEN_PATTERN = re.compile(r"\b[A-Z0-9]{2,20}(?:USDT|USDC|USD|BTC|ETH)\b")
55
58
  CRON_EVERY_MINUTES_PATTERN = re.compile(r"^\*/(\d+)$")
56
59
  MIN_SCHEDULE_INTERVAL_MINUTES = 10
57
60
  DEFAULT_SCHEDULE_TZ = "Asia/Shanghai"
@@ -375,6 +378,21 @@ def validate_manifest(pkg_dir: Path, result: ValidationResult) -> dict:
375
378
  symbols = data.get("trading_symbols", [])
376
379
  if not isinstance(symbols, list) or not all(isinstance(item, str) and item.strip() for item in symbols):
377
380
  result.error("manifest.yaml: 'trading_symbols' must be a non-empty list of strings")
381
+ else:
382
+ normalized_symbols = {str(item).strip().upper() for item in symbols}
383
+ for field in ("display_name", "description"):
384
+ text = str(data.get(field, "") or "")
385
+ unknown_symbols = sorted(
386
+ token
387
+ for token in PUBLIC_SYMBOL_TOKEN_PATTERN.findall(text.upper())
388
+ if token not in normalized_symbols
389
+ )
390
+ if unknown_symbols:
391
+ result.error(
392
+ f"manifest.yaml: '{field}' mentions symbols {unknown_symbols} "
393
+ f"outside trading_symbols {sorted(normalized_symbols)}; "
394
+ "if you corrected a typo or changed the fallback symbol, update all display text"
395
+ )
378
396
 
379
397
  decision_mode = data.get("decision_mode", "")
380
398
  if decision_mode and decision_mode not in DECISION_MODES:
@@ -465,6 +483,15 @@ def validate_backtest_yaml(pkg_dir: Path, manifest: dict, result: ValidationResu
465
483
  if not str(payload.get(field, "") or "").strip():
466
484
  result.error(f"{prefix}: missing '{field}'")
467
485
 
486
+ def _instrument_symbol(item: dict) -> str:
487
+ raw = str(
488
+ item.get("raw_symbol")
489
+ or item.get("symbol")
490
+ or str(item.get("id", "") or "").split(".", 1)[0]
491
+ or ""
492
+ ).strip().upper()
493
+ return raw
494
+
468
495
  def _validate_instrument(item: dict, *, prefix: str) -> None:
469
496
  kind = str(item.get("kind", "") or "").strip().lower()
470
497
  if kind not in BACKTEST_INSTRUMENT_KINDS:
@@ -513,6 +540,24 @@ def validate_backtest_yaml(pkg_dir: Path, manifest: dict, result: ValidationResu
513
540
  continue
514
541
  seen.add(field)
515
542
 
543
+ def _validate_no_provider(payload: object, *, prefix: str) -> None:
544
+ if isinstance(payload, dict):
545
+ if "provider" in payload:
546
+ result.error(
547
+ f"{prefix}: 'provider' is not allowed; GetAgent routes historical "
548
+ "data through the managed DataSDK provider"
549
+ )
550
+ for key, value in payload.items():
551
+ _validate_no_provider(value, prefix=f"{prefix}.{key}")
552
+ elif isinstance(payload, list):
553
+ for index, value in enumerate(payload):
554
+ _validate_no_provider(value, prefix=f"{prefix}[{index}]")
555
+
556
+ def _record_backtest_symbol(item: dict, *, prefix: str) -> None:
557
+ symbol = _instrument_symbol(item)
558
+ if symbol:
559
+ backtest_symbols.append((prefix, symbol))
560
+
516
561
  path = pkg_dir / "backtest.yaml"
517
562
  if not path.exists():
518
563
  return
@@ -522,6 +567,9 @@ def validate_backtest_yaml(pkg_dir: Path, manifest: dict, result: ValidationResu
522
567
  result.error("backtest.yaml: invalid YAML syntax")
523
568
  return
524
569
 
570
+ _validate_no_provider(data, prefix="backtest.yaml")
571
+ backtest_symbols: list[tuple[str, str]] = []
572
+
525
573
  if manifest.get("backtest_support") != "full":
526
574
  result.error("backtest.yaml is only allowed when manifest.yaml sets backtest_support: full")
527
575
 
@@ -572,6 +620,7 @@ def validate_backtest_yaml(pkg_dir: Path, manifest: dict, result: ValidationResu
572
620
  result.error("backtest.yaml: use either 'instrument' or 'instruments', not both")
573
621
  elif has_single:
574
622
  _validate_instrument(data["instrument"], prefix="backtest.yaml.instrument")
623
+ _record_backtest_symbol(data["instrument"], prefix="backtest.yaml.instrument")
575
624
  elif has_many:
576
625
  instruments = data.get("instruments") or []
577
626
  if not instruments:
@@ -581,18 +630,56 @@ def validate_backtest_yaml(pkg_dir: Path, manifest: dict, result: ValidationResu
581
630
  result.error(f"backtest.yaml.instruments[{index}] must be a mapping")
582
631
  continue
583
632
  _validate_instrument(item, prefix=f"backtest.yaml.instruments[{index}]")
633
+ _record_backtest_symbol(item, prefix=f"backtest.yaml.instruments[{index}]")
584
634
  else:
585
635
  result.error("backtest.yaml: missing 'instrument' or 'instruments'")
586
636
 
637
+ manifest_symbols = {
638
+ str(item or "").strip().upper()
639
+ for item in manifest.get("trading_symbols", [])
640
+ if str(item or "").strip()
641
+ }
642
+ if manifest_symbols and backtest_symbols:
643
+ actual_symbols = {symbol for _, symbol in backtest_symbols}
644
+ extra_symbols = [
645
+ f"{prefix}={symbol}"
646
+ for prefix, symbol in backtest_symbols
647
+ if symbol not in manifest_symbols
648
+ ]
649
+ missing_symbols = sorted(manifest_symbols - actual_symbols)
650
+ if extra_symbols or missing_symbols:
651
+ result.error(
652
+ "backtest.yaml: instruments must match manifest.yaml trading_symbols; "
653
+ f"unexpected backtest symbols={extra_symbols or []}, "
654
+ f"missing manifest symbols={missing_symbols or []}"
655
+ )
656
+
657
+ # The replay window is the author's call: execution.start/end are optional
658
+ # bar filters. The platform never polices window presence, length, recency,
659
+ # or ordering — anti-fraud checks (real evidence) are the only backtest gate.
587
660
  execution = data.get("execution")
588
661
  if execution is not None and not isinstance(execution, dict):
589
- result.error("backtest.yaml: 'execution' must be a mapping")
662
+ result.error("backtest.yaml: 'execution' must be a mapping when provided")
590
663
 
591
664
  data_requirements = data.get("data_requirements")
592
665
  if data_requirements is not None and not isinstance(data_requirements, dict):
593
666
  result.error("backtest.yaml: 'data_requirements' must be a mapping")
594
667
  elif isinstance(data_requirements, dict):
595
668
  _validate_required_bar_fields(data_requirements.get("required_bar_fields"))
669
+ required_fields = data_requirements.get("required_bar_fields")
670
+ if isinstance(required_fields, list):
671
+ source_text = "\n".join(
672
+ path.read_text(encoding="utf-8", errors="ignore")
673
+ for path in (pkg_dir / "src").rglob("*.py")
674
+ )
675
+ for field in required_fields:
676
+ if isinstance(field, str) and field.strip() and field.strip() not in source_text:
677
+ result.error(
678
+ "backtest.yaml.data_requirements.required_bar_fields: "
679
+ f"declares '{field.strip()}' but src/** never references it; "
680
+ "build the feature column with backtest.build_feature_frame(...) "
681
+ "or remove the declaration"
682
+ )
596
683
 
597
684
 
598
685
  def _local_import_roots(pkg_dir: Path) -> set[str]:
@@ -764,6 +851,23 @@ def _check_contract_tpsl_helper_call(tree: ast.AST, *, source_path: str) -> list
764
851
  return errors
765
852
 
766
853
 
854
+ def _check_data_provider_keyword(tree: ast.AST, *, source_path: str) -> list[str]:
855
+ errors: list[str] = []
856
+ for node in ast.walk(tree):
857
+ if not isinstance(node, ast.Call):
858
+ continue
859
+ path = _attribute_path(node.func)
860
+ if not path or "data" not in path:
861
+ continue
862
+ for keyword in node.keywords:
863
+ if keyword.arg == "provider":
864
+ errors.append(
865
+ f"{source_path}: do not pass provider=... to getagent.data calls "
866
+ f"(line {keyword.value.lineno}); the managed DataSDK provider is selected by the platform"
867
+ )
868
+ return errors
869
+
870
+
767
871
  def _test_contains_follow_trade_guard(test: ast.AST) -> bool:
768
872
  if isinstance(test, ast.Call):
769
873
  return _attribute_path(test)[-2:] == ["runtime", "is_follow_trade"]
@@ -865,6 +969,8 @@ def validate_src_tree(pkg_dir: Path, result: ValidationResult) -> None:
865
969
  result.error(error)
866
970
  for error in _check_contract_tpsl_helper_call(tree, source_path=rel_path):
867
971
  result.error(error)
972
+ for error in _check_data_provider_keyword(tree, source_path=rel_path):
973
+ result.error(error)
868
974
  for error in _check_live_trade_mutation_guards(tree, source_path=rel_path):
869
975
  result.error(error)
870
976
 
@@ -8,9 +8,10 @@ SKILL_MD="$SKILL_DIR/SKILL.md"
8
8
  CONFIG_FILE="$SKILL_DIR/.env"
9
9
  CHECK_INTERVAL=28800
10
10
 
11
- # GitLab project path URL-encoded: algorithm/upex-algorithm-getall-skill-sdk
12
- PROJECT_PATH="algorithm%2Fupex-algorithm-getall-skill-sdk"
13
- RELEASES_URL="https://gitlab.bitget.tools/api/v4/projects/${PROJECT_PATH}/releases"
11
+ # Published versions live on the public npm registry; this works for both
12
+ # internal and external installs. Silently skip when offline.
13
+ NPM_PACKAGE="@bitget-ai/getagent-skill"
14
+ DIST_TAGS_URL="https://registry.npmjs.org/-/package/${NPM_PACKAGE}/dist-tags"
14
15
 
15
16
  read_local_version() {
16
17
  if [ ! -f "$SKILL_MD" ]; then
@@ -32,11 +33,11 @@ if [ "$elapsed" -lt "$CHECK_INTERVAL" ]; then
32
33
  exit 0
33
34
  fi
34
35
 
35
- remote_tag=$(curl -sf --max-time 5 "$RELEASES_URL" \
36
- | sed -n 's/.*"tag_name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' \
36
+ remote_ver=$(curl -sf --max-time 5 "$DIST_TAGS_URL" \
37
+ | sed -n 's/.*"latest"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' \
37
38
  | head -1 || true)
38
39
 
39
- if [ -z "$remote_tag" ]; then
40
+ if [ -z "$remote_ver" ]; then
40
41
  exit 0
41
42
  fi
42
43
 
@@ -48,15 +49,15 @@ echo "last_check=$now" >> "$tmp_config"
48
49
  mv "$tmp_config" "$CONFIG_FILE"
49
50
 
50
51
  local_tag=$(read_local_version)
51
- if [ -z "$local_tag" ] || [ "$local_tag" = "$remote_tag" ]; then
52
+ local_ver=${local_tag#v}
53
+ if [ -z "$local_ver" ] || [ "$local_ver" = "$remote_ver" ]; then
52
54
  exit 0
53
55
  fi
54
56
 
55
57
  cat <<EOF
56
58
  GetAgent skill update available.
57
- Installed: $local_tag
58
- Latest: $remote_tag
59
- Update with one of:
60
- npx skills add https://gitlab.bitget.tools/algorithm/upex-algorithm-getall-skill-sdk/-/tree/${remote_tag}/skills/getagent --skill getagent -y
61
- git clone --branch ${remote_tag} --depth 1 git@gitlab.bitget.tools:algorithm/upex-algorithm-getall-skill-sdk.git ./.tmp/getagent-skill && cp -R ./.tmp/getagent-skill/skills/getagent/. "${SKILL_DIR}/" && rm -rf ./.tmp/getagent-skill
59
+ Installed: v$local_ver
60
+ Latest: v$remote_ver
61
+ Update with:
62
+ npx ${NPM_PACKAGE}@latest install --client <claude|cursor|codex|all>
62
63
  EOF