desic-okx-agent 0.2.1 → 0.3.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 (239) hide show
  1. package/README.en.md +90 -10
  2. package/README.md +76 -10
  3. package/dist/account/private-websocket.js +4 -4
  4. package/dist/account/private-websocket.js.map +1 -1
  5. package/dist/account/service.d.ts +12 -1
  6. package/dist/account/service.js +18 -0
  7. package/dist/account/service.js.map +1 -1
  8. package/dist/bars/rate-limiter.d.ts +18 -0
  9. package/dist/bars/rate-limiter.js +84 -0
  10. package/dist/bars/rate-limiter.js.map +1 -0
  11. package/dist/bars/schema.d.ts +36 -0
  12. package/dist/bars/schema.js +134 -0
  13. package/dist/bars/schema.js.map +1 -0
  14. package/dist/bars/service.d.ts +60 -0
  15. package/dist/bars/service.js +120 -0
  16. package/dist/bars/service.js.map +1 -0
  17. package/dist/bars/store.d.ts +105 -0
  18. package/dist/bars/store.js +415 -0
  19. package/dist/bars/store.js.map +1 -0
  20. package/dist/bars/timeframe.d.ts +40 -0
  21. package/dist/bars/timeframe.js +146 -0
  22. package/dist/bars/timeframe.js.map +1 -0
  23. package/dist/bars/types.d.ts +68 -0
  24. package/dist/bars/types.js +13 -0
  25. package/dist/bars/types.js.map +1 -0
  26. package/dist/cli/data-render.d.ts +37 -0
  27. package/dist/cli/data-render.js +143 -0
  28. package/dist/cli/data-render.js.map +1 -0
  29. package/dist/cli/doctor.js +7 -3
  30. package/dist/cli/doctor.js.map +1 -1
  31. package/dist/cli/index.js +757 -26
  32. package/dist/cli/index.js.map +1 -1
  33. package/dist/cli/live-render.d.ts +24 -0
  34. package/dist/cli/live-render.js +85 -0
  35. package/dist/cli/live-render.js.map +1 -0
  36. package/dist/cli/range.d.ts +28 -0
  37. package/dist/cli/range.js +63 -0
  38. package/dist/cli/range.js.map +1 -0
  39. package/dist/cli/render.js +3 -0
  40. package/dist/cli/render.js.map +1 -1
  41. package/dist/cli/strategy-render.d.ts +36 -0
  42. package/dist/cli/strategy-render.js +391 -0
  43. package/dist/cli/strategy-render.js.map +1 -0
  44. package/dist/cli/width.d.ts +18 -0
  45. package/dist/cli/width.js +71 -0
  46. package/dist/cli/width.js.map +1 -0
  47. package/dist/config/loader.js +1 -1
  48. package/dist/config/schema.d.ts +7 -0
  49. package/dist/config/schema.js +24 -0
  50. package/dist/config/schema.js.map +1 -1
  51. package/dist/core/okx-client.d.ts +9 -1
  52. package/dist/core/okx-client.js +14 -5
  53. package/dist/core/okx-client.js.map +1 -1
  54. package/dist/i18n/locale.d.ts +24 -0
  55. package/dist/i18n/locale.js +65 -0
  56. package/dist/i18n/locale.js.map +1 -0
  57. package/dist/i18n/messages.d.ts +333 -0
  58. package/dist/i18n/messages.js +660 -0
  59. package/dist/i18n/messages.js.map +1 -0
  60. package/dist/live/account-snapshot.d.ts +30 -0
  61. package/dist/live/account-snapshot.js +130 -0
  62. package/dist/live/account-snapshot.js.map +1 -0
  63. package/dist/live/cutoff-queue.d.ts +42 -0
  64. package/dist/live/cutoff-queue.js +69 -0
  65. package/dist/live/cutoff-queue.js.map +1 -0
  66. package/dist/live/execution-key.d.ts +23 -0
  67. package/dist/live/execution-key.js +31 -0
  68. package/dist/live/execution-key.js.map +1 -0
  69. package/dist/live/failures.d.ts +37 -0
  70. package/dist/live/failures.js +57 -0
  71. package/dist/live/failures.js.map +1 -0
  72. package/dist/live/gates.d.ts +65 -0
  73. package/dist/live/gates.js +136 -0
  74. package/dist/live/gates.js.map +1 -0
  75. package/dist/live/loop.d.ts +56 -0
  76. package/dist/live/loop.js +197 -0
  77. package/dist/live/loop.js.map +1 -0
  78. package/dist/live/preconditions.d.ts +48 -0
  79. package/dist/live/preconditions.js +69 -0
  80. package/dist/live/preconditions.js.map +1 -0
  81. package/dist/live/reconcile.d.ts +46 -0
  82. package/dist/live/reconcile.js +104 -0
  83. package/dist/live/reconcile.js.map +1 -0
  84. package/dist/live/runner.d.ts +57 -0
  85. package/dist/live/runner.js +160 -0
  86. package/dist/live/runner.js.map +1 -0
  87. package/dist/live/schema.d.ts +18 -0
  88. package/dist/live/schema.js +91 -0
  89. package/dist/live/schema.js.map +1 -0
  90. package/dist/live/service.d.ts +144 -0
  91. package/dist/live/service.js +303 -0
  92. package/dist/live/service.js.map +1 -0
  93. package/dist/live/session.d.ts +85 -0
  94. package/dist/live/session.js +234 -0
  95. package/dist/live/session.js.map +1 -0
  96. package/dist/live/sizing.d.ts +62 -0
  97. package/dist/live/sizing.js +79 -0
  98. package/dist/live/sizing.js.map +1 -0
  99. package/dist/live/store.d.ts +123 -0
  100. package/dist/live/store.js +350 -0
  101. package/dist/live/store.js.map +1 -0
  102. package/dist/live/types.d.ts +82 -0
  103. package/dist/live/types.js +2 -0
  104. package/dist/live/types.js.map +1 -0
  105. package/dist/market/websocket.d.ts +16 -1
  106. package/dist/market/websocket.js +60 -5
  107. package/dist/market/websocket.js.map +1 -1
  108. package/dist/mcp/server.d.ts +1 -0
  109. package/dist/mcp/server.js +15 -1
  110. package/dist/mcp/server.js.map +1 -1
  111. package/dist/network/connectivity.d.ts +9 -1
  112. package/dist/network/connectivity.js +28 -1
  113. package/dist/network/connectivity.js.map +1 -1
  114. package/dist/report/chart-script.d.ts +12 -0
  115. package/dist/report/chart-script.js +146 -0
  116. package/dist/report/chart-script.js.map +1 -0
  117. package/dist/report/compare-html.d.ts +8 -0
  118. package/dist/report/compare-html.js +254 -0
  119. package/dist/report/compare-html.js.map +1 -0
  120. package/dist/report/compare-script.d.ts +12 -0
  121. package/dist/report/compare-script.js +109 -0
  122. package/dist/report/compare-script.js.map +1 -0
  123. package/dist/report/compare.d.ts +61 -0
  124. package/dist/report/compare.js +205 -0
  125. package/dist/report/compare.js.map +1 -0
  126. package/dist/report/fetch.d.ts +20 -0
  127. package/dist/report/fetch.js +56 -0
  128. package/dist/report/fetch.js.map +1 -0
  129. package/dist/report/html.d.ts +54 -0
  130. package/dist/report/html.js +641 -0
  131. package/dist/report/html.js.map +1 -0
  132. package/dist/report/open.d.ts +42 -0
  133. package/dist/report/open.js +114 -0
  134. package/dist/report/open.js.map +1 -0
  135. package/dist/runtime/server.d.ts +16 -1
  136. package/dist/runtime/server.js +112 -9
  137. package/dist/runtime/server.js.map +1 -1
  138. package/dist/setup/installer.d.ts +1 -0
  139. package/dist/setup/installer.js +8 -0
  140. package/dist/setup/installer.js.map +1 -1
  141. package/dist/setup/wizard.js +19 -22
  142. package/dist/setup/wizard.js.map +1 -1
  143. package/dist/strategy/constants.d.ts +23 -0
  144. package/dist/strategy/constants.js +24 -0
  145. package/dist/strategy/constants.js.map +1 -0
  146. package/dist/strategy/environment.d.ts +52 -0
  147. package/dist/strategy/environment.js +187 -0
  148. package/dist/strategy/environment.js.map +1 -0
  149. package/dist/strategy/instrument.d.ts +29 -0
  150. package/dist/strategy/instrument.js +39 -0
  151. package/dist/strategy/instrument.js.map +1 -0
  152. package/dist/strategy/optimize.d.ts +73 -0
  153. package/dist/strategy/optimize.js +113 -0
  154. package/dist/strategy/optimize.js.map +1 -0
  155. package/dist/strategy/parameter-space.d.ts +59 -0
  156. package/dist/strategy/parameter-space.js +221 -0
  157. package/dist/strategy/parameter-space.js.map +1 -0
  158. package/dist/strategy/python-bridge.d.ts +24 -0
  159. package/dist/strategy/python-bridge.js +114 -0
  160. package/dist/strategy/python-bridge.js.map +1 -0
  161. package/dist/strategy/schema.d.ts +11 -0
  162. package/dist/strategy/schema.js +94 -0
  163. package/dist/strategy/schema.js.map +1 -0
  164. package/dist/strategy/series-codec.d.ts +50 -0
  165. package/dist/strategy/series-codec.js +103 -0
  166. package/dist/strategy/series-codec.js.map +1 -0
  167. package/dist/strategy/service.d.ts +138 -0
  168. package/dist/strategy/service.js +745 -0
  169. package/dist/strategy/service.js.map +1 -0
  170. package/dist/strategy/settings.d.ts +162 -0
  171. package/dist/strategy/settings.js +243 -0
  172. package/dist/strategy/settings.js.map +1 -0
  173. package/dist/strategy/store.d.ts +96 -0
  174. package/dist/strategy/store.js +369 -0
  175. package/dist/strategy/store.js.map +1 -0
  176. package/dist/strategy/templates.d.ts +11 -0
  177. package/dist/strategy/templates.js +134 -0
  178. package/dist/strategy/templates.js.map +1 -0
  179. package/dist/strategy/types.d.ts +111 -0
  180. package/dist/strategy/types.js +2 -0
  181. package/dist/strategy/types.js.map +1 -0
  182. package/dist/tools/catalog.d.ts +16 -0
  183. package/dist/tools/catalog.js +118 -17
  184. package/dist/tools/catalog.js.map +1 -1
  185. package/dist/trade/service.d.ts +12 -0
  186. package/dist/trade/service.js +24 -7
  187. package/dist/trade/service.js.map +1 -1
  188. package/dist/tui/app.d.ts +23 -0
  189. package/dist/tui/app.js +322 -0
  190. package/dist/tui/app.js.map +1 -0
  191. package/dist/tui/commands.d.ts +70 -0
  192. package/dist/tui/commands.js +313 -0
  193. package/dist/tui/commands.js.map +1 -0
  194. package/dist/tui/entries.d.ts +17 -0
  195. package/dist/tui/entries.js +24 -0
  196. package/dist/tui/entries.js.map +1 -0
  197. package/dist/tui/execute.d.ts +26 -0
  198. package/dist/tui/execute.js +664 -0
  199. package/dist/tui/execute.js.map +1 -0
  200. package/dist/tui/history.d.ts +17 -0
  201. package/dist/tui/history.js +48 -0
  202. package/dist/tui/history.js.map +1 -0
  203. package/dist/tui/index.d.ts +8 -0
  204. package/dist/tui/index.js +48 -0
  205. package/dist/tui/index.js.map +1 -0
  206. package/dist/tui/line-editor.d.ts +44 -0
  207. package/dist/tui/line-editor.js +98 -0
  208. package/dist/tui/line-editor.js.map +1 -0
  209. package/dist/tui/progress.d.ts +23 -0
  210. package/dist/tui/progress.js +46 -0
  211. package/dist/tui/progress.js.map +1 -0
  212. package/dist/tui/settings-editor.d.ts +18 -0
  213. package/dist/tui/settings-editor.js +115 -0
  214. package/dist/tui/settings-editor.js.map +1 -0
  215. package/docs/live-trading.md +455 -0
  216. package/docs/strategy-research.md +608 -0
  217. package/package.json +10 -1
  218. package/python/desic_strategy/__init__.py +34 -0
  219. package/python/desic_strategy/actions.py +158 -0
  220. package/python/desic_strategy/context.py +171 -0
  221. package/python/desic_strategy/engine.py +614 -0
  222. package/python/desic_strategy/indicators.py +159 -0
  223. package/python/desic_strategy/live.py +253 -0
  224. package/python/desic_strategy/policy.py +193 -0
  225. package/python/desic_strategy/portfolio.py +152 -0
  226. package/python/desic_strategy/report.py +319 -0
  227. package/python/desic_strategy/runner.py +574 -0
  228. package/python/desic_strategy/timeframe.py +150 -0
  229. package/python/main.py +18 -0
  230. package/skills/okx-live-trading/SKILL.md +117 -0
  231. package/skills/okx-live-trading/agents/openai.yaml +9 -0
  232. package/skills/okx-live-trading/references/lifecycle.md +128 -0
  233. package/skills/okx-strategy-research/SKILL.md +113 -0
  234. package/skills/okx-strategy-research/agents/openai.yaml +9 -0
  235. package/skills/okx-strategy-research/references/execution-semantics.md +107 -0
  236. package/skills/okx-strategy-research/references/field-traps.md +142 -0
  237. package/skills/okx-strategy-research/references/python-api.md +125 -0
  238. package/skills/okx-strategy-research/references/tools-and-data.md +192 -0
  239. package/skills/okx-trading/SKILL.md +11 -10
@@ -0,0 +1,142 @@
1
+ # Field traps
2
+
3
+ These are the mistakes that actually get made. Each one is rejected by the source
4
+ policy or the engine, so getting it right the first time saves a round trip.
5
+
6
+ ## Never probe for a field name
7
+
8
+ ```python
9
+ # Rejected: getattr is unavailable to a strategy.
10
+ price = getattr(ctx.bar, "close", None)
11
+
12
+ # Correct.
13
+ price = ctx.bar.close
14
+ ```
15
+
16
+ Probing hides a protocol mismatch that should fail loudly. If a field does not
17
+ exist, the run should stop and say so, not silently fall back to a default and
18
+ produce plausible-looking nonsense.
19
+
20
+ The same applies to `setattr`, `dir`, `vars`, `globals`, `locals`, `eval`, `exec`,
21
+ `compile`, `__import__`, `open`, `input`, and any `__dunder__` access.
22
+
23
+ ## `position` is a method with two arguments
24
+
25
+ ```python
26
+ # Wrong: not a property.
27
+ if ctx.portfolio.position:
28
+
29
+ # Wrong: side is required.
30
+ ctx.portfolio.position(ctx.instrument_id)
31
+
32
+ # Correct.
33
+ if ctx.portfolio.position(ctx.instrument_id, "long") is not None:
34
+ ```
35
+
36
+ ## The size field is `quantity`
37
+
38
+ Not `contracts`, `size`, `contractCount`, or `qty`.
39
+
40
+ ```python
41
+ position = ctx.portfolio.position(ctx.instrument_id, "long")
42
+ if position is not None:
43
+ held = position.quantity
44
+ ```
45
+
46
+ ## Decisions carry no size
47
+
48
+ ```python
49
+ # Wrong: no quantity argument exists.
50
+ ctx.open_long(2.0, "momentum")
51
+
52
+ # Correct: reason first, host decides size.
53
+ ctx.open_long("momentum")
54
+ ```
55
+
56
+ ## A limit price is never positional
57
+
58
+ ```python
59
+ # Wrong: this function does not exist.
60
+ ctx.open_long_limit("entry", 67000.0)
61
+
62
+ # Wrong: the second positional argument is protection.
63
+ ctx.open_long("entry", 67000.0)
64
+
65
+ # Correct.
66
+ ctx.open_long("entry", execution=ctx.limit_order(67000.0))
67
+ ```
68
+
69
+ ## Indicators return None during warm-up
70
+
71
+ ```python
72
+ fast = ctx.indicators.ema(ctx.instrument_id, "1m", 20)
73
+ slow = ctx.indicators.ema(ctx.instrument_id, "1m", 60)
74
+
75
+ # Wrong: comparing None raises.
76
+ if fast > slow:
77
+
78
+ # Correct.
79
+ if fast is None or slow is None:
80
+ return ctx.no_action("indicators warming up")
81
+ if fast > slow:
82
+ ...
83
+ ```
84
+
85
+ EMA needs `period` bars; ATR needs `period + 1`.
86
+
87
+ ## Indicators only accept `1m`
88
+
89
+ ```python
90
+ # Rejected: a higher-timeframe bucket can be revised before it confirms.
91
+ ctx.indicators.ema(ctx.instrument_id, "15m", 20)
92
+ ```
93
+
94
+ Compute a higher-timeframe value from `ctx.market.bars(..., "15m", ...)` yourself,
95
+ or use a `1m` period covering an equivalent span.
96
+
97
+ ## `ctx` is read-only
98
+
99
+ ```python
100
+ # Rejected.
101
+ ctx.as_of_ms = 0
102
+ ```
103
+
104
+ ## Return exactly one decision
105
+
106
+ Every path through `on_bar` must return a decision object. Returning `None`, a
107
+ string, or a bare number is rejected. Returning two actions from one bar is not
108
+ expressible — emit one and act on the next bar.
109
+
110
+ ## Reversal must be explicit
111
+
112
+ ```python
113
+ # Rejected while a long is open.
114
+ return ctx.open_short("flip")
115
+
116
+ # Correct: close first, reverse on a later bar.
117
+ if ctx.portfolio.position(ctx.instrument_id, "long") is not None:
118
+ return ctx.close_long("exiting before reversing")
119
+ ```
120
+
121
+ ## `cancel_order` needs a live id
122
+
123
+ ```python
124
+ orders = ctx.portfolio.open_orders
125
+ if orders:
126
+ return ctx.cancel_order(orders[0].id, "no longer wanted")
127
+ ```
128
+
129
+ An id that is not currently open is an error, not a no-op.
130
+
131
+ ## The current bar is the last item
132
+
133
+ ```python
134
+ bars = ctx.market.bars(ctx.instrument_id, "1m", lookback=60)
135
+
136
+ # Includes the bar that just closed — a "prior range high" computed this way
137
+ # is contaminated by the current bar.
138
+ highest = max(bar.high for bar in bars)
139
+
140
+ # Correct: exclude the current bar.
141
+ highest = max(bar.high for bar in bars[:-1])
142
+ ```
@@ -0,0 +1,125 @@
1
+ # Strategy API
2
+
3
+ Every object is immutable and bounded by `ctx.as_of_ms`. Field names are exactly
4
+ as written here.
5
+
6
+ ## Handlers
7
+
8
+ ```python
9
+ def on_bar(ctx): # required, runs after each confirmed 1m close
10
+ ...
11
+
12
+ def on_start(ctx): # optional, initialization only, must return ctx.no_action(...)
13
+ ...
14
+ ```
15
+
16
+ Both are synchronous and take exactly one positional argument. Ordinary helper
17
+ functions may be defined freely; the host never calls them.
18
+
19
+ ## Context
20
+
21
+ | Field | Meaning |
22
+ | --- | --- |
23
+ | `ctx.as_of_ms` | Cutoff in Unix milliseconds. During `on_bar`, the active bar's `closeTimeMs`. |
24
+ | `ctx.instrument_id` | Active instrument, for example `BTC-USDT-SWAP`. |
25
+ | `ctx.interval` | Event interval. Always `1m`. |
26
+ | `ctx.kind` | `start` or `bar`. |
27
+ | `ctx.bar` | The bar that just closed. Populated only during `on_bar`. |
28
+ | `ctx.params` | Immutable mapping of saved parameters. `ctx.params.get("fastPeriod", 20)`. |
29
+
30
+ ## Market data
31
+
32
+ ```python
33
+ bars = ctx.market.bars(ctx.instrument_id, "1m", lookback=240)
34
+ ```
35
+
36
+ `lookback` returns the final N items and must be a positive integer. Supported
37
+ intervals are `1m`, `3m`, `5m`, `15m`, `30m`, `1H`, `2H`, `4H`, `6H`, `12H`, and
38
+ `1D`, but a higher timeframe must be requested for the run before it is readable.
39
+
40
+ Always pass `lookback` when only a tail is needed. It costs the size of the window
41
+ asked for, whereas omitting it copies every bar seen so far on every call — on a
42
+ long run that is the difference between a few seconds and a few minutes.
43
+
44
+ `Bar` fields: `openTimeMs`, `closeTimeMs`, `open`, `high`, `low`, `close`,
45
+ `volume`, `confirmed`.
46
+
47
+ All `1m` bars are confirmed. For higher intervals only the final item can be
48
+ `confirmed=False`; its OHLCV contains solely the minutes already known. Require
49
+ `bar.confirmed` before treating a higher-timeframe value as a confirmation.
50
+
51
+ ## Indicators
52
+
53
+ ```python
54
+ fast = ctx.indicators.ema(ctx.instrument_id, "1m", 20)
55
+ atr = ctx.indicators.atr(ctx.instrument_id, "1m", 14, offset=1)
56
+ ```
57
+
58
+ Both accept only `"1m"`. `offset=0` is the current bar, `offset=1` the previous
59
+ one. EMA returns `None` until `period` bars exist; ATR until `period + 1`, because
60
+ true range needs a prior close. Always handle `None` explicitly.
61
+
62
+ ## Portfolio
63
+
64
+ ```python
65
+ position = ctx.portfolio.position(ctx.instrument_id, "long") # Position or None
66
+ ```
67
+
68
+ `position(instrument_id, side)` is a **method** taking both arguments, not a
69
+ property. `side` is `"long"` or `"short"`. `ctx.position(...)` is an alias.
70
+
71
+ | Accessor | Meaning |
72
+ | --- | --- |
73
+ | `ctx.portfolio.cash_usdt`, `equity_usdt` | Virtual cash and account equity. |
74
+ | `ctx.portfolio.used_margin_usdt`, `available_margin_usdt` | Virtual margin. |
75
+ | `ctx.portfolio.positions` | Tuple of open positions. |
76
+ | `ctx.portfolio.positions_for(instrument_id)` | Positions for one instrument. |
77
+ | `ctx.portfolio.open_orders` | Resting limit orders. |
78
+ | `ctx.portfolio.recent_fills`, `trades` | Simulated fills and closed trades. |
79
+
80
+ `Position`: `instrumentId`, `side`, `quantity`, `averageEntryPrice`, `markPrice`,
81
+ `contractValue`, `notionalUsdt`, `usedMarginUsdt`, `leverage`,
82
+ `marginSafetyMultiplier`, `unrealizedPnlUsdt`, `entryFeeUsdt`, `stopLossPrice`,
83
+ `takeProfitPrice`, `openedAtMs`, `updatedAtMs`.
84
+
85
+ The size field is `quantity` — never `contracts`, `size`, or `contractCount`.
86
+
87
+ `OpenOrder`: `id`, `instrumentId`, `action`, `quantity`, `filledQuantity`,
88
+ `status`, `price`, `createdAtMs`.
89
+
90
+ `Trade`: `id`, `instrumentId`, `side`, `quantity`, `entryPrice`, `exitPrice`,
91
+ `usedMarginUsdt`, `leverage`, `realizedPnlUsdt`, `feesUsdt`, `openedAtMs`,
92
+ `closedAtMs`.
93
+
94
+ ## Decisions
95
+
96
+ Return exactly one per bar:
97
+
98
+ ```python
99
+ ctx.no_action(reason)
100
+ ctx.open_long(reason, protection=None, execution=None)
101
+ ctx.open_short(reason, protection=None, execution=None)
102
+ ctx.close_long(reason, execution=None)
103
+ ctx.close_short(reason, execution=None)
104
+ ctx.cancel_order(order_id, reason)
105
+ ```
106
+
107
+ `reason` is required and is the first positional argument. Every other argument
108
+ must be passed by keyword.
109
+
110
+ Opening and closing decisions carry **no size**. The host derives a legal
111
+ contract count from its budget and the instrument's rules.
112
+
113
+ `execution` defaults to `ctx.market_order()`. The only alternative is
114
+ `ctx.limit_order(price)`. There is no `open_long_limit`.
115
+
116
+ `protection` accompanies an opening decision only, as a mapping of absolute
117
+ prices:
118
+
119
+ ```python
120
+ ctx.open_long("breakout", protection={"stopLossPrice": 67000.0, "takeProfitPrice": 70000.0})
121
+ ```
122
+
123
+ `cancel_order` must name an id currently in `ctx.portfolio.open_orders`.
124
+
125
+ Reversal is explicit: close the current side before opening the opposite one.
@@ -0,0 +1,192 @@
1
+ # Tools and data
2
+
3
+ ## Data is one-minute only
4
+
5
+ Only confirmed one-minute bars are stored. Every higher timeframe is derived from
6
+ them at read time, which is why a backtest and a live evaluation can never
7
+ disagree about where a bucket starts.
8
+
9
+ There is no `bar` parameter on any data tool.
10
+
11
+ ## Tools
12
+
13
+ | Tool | Use |
14
+ | --- | --- |
15
+ | `data_list_instruments` | Every instrument with local history and its covered range. |
16
+ | `data_coverage` | One instrument's range, bar count, and gap list. |
17
+ | `data_download` | Download or repair an exact range. |
18
+ | `data_download_progress` | Progress of a running download, for a progress display. |
19
+ | `strategy_settings` | Read the assumptions every backtest uses by default. |
20
+ | `strategy_settings_update` | Change saved assumptions. Pass only the keys to change. |
21
+ | `strategy_environment` | Whether Python is ready. |
22
+ | `strategy_environment_setup` | Create the venv and install packages. Prefer telling the user to run the CLI. |
23
+ | `strategy_validate_source` | Static policy check with line numbers. |
24
+ | `strategy_run_backtest` | Queue a run, returns `runId` immediately. |
25
+ | `strategy_run_optimize` | Queue a parameter search. Splits the window, ranks out of sample. |
26
+ | `strategy_get_optimization` | Ranked candidates with training and validation metrics side by side. |
27
+ | `strategy_get_run` | Status, progress, summary metrics. |
28
+ | `strategy_list_runs` | Recent runs, newest first. |
29
+ | `strategy_get_run_equity` | Paged equity curve. |
30
+ | `strategy_get_run_trades` | Paged closed trades. |
31
+ | `strategy_get_run_actions` | Paged decisions the strategy emitted. |
32
+ | `strategy_compare_runs` | Two or more runs side by side, with the assumptions that differ and comparability warnings. |
33
+ | `strategy_cancel_run` | Cancel a queued or running run. |
34
+ | `strategy_delete_run` | Delete a run and its stored series. Confirm with the user first. |
35
+
36
+ ## Check coverage before backtesting
37
+
38
+ ```
39
+ data_coverage { "instId": "BTC-USDT-SWAP" }
40
+ ```
41
+
42
+ `missingCount` above zero means the range has holes. A backtest over a window
43
+ containing one fails outright, so repair it first:
44
+
45
+ ```
46
+ data_download { "instId": "BTC-USDT-SWAP", "days": 90 }
47
+ data_download { "instId": "BTC-USDT-SWAP", "fromMs": 1767225600000, "toMs": 1782950400000 }
48
+ ```
49
+
50
+ `exhaustedBefore` means OKX has no data earlier than that point. Requesting more
51
+ history below it will not help; say so instead of retrying.
52
+
53
+ ## Assumptions
54
+
55
+ Leverage, entry budget, fees, slippage, margin safety, preload bars, and
56
+ close-at-end are saved settings, readable with `strategy_settings`. A backtest
57
+ request may override any of them for one run.
58
+
59
+ State them when reporting a result: a return figure means nothing without the
60
+ leverage and costs that produced it, and two runs are comparable only when these
61
+ match. Every completed run records what it used, returned as `assumptions` from
62
+ `strategy_get_run`.
63
+
64
+ ## Backtest windows
65
+
66
+ `fromMs`/`toMs` bound the **evaluation** range. `preloadBars` are loaded entirely
67
+ *before* it, as warm-up context, and are excluded from every reported statistic.
68
+
69
+ The evaluation end is always clamped to at least one hour behind now, because the
70
+ most recent minutes are the ones an exchange is most likely to revise.
71
+
72
+ Limits: 365 evaluation days, 600,000 total bars including preload, minimum 2
73
+ preload bars.
74
+
75
+ ## Runs are asynchronous
76
+
77
+ `strategy_run_backtest` returns `{ runId, status: "queued" }`. Poll
78
+ `strategy_get_run` until `status` is `completed`, `failed`, or `cancelled`.
79
+ `progressPct` and `etaMs` are populated while running.
80
+
81
+ A returned call is not a finished run. Never report metrics without confirming
82
+ `status` is `completed`.
83
+
84
+ ## Read details in pages
85
+
86
+ `strategy_get_run` carries summary metrics only. Equity, trades, and actions are
87
+ separate paged reads with `offset` and `limit`. A full curve is tens of thousands
88
+ of points; fetch only what supports the point you are making.
89
+
90
+ ## Parameter search
91
+
92
+ `strategy_run_optimize` takes a backtest request plus a `space` and a `budget`:
93
+
94
+ ```
95
+ strategy_run_optimize {
96
+ "instId": "BTC-USDT-SWAP",
97
+ "source": "...",
98
+ "params": { "fastPeriod": 20, "slowPeriod": 60 },
99
+ "space": {
100
+ "fastPeriod": { "min": 5, "max": 50, "step": 5 },
101
+ "slowPeriod": { "min": 20, "max": 200, "step": 10 }
102
+ },
103
+ "budget": 100,
104
+ "days": 90
105
+ }
106
+ ```
107
+
108
+ Pass `params` with the strategy's own defaults. Every key in `space` is checked
109
+ against them, and a range naming a parameter the strategy never reads is rejected
110
+ — otherwise every candidate would behave identically and the winner would be
111
+ noise.
112
+
113
+ Limits: at most 5,000 combinations in the space and a budget of at most 1,000. A
114
+ budget below the widest parameter's value count is refused, because covering every
115
+ value would then be impossible.
116
+
117
+ The window is split **70/30**. Candidates are searched over the first segment and
118
+ **ranked by the second**, which they never saw. Both segments come from one bar
119
+ load, so they cannot disagree about the underlying data.
120
+
121
+ Read the result with `strategy_get_optimization`, which returns candidates in rank
122
+ order with `train` and `validation` metrics on each, plus a `verdict`:
123
+
124
+ | Verdict | Meaning |
125
+ | --- | --- |
126
+ | `holds` | Profitable on the held-back segment, keeping a fair share of the training result. |
127
+ | `overfit` | Profitable while being chosen, unprofitable or badly decayed afterwards. |
128
+ | `weak` | Unprofitable on the held-back segment either way. |
129
+ | `failed` | The candidate errored; read its `error`. |
130
+
131
+ `rankBasis` names the formula behind `rankScore`. It is `calmar` when the
132
+ validation segment is long enough to annualize, `return_over_drawdown` when it is
133
+ not, and `return` when no candidate drew down. Quote it — the column is not always
134
+ the same quantity.
135
+
136
+ ## Reporting a search
137
+
138
+ Quote the **validation** figures, not the training ones. The training numbers
139
+ describe performance over the bars that selected the parameters, so they are not
140
+ evidence and must never be presented as the result.
141
+
142
+ Show both columns anyway. The gap between them is the finding: a large training
143
+ return beside a weak validation return means the parameters fitted noise, and that
144
+ is worth saying plainly rather than reporting the best row as a success.
145
+
146
+ Say that the winner's validation figure is still optimistic. Ranking many
147
+ candidates by that score selects for a high one, so the split removes most of the
148
+ bias but not all of it, and the remainder grows with the budget. A clean estimate
149
+ needs a period later than the whole search window. Never present the leader's
150
+ validation return as an expected future return.
151
+
152
+ Prefer a candidate whose neighbours also hold over a single sharp optimum. A lone
153
+ spike surrounded by poor results is a coincidence in the data, not a setting.
154
+
155
+ To use a winning set, write it to `<strategy>.params.json` beside the strategy —
156
+ never edit the strategy source, which is the user's. Then run one ordinary
157
+ backtest with those parameters to confirm, and say that this confirmation still
158
+ covers the same period the search used.
159
+
160
+ ## Comparing runs
161
+
162
+ ```
163
+ strategy_compare_runs { "runIds": ["bt_abc123", "bt_def456"] }
164
+ ```
165
+
166
+ Two to six runs. The response carries each run's metrics, the assumption keys whose
167
+ values differ, the ones every run shares, and a `warnings` array.
168
+
169
+ **Read `warnings` before the metrics and repeat them to the user.** They are the
170
+ reason a comparison may be meaningless:
171
+
172
+ | Warning code | Meaning |
173
+ | --- | --- |
174
+ | data&#95;snapshot | The runs consumed different bars. Any difference may come from the data rather than the strategy. |
175
+ | instrument | Different instruments. Each return reflects its own market's move. |
176
+ | window | Different evaluation windows, so the totals span different amounts of time. |
177
+ | assumptions | Different leverage, fees, slippage, or budget. |
178
+ | incomplete | A run did not finish and has no metrics. |
179
+
180
+ A comparison is only evidence that one variant beats another when every run shares
181
+ one `dataSnapshotId`, one instrument, one window, and one set of costs. When it does
182
+ not, say which of those differed instead of naming a winner.
183
+
184
+ Equity curves are indexed to 100 at each run's own start, so different initial
185
+ equity does not change the shape. Comparing absolute equity between runs that
186
+ started from different balances says nothing.
187
+
188
+ ## Reproducibility
189
+
190
+ Each run records a `dataSnapshotId` whose hash covers the exact bars consumed. Two
191
+ identical requests over unchanged data yield the same id and the same metrics. A
192
+ differing id means the underlying data changed — quote the id when comparing runs.
@@ -1,16 +1,17 @@
1
1
  ---
2
2
  name: okx-trading
3
- description: Precheck and execute direct OKX leverage, ordinary-order, algo-order, amend, cancel, and position-close operations. Use when the user explicitly requests an OKX trading action and has configured an API key with the required official permission.
3
+ description: Precheck and execute direct OKX perpetual-swap leverage, ordinary-order, algo-order, amend, cancel, and position-close operations. Use when the user explicitly requests an OKX perpetual trading action and has configured an API key with the required official permission.
4
4
  ---
5
5
 
6
6
  # OKX Trading
7
7
 
8
- 1. Identify the account alias, environment, instrument, side, margin mode, position side, order type, price, and size from the user's request. Do not invent missing trading intent.
9
- 2. Call `market_get_instrument`; treat `size` as contracts for derivatives and respect `lotSz`, `minSz`, and `tickSz`.
10
- 3. Call `market_get_decision_snapshot`. Do not proceed when `consistent` is false.
11
- 4. Call `trade_evaluate_plan`, then `trade_precheck_order` for ordinary orders or `trade_precheck_algo_order` for strategy orders. Resolve every blocker before submitting.
12
- 5. Create a stable, unique `executionKey` for each intended mutation. Reuse it only when retrying the exact same intent.
13
- 6. Call the narrowest write tool that matches the request. Do not substitute batch cancellation or full position closure for a narrower action.
14
- 7. If a result is `AMBIGUOUS_WRITE`, inspect remote order state. Never retry with a new execution key until the original outcome is resolved.
15
- 8. Report the exchange environment, returned order identifiers, status, reconciliation state, and any warnings.
16
- 9. Never use or propose withdrawal, transfer, deposit, or API-key-management operations.
8
+ 1. This workflow supports OKX perpetual swaps only. Require an `instId` ending in `-SWAP`; refuse spot, dated futures, options, and every other instrument type.
9
+ 2. Identify the account alias, instrument, side, margin mode, position side, order type, price, and size from the user's request. Use the account environment detected by the Runtime; do not ask the user to classify the API key or invent missing trading intent.
10
+ 3. Call `market_get_instrument`; treat `size` as contracts and respect `lotSz`, `minSz`, and `tickSz`.
11
+ 4. Call `market_get_decision_snapshot`. Do not proceed when `consistent` is false.
12
+ 5. Call `trade_evaluate_plan`, then `trade_precheck_order` for ordinary orders or `trade_precheck_algo_order` for strategy orders. Resolve every blocker before submitting.
13
+ 6. Create a stable, unique `executionKey` for each intended mutation. Reuse it only when retrying the exact same intent.
14
+ 7. Call the narrowest write tool that matches the request. Do not substitute batch cancellation or full position closure for a narrower action.
15
+ 8. If a result is `AMBIGUOUS_WRITE`, inspect remote order state. Never retry with a new execution key until the original outcome is resolved.
16
+ 9. Report the exchange environment, returned order identifiers, status, reconciliation state, and any warnings.
17
+ 10. Never use or propose withdrawal, transfer, deposit, or API-key-management operations.