@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,552 @@
1
+ # Playbook Package Schema
2
+
3
+ ## Contents
4
+
5
+ - [Directory Layout](#directory-layout)
6
+ - [manifest.yaml](#manifestyaml)
7
+ - [backtest.yaml](#backtestyaml)
8
+ - [src/main.py](#srcmainpy)
9
+
10
+ ## Directory Layout
11
+
12
+ Implemented upload layout:
13
+
14
+ ```text
15
+ <package-name>/
16
+ ├── README.md
17
+ ├── manifest.yaml
18
+ ├── src/
19
+ │ ├── main.py
20
+ │ ├── strategy.py
21
+ │ ├── indicators.py
22
+ │ ├── risk.py
23
+ │ ├── execution.py
24
+ │ ├── prompts.py # optional
25
+ │ ├── llm_policy.py # optional
26
+ │ └── agent_tools.py # optional
27
+ └── backtest.yaml # optional; only for backtest_support: full
28
+ ```
29
+
30
+ Design rules:
31
+
32
+ - **Required:** `README.md`, `manifest.yaml`, `src/main.py`
33
+ - **Optional in upload:** extra modules under `src/`, `backtest.yaml`
34
+ - **Local-only (must not upload):** `tests/`, `notebooks/`, `research/`, `data/`,
35
+ `backtest_results/`, `logs/`, `output/`, virtualenv and cache directories
36
+ - **Do not infer strategy class from file layout.** Public behavior belongs in
37
+ `manifest.yaml`.
38
+
39
+ ## README.md
40
+
41
+ Human-readable strategy explanation. This file is required because subscribers
42
+ need to understand the Playbook before trusting its signals.
43
+
44
+ Write in plain language, not implementation notes. Avoid code blocks unless they
45
+ make the explanation easier to understand. Cover at least:
46
+
47
+ - What market behavior this strategy tries to capture
48
+ - When it opens a long or short position
49
+ - When it closes, takes profit, or stops loss
50
+ - What each user-tunable parameter means and what happens when it is raised or
51
+ lowered
52
+ - How to read the backtest metrics, especially strategy return vs account return
53
+ - Main risks and market conditions where the strategy can perform poorly
54
+
55
+ `README.md` must be UTF-8 text, at least 200 characters, and include enough
56
+ plain-language content for the upload validator to detect sections covering
57
+ 策略、开仓、平仓、风险.
58
+
59
+ ## manifest.yaml
60
+
61
+ Package identity, public behavior, and runtime contract.
62
+
63
+
64
+ | Field | Type | Required | Description |
65
+ | ------------------------ | ------------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
66
+ | `name` | string | yes | URL-safe identifier. Lowercase, alphanumeric, hyphens only. 1-63 chars. |
67
+ | `display_name` | string | yes | Human-readable name. |
68
+ | `version` | string | no | Server-assigned on publish. Do not write this for drafts; `upload` returns a `draft_id`, and `publish` returns the formal semver. |
69
+ | `description` | string | yes | One-line description. |
70
+ | `long_description` | string | yes | 300–400 English words. Plain-language strategy summary that subscribers read in the marketplace UI. Must cover what the strategy captures, when it enters/exits, what each tunable parameter does, and where it underperforms — without revealing numeric parameters, indicator periods, thresholds, or formulas. See [`long_description` Writing Rules](#long_description-writing-rules). |
71
+ | `market_type` | string | yes | `"spot"` or `"contract"`. |
72
+ | `trading_symbols` | list[string] | yes | Trading pairs, e.g. `["BTCUSDT"]`. |
73
+ | `decision_mode` | string | yes | `deterministic`, `llm_assisted`, or `agentic`. |
74
+ | `backtest_support` | string | yes | `full` or `none`. |
75
+ | `runtime_profile` | string | yes | `deterministic`, `llm_bounded`, or `agentic`. Historical backtest uses `deterministic`; live-only bounded model execution uses `llm_bounded`. |
76
+ | `execution_mode` | string | yes | `signal_only` or `follow_trade`. |
77
+ | `follow_trade_supported` | bool | yes | Whether the Playbook can follow trades using a subscriber's bound subaccount. |
78
+ | `strategy_config` | mapping | no | User-tunable strategy parameters read by both live and backtest code paths. |
79
+ | `user_config_schema` | mapping | no | Authoritative declaration of which `strategy_config` fields users may override when subscribing. |
80
+ | `tags` | list[string] | no | Searchable tags. |
81
+ | `schedule.cron` | string | no | Suggested publish-time schedule, e.g. `"0 */4 * * *"`. |
82
+ | `schedule.tz` | string | required with `schedule.cron` | Default IANA timezone for subscription instances, e.g. `"Asia/Shanghai"`. Users may override it when creating their own instance. |
83
+ | `official_evidence_kind` | string | no | For live-only strategies, optional declared evidence type: `paper` or `live`. |
84
+
85
+
86
+ **Name rules:** must match `^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$`.
87
+
88
+ ### Contract rules
89
+
90
+ - Use `backtest_support: full` only when the core trading logic is fairly replayable on historical data.
91
+ - Do not manually bump `version` in `manifest.yaml`. Versions belong to published
92
+ artifacts and are assigned by the server from the strategy's latest published
93
+ version.
94
+ - Use `backtest_support: none` when open-ended LLM reasoning or unreplayable external context determines the trade.
95
+ - `decision_mode: agentic` requires `runtime_profile: agentic`.
96
+ - `runtime_profile: llm_bounded` requires `backtest_support: none`.
97
+ - `execution_mode: follow_trade` requires `follow_trade_supported: true`.
98
+ - Live-only playbooks must not declare `official_evidence_kind: backtest`.
99
+ - Backtest-capable playbooks should not predeclare paper/live official evidence.
100
+ - `schedule.cron` must not run more often than every 10 minutes. This limits
101
+ live execution frequency only; Playbooks may still use 5m or other finer-grain
102
+ bars for historical replay and signal features.
103
+ - `schedule.tz` is required whenever `schedule.cron` is present. Use a valid
104
+ IANA timezone such as `Asia/Shanghai`. This is the default for new
105
+ subscription instances, not a strategy parameter, so do not put timezone in
106
+ `strategy_config` or `user_config_schema`.
107
+ - Live contract order prices must align with exchange tick size. Do not pass
108
+ fixed decimal rounding such as `str(round(tp_price, 2))` to
109
+ `tp_trigger_price` / `sl_trigger_price`; use
110
+ `trade.helpers.resolve_contract_tpsl(...)` or quantize with
111
+ `trade.helpers.contract_rules(symbol).price_step`. The helper is
112
+ keyword-only and does not accept percentage override kwargs such as
113
+ `tp_pct_override` or `sl_pct_override`; convert percentage TP/SL tunables to
114
+ concrete trigger prices before calling it.
115
+ - Live code should use `runtime.emit_signal_or_follow(...)` to emit the signal
116
+ and let the runtime decide whether to call the provided trade callback. In
117
+ `signal_only`, it returns successfully without placing orders.
118
+ - Do not add `run_mode`. Historical vs live execution is selected by
119
+ `runtime.evaluation_mode`, injected by the platform at run time.
120
+ - Every user-editable strategy parameter must appear in both `strategy_config`
121
+ (default runtime value) and `user_config_schema` (type/range/options for UI
122
+ and API validation). Do not declare fake editable fields that strategy code
123
+ does not read.
124
+ - Playbook code must read editable values from
125
+ `runtime.manifest["strategy_config"]`; never hardcode user-editable values in
126
+ Python constants.
127
+ - For configurable trading pairs, keep top-level `trading_symbols` and
128
+ `strategy_config.trading_symbols` aligned. The platform syncs the top-level
129
+ field when a user override changes `strategy_config.trading_symbols`.
130
+ - **`strategy_config.margin_budget` is required** when `backtest_support: full`
131
+ or `execution_mode: follow_trade`. It is the per-strategy denominator the
132
+ platform uses to compute user-facing return % (`net_pnl / margin_budget`).
133
+ Validation rejects manifests that omit it or set it to a non-positive value.
134
+ Subscribers may override it via `config_overrides.margin_budget`; otherwise
135
+ the manifest default applies. Pure signal-only / live-only Playbooks that
136
+ never surface a return % may omit the field.
137
+
138
+ Example:
139
+
140
+ ```yaml
141
+ name: btc-ema-crossover
142
+ display_name: "BTC EMA Crossover"
143
+ description: "Trend following strategy based on EMA 12/26 crossover on 4h chart"
144
+ long_description: |
145
+ This Playbook is a trend-following strategy on BTC perpetual futures. It is
146
+ built on the assumption that once the broader market enters a sustained
147
+ directional move, price tends to travel along that direction in a relatively
148
+ clean trend rather than chopping back and forth. The goal is to capture the
149
+ middle portion of those trends and stay out of grinding sideways markets where
150
+ most signals are noise.
151
+
152
+ Entry decisions are driven by alignment between a shorter-term and a
153
+ longer-term directional read. When both reads point clearly upward, the
154
+ strategy opens a long; when both point clearly downward, it opens a short. It
155
+ deliberately waits until momentum has formed and is still in motion, rather
156
+ than trying to fade extremes or call tops and bottoms.
157
+
158
+ Exits work by inversion. When the shorter-term direction flips before the
159
+ longer-term direction does, the strategy treats that as a fade in conviction,
160
+ retracts the trade signal, and closes the position. The intent is to lock in
161
+ whatever was captured rather than insist on every trade winning. Many small
162
+ losing trades are accepted as the cost of catching the occasional larger
163
+ trend run.
164
+
165
+ Two parameters are exposed to subscribers: leverage and margin budget. Higher
166
+ leverage amplifies both upside and drawdowns equally; the strategy does not
167
+ become more selective when leverage rises. Margin budget is the per-strategy
168
+ cap that the platform sizes orders against and uses as the denominator for
169
+ return percentage; treat it as the maximum amount of capital you are willing
170
+ to put at risk on this Playbook.
171
+
172
+ The strategy underperforms in choppy, range-bound markets where short-term
173
+ direction flips repeatedly without committing to a real trend. Gap-driven
174
+ moves around major events, illiquid pairs, and persistent funding-rate
175
+ dislocation can also produce a string of stops or trapped positions. Strong
176
+ historical backtest metrics are not a guarantee of live profitability — match
177
+ this Playbook against your own risk tolerance before subscribing.
178
+ market_type: contract
179
+ trading_symbols: ["BTCUSDT"]
180
+ tags: ["trend", "ema", "btc", "contract"]
181
+ schedule:
182
+ cron: "0 */4 * * *"
183
+ tz: "Asia/Shanghai"
184
+ decision_mode: deterministic
185
+ backtest_support: full
186
+ runtime_profile: deterministic
187
+ execution_mode: follow_trade
188
+ follow_trade_supported: true
189
+
190
+ strategy_config:
191
+ trading_symbols: ["BTCUSDT"]
192
+ fast_period: 12
193
+ slow_period: 26
194
+ leverage: 5
195
+ margin_budget: "50"
196
+
197
+ user_config_schema:
198
+ trading_symbols:
199
+ type: array
200
+ item_type: string
201
+ default: ["BTCUSDT"]
202
+ options: ["BTCUSDT", "ETHUSDT", "SOLUSDT", "BNBUSDT", "XRPUSDT"]
203
+ aliases:
204
+ btc: BTCUSDT
205
+ bitcoin: BTCUSDT
206
+ eth: ETHUSDT
207
+ ethereum: ETHUSDT
208
+ min_items: 1
209
+ max_items: 1
210
+ label: "Trading symbol"
211
+ leverage:
212
+ type: integer
213
+ default: 5
214
+ min: 1
215
+ max: 20
216
+ aliases:
217
+ 10x: 10
218
+ 10倍: 10
219
+ label: "Leverage"
220
+ margin_budget:
221
+ type: string
222
+ default: "50"
223
+ pattern: "^[0-9]+(\\.[0-9]+)?$"
224
+ label: "Margin budget USDT"
225
+ ```
226
+
227
+ Supported `user_config_schema.*.type` values are `string`, `integer`,
228
+ `number`, `boolean`, and `array`. `array` may declare `item_type`, `min_items`,
229
+ and `max_items`. `options` restricts scalar values or each array item.
230
+ `aliases` maps common user phrases to canonical values. The platform also
231
+ normalizes common raw user intent before validation: `eth` -> `ETHUSDT`,
232
+ `10倍` / `10x` -> `10`, and `200U` / `200 USDT` -> `200` for budget fields.
233
+ `key_aliases` or `synonyms` may be used when a field needs custom input names
234
+ beyond built-ins such as `symbol` / `交易对` -> `trading_symbols` and
235
+ `杠杆` / `倍数` -> `leverage`.
236
+
237
+ ### long_description Writing Rules
238
+
239
+ `manifest.long_description` is the strategy summary that subscribers see in the
240
+ marketplace UI when deciding whether to subscribe. Unlike `description` (one
241
+ line) or `README.md` (full reference), it is a single short essay. Two goals
242
+ have to coexist:
243
+
244
+ 1. **Make a non-technical user understand the strategy** — what market behavior
245
+ it bets on, when it enters and exits, what they can tune, and where it can
246
+ lose them money.
247
+ 2. **Protect the strategy** — never reveal the exact recipe a competitor or
248
+ reader could use to clone or front-run it.
249
+
250
+ #### Length
251
+
252
+ - Target **300–400 English words**.
253
+ - Hard floor: **250 words**. Hard ceiling: **500 words**. Outside this range,
254
+ `validate.py` fails the upload.
255
+ - Write in English only. Do not mix Chinese into the same field.
256
+
257
+ #### Required 5-section skeleton
258
+
259
+ The text must read as continuous prose (no bullet headers, no numbered lists),
260
+ but must cover all five logical sections in order. Aim for 60–90 words per
261
+ section.
262
+
263
+ 1. **What it does and what behavior it captures.** The strategy thesis. Which
264
+ market(s), which side (long/short/both), which kind of price behavior it
265
+ bets on.
266
+ 2. **How it decides to enter.** Direction-of-decision language only ("enters
267
+ long when momentum aligns with trend"). No periods, no thresholds, no
268
+ formulas.
269
+ 3. **How it decides to exit, take profit, or stop loss.** Same direction-of-
270
+ decision phrasing.
271
+ 4. **What subscribers can tune and what each tunable does.** Describe the
272
+ *effect* of raising or lowering each user-editable parameter declared in
273
+ `user_config_schema`. Do not show default values inside the prose.
274
+ 5. **Risks and unsuitable market conditions.** Be honest about where the
275
+ strategy underperforms or actively loses money. Past backtest performance
276
+ disclaimer should appear here.
277
+
278
+ The validator confirms each section is present by detecting required keyword
279
+ clusters (`captures`/`thesis`/`aim` for §1, `enter`/`open`/`long`/`short` for
280
+ §2, `exit`/`close`/`stop`/`take profit` for §3, `parameter`/`tunable`/
281
+ `leverage`/`margin`/`adjust` for §4, `risk`/`drawdown`/`underperform`/
282
+ `unsuitable`/`lose` for §5). Missing any cluster fails validation.
283
+
284
+ #### What you may write (allowed)
285
+
286
+ - The strategy *category* in plain English: `trend-following`, `mean
287
+ reversion`, `momentum`, `breakout`, `range`, `arbitrage`, `grid`,
288
+ `event-driven`, `risk parity`, `pairs`.
289
+ - The general *kind* of feature input, e.g. "price action", "volatility
290
+ regime", "open-interest behavior", "funding-rate behavior", "liquidation
291
+ pressure", "order-book imbalance".
292
+ - The general *direction* of decisions ("waits for momentum to align with the
293
+ trend before entering", "fades extreme moves after volatility contracts").
294
+ - The *effect* of each tunable parameter ("higher leverage amplifies both
295
+ upside and drawdowns equally", "raising the symbol universe spreads risk
296
+ across more pairs but increases data load").
297
+ - The *qualitative* market regimes where it works/fails ("performs well in
298
+ sustained trends; underperforms in choppy ranges or around major news
299
+ gaps").
300
+
301
+ #### What you must not write (forbidden — validator hard-fails)
302
+
303
+ - **Numeric indicator periods.** `EMA 12`, `RSI 14`, `MA 200`, `ATR 1.5`,
304
+ `Bollinger 20`, `MACD 9`, `ADX 14`, `Stochastic 5`, `VWAP 30`, `MFI 14`,
305
+ `CCI 20`, `OBV 50`. Pattern: any indicator name immediately followed by a
306
+ number.
307
+ - **Numeric lookback windows.** `14 bars`, `20 candles`, `5-period`, `30-day`,
308
+ `4-hour`, `15-minute`, `90 days`, `1 week`. Pattern: any number followed by
309
+ a time/candle/period unit.
310
+ - **Explicit numeric thresholds.** `> 30`, `<= 0.7`, `>= 3%`, `drops below 5%`,
311
+ `volatility above 25%`. Any explicit comparison operator with a number, or
312
+ any standalone percentage with a number, is forbidden.
313
+ - **Multipliers and ratios.** `1.5x ATR`, `2x volatility`, `3:1 reward/risk`.
314
+ Any `<number>x` or numeric ratio is forbidden.
315
+ - **Formulas or pseudocode.** No `=`, no arithmetic expressions, no `if/then`
316
+ blocks, no parameter names from the source code.
317
+ - **Default values for tunable parameters.** Do not mention specific defaults
318
+ even though they are public in `manifest.strategy_config`. Describe the
319
+ *effect* of changing the parameter, not its default.
320
+ - **Specific provider/endpoint names.** No `data.crypto.futures.kline`, no
321
+ `Coinglass`, no `Binance funding_weighted`. Stay one level above the SDK.
322
+
323
+ #### Example
324
+
325
+ See the `btc-ema-crossover` manifest example above for a full 300–400 word
326
+ `long_description` block that satisfies all rules.
327
+
328
+ #### Common authoring mistakes
329
+
330
+ - Repeating `description` verbatim or padding it to length. The validator
331
+ catches near-duplicate strings.
332
+ - Writing in numbered list / bullet form ("**1.** Goal: …"). Keep it as
333
+ flowing prose.
334
+ - Naming the indicators by acronym alone with no number (`uses EMA`, `uses
335
+ RSI`). The validator allows this — a clue is fine, an exact period is not —
336
+ but treat naked indicator-name name-dropping as a code smell. Prefer
337
+ category-level language ("uses moving-average crossovers", "uses momentum
338
+ oscillator").
339
+ - Mentioning *backtest* configuration parameters (`max_backtest_symbols`,
340
+ `backtest_batch_size`). These are operational, not strategy semantics, and
341
+ do not belong in this field.
342
+ - Any text that translates to "buy when X drops more than N percent". Rewrite
343
+ as "buys after a sharp washout move" without a number.
344
+
345
+ ## backtest.yaml
346
+
347
+ Optional Nautilus replay specification. This file is only valid when
348
+ `manifest.yaml` sets `backtest_support: full`.
349
+
350
+ The runner exposes this file to Playbook code via `runtime.backtest_spec`.
351
+
352
+ Sections:
353
+
354
+
355
+ | Field | Type | Description |
356
+ | ----------------------------- | ------------------------ | ------------------------------------------------------------------- |
357
+ | `venue` | mapping | Venue name, account type, OMS type, and starting balances |
358
+ | `instrument` or `instruments` | mapping or list[mapping] | Explicit instrument definitions and `bar_type` declarations |
359
+ | `strategy` | mapping | Author strategy module/class entry plus config payload |
360
+ | `execution` | mapping | Optional replay window such as `start` / `end` |
361
+ | `data_requirements` | mapping | Optional replay data contract such as required enriched bar columns |
362
+
363
+
364
+ Optional `data_requirements.required_bar_fields` lets a deterministic strategy
365
+ declare extra normalized bar columns it expects in addition to core OHLCV.
366
+ These names should use lower snake case to match the replay engine's normalized
367
+ DataFrame columns, for example `quote_volume`, `trade_count`, or
368
+ `taker_buy_volume`.
369
+
370
+ Every backtest instrument must declare explicit `maker_fee` and `taker_fee`.
371
+ Do not rely on zero-fee defaults. High-frequency Playbooks can look profitable
372
+ or only slightly negative without fees, while real execution would pay a fee on
373
+ every entry and exit. Use conservative exchange rates for the target venue, for
374
+ example `maker_fee: "0.0002"` and `taker_fee: "0.0005"` for a simple Binance
375
+ contract assumption.
376
+
377
+ Data coverage is part of the package contract. If a strategy requires enriched
378
+ columns such as `quote_volume`, `taker_buy_ratio`, open interest, funding, or
379
+ liquidation data, author code must prove that the selected DataSDK
380
+ endpoint/provider/symbol combination returns enough rows for the claimed replay
381
+ window and the exact fields used by the strategy. Do not silently fill a missing
382
+ decision feature with constants such as `0`, `0.5`, or `False` and still claim a
383
+ valid backtest. If a feature is degraded, expose that in metrics/meta or fail
384
+ the run with a clear error.
385
+
386
+ Large multi-symbol backtests must batch data fetches instead of issuing one
387
+ tight loop over every symbol and endpoint. If a Playbook scans many symbols
388
+ across several historical datasets, expose controls such as
389
+ `max_backtest_symbols`, `backtest_batch_size`, and
390
+ `backtest_batch_sleep_seconds` in both `strategy_config` and
391
+ `user_config_schema`. Default to a modest slice, for example 30 symbols in
392
+ batches of 5 with a short sleep between batches, and report
393
+ `symbols_requested`, `symbols_loaded`, batch settings, and skipped symbols in
394
+ metrics/meta.
395
+
396
+ When `backtest.yaml` declares more instruments than the data fetch actually
397
+ loads, author code must filter the effective runtime spec to the loaded
398
+ instrument ids before calling `backtest.run(...)`. The engine expects OHLCV data
399
+ for every declared instrument and will fail if the spec still includes missing
400
+ symbols. For full-universe evidence, split the symbol universe across multiple
401
+ runs or a controlled workflow rather than making one default run fetch every
402
+ symbol.
403
+
404
+ Sizing is also part of the public contract. Backtest and live signal output
405
+ should expose `notional_usdt`, `min_open_notional_usdt`, and `sizing_ok` when
406
+ orders are sized from margin budgets. If the configured margin and leverage are
407
+ too small for exchange lot size, tell the user to raise margin or leverage.
408
+
409
+ When the strategy class defines `set_feature_frames(...)` or exposes a
410
+ `feature_frames` attribute, the replay engine injects one full DataFrame per
411
+ instrument ID with those extra columns preserved. Nautilus bar construction
412
+ still consumes OHLCV, but the strategy can read richer columns from the
413
+ injected feature frames.
414
+
415
+ Use `getagent.backtest.prepare_frame()` / `getagent.backtest.build_feature_frame()`
416
+ in author code to assemble those extra columns from multiple `getagent.data`
417
+ datasets before calling `backtest.run(...)`.
418
+
419
+ Minimal example:
420
+
421
+ ```yaml
422
+ venue:
423
+ name: BINANCE
424
+ account_type: CASH
425
+ oms_type: NETTING
426
+ starting_balances:
427
+ - amount: 100000
428
+ currency: USDT
429
+
430
+ strategy:
431
+ module: strategy
432
+ class: DemoStrategy
433
+ config_class: DemoStrategyConfig
434
+ config:
435
+ order_id_tag: "001"
436
+ trade_size: "0.01"
437
+
438
+ instrument:
439
+ id: BTCUSDT.BINANCE
440
+ kind: spot
441
+ raw_symbol: BTCUSDT
442
+ base_currency: BTC
443
+ quote_currency: USDT
444
+ price_precision: 2
445
+ size_precision: 6
446
+ price_increment: "0.01"
447
+ size_increment: "0.000001"
448
+ lot_size: "0.000001"
449
+ maker_fee: "0.0002"
450
+ taker_fee: "0.0005"
451
+ bar_type: BTCUSDT.BINANCE-1-HOUR-LAST-EXTERNAL
452
+ ```
453
+
454
+ Feature-aware example:
455
+
456
+ ```yaml
457
+ venue:
458
+ name: BINANCE
459
+ account_type: CASH
460
+ oms_type: NETTING
461
+ starting_balances:
462
+ - amount: 100000
463
+ currency: USDT
464
+
465
+ strategy:
466
+ module: strategy
467
+ class: PriceVolStrategy
468
+ config_class: PriceVolStrategyConfig
469
+
470
+ instrument:
471
+ id: BTCUSDT.BINANCE
472
+ kind: spot
473
+ raw_symbol: BTCUSDT
474
+ base_currency: BTC
475
+ quote_currency: USDT
476
+ price_precision: 2
477
+ size_precision: 6
478
+ price_increment: "0.01"
479
+ size_increment: "0.000001"
480
+ lot_size: "0.000001"
481
+ maker_fee: "0.0002"
482
+ taker_fee: "0.0005"
483
+ bar_type: BTCUSDT.BINANCE-1-HOUR-LAST-EXTERNAL
484
+
485
+ data_requirements:
486
+ required_bar_fields:
487
+ - quote_volume
488
+ - trade_count
489
+ - taker_buy_volume
490
+ ```
491
+
492
+ ## src/main.py
493
+
494
+ Sandbox entry point. The runner executes the package as `python -m src.main`.
495
+ `src/main.py` remains the required handoff into the runtime, but additional
496
+ modules under `src/**` are first-class and encouraged.
497
+
498
+ For packages with both live and backtest behavior, keep `manifest.yaml`
499
+ immutable and route in code:
500
+
501
+ ```python
502
+ from getagent import runtime
503
+ from . import main_backtest, main_live
504
+
505
+ if runtime.is_historical():
506
+ main_backtest.run()
507
+ elif runtime.is_live():
508
+ main_live.run()
509
+ else:
510
+ raise ValueError(f"unsupported evaluation_mode={runtime.evaluation_mode!r}")
511
+ ```
512
+
513
+ Put shared data fetch and feature engineering in a neutral module such as
514
+ `features.py`. `main_backtest.py` must not import live trading code, because
515
+ historical runs should not require a configured trading identity.
516
+
517
+ **Allowed imports:** `getagent`, `nautilus_trader`, `pandas`, `numpy`, `json`,
518
+ `math`, `datetime`, `pathlib`, `asyncio`, `typing`, `dataclasses`,
519
+ `collections`, `functools`, `re`, `decimal`, `statistics`, `itertools`, plus
520
+ local modules under `src/`**.
521
+
522
+ **Blocked imports:** `requests`, `httpx`, `trade_sdk`, `ccxt`, `os`, `subprocess`,
523
+ `importlib`, `socket`, and other network/system/database libraries.
524
+
525
+ **Output convention:** signals via `runtime.emit_signal()`, reports/artifacts via
526
+ `/workspace/output/`, and public backtest charts via `backtest.generate_chart()`.
527
+
528
+ ## Nautilus strategy lifecycle
529
+
530
+ The Playbook runner uses a NautilusTrader version where strategy lifecycle
531
+ cleanup APIs are instrument-scoped. Do not call these methods without an
532
+ instrument id:
533
+
534
+ ```python
535
+ def on_stop(self) -> None:
536
+ if self.config.instrument_id is not None:
537
+ self.cancel_all_orders(self.config.instrument_id)
538
+ self.close_all_positions(self.config.instrument_id)
539
+ ```
540
+
541
+ For multi-instrument strategies, iterate `instrument_ids`:
542
+
543
+ ```python
544
+ def on_stop(self) -> None:
545
+ for instrument_id in self.config.instrument_ids:
546
+ self.cancel_all_orders(instrument_id)
547
+ self.close_all_positions(instrument_id)
548
+ ```
549
+
550
+ Invalid patterns such as `self.cancel_all_orders()` or
551
+ `self.close_all_positions()` are rejected by `scripts/validate.py` and
552
+ the server-side upload validator because they fail at replay shutdown.