@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,466 @@
1
+ # Trade Helpers Reference
2
+
3
+ Deterministic symbol, qty, TP/SL, and selector helpers.
4
+
5
+ These signatures are written against the `getagent.trade` public contract.
6
+ Runner-managed identity kwargs (`user_id`, `channel`, `trace_id`) are
7
+ intentionally omitted from the author-facing signatures below.
8
+
9
+ ## Contents
10
+ - [`trade.helpers.normalize_trading_symbol`](#tradehelpersnormalize-trading-symbol)
11
+ - [`trade.helpers.normalize_trading_symbols`](#tradehelpersnormalize-trading-symbols)
12
+ - [`trade.helpers.parse_budget`](#tradehelpersparse-budget)
13
+ - [`trade.helpers.spot_price_quote`](#tradehelpersspot-price-quote)
14
+ - [`trade.helpers.spot_price`](#tradehelpersspot-price)
15
+ - [`trade.helpers.contract_price_quote`](#tradehelperscontract-price-quote)
16
+ - [`trade.helpers.contract_price`](#tradehelperscontract-price)
17
+ - [`trade.helpers.spot_rules`](#tradehelpersspot-rules)
18
+ - [`trade.helpers.contract_rules`](#tradehelperscontract-rules)
19
+ - [`trade.helpers.compute_qty`](#tradehelperscompute-qty)
20
+ - [`trade.helpers.resolve_contract_tpsl`](#tradehelpersresolve-contract-tpsl)
21
+ - [`trade.helpers.select_spot_order`](#tradehelpersselect-spot-order)
22
+ - [`trade.helpers.select_contract_order`](#tradehelpersselect-contract-order)
23
+ - [`trade.helpers.select_tp_plan_order`](#tradehelpersselect-tp-plan-order)
24
+ - [`trade.helpers.select_sl_plan_order`](#tradehelpersselect-sl-plan-order)
25
+ - [`trade.helpers.select_contract_position`](#tradehelpersselect-contract-position)
26
+ - [`trade.helpers.find_contract_position`](#tradehelpersfind-contract-position)
27
+ - [`trade.helpers.contract_position_records`](#tradehelperscontract-position-records)
28
+ - [`trade.helpers.count_open_contract_positions`](#tradehelperscount-open-contract-positions)
29
+ - [`trade.helpers.contract_open_symbols`](#tradehelperscontract-open-symbols)
30
+ - [`trade.helpers.find_spot_order`](#tradehelpersfind-spot-order)
31
+ - [`trade.helpers.find_contract_order`](#tradehelpersfind-contract-order)
32
+
33
+ ## Method reference
34
+
35
+ ### `trade.helpers.normalize_trading_symbol`
36
+
37
+ ```python
38
+ trade.helpers.normalize_trading_symbol(symbol, quote='USDT')
39
+ ```
40
+
41
+ Summary: -
42
+
43
+ | Param | Required | Type | Default |
44
+ |---|---|---|---|
45
+ | `symbol` | `yes` | `Any` | - |
46
+ | `quote` | `no` | `str` | `USDT` |
47
+
48
+ Returns: `str`
49
+
50
+ ---
51
+
52
+ ### `trade.helpers.normalize_trading_symbols`
53
+
54
+ ```python
55
+ trade.helpers.normalize_trading_symbols(symbols, quote='USDT')
56
+ ```
57
+
58
+ Summary: -
59
+
60
+ | Param | Required | Type | Default |
61
+ |---|---|---|---|
62
+ | `symbols` | `yes` | `list[Any] | tuple[Any, ...]` | - |
63
+ | `quote` | `no` | `str` | `USDT` |
64
+
65
+ Returns: `list[str]`
66
+
67
+ ---
68
+
69
+ ### `trade.helpers.parse_budget`
70
+
71
+ ```python
72
+ trade.helpers.parse_budget(value, default_quote='USDT')
73
+ ```
74
+
75
+ Summary: -
76
+
77
+ | Param | Required | Type | Default |
78
+ |---|---|---|---|
79
+ | `value` | `yes` | `Any` | - |
80
+ | `default_quote` | `no` | `str` | `USDT` |
81
+
82
+ Returns: `ParsedBudget`
83
+
84
+ ---
85
+
86
+ ### `trade.helpers.spot_price_quote`
87
+
88
+ ```python
89
+ trade.helpers.spot_price_quote(symbol)
90
+ ```
91
+
92
+ Summary: -
93
+
94
+ | Param | Required | Type | Default |
95
+ |---|---|---|---|
96
+ | `symbol` | `yes` | `Any` | - |
97
+
98
+ Returns: `PriceQuote`
99
+
100
+ ---
101
+
102
+ ### `trade.helpers.spot_price`
103
+
104
+ ```python
105
+ trade.helpers.spot_price(symbol)
106
+ ```
107
+
108
+ Summary: Return the latest Bitget spot price as a Decimal.
109
+
110
+ | Param | Required | Type | Default |
111
+ |---|---|---|---|
112
+ | `symbol` | `yes` | `Any` | - |
113
+
114
+ Returns: `Decimal`
115
+
116
+ ---
117
+
118
+ ### `trade.helpers.contract_price_quote`
119
+
120
+ ```python
121
+ trade.helpers.contract_price_quote(symbol, product_type='USDT-FUTURES')
122
+ ```
123
+
124
+ Summary: -
125
+
126
+ | Param | Required | Type | Default |
127
+ |---|---|---|---|
128
+ | `symbol` | `yes` | `Any` | - |
129
+ | `product_type` | `no` | `str` | `USDT-FUTURES` |
130
+
131
+ Returns: `PriceQuote`
132
+
133
+ ---
134
+
135
+ ### `trade.helpers.contract_price`
136
+
137
+ ```python
138
+ trade.helpers.contract_price(symbol, product_type='USDT-FUTURES')
139
+ ```
140
+
141
+ Summary: Return the latest Bitget contract last price as a Decimal.
142
+
143
+ | Param | Required | Type | Default |
144
+ |---|---|---|---|
145
+ | `symbol` | `yes` | `Any` | - |
146
+ | `product_type` | `no` | `str` | `USDT-FUTURES` |
147
+
148
+ Returns: `Decimal`
149
+
150
+ ---
151
+
152
+ ### `trade.helpers.spot_rules`
153
+
154
+ ```python
155
+ trade.helpers.spot_rules(symbol)
156
+ ```
157
+
158
+ Summary: Load normalized Bitget spot instrument rules for a symbol.
159
+
160
+ | Param | Required | Type | Default |
161
+ |---|---|---|---|
162
+ | `symbol` | `yes` | `Any` | - |
163
+
164
+ Returns: `InstrumentRules`
165
+
166
+ ---
167
+
168
+ ### `trade.helpers.contract_rules`
169
+
170
+ ```python
171
+ trade.helpers.contract_rules(symbol, product_type='USDT-FUTURES')
172
+ ```
173
+
174
+ Summary: Load normalized Bitget contract instrument rules for a symbol.
175
+
176
+ | Param | Required | Type | Default |
177
+ |---|---|---|---|
178
+ | `symbol` | `yes` | `Any` | - |
179
+ | `product_type` | `no` | `str` | `USDT-FUTURES` |
180
+
181
+ Returns: `InstrumentRules`
182
+
183
+ ---
184
+
185
+ ### `trade.helpers.compute_qty`
186
+
187
+ ```python
188
+ trade.helpers.compute_qty(symbol, market, budget_amount, leverage=None, price="", product_type='USDT-FUTURES')
189
+ ```
190
+
191
+ Summary: Convert a quote budget into executable spot or contract qty.
192
+
193
+ | Param | Required | Type | Default |
194
+ |---|---|---|---|
195
+ | `symbol` | `yes` | `Any` | - |
196
+ | `market` | `yes` | `MarketLiteral` | - |
197
+ | `budget_amount` | `yes` | `Any` | - |
198
+ | `leverage` | `no` | `Any` | `None` |
199
+ | `price` | `no` | `Any` | `""` |
200
+ | `product_type` | `no` | `str` | `USDT-FUTURES` |
201
+
202
+ Returns: `QtyPlan`
203
+
204
+ ---
205
+
206
+ ### `trade.helpers.resolve_contract_tpsl`
207
+
208
+ ```python
209
+ trade.helpers.resolve_contract_tpsl(*, symbol, side, leverage, tp_trigger_price="", sl_trigger_price="", reference_price="", product_type='USDT-FUTURES')
210
+ ```
211
+
212
+ Summary: Validate or auto-fill contract TP/SL using live price and instrument rules.
213
+
214
+ | Param | Required | Type | Default |
215
+ |---|---|---|---|
216
+ | `symbol` | `yes` | `Any` | - |
217
+ | `side` | `yes` | `TpSlSideLiteral` | - |
218
+ | `leverage` | `yes` | `Any` | - |
219
+ | `tp_trigger_price` | `no` | `Any` | `""` |
220
+ | `sl_trigger_price` | `no` | `Any` | `""` |
221
+ | `reference_price` | `no` | `Any` | `""` |
222
+ | `product_type` | `no` | `str` | `USDT-FUTURES` |
223
+
224
+ Returns: `ContractTpslPlan`
225
+
226
+ Live contract order rule: never pass `str(round(price, 2))` or any other fixed
227
+ decimal precision directly to `tp_trigger_price` / `sl_trigger_price`. Bitget
228
+ contract trigger prices must align with the instrument `price_step` / tick size
229
+ (for example BTCUSDT may require `0.1` increments). Use
230
+ `trade.helpers.resolve_contract_tpsl(...)` when possible, or load
231
+ `trade.helpers.contract_rules(symbol)` and quantize custom ATR/indicator trigger
232
+ prices to `rules.price_step` before calling `trade.contract.open_long_market(...)`
233
+ or `trade.contract.open_short_market(...)`.
234
+
235
+ `resolve_contract_tpsl` is keyword-only and accepts only the parameters listed
236
+ above. It does **not** accept percentage override kwargs such as
237
+ `tp_pct_override`, `sl_pct_override`, `take_profit_pct`, or `stop_loss_pct`. If a
238
+ strategy exposes percentage-based TP/SL tunables, compute concrete
239
+ `tp_trigger_price` and `sl_trigger_price` values first, then pass those prices to
240
+ this helper for validation and tick-size alignment.
241
+
242
+ ---
243
+
244
+ ### `trade.helpers.select_spot_order`
245
+
246
+ ```python
247
+ trade.helpers.select_spot_order(pending_orders_result, symbol="", prefer_first=False)
248
+ ```
249
+
250
+ Summary: -
251
+
252
+ | Param | Required | Type | Default |
253
+ |---|---|---|---|
254
+ | `pending_orders_result` | `yes` | `dict[str, Any]` | - |
255
+ | `symbol` | `no` | `str` | `""` |
256
+ | `prefer_first` | `no` | `bool` | `false` |
257
+
258
+ Returns: `OrderSelection`
259
+
260
+ ---
261
+
262
+ ### `trade.helpers.select_contract_order`
263
+
264
+ ```python
265
+ trade.helpers.select_contract_order(pending_orders_result, symbol="", prefer_first=False)
266
+ ```
267
+
268
+ Summary: -
269
+
270
+ | Param | Required | Type | Default |
271
+ |---|---|---|---|
272
+ | `pending_orders_result` | `yes` | `dict[str, Any]` | - |
273
+ | `symbol` | `no` | `str` | `""` |
274
+ | `prefer_first` | `no` | `bool` | `false` |
275
+
276
+ Returns: `OrderSelection`
277
+
278
+ ---
279
+
280
+ ### `trade.helpers.select_tp_plan_order`
281
+
282
+ ```python
283
+ trade.helpers.select_tp_plan_order(plan_orders_result, symbol="", prefer_first=False)
284
+ ```
285
+
286
+ Summary: Pick a take-profit plan order candidate from a live plan-order payload.
287
+
288
+ | Param | Required | Type | Default |
289
+ |---|---|---|---|
290
+ | `plan_orders_result` | `yes` | `dict[str, Any]` | - |
291
+ | `symbol` | `no` | `str` | `""` |
292
+ | `prefer_first` | `no` | `bool` | `false` |
293
+
294
+ Returns: `PlanOrderSelection`
295
+
296
+ ---
297
+
298
+ ### `trade.helpers.select_sl_plan_order`
299
+
300
+ ```python
301
+ trade.helpers.select_sl_plan_order(plan_orders_result, symbol="", prefer_first=False)
302
+ ```
303
+
304
+ Summary: Pick a stop-loss plan order candidate from a live plan-order payload.
305
+
306
+ | Param | Required | Type | Default |
307
+ |---|---|---|---|
308
+ | `plan_orders_result` | `yes` | `dict[str, Any]` | - |
309
+ | `symbol` | `no` | `str` | `""` |
310
+ | `prefer_first` | `no` | `bool` | `false` |
311
+
312
+ Returns: `PlanOrderSelection`
313
+
314
+ ---
315
+
316
+ ### `trade.helpers.select_contract_position`
317
+
318
+ ```python
319
+ trade.helpers.select_contract_position(current_position_result, symbol, hold_side="", prefer_first=False)
320
+ ```
321
+
322
+ Summary: Pick a contract position candidate from a live current-position payload.
323
+ Raises when the account is flat for the requested symbol or when multiple
324
+ positions match without a tiebreaker. Do not use `.found`; `PositionSelection`
325
+ does not expose that field. For first live tick / empty account checks, prefer
326
+ `trade.helpers.find_contract_position(...)` or `contract_position_records(...)`.
327
+
328
+ | Param | Required | Type | Default |
329
+ |---|---|---|---|
330
+ | `current_position_result` | `yes` | `dict[str, Any]` | - |
331
+ | `symbol` | `yes` | `str` | - |
332
+ | `hold_side` | `no` | `str` | `""` |
333
+ | `prefer_first` | `no` | `bool` | `false` |
334
+
335
+ Returns: `PositionSelection`
336
+
337
+ `PositionSelection` exposes only:
338
+
339
+ - `symbol`
340
+ - `hold_side`
341
+ - `size`
342
+ - `leverage`
343
+ - `candidate_count`
344
+ - `raw`
345
+
346
+ Do not access convenience fields such as `.open_price`, `.entry_price`,
347
+ `.avg_price`, or `.average_open_price`; they are not part of the helper return
348
+ contract. If live logic needs an exchange-specific entry/open price, read it
349
+ from `selection.raw` with explicit fallback keys, or use
350
+ `contract_position_records(...)` and parse the normalized position dictionaries.
351
+
352
+ ---
353
+
354
+ ### `trade.helpers.find_contract_position`
355
+
356
+ ```python
357
+ trade.helpers.find_contract_position(current_position_result, symbol, hold_side="", prefer_first=False)
358
+ ```
359
+
360
+ Summary: Return a matching open contract position, or `None` when the account is
361
+ flat for the requested symbol. Still raises on ambiguous multiple matches unless
362
+ `hold_side` is specific or `prefer_first=True` is intentional.
363
+
364
+ | Param | Required | Type | Default |
365
+ |---|---|---|---|
366
+ | `current_position_result` | `yes` | `Any` | - |
367
+ | `symbol` | `yes` | `str` | - |
368
+ | `hold_side` | `no` | `str` | `""` |
369
+ | `prefer_first` | `no` | `bool` | `false` |
370
+
371
+ Returns: `PositionSelection | None`
372
+
373
+ When a position is found, the returned `PositionSelection` has the same field
374
+ contract as `select_contract_position(...)`: `symbol`, `hold_side`, `size`,
375
+ `leverage`, `candidate_count`, and `raw` only. Do not access `.open_price`,
376
+ `.entry_price`, `.avg_price`, or `.average_open_price`; parse
377
+ exchange-specific price fields from `.raw` instead.
378
+
379
+ ---
380
+
381
+ ### `trade.helpers.contract_position_records`
382
+
383
+ ```python
384
+ trade.helpers.contract_position_records(current_position_result, symbol="")
385
+ ```
386
+
387
+ Summary: Normalize varied live `current_position(...)` response shapes into a
388
+ list of position dictionaries, optionally filtered by symbol.
389
+
390
+ | Param | Required | Type | Default |
391
+ |---|---|---|---|
392
+ | `current_position_result` | `yes` | `Any` | - |
393
+ | `symbol` | `no` | `str` | `""` |
394
+
395
+ Returns: `list[dict[str, Any]]`
396
+
397
+ ---
398
+
399
+ ### `trade.helpers.count_open_contract_positions`
400
+
401
+ ```python
402
+ trade.helpers.count_open_contract_positions(current_position_result, symbol="")
403
+ ```
404
+
405
+ Summary: Count non-zero contract positions from a live `current_position(...)`
406
+ payload. Handles direct list payloads and nested `data.list` / `positions`
407
+ payloads.
408
+
409
+ | Param | Required | Type | Default |
410
+ |---|---|---|---|
411
+ | `current_position_result` | `yes` | `Any` | - |
412
+ | `symbol` | `no` | `str` | `""` |
413
+
414
+ Returns: `int`
415
+
416
+ ---
417
+
418
+ ### `trade.helpers.contract_open_symbols`
419
+
420
+ ```python
421
+ trade.helpers.contract_open_symbols(current_position_result)
422
+ ```
423
+
424
+ Summary: Return sorted unique symbols that have a non-zero contract position.
425
+
426
+ | Param | Required | Type | Default |
427
+ |---|---|---|---|
428
+ | `current_position_result` | `yes` | `Any` | - |
429
+
430
+ Returns: `list[str]`
431
+
432
+ ---
433
+
434
+ ### `trade.helpers.find_spot_order`
435
+
436
+ ```python
437
+ trade.helpers.find_spot_order(pending_orders_result, order_id)
438
+ ```
439
+
440
+ Summary: Locate a spot order by exact ``order_id`` (bypasses multi-candidate raise).
441
+
442
+ | Param | Required | Type | Default |
443
+ |---|---|---|---|
444
+ | `pending_orders_result` | `yes` | `dict[str, Any]` | - |
445
+ | `order_id` | `yes` | `str` | - |
446
+
447
+ Returns: `OrderSelection`
448
+
449
+ ---
450
+
451
+ ### `trade.helpers.find_contract_order`
452
+
453
+ ```python
454
+ trade.helpers.find_contract_order(pending_orders_result, order_id)
455
+ ```
456
+
457
+ Summary: Locate a contract order by exact ``order_id`` (bypasses multi-candidate raise).
458
+
459
+ | Param | Required | Type | Default |
460
+ |---|---|---|---|
461
+ | `pending_orders_result` | `yes` | `dict[str, Any]` | - |
462
+ | `order_id` | `yes` | `str` | - |
463
+
464
+ Returns: `OrderSelection`
465
+
466
+ ---
@@ -0,0 +1,28 @@
1
+ # Trade Market Reference
2
+
3
+ Market capability checks before order placement.
4
+
5
+ These signatures are written against the `getagent.trade` public contract.
6
+ Runner-managed identity kwargs (`user_id`, `channel`, `trace_id`) are
7
+ intentionally omitted from the author-facing signatures below.
8
+
9
+ ## Contents
10
+ - [`trade.market.check_symbol_support`](#trademarketcheck-symbol-support)
11
+
12
+ ## Method reference
13
+
14
+ ### `trade.market.check_symbol_support`
15
+
16
+ ```python
17
+ trade.market.check_symbol_support(symbols)
18
+ ```
19
+
20
+ Summary: Check which trading pairs are tradable for the current subaccount.
21
+
22
+ | Param | Required | Type | Default |
23
+ |---|---|---|---|
24
+ | `symbols` | `yes` | `list[str] | tuple[str, ...]` | - |
25
+
26
+ Returns: `CheckSymbolSupportResult`
27
+
28
+ ---
@@ -0,0 +1,102 @@
1
+ # Trade Execution Patterns
2
+
3
+ Use these patterns before reading the full namespace reference.
4
+
5
+ ## Pattern 1: Signal first, follow-trade execution second
6
+
7
+ ```python
8
+ from getagent import runtime
9
+
10
+
11
+ def run() -> None:
12
+ decision = build_live_decision()
13
+
14
+ runtime.emit_signal_or_follow(
15
+ action=decision.action,
16
+ symbol=decision.symbol,
17
+ confidence=decision.confidence,
18
+ metrics=decision.metrics,
19
+ meta=decision.meta,
20
+ execute_trade=lambda: execute_trade(decision),
21
+ )
22
+ ```
23
+
24
+ `runtime.emit_signal_or_follow(...)` always emits the signal. It calls
25
+ `execute_trade` only when the current subscription is `follow_trade` and the
26
+ signal action is actionable.
27
+
28
+ ## Pattern 2: Spot market buy from quote budget
29
+
30
+ ```python
31
+ from getagent import trade
32
+
33
+ symbol = trade.helpers.normalize_trading_symbol("BTC")
34
+ qty_plan = trade.helpers.compute_qty(
35
+ symbol=symbol,
36
+ market="spot",
37
+ budget_amount="100",
38
+ )
39
+ result = trade.spot.market_buy(symbol=symbol, qty=qty_plan.qty)
40
+ if not trade.is_success(result):
41
+ raise RuntimeError(f"spot market buy failed: {result}")
42
+ ```
43
+
44
+ ## Pattern 3: Contract long from margin budget
45
+
46
+ ```python
47
+ from getagent import trade
48
+
49
+ symbol = trade.helpers.normalize_trading_symbol("BTC")
50
+ leverage = 5
51
+ qty_plan = trade.helpers.compute_qty(
52
+ symbol=symbol,
53
+ market="contract",
54
+ budget_amount="20",
55
+ leverage=leverage,
56
+ )
57
+ tpsl_plan = trade.helpers.resolve_contract_tpsl(
58
+ symbol=symbol,
59
+ side="long",
60
+ leverage=leverage,
61
+ )
62
+ result = trade.contract.open_long_market(
63
+ symbol=symbol,
64
+ qty=qty_plan.qty,
65
+ leverage=leverage,
66
+ tp_trigger_price=tpsl_plan.tp_trigger_price,
67
+ sl_trigger_price=tpsl_plan.sl_trigger_price,
68
+ )
69
+ if not trade.is_success(result):
70
+ raise RuntimeError(f"contract open failed: {result}")
71
+ ```
72
+
73
+ For percentage-based TP/SL strategy settings, compute concrete trigger prices
74
+ from the reference price first, then pass `tp_trigger_price` and
75
+ `sl_trigger_price` into `resolve_contract_tpsl(...)`. Do not invent helper kwargs
76
+ such as `tp_pct_override` or `sl_pct_override`; the helper only accepts the
77
+ documented keyword-only parameters.
78
+
79
+ ## Pattern 4: PRE-CHECK -> EXECUTE -> POST-CHECK
80
+
81
+ Use this for cancel, close, and TP/SL modification flows.
82
+
83
+ 1. PRE-CHECK:
84
+ - query `pending_orders(...)`, `plan_pending_orders(...)`, or `current_position(...)`
85
+ - use selector helpers to choose the exact target; for contract positions,
86
+ use `find_contract_position(...)` when a flat account is a normal branch
87
+ 2. EXECUTE:
88
+ - call the mutation with the selected `order_id` or live `hold_side`
89
+ 3. POST-CHECK:
90
+ - query live state again and verify the mutation took effect
91
+
92
+ ## Hard rules
93
+
94
+ - Never import `trade_sdk` directly in Playbook code.
95
+ - Never hardcode `user_id`, `base_url`, or `channel`.
96
+ - Never convert quote or margin budgets to qty manually.
97
+ - Never call trade mutation APIs in a signal-only branch. Emit the signal and
98
+ let `runtime.emit_signal_or_follow(...)` decide whether to call the trade
99
+ callback.
100
+ - Never modify or close a position without checking live state first.
101
+ - Never assume `select_contract_position(...)` returns `.found`; it raises when
102
+ no position exists. First live ticks commonly start flat.