@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.
- package/.claude-plugin/marketplace.json +15 -4
- package/.claude-plugin/plugin.json +1 -1
- package/VERSION +1 -1
- package/package.json +1 -1
- package/skills/getagent/SKILL.md +169 -6
- package/skills/getagent/examples/btc-ema-cross-demo/backtest.yaml +4 -0
- package/skills/getagent/references/api/confirm.md +59 -0
- package/skills/getagent/references/api/index.md +9 -7
- package/skills/getagent/references/api/publish.md +13 -6
- package/skills/getagent/references/api/run.md +5 -5
- package/skills/getagent/references/api/upload.md +22 -4
- package/skills/getagent/references/backtest-engine.md +82 -152
- package/skills/getagent/references/package-schema.md +31 -8
- package/skills/getagent/references/sdk/backtest/catalog.md +17 -2
- package/skills/getagent/references/sdk/data/arxiv.md +8 -10
- package/skills/getagent/references/sdk/data/catalog.md +3 -8
- package/skills/getagent/references/sdk/data/commodity.md +71 -39
- package/skills/getagent/references/sdk/data/coverage.md +0 -3
- package/skills/getagent/references/sdk/data/crypto.md +1583 -402
- package/skills/getagent/references/sdk/data/currency.md +55 -15
- package/skills/getagent/references/sdk/data/derivatives.md +161 -50
- package/skills/getagent/references/sdk/data/economy.md +911 -259
- package/skills/getagent/references/sdk/data/equity.md +1719 -393
- package/skills/getagent/references/sdk/data/etf.md +550 -64
- package/skills/getagent/references/sdk/data/famafrench.md +38 -50
- package/skills/getagent/references/sdk/data/fixedincome.md +574 -139
- package/skills/getagent/references/sdk/data/imf_utils.md +0 -8
- package/skills/getagent/references/sdk/data/index.md +18 -32
- package/skills/getagent/references/sdk/data/news.md +128 -58
- package/skills/getagent/references/sdk/data/regulators.md +108 -43
- package/skills/getagent/references/sdk/data/sentiment.md +188 -34
- package/skills/getagent/references/sdk/data/uscongress.md +13 -21
- package/skills/getagent/references/sdk/data/web_search.md +3 -7
- package/skills/getagent/references/sdk/data/wikipedia.md +12 -18
- package/skills/getagent/references/sdk.md +14 -5
- package/skills/getagent/scripts/validate.py +109 -3
- package/skills/getagent/scripts/version_check.sh +13 -12
- 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/
|
|
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/
|
|
98
|
-
|
|
99
|
-
2. `[sdk/data/
|
|
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
|
-
|
|
6
|
-
|
|
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
|
-
#
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
36
|
-
| sed -n 's/.*"
|
|
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 "$
|
|
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
|
-
|
|
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: $
|
|
58
|
-
Latest: $
|
|
59
|
-
Update with
|
|
60
|
-
npx
|
|
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
|