@bitget-ai/getagent-skill 0.2.1 → 0.2.2
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 +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/VERSION +1 -1
- package/package.json +1 -1
- package/skills/getagent/SKILL.md +29 -1
- package/skills/getagent/examples/btc-ema-cross-demo/backtest.yaml +4 -0
- package/skills/getagent/references/api/publish.md +4 -0
- package/skills/getagent/references/backtest-engine.md +80 -151
- package/skills/getagent/references/package-schema.md +30 -8
- package/skills/getagent/references/sdk/backtest/catalog.md +15 -2
- package/skills/getagent/references/sdk/data/arxiv.md +8 -10
- package/skills/getagent/references/sdk/data/catalog.md +1 -4
- package/skills/getagent/references/sdk/data/commodity.md +25 -39
- package/skills/getagent/references/sdk/data/coverage.md +0 -3
- package/skills/getagent/references/sdk/data/crypto.md +158 -357
- package/skills/getagent/references/sdk/data/currency.md +7 -15
- package/skills/getagent/references/sdk/data/derivatives.md +35 -50
- package/skills/getagent/references/sdk/data/economy.md +175 -259
- package/skills/getagent/references/sdk/data/equity.md +257 -393
- package/skills/getagent/references/sdk/data/etf.md +40 -64
- package/skills/getagent/references/sdk/data/famafrench.md +38 -50
- package/skills/getagent/references/sdk/data/fixedincome.md +89 -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 +52 -58
- package/skills/getagent/references/sdk/data/playbook-supported.md +965 -1600
- package/skills/getagent/references/sdk/data/regulators.md +23 -43
- package/skills/getagent/references/sdk/data/sentiment.md +12 -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/scripts/validate.py +127 -1
|
@@ -14,7 +14,7 @@ are callable through the DataSDK wrapper.
|
|
|
14
14
|
### `wikipedia.content`
|
|
15
15
|
|
|
16
16
|
```python
|
|
17
|
-
data.wikipedia.content(title=None, lang='en', intro_only=False
|
|
17
|
+
data.wikipedia.content(title=None, lang='en', intro_only=False)
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
Summary: Content
|
|
@@ -24,7 +24,6 @@ Summary: Content
|
|
|
24
24
|
| Endpoint ID | `wikipedia.content` |
|
|
25
25
|
| HTTP | `GET` |
|
|
26
26
|
| Path | `/inner/v1/agent-data/wikipedia/content` |
|
|
27
|
-
| Default provider | `wikipedia` |
|
|
28
27
|
| SDK | `supported` |
|
|
29
28
|
| Host | `supported` |
|
|
30
29
|
| Notes | - |
|
|
@@ -33,17 +32,16 @@ Summary: Content
|
|
|
33
32
|
|
|
34
33
|
| Param | Required | Type | Default | Notes |
|
|
35
34
|
|---|---|---|---|---|
|
|
36
|
-
| `title` | `no` | `string | null` | `-` | Wikipedia article title to retrieve.
|
|
37
|
-
| `lang` | `no` | `string` | `en` | Wikipedia language edition.
|
|
38
|
-
| `intro_only` | `no` | `boolean` | `false` | Return only the introductory section instead of the full article.
|
|
39
|
-
| `provider` | `no` | `string` | `wikipedia` | - |
|
|
35
|
+
| `title` | `no` | `string | null` | `-` | Wikipedia article title to retrieve. |
|
|
36
|
+
| `lang` | `no` | `string` | `en` | Wikipedia language edition. |
|
|
37
|
+
| `intro_only` | `no` | `boolean` | `false` | Return only the introductory section instead of the full article. |
|
|
40
38
|
|
|
41
39
|
---
|
|
42
40
|
|
|
43
41
|
### `wikipedia.search`
|
|
44
42
|
|
|
45
43
|
```python
|
|
46
|
-
data.wikipedia.search(query=None, max_results=10, page=1, lang='en'
|
|
44
|
+
data.wikipedia.search(query=None, max_results=10, page=1, lang='en')
|
|
47
45
|
```
|
|
48
46
|
|
|
49
47
|
Summary: Search
|
|
@@ -53,7 +51,6 @@ Summary: Search
|
|
|
53
51
|
| Endpoint ID | `wikipedia.search` |
|
|
54
52
|
| HTTP | `GET` |
|
|
55
53
|
| Path | `/inner/v1/agent-data/wikipedia/search` |
|
|
56
|
-
| Default provider | `wikipedia` |
|
|
57
54
|
| SDK | `supported` |
|
|
58
55
|
| Host | `supported` |
|
|
59
56
|
| Notes | - |
|
|
@@ -62,18 +59,17 @@ Summary: Search
|
|
|
62
59
|
|
|
63
60
|
| Param | Required | Type | Default | Notes |
|
|
64
61
|
|---|---|---|---|---|
|
|
65
|
-
| `query` | `no` | `string | null` | `-` | Search query string.
|
|
66
|
-
| `max_results` | `no` | `integer` | `10` | Maximum number of results.
|
|
67
|
-
| `page` | `no` | `integer` | `1` | Page number, starting at 1.
|
|
68
|
-
| `lang` | `no` | `string` | `en` | Wikipedia language edition.
|
|
69
|
-
| `provider` | `no` | `string` | `wikipedia` | - |
|
|
62
|
+
| `query` | `no` | `string | null` | `-` | Search query string. |
|
|
63
|
+
| `max_results` | `no` | `integer` | `10` | Maximum number of results. |
|
|
64
|
+
| `page` | `no` | `integer` | `1` | Page number, starting at 1. |
|
|
65
|
+
| `lang` | `no` | `string` | `en` | Wikipedia language edition. |
|
|
70
66
|
|
|
71
67
|
---
|
|
72
68
|
|
|
73
69
|
### `wikipedia.summary`
|
|
74
70
|
|
|
75
71
|
```python
|
|
76
|
-
data.wikipedia.summary(title=None, lang='en'
|
|
72
|
+
data.wikipedia.summary(title=None, lang='en')
|
|
77
73
|
```
|
|
78
74
|
|
|
79
75
|
Summary: Summary
|
|
@@ -83,7 +79,6 @@ Summary: Summary
|
|
|
83
79
|
| Endpoint ID | `wikipedia.summary` |
|
|
84
80
|
| HTTP | `GET` |
|
|
85
81
|
| Path | `/inner/v1/agent-data/wikipedia/summary` |
|
|
86
|
-
| Default provider | `wikipedia` |
|
|
87
82
|
| SDK | `supported` |
|
|
88
83
|
| Host | `supported` |
|
|
89
84
|
| Notes | - |
|
|
@@ -92,6 +87,5 @@ Summary: Summary
|
|
|
92
87
|
|
|
93
88
|
| Param | Required | Type | Default | Notes |
|
|
94
89
|
|---|---|---|---|---|
|
|
95
|
-
| `title` | `no` | `string | null` | `-` | Wikipedia article title to retrieve.
|
|
96
|
-
| `lang` | `no` | `string` | `en` | Wikipedia language edition.
|
|
97
|
-
| `provider` | `no` | `string` | `wikipedia` | - |
|
|
90
|
+
| `title` | `no` | `string | null` | `-` | Wikipedia article title to retrieve. |
|
|
91
|
+
| `lang` | `no` | `string` | `en` | Wikipedia language edition. |
|
|
@@ -19,6 +19,7 @@ from __future__ import annotations
|
|
|
19
19
|
import ast
|
|
20
20
|
import re
|
|
21
21
|
import sys
|
|
22
|
+
from datetime import datetime
|
|
22
23
|
from pathlib import Path, PurePosixPath
|
|
23
24
|
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
|
24
25
|
|
|
@@ -52,6 +53,7 @@ MANIFEST_REQUIRED_FIELDS = [
|
|
|
52
53
|
|
|
53
54
|
NAME_PATTERN = re.compile(r"^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$")
|
|
54
55
|
BACKTEST_BAR_FIELD_PATTERN = re.compile(r"^[a-z][a-z0-9_]*$")
|
|
56
|
+
PUBLIC_SYMBOL_TOKEN_PATTERN = re.compile(r"\b[A-Z0-9]{2,20}(?:USDT|USDC|USD|BTC|ETH)\b")
|
|
55
57
|
CRON_EVERY_MINUTES_PATTERN = re.compile(r"^\*/(\d+)$")
|
|
56
58
|
MIN_SCHEDULE_INTERVAL_MINUTES = 10
|
|
57
59
|
DEFAULT_SCHEDULE_TZ = "Asia/Shanghai"
|
|
@@ -375,6 +377,21 @@ def validate_manifest(pkg_dir: Path, result: ValidationResult) -> dict:
|
|
|
375
377
|
symbols = data.get("trading_symbols", [])
|
|
376
378
|
if not isinstance(symbols, list) or not all(isinstance(item, str) and item.strip() for item in symbols):
|
|
377
379
|
result.error("manifest.yaml: 'trading_symbols' must be a non-empty list of strings")
|
|
380
|
+
else:
|
|
381
|
+
normalized_symbols = {str(item).strip().upper() for item in symbols}
|
|
382
|
+
for field in ("display_name", "description"):
|
|
383
|
+
text = str(data.get(field, "") or "")
|
|
384
|
+
unknown_symbols = sorted(
|
|
385
|
+
token
|
|
386
|
+
for token in PUBLIC_SYMBOL_TOKEN_PATTERN.findall(text.upper())
|
|
387
|
+
if token not in normalized_symbols
|
|
388
|
+
)
|
|
389
|
+
if unknown_symbols:
|
|
390
|
+
result.error(
|
|
391
|
+
f"manifest.yaml: '{field}' mentions symbols {unknown_symbols} "
|
|
392
|
+
f"outside trading_symbols {sorted(normalized_symbols)}; "
|
|
393
|
+
"if you corrected a typo or changed the fallback symbol, update all display text"
|
|
394
|
+
)
|
|
378
395
|
|
|
379
396
|
decision_mode = data.get("decision_mode", "")
|
|
380
397
|
if decision_mode and decision_mode not in DECISION_MODES:
|
|
@@ -465,6 +482,24 @@ def validate_backtest_yaml(pkg_dir: Path, manifest: dict, result: ValidationResu
|
|
|
465
482
|
if not str(payload.get(field, "") or "").strip():
|
|
466
483
|
result.error(f"{prefix}: missing '{field}'")
|
|
467
484
|
|
|
485
|
+
def _parse_backtest_datetime(value: object) -> datetime | None:
|
|
486
|
+
text = str(value or "").strip()
|
|
487
|
+
if not text:
|
|
488
|
+
return None
|
|
489
|
+
try:
|
|
490
|
+
return datetime.fromisoformat(text.replace("Z", "+00:00"))
|
|
491
|
+
except ValueError:
|
|
492
|
+
return None
|
|
493
|
+
|
|
494
|
+
def _instrument_symbol(item: dict) -> str:
|
|
495
|
+
raw = str(
|
|
496
|
+
item.get("raw_symbol")
|
|
497
|
+
or item.get("symbol")
|
|
498
|
+
or str(item.get("id", "") or "").split(".", 1)[0]
|
|
499
|
+
or ""
|
|
500
|
+
).strip().upper()
|
|
501
|
+
return raw
|
|
502
|
+
|
|
468
503
|
def _validate_instrument(item: dict, *, prefix: str) -> None:
|
|
469
504
|
kind = str(item.get("kind", "") or "").strip().lower()
|
|
470
505
|
if kind not in BACKTEST_INSTRUMENT_KINDS:
|
|
@@ -513,6 +548,24 @@ def validate_backtest_yaml(pkg_dir: Path, manifest: dict, result: ValidationResu
|
|
|
513
548
|
continue
|
|
514
549
|
seen.add(field)
|
|
515
550
|
|
|
551
|
+
def _validate_no_provider(payload: object, *, prefix: str) -> None:
|
|
552
|
+
if isinstance(payload, dict):
|
|
553
|
+
if "provider" in payload:
|
|
554
|
+
result.error(
|
|
555
|
+
f"{prefix}: 'provider' is not allowed; GetAgent routes historical "
|
|
556
|
+
"data through the managed DataSDK provider"
|
|
557
|
+
)
|
|
558
|
+
for key, value in payload.items():
|
|
559
|
+
_validate_no_provider(value, prefix=f"{prefix}.{key}")
|
|
560
|
+
elif isinstance(payload, list):
|
|
561
|
+
for index, value in enumerate(payload):
|
|
562
|
+
_validate_no_provider(value, prefix=f"{prefix}[{index}]")
|
|
563
|
+
|
|
564
|
+
def _record_backtest_symbol(item: dict, *, prefix: str) -> None:
|
|
565
|
+
symbol = _instrument_symbol(item)
|
|
566
|
+
if symbol:
|
|
567
|
+
backtest_symbols.append((prefix, symbol))
|
|
568
|
+
|
|
516
569
|
path = pkg_dir / "backtest.yaml"
|
|
517
570
|
if not path.exists():
|
|
518
571
|
return
|
|
@@ -522,6 +575,9 @@ def validate_backtest_yaml(pkg_dir: Path, manifest: dict, result: ValidationResu
|
|
|
522
575
|
result.error("backtest.yaml: invalid YAML syntax")
|
|
523
576
|
return
|
|
524
577
|
|
|
578
|
+
_validate_no_provider(data, prefix="backtest.yaml")
|
|
579
|
+
backtest_symbols: list[tuple[str, str]] = []
|
|
580
|
+
|
|
525
581
|
if manifest.get("backtest_support") != "full":
|
|
526
582
|
result.error("backtest.yaml is only allowed when manifest.yaml sets backtest_support: full")
|
|
527
583
|
|
|
@@ -572,6 +628,7 @@ def validate_backtest_yaml(pkg_dir: Path, manifest: dict, result: ValidationResu
|
|
|
572
628
|
result.error("backtest.yaml: use either 'instrument' or 'instruments', not both")
|
|
573
629
|
elif has_single:
|
|
574
630
|
_validate_instrument(data["instrument"], prefix="backtest.yaml.instrument")
|
|
631
|
+
_record_backtest_symbol(data["instrument"], prefix="backtest.yaml.instrument")
|
|
575
632
|
elif has_many:
|
|
576
633
|
instruments = data.get("instruments") or []
|
|
577
634
|
if not instruments:
|
|
@@ -581,18 +638,68 @@ def validate_backtest_yaml(pkg_dir: Path, manifest: dict, result: ValidationResu
|
|
|
581
638
|
result.error(f"backtest.yaml.instruments[{index}] must be a mapping")
|
|
582
639
|
continue
|
|
583
640
|
_validate_instrument(item, prefix=f"backtest.yaml.instruments[{index}]")
|
|
641
|
+
_record_backtest_symbol(item, prefix=f"backtest.yaml.instruments[{index}]")
|
|
584
642
|
else:
|
|
585
643
|
result.error("backtest.yaml: missing 'instrument' or 'instruments'")
|
|
586
644
|
|
|
645
|
+
manifest_symbols = {
|
|
646
|
+
str(item or "").strip().upper()
|
|
647
|
+
for item in manifest.get("trading_symbols", [])
|
|
648
|
+
if str(item or "").strip()
|
|
649
|
+
}
|
|
650
|
+
if manifest_symbols and backtest_symbols:
|
|
651
|
+
actual_symbols = {symbol for _, symbol in backtest_symbols}
|
|
652
|
+
extra_symbols = [
|
|
653
|
+
f"{prefix}={symbol}"
|
|
654
|
+
for prefix, symbol in backtest_symbols
|
|
655
|
+
if symbol not in manifest_symbols
|
|
656
|
+
]
|
|
657
|
+
missing_symbols = sorted(manifest_symbols - actual_symbols)
|
|
658
|
+
if extra_symbols or missing_symbols:
|
|
659
|
+
result.error(
|
|
660
|
+
"backtest.yaml: instruments must match manifest.yaml trading_symbols; "
|
|
661
|
+
f"unexpected backtest symbols={extra_symbols or []}, "
|
|
662
|
+
f"missing manifest symbols={missing_symbols or []}"
|
|
663
|
+
)
|
|
664
|
+
|
|
587
665
|
execution = data.get("execution")
|
|
588
|
-
if
|
|
666
|
+
if not isinstance(execution, dict):
|
|
589
667
|
result.error("backtest.yaml: 'execution' must be a mapping")
|
|
668
|
+
else:
|
|
669
|
+
start = str(execution.get("start") or "").strip()
|
|
670
|
+
end = str(execution.get("end") or "").strip()
|
|
671
|
+
if not start or not end:
|
|
672
|
+
result.error(
|
|
673
|
+
"backtest.yaml.execution: 'start' and 'end' are required so "
|
|
674
|
+
"the sandbox can prove the replay window has bars"
|
|
675
|
+
)
|
|
676
|
+
else:
|
|
677
|
+
parsed_start = _parse_backtest_datetime(start)
|
|
678
|
+
parsed_end = _parse_backtest_datetime(end)
|
|
679
|
+
if parsed_start is None or parsed_end is None:
|
|
680
|
+
result.error("backtest.yaml.execution: 'start' and 'end' must be ISO datetimes")
|
|
681
|
+
elif parsed_end <= parsed_start:
|
|
682
|
+
result.error("backtest.yaml.execution: 'end' must be after 'start'")
|
|
590
683
|
|
|
591
684
|
data_requirements = data.get("data_requirements")
|
|
592
685
|
if data_requirements is not None and not isinstance(data_requirements, dict):
|
|
593
686
|
result.error("backtest.yaml: 'data_requirements' must be a mapping")
|
|
594
687
|
elif isinstance(data_requirements, dict):
|
|
595
688
|
_validate_required_bar_fields(data_requirements.get("required_bar_fields"))
|
|
689
|
+
required_fields = data_requirements.get("required_bar_fields")
|
|
690
|
+
if isinstance(required_fields, list):
|
|
691
|
+
source_text = "\n".join(
|
|
692
|
+
path.read_text(encoding="utf-8", errors="ignore")
|
|
693
|
+
for path in (pkg_dir / "src").rglob("*.py")
|
|
694
|
+
)
|
|
695
|
+
for field in required_fields:
|
|
696
|
+
if isinstance(field, str) and field.strip() and field.strip() not in source_text:
|
|
697
|
+
result.error(
|
|
698
|
+
"backtest.yaml.data_requirements.required_bar_fields: "
|
|
699
|
+
f"declares '{field.strip()}' but src/** never references it; "
|
|
700
|
+
"build the feature column with backtest.build_feature_frame(...) "
|
|
701
|
+
"or remove the declaration"
|
|
702
|
+
)
|
|
596
703
|
|
|
597
704
|
|
|
598
705
|
def _local_import_roots(pkg_dir: Path) -> set[str]:
|
|
@@ -764,6 +871,23 @@ def _check_contract_tpsl_helper_call(tree: ast.AST, *, source_path: str) -> list
|
|
|
764
871
|
return errors
|
|
765
872
|
|
|
766
873
|
|
|
874
|
+
def _check_data_provider_keyword(tree: ast.AST, *, source_path: str) -> list[str]:
|
|
875
|
+
errors: list[str] = []
|
|
876
|
+
for node in ast.walk(tree):
|
|
877
|
+
if not isinstance(node, ast.Call):
|
|
878
|
+
continue
|
|
879
|
+
path = _attribute_path(node.func)
|
|
880
|
+
if not path or "data" not in path:
|
|
881
|
+
continue
|
|
882
|
+
for keyword in node.keywords:
|
|
883
|
+
if keyword.arg == "provider":
|
|
884
|
+
errors.append(
|
|
885
|
+
f"{source_path}: do not pass provider=... to getagent.data calls "
|
|
886
|
+
f"(line {keyword.value.lineno}); the managed DataSDK provider is selected by the platform"
|
|
887
|
+
)
|
|
888
|
+
return errors
|
|
889
|
+
|
|
890
|
+
|
|
767
891
|
def _test_contains_follow_trade_guard(test: ast.AST) -> bool:
|
|
768
892
|
if isinstance(test, ast.Call):
|
|
769
893
|
return _attribute_path(test)[-2:] == ["runtime", "is_follow_trade"]
|
|
@@ -865,6 +989,8 @@ def validate_src_tree(pkg_dir: Path, result: ValidationResult) -> None:
|
|
|
865
989
|
result.error(error)
|
|
866
990
|
for error in _check_contract_tpsl_helper_call(tree, source_path=rel_path):
|
|
867
991
|
result.error(error)
|
|
992
|
+
for error in _check_data_provider_keyword(tree, source_path=rel_path):
|
|
993
|
+
result.error(error)
|
|
868
994
|
for error in _check_live_trade_mutation_guards(tree, source_path=rel_path):
|
|
869
995
|
result.error(error)
|
|
870
996
|
|