@bitget-ai/getagent-skill 0.2.1

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 (57) hide show
  1. package/.claude-plugin/marketplace.json +28 -0
  2. package/.claude-plugin/plugin.json +12 -0
  3. package/README.md +99 -0
  4. package/VERSION +1 -0
  5. package/bin/getagent-skill.js +140 -0
  6. package/package.json +45 -0
  7. package/skills/getagent/SKILL.md +129 -0
  8. package/skills/getagent/examples/btc-ema-cross-demo/README.md +61 -0
  9. package/skills/getagent/examples/btc-ema-cross-demo/backtest.yaml +33 -0
  10. package/skills/getagent/examples/btc-ema-cross-demo/manifest.yaml +94 -0
  11. package/skills/getagent/examples/btc-ema-cross-demo/src/main.py +88 -0
  12. package/skills/getagent/examples/btc-ema-cross-demo/src/strategy.py +118 -0
  13. package/skills/getagent/references/api/enable.md +95 -0
  14. package/skills/getagent/references/api/error-responses.md +77 -0
  15. package/skills/getagent/references/api/index.md +38 -0
  16. package/skills/getagent/references/api/list.md +80 -0
  17. package/skills/getagent/references/api/my-playbooks.md +41 -0
  18. package/skills/getagent/references/api/publish.md +76 -0
  19. package/skills/getagent/references/api/run.md +149 -0
  20. package/skills/getagent/references/api/upload.md +76 -0
  21. package/skills/getagent/references/backtest-engine.md +438 -0
  22. package/skills/getagent/references/package-schema.md +552 -0
  23. package/skills/getagent/references/sandbox-runtime.md +201 -0
  24. package/skills/getagent/references/sdk/backtest/catalog.md +208 -0
  25. package/skills/getagent/references/sdk/data/arxiv.md +41 -0
  26. package/skills/getagent/references/sdk/data/catalog.md +56 -0
  27. package/skills/getagent/references/sdk/data/commodity.md +226 -0
  28. package/skills/getagent/references/sdk/data/coverage.md +82 -0
  29. package/skills/getagent/references/sdk/data/crypto.md +2906 -0
  30. package/skills/getagent/references/sdk/data/currency.md +123 -0
  31. package/skills/getagent/references/sdk/data/derivatives.md +269 -0
  32. package/skills/getagent/references/sdk/data/economy.md +1348 -0
  33. package/skills/getagent/references/sdk/data/equity.md +2120 -0
  34. package/skills/getagent/references/sdk/data/etf.md +372 -0
  35. package/skills/getagent/references/sdk/data/famafrench.md +201 -0
  36. package/skills/getagent/references/sdk/data/fixedincome.md +804 -0
  37. package/skills/getagent/references/sdk/data/imf_utils.md +225 -0
  38. package/skills/getagent/references/sdk/data/index.md +216 -0
  39. package/skills/getagent/references/sdk/data/news.md +149 -0
  40. package/skills/getagent/references/sdk/data/playbook-supported.md +9871 -0
  41. package/skills/getagent/references/sdk/data/regulators.md +299 -0
  42. package/skills/getagent/references/sdk/data/sentiment.md +323 -0
  43. package/skills/getagent/references/sdk/data/uscongress.md +126 -0
  44. package/skills/getagent/references/sdk/data/web_search.md +68 -0
  45. package/skills/getagent/references/sdk/data/wikipedia.md +97 -0
  46. package/skills/getagent/references/sdk/llm/catalog.md +117 -0
  47. package/skills/getagent/references/sdk/runtime/catalog.md +195 -0
  48. package/skills/getagent/references/sdk/trade/account.md +61 -0
  49. package/skills/getagent/references/sdk/trade/catalog.md +35 -0
  50. package/skills/getagent/references/sdk/trade/contract.md +331 -0
  51. package/skills/getagent/references/sdk/trade/helpers.md +466 -0
  52. package/skills/getagent/references/sdk/trade/market.md +28 -0
  53. package/skills/getagent/references/sdk/trade/patterns.md +102 -0
  54. package/skills/getagent/references/sdk/trade/spot.md +165 -0
  55. package/skills/getagent/references/sdk.md +198 -0
  56. package/skills/getagent/scripts/validate.py +965 -0
  57. package/skills/getagent/scripts/version_check.sh +62 -0
@@ -0,0 +1,88 @@
1
+ """Entry point for the BTC EMA Crossover Demo Playbook.
2
+
3
+ For backtest_support: full playbooks, the platform injects
4
+ runtime.evaluation_mode="historical" on /api/v1/playbook/run. We only handle
5
+ that path here; live cron is not configured for this demo.
6
+ """
7
+ import math
8
+ from typing import Any
9
+
10
+ from getagent import backtest, data, runtime
11
+
12
+
13
+ def _sanitize(value: Any) -> Any:
14
+ if isinstance(value, float) and not math.isfinite(value):
15
+ return None
16
+ return value
17
+
18
+
19
+ def _sanitize_metrics(metrics: dict[str, Any]) -> dict[str, Any]:
20
+ return {key: _sanitize(val) for key, val in metrics.items()}
21
+
22
+
23
+ def run() -> None:
24
+ cfg = runtime.manifest.get("strategy_config", {}) or {}
25
+ symbols = cfg.get("trading_symbols") or ["BTCUSDT"]
26
+ symbol = symbols[0]
27
+
28
+ bars = data.crypto.futures.kline(
29
+ symbol=symbol,
30
+ interval="1h",
31
+ limit=1000,
32
+ )
33
+ replay_frame = backtest.prepare_frame(bars, datetime_index="date")
34
+
35
+ if replay_frame.empty:
36
+ runtime.emit_signal(
37
+ action="watch",
38
+ symbol=symbol,
39
+ confidence=0.0,
40
+ metrics={"rows": 0},
41
+ meta={"reason": "no historical bars returned"},
42
+ )
43
+ return
44
+
45
+ instrument_key = f"{symbol}.BINANCE"
46
+ result = backtest.run(
47
+ ohlcv_data={instrument_key: replay_frame},
48
+ spec=runtime.backtest_spec,
49
+ )
50
+
51
+ chart_path = backtest.generate_chart(result)
52
+ summary = result.summary or {}
53
+ net_pnl_raw = summary.get("net_pnl", 0)
54
+ try:
55
+ net_pnl = float(net_pnl_raw or 0)
56
+ except (TypeError, ValueError):
57
+ net_pnl = 0.0
58
+
59
+ action = "long" if net_pnl > 0 else "watch"
60
+ metrics = _sanitize_metrics(
61
+ {
62
+ "total_return_pct": result.total_return_pct,
63
+ "net_pnl": net_pnl,
64
+ "starting_balance": summary.get("starting_balance"),
65
+ "sharpe_ratio": result.sharpe_ratio,
66
+ "max_drawdown_pct": result.max_drawdown_pct,
67
+ "win_rate": result.win_rate,
68
+ "total_trades": result.total_trades,
69
+ "profit_factor": result.profit_factor,
70
+ "rows": len(replay_frame),
71
+ }
72
+ )
73
+
74
+ runtime.emit_signal(
75
+ action=action,
76
+ symbol=symbol,
77
+ confidence=_sanitize(result.win_rate) or 0.0,
78
+ metrics=metrics,
79
+ meta={
80
+ "chart_path": chart_path,
81
+ "fast_period": cfg.get("fast_period"),
82
+ "slow_period": cfg.get("slow_period"),
83
+ },
84
+ )
85
+
86
+
87
+ if __name__ == "__main__":
88
+ run()
@@ -0,0 +1,118 @@
1
+ from decimal import Decimal
2
+ from typing import Optional
3
+
4
+ from nautilus_trader.config import StrategyConfig
5
+ from nautilus_trader.model.data import Bar, BarType
6
+ from nautilus_trader.model.enums import OrderSide, TimeInForce
7
+ from nautilus_trader.model.identifiers import InstrumentId
8
+ from nautilus_trader.model.instruments import Instrument
9
+ from nautilus_trader.model.objects import Quantity
10
+ from nautilus_trader.trading.strategy import Strategy
11
+
12
+
13
+ class EmaCrossStrategyConfig(StrategyConfig):
14
+ instrument_id: Optional[InstrumentId] = None
15
+ bar_type: Optional[BarType] = None
16
+ instrument_ids: tuple[InstrumentId, ...] = ()
17
+ bar_types: tuple[BarType, ...] = ()
18
+ trade_size: str = "0.01"
19
+ fast_period: int = 12
20
+ slow_period: int = 26
21
+
22
+
23
+ class EmaCrossStrategy(Strategy):
24
+ def __init__(self, config: EmaCrossStrategyConfig) -> None:
25
+ super().__init__(config)
26
+ self.cfg = config
27
+ self._closes: list[float] = []
28
+ self._fast_ema: Optional[float] = None
29
+ self._slow_ema: Optional[float] = None
30
+ self._prev_diff: Optional[float] = None
31
+ self._position: str = "NONE"
32
+ self._instrument: Optional[Instrument] = None
33
+
34
+ def on_start(self) -> None:
35
+ bar_type = self.cfg.bar_type or (
36
+ self.cfg.bar_types[0] if self.cfg.bar_types else None
37
+ )
38
+ instrument_id = self.cfg.instrument_id or (
39
+ self.cfg.instrument_ids[0] if self.cfg.instrument_ids else None
40
+ )
41
+ if bar_type is None or instrument_id is None:
42
+ raise RuntimeError("bar_type and instrument_id must be set")
43
+ self._instrument = self.cache.instrument(instrument_id)
44
+ self.subscribe_bars(bar_type)
45
+
46
+ def on_bar(self, bar: Bar) -> None:
47
+ close = float(bar.close)
48
+ self._closes.append(close)
49
+
50
+ # Need enough warm-up before either EMA is meaningful.
51
+ warmup = max(self.cfg.slow_period, self.cfg.fast_period) + 1
52
+ if len(self._closes) < warmup:
53
+ self._fast_ema = self._update_ema(self._fast_ema, close, self.cfg.fast_period)
54
+ self._slow_ema = self._update_ema(self._slow_ema, close, self.cfg.slow_period)
55
+ return
56
+
57
+ self._fast_ema = self._update_ema(self._fast_ema, close, self.cfg.fast_period)
58
+ self._slow_ema = self._update_ema(self._slow_ema, close, self.cfg.slow_period)
59
+
60
+ diff = self._fast_ema - self._slow_ema # type: ignore[operator]
61
+ if self._prev_diff is None:
62
+ self._prev_diff = diff
63
+ return
64
+
65
+ cross_up = self._prev_diff <= 0.0 < diff
66
+ cross_down = self._prev_diff >= 0.0 > diff
67
+ self._prev_diff = diff
68
+
69
+ instrument = self._instrument
70
+ if instrument is None:
71
+ return
72
+ qty = Quantity(Decimal(self.cfg.trade_size), instrument.size_precision)
73
+
74
+ if self._position == "NONE":
75
+ if cross_up:
76
+ self._submit(instrument.id, OrderSide.BUY, qty)
77
+ self._position = "LONG"
78
+ elif cross_down:
79
+ self._submit(instrument.id, OrderSide.SELL, qty)
80
+ self._position = "SHORT"
81
+ return
82
+
83
+ if self._position == "LONG" and cross_down:
84
+ self._close_open(instrument.id, OrderSide.SELL)
85
+ self._position = "NONE"
86
+ elif self._position == "SHORT" and cross_up:
87
+ self._close_open(instrument.id, OrderSide.BUY)
88
+ self._position = "NONE"
89
+
90
+ @staticmethod
91
+ def _update_ema(prev: Optional[float], value: float, period: int) -> float:
92
+ if prev is None:
93
+ return value
94
+ alpha = 2.0 / (period + 1)
95
+ return alpha * value + (1.0 - alpha) * prev
96
+
97
+ def _submit(
98
+ self,
99
+ instrument_id: InstrumentId,
100
+ side: OrderSide,
101
+ quantity: Quantity,
102
+ ) -> None:
103
+ order = self.order_factory.market(
104
+ instrument_id=instrument_id,
105
+ order_side=side,
106
+ quantity=quantity,
107
+ time_in_force=TimeInForce.GTC,
108
+ )
109
+ self.submit_order(order)
110
+
111
+ def _close_open(self, instrument_id: InstrumentId, side: OrderSide) -> None:
112
+ for position in self.cache.positions_open(instrument_id=instrument_id):
113
+ self._submit(instrument_id, side, position.quantity)
114
+
115
+ def on_stop(self) -> None:
116
+ if self._instrument is not None:
117
+ self.cancel_all_orders(self._instrument.id)
118
+ self.close_all_positions(self._instrument.id)
@@ -0,0 +1,95 @@
1
+ # Enable / Disable Playbook
2
+
3
+ Subscribe or unsubscribe a user from a published Playbook.
4
+
5
+ Subscribing deploys a published Playbook version for one user. It does not
6
+ create a new strategy, a new public version, or a private package fork.
7
+
8
+ ## `POST /api/v1/playbook/enable`
9
+
10
+ **Auth**: `ACCESS-KEY` header from the Bitget OpenAPI credential. Missing it returns 401.
11
+
12
+ ```json
13
+ {
14
+ "version_id": "version-...",
15
+ "chat_id": "123456789",
16
+ "channel": "telegram",
17
+ "schedule_timezone": "Asia/Shanghai"
18
+ }
19
+ ```
20
+
21
+ Enables the published version for the current user and stores the Telegram
22
+ `chat_id` used for signal delivery.
23
+
24
+ **Important:** `chat_id` is required for Telegram delivery.
25
+ `schedule_timezone` is optional. When omitted, the server uses the user's
26
+ profile timezone, then `manifest.schedule.tz`, then `Asia/Shanghai`.
27
+
28
+ ### Success Response
29
+
30
+ ```json
31
+ {
32
+ "instance_id": "inst-...",
33
+ "strategy_id": "strategy-...",
34
+ "version_id": "version-...",
35
+ "schedule_tz": "Asia/Shanghai",
36
+ "status": "active"
37
+ }
38
+ ```
39
+
40
+ ### Subscription Semantics
41
+
42
+ Subscribing means:
43
+
44
+ - a `PlaybookInstance` points at the immutable published `version_id`
45
+ - per-user `config_overrides` are stored on the deployment instance
46
+ - a personal reminder named `playbook-instance:{instance_id}` drives execution
47
+ - the effective schedule timezone is stored on the instance
48
+ - future executions run with that subscriber's own trading context
49
+ - any trade-capable behavior still respects the Playbook's published guard mode
50
+ - published Playbooks with `runtime_profile: deterministic` can be enabled
51
+ - published Playbooks with `runtime_profile: llm_bounded` can also be enabled when the deployment has managed Playbook LLM runtime enabled
52
+
53
+ Subscription does **not** mean:
54
+
55
+ - enabling automatic follow trading without the user's explicit mode choice
56
+ - forcing a live-only strategy to pretend it has historical backtests
57
+ - granting users permission to edit the Playbook's core logic
58
+ - creating another Playbook strategy or version just because parameters changed
59
+
60
+ ## `POST /api/v1/playbook/disable`
61
+
62
+ **Auth**: `ACCESS-KEY` header from the Bitget OpenAPI credential. Missing it returns 401.
63
+
64
+ ```json
65
+ {
66
+ "instance_id": "inst-..."
67
+ }
68
+ ```
69
+
70
+ Disables one deployed subscription instance so the user stops receiving signals.
71
+ The server also disables that instance's personal reminder.
72
+
73
+ ### Disable Response
74
+
75
+ ```json
76
+ {
77
+ "instance_id": "inst-...",
78
+ "strategy_id": "strategy-...",
79
+ "version_id": "version-...",
80
+ "status": "disabled"
81
+ }
82
+ ```
83
+
84
+ ### Auto-trading Note
85
+
86
+ If a Playbook advertises automated trading support, the effective behavior still
87
+ depends on its published metadata:
88
+
89
+ - `execution_mode`
90
+ - `follow_trade_supported`
91
+
92
+ Recommended defaults:
93
+
94
+ - replayable strategies can opt into `follow_trade` when follow trading is supported
95
+ - live-only strategies should default to `signal_only`
@@ -0,0 +1,77 @@
1
+ # Error Responses
2
+
3
+ Error response format for all Playbook API endpoints.
4
+
5
+ ## Standard Format
6
+
7
+ ```json
8
+ {
9
+ "detail": "Error description"
10
+ }
11
+ ```
12
+
13
+ Or with structured error list:
14
+
15
+ ```json
16
+ {
17
+ "detail": {
18
+ "errors": [
19
+ "manifest.yaml: missing 'name'",
20
+ "src/main.py: blocked import 'requests' (line 3)"
21
+ ]
22
+ }
23
+ }
24
+ ```
25
+
26
+ ## Authentication Contract
27
+
28
+ Every authenticated Playbook endpoint on the prod OpenAPI gateway requires the following header. Missing it returns 401:
29
+
30
+ - `ACCESS-KEY` — Bitget OpenAPI access key for the caller
31
+
32
+ `X-User-Id`, `X-Api-Key`, and `X-Principal-Id` are **not** prod OpenAPI client credentials here. The gateway resolves the caller from `ACCESS-KEY`; do not use backend-internal identity headers in client code.
33
+
34
+ If an access key is revoked or cannot resolve to an active principal, authenticated endpoints return 403.
35
+
36
+ ## HTTP Status Codes
37
+
38
+ | Status | Meaning | Common Scenario |
39
+ |--------|---------|-----------------|
40
+ | 200 | Success | Normal response |
41
+ | 401 | Unauthorized | Missing `ACCESS-KEY` header |
42
+ | 403 | Forbidden | Access key has been revoked, principal not found / inactive, or caller is not the resource owner |
43
+ | 404 | Not Found | draft_id or version_id does not exist |
44
+ | 409 | Conflict | Status doesn't allow operation; user already has another active Playbook; Playbook is live-only; or the declared runtime profile is not currently executable |
45
+ | 413 | Payload Too Large | Upload exceeds 10MB |
46
+ | 422 | Validation Failed | Incomplete structure, missing fields, blocked imports, etc. |
47
+ | 503 | Service Unavailable | Sandbox pool not ready |
48
+
49
+ ## Validation Error Details (422)
50
+
51
+ The upload endpoint returns 422 for:
52
+
53
+ **Structure**
54
+ - `Missing manifest.yaml`
55
+ - `Missing src/main.py`
56
+
57
+ **Fields**
58
+ - `manifest.yaml: missing 'name'`
59
+ - `manifest.yaml: invalid name 'My Strategy' (must be DNS label format)`
60
+ - `manifest.yaml: decision_mode must be one of ['agentic', 'deterministic', 'llm_assisted']`
61
+ - `manifest.yaml: live-only playbooks cannot default to execution_mode 'follow_trade'`
62
+ - `manifest.yaml: runtime_profile 'llm_bounded' requires backtest_support = 'none'`
63
+ - `backtest.yaml is only allowed when manifest.yaml sets backtest_support = 'full'`
64
+
65
+ **Code**
66
+ - `src/main.py syntax error at line 5: invalid syntax`
67
+ - `src/main.py: blocked import 'requests' (line 3)`
68
+ - `src/main.py: disallowed import 'some_unknown_lib' (line 7)`
69
+
70
+ ## Handling Recommendations
71
+
72
+ 1. **401** — Make sure `ACCESS-KEY` is sent.
73
+ 2. **403 revoked key** — The access key was revoked server-side; mint a new one and retry.
74
+ 3. **409 publish status conflict** — Publish only a draft that has the required evidence; do not bump `manifest.version`
75
+ 4. **409 backtest/runtime conflict** — Check `backtest_support` and `runtime_profile`. If the strategy is `none` or `llm_bounded`, use paper/live evidence instead of historical backtest claims
76
+ 5. **422 validation failed** — Fix each error in the `errors` list and re-upload
77
+ 6. **503 sandbox unavailable** — Wait or contact ops
@@ -0,0 +1,38 @@
1
+ # Playbook API Index
2
+
3
+ This folder documents the HTTP control plane for Playbook packaging,
4
+ publication, execution, and subscription management.
5
+
6
+ Prod OpenAPI base URL: `https://api.bitget.com`.
7
+ Authenticated calls use the `ACCESS-KEY` header.
8
+
9
+ Use these docs after the local package is ready. For Python imports inside
10
+ `src/**`, read [`../sdk.md`](../sdk.md) instead.
11
+
12
+ ## Read order
13
+
14
+ 1. [`upload.md`](upload.md) — upload a packaged Playbook archive
15
+ 2. [`publish.md`](publish.md) — publish a validated Playbook
16
+ 3. [`run.md`](run.md) — trigger a manual backtest/evaluation run
17
+ 4. [`list.md`](list.md) and `detail` response docs — inspect public Playbooks
18
+ 5. [`enable.md`](enable.md) and [`my-playbooks.md`](my-playbooks.md) — manage
19
+ subscriptions
20
+ 6. [`error-responses.md`](error-responses.md) — common failure modes
21
+
22
+ ## Control-plane docs
23
+
24
+ | Document | Purpose |
25
+ |---|---|
26
+ | [`upload.md`](upload.md) | Request format, package validation, and server-side checks |
27
+ | [`publish.md`](publish.md) | Publish contract, evidence requirements, and 409 cases |
28
+ | [`run.md`](run.md) | Manual run contract and runtime gating |
29
+ | [`list.md`](list.md) | Public list surface |
30
+ | [`enable.md`](enable.md) | Enable/disable subscription execution |
31
+ | [`my-playbooks.md`](my-playbooks.md) | User subscription status |
32
+ | [`error-responses.md`](error-responses.md) | Shared error shapes and examples |
33
+
34
+ ## Boundary
35
+
36
+ - `api/` is for HTTP requests to GetAgent cloud services
37
+ - `sdk/` is for Python code running inside the Playbook sandbox
38
+ - Do not confuse `data.crypto.futures.kline(...)` with `/api/v1/playbook/...`
@@ -0,0 +1,80 @@
1
+ # List Playbooks
2
+
3
+ List existing Playbooks for creator or consumer flows.
4
+
5
+ ## `GET /api/v1/playbook/list`
6
+
7
+ **Auth**: Optional for the default published catalog, required for private states such as `draft`. Authenticated calls to the prod OpenAPI gateway use `ACCESS-KEY`.
8
+
9
+ ### Parameters
10
+
11
+ | Param | Type | Required | Description |
12
+ |-------|------|----------|-------------|
13
+ | `status` | string | no | Filter by status. Default: `published`. Options: `draft`, `published`, `deprecated` |
14
+
15
+ ### Request Examples
16
+
17
+ Endpoint is the fixed GetAgent prod OpenAPI URL. For authenticated calls, substitute `<access_key>` with the value the user provided in chat.
18
+
19
+ ```bash
20
+ # List all published Playbooks (no auth)
21
+ curl -s "https://api.bitget.com/api/v1/playbook/list"
22
+
23
+ # List own drafts (auth required)
24
+ curl -s \
25
+ -H "ACCESS-KEY: <access_key>" \
26
+ "https://api.bitget.com/api/v1/playbook/list?status=draft"
27
+ ```
28
+
29
+ ### Response Shape
30
+
31
+ Published Playbooks should surface enough metadata for public `list / view`
32
+ surfaces to distinguish what kind of evidence the user can trust.
33
+
34
+ Recommended fields:
35
+
36
+ - identity: `strategy_id`, `version_id`, `name`, `display_name`, `version`, `description`
37
+ - public behavior: `decision_mode`, `backtest_support`, `execution_mode`
38
+ - public evidence: `official_evidence_kind`, `official_metrics`
39
+ - subscription hint: `follow_trade_supported`
40
+
41
+ ### Success Response
42
+
43
+ ```json
44
+ [
45
+ {
46
+ "strategy_id": "strategy-...",
47
+ "version_id": "version-...",
48
+ "name": "btc-ema-crossover",
49
+ "display_name": "BTC EMA Crossover",
50
+ "version": "1.0.0",
51
+ "status": "published",
52
+ "description": "Trend following strategy based on EMA 12/26 crossover",
53
+ "trading_symbols": ["BTCUSDT"],
54
+ "tags": ["trend", "ema", "btc"],
55
+ "decision_mode": "deterministic",
56
+ "backtest_support": "full",
57
+ "execution_mode": "follow_trade",
58
+ "follow_trade_supported": true,
59
+ "official_evidence_kind": "backtest",
60
+ "official_metrics": {
61
+ "total_return_pct": 23.5,
62
+ "sharpe_ratio": 1.8,
63
+ "max_drawdown_pct": 8.4,
64
+ "win_rate": 0.62,
65
+ "total_trades": 41
66
+ }
67
+ }
68
+ ]
69
+ ```
70
+
71
+ ### Public Display Rules
72
+
73
+ - `backtest_support: full`
74
+ - show official backtest summary in list / detail
75
+ - `backtest_support: none`
76
+ - do not show fake Sharpe / win rate / total return
77
+ - show `official_evidence_kind` such as `paper` or `live`
78
+ - explain that the strategy is live-only because the core decision cannot be fairly replayed
79
+
80
+ Returns max 100 results, ordered by creation time descending.
@@ -0,0 +1,41 @@
1
+ # My Playbooks
2
+
3
+ List the currently enabled Playbook deployments for the authenticated user.
4
+
5
+ ## `GET /api/v1/playbook/my-playbooks`
6
+
7
+ **Auth**: `ACCESS-KEY` header from the Bitget OpenAPI credential. Missing it returns 401.
8
+
9
+ ### Success Response
10
+
11
+ ```json
12
+ [
13
+ {
14
+ "instance_id": "e1f2g3-...",
15
+ "strategy_id": "strategy-...",
16
+ "version_id": "version-...",
17
+ "playbook_name": "btc-ema-crossover",
18
+ "display_name": "BTC EMA Crossover",
19
+ "version": "1.0.0",
20
+ "strategy_display_name": "BTC EMA Crossover",
21
+ "version_owner_id": "creator-principal-id",
22
+ "status": "active",
23
+ "execution_mode": "follow_trade",
24
+ "follow_trade_supported": true,
25
+ "channel": "telegram",
26
+ "chat_id": "123456789",
27
+ "reminder_id": "reminder-id"
28
+ }
29
+ ]
30
+ ```
31
+
32
+ Returns the caller's active deployments. `strategy_id` is the stable strategy
33
+ tree; `version_id` is the immutable published artifact that scheduled runs
34
+ execute with the instance's `config_overrides`.
35
+
36
+ The list should be enough for Telegram to explain what kind of behavior the
37
+ user enabled:
38
+
39
+ - signal-only
40
+ - manual-confirm execution
41
+ - future automation eligibility
@@ -0,0 +1,76 @@
1
+ # Publish Playbook
2
+
3
+ Publish a draft Playbook to make it publicly available. Publishing is the only
4
+ step that assigns the formal public version number.
5
+
6
+ ## `POST /api/v1/playbook/publish`
7
+
8
+ **Auth**: `ACCESS-KEY` header from the Bitget OpenAPI credential. Missing it returns 401.
9
+
10
+ **Content-Type**: `application/json`
11
+
12
+ ### Request
13
+
14
+ ```json
15
+ {
16
+ "draft_id": "draft-...",
17
+ "bump_type": "patch"
18
+ }
19
+ ```
20
+
21
+ `bump_type` is optional and defaults to `patch`. Valid values are `patch`,
22
+ `minor`, and `major`. The server computes the final semver from the strategy's
23
+ latest published version; clients must not edit `manifest.version` manually.
24
+
25
+ ### Success Response
26
+
27
+ ```json
28
+ {
29
+ "strategy_id": "strategy-...",
30
+ "version_id": "version-...",
31
+ "version": "1.0.1",
32
+ "status": "published",
33
+ "published_at": "2026-04-09T12:00:00+00:00"
34
+ }
35
+ ```
36
+
37
+ ### Permissions
38
+
39
+ Only the Playbook owner can publish. Others receive 403.
40
+
41
+ ### Publish Contract
42
+
43
+ Publishing freezes the draft artifact into an immutable version and records its
44
+ parent version within the same strategy tree. A published Playbook should carry a
45
+ stable public contract for:
46
+
47
+ - `decision_mode`
48
+ - `backtest_support`
49
+ - `runtime_profile`
50
+ - `execution_mode`
51
+ - `follow_trade_supported`
52
+
53
+ ### Evidence Requirements
54
+
55
+ - If `backtest_support = full`
56
+ - the current runtime must be able to execute it (`runtime_profile = deterministic`)
57
+ - the owner must first produce one successful historical evaluation run
58
+ - the Playbook should expose one official public backtest snapshot
59
+ - list / detail surfaces should use that snapshot, not an arbitrary recent run
60
+ - If `backtest_support = none`
61
+ - `runtime_profile = deterministic` and `runtime_profile = llm_bounded` are both publishable
62
+ - `runtime_profile = llm_bounded` additionally requires deployment-side managed LLM runtime to be enabled
63
+ - do not publish fake Sharpe / win rate / total return claims
64
+ - use paper or live evidence instead
65
+
66
+ Publishing a live-only Playbook is allowed. Publishing misleading historical
67
+ performance is not.
68
+
69
+ ### Error Responses
70
+
71
+ | Status | Description |
72
+ |--------|-------------|
73
+ | 401 | Missing `ACCESS-KEY` header |
74
+ | 403 | Not the Playbook owner, access key revoked, or principal not found / inactive |
75
+ | 404 | draft_id not found |
76
+ | 409 | Status is not draft; no successful historical evaluation exists for `backtest_support=full`; or runtime profile is not currently publishable |