desic-okx-agent 0.2.0 → 0.3.0

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 (237) hide show
  1. package/README.en.md +353 -0
  2. package/README.md +194 -190
  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 +758 -27
  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.d.ts +3 -0
  142. package/dist/setup/wizard.js +131 -6
  143. package/dist/setup/wizard.js.map +1 -1
  144. package/dist/strategy/constants.d.ts +23 -0
  145. package/dist/strategy/constants.js +24 -0
  146. package/dist/strategy/constants.js.map +1 -0
  147. package/dist/strategy/environment.d.ts +52 -0
  148. package/dist/strategy/environment.js +187 -0
  149. package/dist/strategy/environment.js.map +1 -0
  150. package/dist/strategy/instrument.d.ts +29 -0
  151. package/dist/strategy/instrument.js +39 -0
  152. package/dist/strategy/instrument.js.map +1 -0
  153. package/dist/strategy/optimize.d.ts +73 -0
  154. package/dist/strategy/optimize.js +113 -0
  155. package/dist/strategy/optimize.js.map +1 -0
  156. package/dist/strategy/parameter-space.d.ts +59 -0
  157. package/dist/strategy/parameter-space.js +221 -0
  158. package/dist/strategy/parameter-space.js.map +1 -0
  159. package/dist/strategy/python-bridge.d.ts +24 -0
  160. package/dist/strategy/python-bridge.js +114 -0
  161. package/dist/strategy/python-bridge.js.map +1 -0
  162. package/dist/strategy/schema.d.ts +9 -0
  163. package/dist/strategy/schema.js +91 -0
  164. package/dist/strategy/schema.js.map +1 -0
  165. package/dist/strategy/service.d.ts +138 -0
  166. package/dist/strategy/service.js +745 -0
  167. package/dist/strategy/service.js.map +1 -0
  168. package/dist/strategy/settings.d.ts +162 -0
  169. package/dist/strategy/settings.js +243 -0
  170. package/dist/strategy/settings.js.map +1 -0
  171. package/dist/strategy/store.d.ts +96 -0
  172. package/dist/strategy/store.js +367 -0
  173. package/dist/strategy/store.js.map +1 -0
  174. package/dist/strategy/templates.d.ts +11 -0
  175. package/dist/strategy/templates.js +134 -0
  176. package/dist/strategy/templates.js.map +1 -0
  177. package/dist/strategy/types.d.ts +111 -0
  178. package/dist/strategy/types.js +2 -0
  179. package/dist/strategy/types.js.map +1 -0
  180. package/dist/tools/catalog.d.ts +16 -0
  181. package/dist/tools/catalog.js +118 -17
  182. package/dist/tools/catalog.js.map +1 -1
  183. package/dist/trade/service.d.ts +12 -0
  184. package/dist/trade/service.js +24 -7
  185. package/dist/trade/service.js.map +1 -1
  186. package/dist/tui/app.d.ts +23 -0
  187. package/dist/tui/app.js +322 -0
  188. package/dist/tui/app.js.map +1 -0
  189. package/dist/tui/commands.d.ts +70 -0
  190. package/dist/tui/commands.js +313 -0
  191. package/dist/tui/commands.js.map +1 -0
  192. package/dist/tui/entries.d.ts +17 -0
  193. package/dist/tui/entries.js +24 -0
  194. package/dist/tui/entries.js.map +1 -0
  195. package/dist/tui/execute.d.ts +26 -0
  196. package/dist/tui/execute.js +664 -0
  197. package/dist/tui/execute.js.map +1 -0
  198. package/dist/tui/history.d.ts +17 -0
  199. package/dist/tui/history.js +48 -0
  200. package/dist/tui/history.js.map +1 -0
  201. package/dist/tui/index.d.ts +8 -0
  202. package/dist/tui/index.js +48 -0
  203. package/dist/tui/index.js.map +1 -0
  204. package/dist/tui/line-editor.d.ts +44 -0
  205. package/dist/tui/line-editor.js +98 -0
  206. package/dist/tui/line-editor.js.map +1 -0
  207. package/dist/tui/progress.d.ts +23 -0
  208. package/dist/tui/progress.js +46 -0
  209. package/dist/tui/progress.js.map +1 -0
  210. package/dist/tui/settings-editor.d.ts +18 -0
  211. package/dist/tui/settings-editor.js +115 -0
  212. package/dist/tui/settings-editor.js.map +1 -0
  213. package/docs/live-trading.md +455 -0
  214. package/docs/strategy-research.md +597 -0
  215. package/package.json +11 -1
  216. package/python/desic_strategy/__init__.py +34 -0
  217. package/python/desic_strategy/actions.py +158 -0
  218. package/python/desic_strategy/context.py +164 -0
  219. package/python/desic_strategy/engine.py +614 -0
  220. package/python/desic_strategy/indicators.py +159 -0
  221. package/python/desic_strategy/live.py +253 -0
  222. package/python/desic_strategy/policy.py +193 -0
  223. package/python/desic_strategy/portfolio.py +152 -0
  224. package/python/desic_strategy/report.py +319 -0
  225. package/python/desic_strategy/runner.py +574 -0
  226. package/python/desic_strategy/timeframe.py +150 -0
  227. package/python/main.py +18 -0
  228. package/skills/okx-live-trading/SKILL.md +117 -0
  229. package/skills/okx-live-trading/agents/openai.yaml +9 -0
  230. package/skills/okx-live-trading/references/lifecycle.md +128 -0
  231. package/skills/okx-strategy-research/SKILL.md +113 -0
  232. package/skills/okx-strategy-research/agents/openai.yaml +9 -0
  233. package/skills/okx-strategy-research/references/execution-semantics.md +107 -0
  234. package/skills/okx-strategy-research/references/field-traps.md +142 -0
  235. package/skills/okx-strategy-research/references/python-api.md +121 -0
  236. package/skills/okx-strategy-research/references/tools-and-data.md +192 -0
  237. package/skills/okx-trading/SKILL.md +11 -10
@@ -0,0 +1,303 @@
1
+ import { RuntimeError } from "../core/errors.js";
2
+ import { resultMeta } from "../core/types.js";
3
+ import { loadContractSpec } from "../strategy/instrument.js";
4
+ import { pythonStatus } from "../strategy/environment.js";
5
+ import { checkPreconditions } from "./preconditions.js";
6
+ import { reconcileSignals } from "./reconcile.js";
7
+ import { describeAutoDisable, evaluateStreak } from "./failures.js";
8
+ /** Consecutive failures after which a profile stops itself. */
9
+ export const ERROR_STREAK_LIMIT = 3;
10
+ /**
11
+ * Owns live profiles: their lifecycle, and what has to be true before one trades.
12
+ *
13
+ * Creating a profile never starts it. Enabling is the only step that can lead to an
14
+ * order, and it is the step that checks preconditions — so a profile can be written,
15
+ * inspected, and left alone safely.
16
+ */
17
+ export class LiveService {
18
+ store;
19
+ strategyStore;
20
+ bars;
21
+ market;
22
+ accounts;
23
+ client;
24
+ trade;
25
+ errorStreakLimit;
26
+ constructor(store, strategyStore, bars, market, accounts, client,
27
+ /** Used to apply the profile's leverage at enable time. See `applyLeverage`. */
28
+ trade,
29
+ /** Consecutive systemic failures after which a profile stops itself. */
30
+ errorStreakLimit = ERROR_STREAK_LIMIT) {
31
+ this.store = store;
32
+ this.strategyStore = strategyStore;
33
+ this.bars = bars;
34
+ this.market = market;
35
+ this.accounts = accounts;
36
+ this.client = client;
37
+ this.trade = trade;
38
+ this.errorStreakLimit = errorStreakLimit;
39
+ }
40
+ onDisabled;
41
+ /**
42
+ * Marks profiles a dead worker left running.
43
+ *
44
+ * Called at startup. Their generation advances, so an order decided by the previous
45
+ * process cannot be submitted if it somehow still arrives.
46
+ */
47
+ recoverInterrupted() {
48
+ return this.store.failInterruptedProfiles();
49
+ }
50
+ /**
51
+ * Resolves signals left unresolved by a previous process.
52
+ *
53
+ * Every one is checked against the exchange rather than assumed. Nothing is resubmitted:
54
+ * re-sending an order that may already exist is the one mistake worse than missing one,
55
+ * so anything that cannot be resolved is marked ambiguous and waits for a person.
56
+ */
57
+ async reconcile() {
58
+ const outcome = await reconcileSignals({
59
+ client: this.client,
60
+ accounts: this.accounts,
61
+ store: this.store
62
+ });
63
+ return { data: outcome, meta: resultMeta({ source: "mixed" }) };
64
+ }
65
+ createProfile(input) {
66
+ validateBudgets(input);
67
+ // Deliberately does not check preconditions: a profile that cannot yet be enabled
68
+ // is still worth creating, and refusing here would mean writing the definition and
69
+ // the evidence in one step.
70
+ return { data: this.store.createProfile(input), meta: resultMeta({ source: "sqlite" }) };
71
+ }
72
+ listProfiles() {
73
+ return { data: this.store.listProfiles(), meta: resultMeta({ source: "sqlite" }) };
74
+ }
75
+ getProfile(id) {
76
+ return { data: this.store.requireProfile(id), meta: resultMeta({ source: "sqlite" }) };
77
+ }
78
+ signals(profileId, limit = 50) {
79
+ this.store.requireProfile(profileId);
80
+ return { data: this.store.listSignals(profileId, limit), meta: resultMeta({ source: "sqlite" }) };
81
+ }
82
+ /**
83
+ * Reports whether a profile could be enabled, without enabling it.
84
+ *
85
+ * Separate from `enable` so the list of blockers can be read before committing to
86
+ * anything — and so a caller that wants to show them does not have to trigger a
87
+ * failure to find out.
88
+ */
89
+ async readiness(id) {
90
+ const profile = this.store.requireProfile(id);
91
+ const facts = await this.gatherFacts(profile);
92
+ return {
93
+ data: {
94
+ profile,
95
+ ready: facts.failures.length === 0,
96
+ failures: facts.failures,
97
+ backtestRunId: facts.backtestRunId
98
+ },
99
+ meta: resultMeta({ source: "mixed" })
100
+ };
101
+ }
102
+ /**
103
+ * Enables a profile, refusing unless every precondition holds.
104
+ *
105
+ * The backtest that justified it is recorded on the profile, so a later reader can
106
+ * see which run was the evidence rather than having to trust that one existed.
107
+ */
108
+ async enable(id) {
109
+ const profile = this.store.requireProfile(id);
110
+ const facts = await this.gatherFacts(profile);
111
+ if (facts.failures.length > 0) {
112
+ throw new RuntimeError("VALIDATION", `Cannot enable '${profile.name}': ${facts.failures.map((failure) => failure.message).join(" ")}`, false, { failures: facts.failures });
113
+ }
114
+ // Before the row is written, so a profile is never enabled at a leverage other than
115
+ // its own. Sizing divides the entry budget by `profile.leverage`; if the exchange
116
+ // holds a different figure, the margin actually committed differs by that ratio and
117
+ // the budget stops meaning what it says. Measured on demo: a profile set to 10x on
118
+ // an account left at 3x turned a 60 USDT entry budget into roughly 198 USDT of
119
+ // margin.
120
+ const applied = await this.applyLeverage(profile);
121
+ return {
122
+ data: {
123
+ profile: this.store.enableProfile(id, facts.backtestRunId),
124
+ backtestRunId: facts.backtestRunId,
125
+ leverage: applied
126
+ },
127
+ meta: resultMeta({ source: "mixed" })
128
+ };
129
+ }
130
+ /**
131
+ * Writes the profile's leverage to the exchange, and refuses to enable if it cannot.
132
+ *
133
+ * This changes a setting the account shares across everything trading that instrument
134
+ * and margin mode, including positions opened by hand. That is the cost of the budgets
135
+ * meaning what they say, and it is why it happens at enable rather than silently on
136
+ * every bar.
137
+ *
138
+ * A failure here stops the enable. Continuing would leave the profile trading at a
139
+ * leverage nobody chose, which is the exact condition this call exists to remove.
140
+ */
141
+ async applyLeverage(profile) {
142
+ const lever = String(profile.leverage);
143
+ try {
144
+ await this.trade.setLeverage({
145
+ account: profile.account,
146
+ // Deterministic, so a retried enable reuses the same execution record instead of
147
+ // writing a second one for the same intent.
148
+ executionKey: `live-leverage:${profile.id}:${lever}:${profile.marginMode}`,
149
+ instId: profile.instId,
150
+ lever,
151
+ mgnMode: profile.marginMode
152
+ });
153
+ return { requested: profile.leverage, applied: true };
154
+ }
155
+ catch (error) {
156
+ const detail = error instanceof Error ? error.message : String(error);
157
+ throw new RuntimeError("VALIDATION", `Cannot enable '${profile.name}': setting ${profile.instId} to ${lever}x ${profile.marginMode} was refused (${detail}). Its entry budget is sized against ${lever}x, so trading at whatever the account currently holds would commit a different amount of margin than the budget states.`, false, { instId: profile.instId, leverage: profile.leverage, marginMode: profile.marginMode });
158
+ }
159
+ }
160
+ /**
161
+ * Stops a profile.
162
+ *
163
+ * Always allowed and never precondition-checked: whatever is wrong, stopping is the
164
+ * safe direction, and a stop that could fail would be worse than useless.
165
+ */
166
+ disable(id, reason) {
167
+ this.store.requireProfile(id);
168
+ const profile = this.store.disableProfile(id, reason ?? null);
169
+ // The row is written first, so a stop takes effect even if releasing the session fails.
170
+ // Releasing promptly matters because a session owns a Python child process; waiting for
171
+ // the next bar would leave it running against a profile that has stopped.
172
+ void this.onDisabled?.(id);
173
+ return { data: profile, meta: resultMeta({ source: "sqlite" }) };
174
+ }
175
+ /** Registers the loop's session release, called after a profile is disabled. */
176
+ onProfileDisabled(release) {
177
+ this.onDisabled = release;
178
+ }
179
+ /**
180
+ * Records a failed evaluation and stops the profile if they keep coming.
181
+ *
182
+ * One failure never stops anything: the risk gates still refuse whatever is unsafe, and
183
+ * halting on a single dropped connection would make the whole thing unusable. A run of
184
+ * failures is different — it means something systemic, and continuing only accumulates
185
+ * errors against an exchange nobody is watching.
186
+ *
187
+ * Only failures that will not fix themselves count. A profile that could not reach OKX
188
+ * for one minute is in a different position from one whose strategy throws on every bar.
189
+ * Transient failures are still recorded, so the minute is never silently dropped; they
190
+ * just do not push toward a shutdown.
191
+ */
192
+ recordFailure(id, error) {
193
+ const profile = this.store.requireProfile(id);
194
+ const message = error instanceof Error ? error.message : String(error);
195
+ const decision = evaluateStreak({
196
+ error,
197
+ currentStreak: profile.errorStreak,
198
+ limit: this.errorStreakLimit
199
+ });
200
+ if (!decision.counted) {
201
+ // Kept as the profile's last error so a reader can see what happened, without
202
+ // advancing the streak.
203
+ this.store.setLastError(id, message);
204
+ return {
205
+ data: { kind: decision.kind, streak: decision.streak, disabled: false },
206
+ meta: resultMeta({ source: "sqlite" })
207
+ };
208
+ }
209
+ const streak = this.store.recordError(id, message);
210
+ if (streak < this.errorStreakLimit) {
211
+ return { data: { kind: decision.kind, streak, disabled: false }, meta: resultMeta({ source: "sqlite" }) };
212
+ }
213
+ // Disabling also advances the generation, so an action this profile already decided
214
+ // cannot still be submitted.
215
+ this.store.disableProfile(id, describeAutoDisable(streak, message));
216
+ return { data: { kind: decision.kind, streak, disabled: true }, meta: resultMeta({ source: "sqlite" }) };
217
+ }
218
+ /** Clears the streak after an evaluation that worked. */
219
+ recordSuccess(id) {
220
+ // Only touched when there is something to clear: a healthy profile would otherwise
221
+ // write a row on every closed minute for no reason.
222
+ if (this.store.requireProfile(id).errorStreak > 0)
223
+ this.store.clearErrorStreak(id);
224
+ }
225
+ deleteProfile(id) {
226
+ const profile = this.store.requireProfile(id);
227
+ if (profile.enabled) {
228
+ throw new RuntimeError("VALIDATION", `'${profile.name}' is enabled. Stop it before deleting, so its signal history is not removed while it is still trading.`);
229
+ }
230
+ return { data: { deleted: this.store.deleteProfile(id) }, meta: resultMeta({ source: "sqlite" }) };
231
+ }
232
+ /**
233
+ * Collects everything the preconditions need.
234
+ *
235
+ * Each lookup is guarded on its own: a profile blocked by a missing backtest should
236
+ * still learn that its account is also unverified, and one failing probe must not
237
+ * hide the rest of the list.
238
+ */
239
+ async gatherFacts(profile) {
240
+ const backtest = this.strategyStore.latestCompletedRun(profile.strategyHash, profile.instId);
241
+ const python = await pythonStatus().catch(() => ({ ready: false, problems: ["Python status could not be read"] }));
242
+ let accountVerified = false;
243
+ let accountPermissions = null;
244
+ try {
245
+ const verified = await this.accounts.verify(profile.account);
246
+ accountVerified = true;
247
+ const permission = verified.data.perm;
248
+ accountPermissions = typeof permission === "string" && permission ? permission : null;
249
+ }
250
+ catch {
251
+ // Left unverified; the precondition reports it, and the exact exchange error is
252
+ // not more useful than "OKX did not accept these credentials".
253
+ }
254
+ const contractReadable = await loadContractSpec(this.market, profile.instId).then(() => true, () => false);
255
+ return {
256
+ backtestRunId: backtest?.id ?? null,
257
+ failures: checkPreconditions({
258
+ profile,
259
+ backtestRunId: backtest?.id ?? null,
260
+ pythonReady: python.ready,
261
+ pythonProblems: python.problems,
262
+ accountVerified,
263
+ accountPermissions,
264
+ contractReadable,
265
+ marketEnvironment: this.bars.environment === "demo" ? "demo" : "live"
266
+ })
267
+ };
268
+ }
269
+ }
270
+ /**
271
+ * Rejects budgets that cannot describe a real position.
272
+ *
273
+ * Checked at creation because these are the numbers every risk gate measures against: a
274
+ * profile whose side budget is below its entry budget would refuse every order it ever
275
+ * decided, which reads as a broken strategy rather than a bad setting.
276
+ */
277
+ function validateBudgets(input) {
278
+ // An agent must say why. A profile that arrived with no reasoning is one nobody can review
279
+ // later, and reviewing it later is the only check left once it is trading.
280
+ if (input.origin === "agent" && !input.rationale?.trim()) {
281
+ throw new RuntimeError("VALIDATION", "An agent-created profile requires a rationale: which backtest justified it, and why these budgets");
282
+ }
283
+ const positives = [
284
+ ["entryBudgetUsdt", input.entryBudgetUsdt],
285
+ ["sideBudgetUsdt", input.sideBudgetUsdt],
286
+ ["dailyLossLimitUsdt", input.dailyLossLimitUsdt],
287
+ ["leverage", input.leverage]
288
+ ];
289
+ for (const [field, value] of positives) {
290
+ if (!Number.isFinite(value) || value <= 0) {
291
+ throw new RuntimeError("VALIDATION", `${field} must be greater than zero`);
292
+ }
293
+ }
294
+ if (input.sideBudgetUsdt < input.entryBudgetUsdt) {
295
+ throw new RuntimeError("VALIDATION", `sideBudgetUsdt (${input.sideBudgetUsdt}) is below entryBudgetUsdt (${input.entryBudgetUsdt}), so no entry could ever be placed`);
296
+ }
297
+ // Both default to allowed when omitted, matching the store. Reading `undefined` as
298
+ // "not allowed" would reject every profile that simply did not mention direction.
299
+ if (input.allowLong === false && input.allowShort === false) {
300
+ throw new RuntimeError("VALIDATION", "A profile with neither direction allowed can never trade");
301
+ }
302
+ }
303
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/live/service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ import type { Bar } from "../bars/types.js";
2
+ export interface LiveSessionOptions {
3
+ interpreter: string;
4
+ engineRoot: string;
5
+ source: string;
6
+ instrument: Record<string, unknown>;
7
+ params: Record<string, unknown>;
8
+ intervals: string[];
9
+ /** Test seam. */
10
+ evaluationTimeoutMs?: number;
11
+ }
12
+ /** A strategy's intent for one cutoff. Carries no contract count. */
13
+ export interface LiveDecision {
14
+ action: string;
15
+ reason: string;
16
+ asOfMs: number;
17
+ protection?: {
18
+ stopLossPrice?: number;
19
+ takeProfitPrice?: number;
20
+ };
21
+ execution?: {
22
+ orderType: string;
23
+ limitPrice?: number;
24
+ };
25
+ orderId?: string;
26
+ }
27
+ /** The account state a strategy sees, as reported by the exchange. */
28
+ export interface LivePortfolio {
29
+ cashUsdt: number;
30
+ equityUsdt: number;
31
+ usedMarginUsdt: number;
32
+ availableMarginUsdt: number;
33
+ positions: Array<Record<string, unknown>>;
34
+ openOrders: Array<Record<string, unknown>>;
35
+ }
36
+ /**
37
+ * Owns one long-lived Python process speaking the `live` protocol.
38
+ *
39
+ * Requests are strictly serialised: one line out, one line back. The protocol has
40
+ * no request ids, so a second concurrent write would make it impossible to tell
41
+ * which reply belonged to which bar — and mismatching a decision to a minute is how
42
+ * a stale intent reaches the exchange.
43
+ */
44
+ export declare class PythonLiveSession {
45
+ private readonly options;
46
+ private child?;
47
+ private buffer;
48
+ private pending?;
49
+ private queue;
50
+ private stderrText;
51
+ /**
52
+ * Replies still owed by the engine for requests that already timed out.
53
+ *
54
+ * The engine answers every request eventually, in order. A timeout abandons the
55
+ * caller but not the request, so that many arriving replies must be discarded
56
+ * before the stream lines up with whoever is waiting now.
57
+ */
58
+ private owedReplies;
59
+ private exited?;
60
+ private readonly timeoutMs;
61
+ constructor(options: LiveSessionOptions);
62
+ get running(): boolean;
63
+ /** Starts the process and completes the handshake. */
64
+ start(): Promise<void>;
65
+ /** Feeds history without evaluating it. */
66
+ warm(bars: Bar[]): Promise<number>;
67
+ /**
68
+ * Evaluates one confirmed bar and returns the strategy's intent.
69
+ *
70
+ * `portfolio` must be the exchange's own view, read immediately before this call.
71
+ */
72
+ evaluate(bar: Bar, portfolio: LivePortfolio): Promise<LiveDecision>;
73
+ /** Asks the process to exit, then makes sure it did. */
74
+ stop(): Promise<void>;
75
+ /**
76
+ * Serialises one request/response exchange.
77
+ *
78
+ * Every call is chained onto the previous one, so two callers cannot interleave
79
+ * writes on a protocol that has no request ids.
80
+ */
81
+ private request;
82
+ private onStdout;
83
+ private fail;
84
+ private describeExit;
85
+ }
@@ -0,0 +1,234 @@
1
+ import { spawn } from "node:child_process";
2
+ import path from "node:path";
3
+ import { RuntimeError } from "../core/errors.js";
4
+ /**
5
+ * How long one evaluation may take before it is treated as a strategy fault.
6
+ *
7
+ * A decision is only useful within the minute it was made for, so a strategy that
8
+ * stalls is skipped rather than waited on. The session survives the timeout: the
9
+ * warmed indicator state is still valid for the next minute.
10
+ */
11
+ const EVALUATION_TIMEOUT_MS = 5_000;
12
+ /** Handshake and warm-up move more data, so they get a longer allowance. */
13
+ const SETUP_TIMEOUT_MS = 60_000;
14
+ /** One reply is a single JSON line; more than this means something is wrong. */
15
+ const MAX_LINE_BYTES = 4 * 1024 * 1024;
16
+ /**
17
+ * Owns one long-lived Python process speaking the `live` protocol.
18
+ *
19
+ * Requests are strictly serialised: one line out, one line back. The protocol has
20
+ * no request ids, so a second concurrent write would make it impossible to tell
21
+ * which reply belonged to which bar — and mismatching a decision to a minute is how
22
+ * a stale intent reaches the exchange.
23
+ */
24
+ export class PythonLiveSession {
25
+ options;
26
+ child;
27
+ buffer = "";
28
+ pending;
29
+ queue = Promise.resolve();
30
+ stderrText = "";
31
+ /**
32
+ * Replies still owed by the engine for requests that already timed out.
33
+ *
34
+ * The engine answers every request eventually, in order. A timeout abandons the
35
+ * caller but not the request, so that many arriving replies must be discarded
36
+ * before the stream lines up with whoever is waiting now.
37
+ */
38
+ owedReplies = 0;
39
+ exited;
40
+ timeoutMs;
41
+ constructor(options) {
42
+ this.options = options;
43
+ this.timeoutMs = options.evaluationTimeoutMs ?? EVALUATION_TIMEOUT_MS;
44
+ }
45
+ get running() {
46
+ return this.child !== undefined && this.exited === undefined;
47
+ }
48
+ /** Starts the process and completes the handshake. */
49
+ async start() {
50
+ if (this.child)
51
+ throw new RuntimeError("VALIDATION", "This session is already started");
52
+ const entry = path.join(this.options.engineRoot, "main.py");
53
+ // `-I` ignores PYTHONPATH and the user site directory, so a live strategy
54
+ // cannot be redirected by ambient configuration. `-u` keeps replies flowing
55
+ // instead of sitting in a pipe buffer.
56
+ const child = spawn(this.options.interpreter, ["-I", "-u", entry], {
57
+ stdio: ["pipe", "pipe", "pipe"],
58
+ cwd: this.options.engineRoot,
59
+ windowsHide: true
60
+ });
61
+ this.child = child;
62
+ child.stdout.setEncoding("utf8");
63
+ child.stdout.on("data", (chunk) => this.onStdout(chunk));
64
+ child.stderr.setEncoding("utf8");
65
+ child.stderr.on("data", (chunk) => {
66
+ // Kept for diagnostics; an unexpected exit is undiagnosable without it.
67
+ if (this.stderrText.length < 64 * 1024)
68
+ this.stderrText += chunk;
69
+ });
70
+ child.on("error", (error) => this.fail(new RuntimeError("CAPABILITY_UNAVAILABLE", `Could not start the strategy engine: ${error.message}`)));
71
+ child.on("close", (code, signal) => {
72
+ this.exited = { code, signal };
73
+ this.fail(new RuntimeError("INTERNAL", `The strategy engine exited (${this.describeExit()})`));
74
+ });
75
+ child.stdin.on("error", () => {
76
+ // The close handler reports the real cause; a broken pipe here is a symptom.
77
+ });
78
+ const ready = await this.request({
79
+ command: "live",
80
+ source: this.options.source,
81
+ instrument: this.options.instrument,
82
+ params: this.options.params,
83
+ intervals: this.options.intervals
84
+ }, SETUP_TIMEOUT_MS);
85
+ if (ready.ok !== true)
86
+ throw pythonError(ready);
87
+ }
88
+ /** Feeds history without evaluating it. */
89
+ async warm(bars) {
90
+ const response = await this.request({ kind: "warm", bars: bars.map(toPayload) }, SETUP_TIMEOUT_MS);
91
+ if (response.ok !== true)
92
+ throw pythonError(response);
93
+ return Number(response.barCount ?? 0);
94
+ }
95
+ /**
96
+ * Evaluates one confirmed bar and returns the strategy's intent.
97
+ *
98
+ * `portfolio` must be the exchange's own view, read immediately before this call.
99
+ */
100
+ async evaluate(bar, portfolio) {
101
+ const response = await this.request({ kind: "bar", bar: toPayload(bar), portfolio }, this.timeoutMs);
102
+ if (response.ok !== true)
103
+ throw pythonError(response);
104
+ return {
105
+ action: String(response.action ?? ""),
106
+ reason: String(response.reason ?? ""),
107
+ asOfMs: Number(response.asOfMs ?? bar.closeTimeMs),
108
+ ...(response.protection ? { protection: response.protection } : {}),
109
+ ...(response.execution ? { execution: response.execution } : {}),
110
+ ...(response.orderId ? { orderId: String(response.orderId) } : {})
111
+ };
112
+ }
113
+ /** Asks the process to exit, then makes sure it did. */
114
+ async stop() {
115
+ const child = this.child;
116
+ if (!child || this.exited) {
117
+ this.child = undefined;
118
+ return;
119
+ }
120
+ try {
121
+ await this.request({ command: "shutdown" }, 2_000);
122
+ }
123
+ catch {
124
+ // A session being stopped because it misbehaved will not answer; the kill
125
+ // below is the point.
126
+ }
127
+ if (this.exited === undefined)
128
+ child.kill("SIGKILL");
129
+ this.child = undefined;
130
+ }
131
+ /**
132
+ * Serialises one request/response exchange.
133
+ *
134
+ * Every call is chained onto the previous one, so two callers cannot interleave
135
+ * writes on a protocol that has no request ids.
136
+ */
137
+ request(message, timeoutMs) {
138
+ const run = async () => {
139
+ const child = this.child;
140
+ if (!child || this.exited) {
141
+ throw new RuntimeError("INTERNAL", `The strategy engine is not running (${this.describeExit()})`);
142
+ }
143
+ return new Promise((resolve, reject) => {
144
+ const timer = setTimeout(() => {
145
+ this.pending = undefined;
146
+ // The engine is still working on this request and will answer eventually.
147
+ // That reply now belongs to nobody, so it is counted here and dropped on
148
+ // arrival — handing it to the next caller would pair a decision with a
149
+ // different minute than the one it was computed for.
150
+ this.owedReplies += 1;
151
+ // The process is left alive: its indicator state is still valid, and the
152
+ // caller treats this minute as a skipped evaluation.
153
+ reject(new RuntimeError("INTERNAL", `The strategy took longer than ${timeoutMs}ms to decide`));
154
+ }, timeoutMs);
155
+ timer.unref();
156
+ this.pending = { resolve, reject, timer };
157
+ child.stdin.write(`${JSON.stringify(message)}\n`);
158
+ });
159
+ };
160
+ // Failures must not poison the chain for later calls.
161
+ const chained = this.queue.then(run, run);
162
+ this.queue = chained.then(() => undefined, () => undefined);
163
+ return chained;
164
+ }
165
+ onStdout(chunk) {
166
+ this.buffer += chunk;
167
+ if (this.buffer.length > MAX_LINE_BYTES) {
168
+ this.buffer = "";
169
+ this.fail(new RuntimeError("INTERNAL", "The strategy engine produced an oversized reply"));
170
+ return;
171
+ }
172
+ for (;;) {
173
+ const newline = this.buffer.indexOf("\n");
174
+ if (newline === -1)
175
+ return;
176
+ const line = this.buffer.slice(0, newline).trim();
177
+ this.buffer = this.buffer.slice(newline + 1);
178
+ if (!line)
179
+ continue;
180
+ // A timed-out request is still owed a reply. Discarding it here — before
181
+ // looking at who is waiting — is what keeps the stream aligned: otherwise the
182
+ // late answer would satisfy the *next* evaluation, pairing a decision with a
183
+ // different minute than the one it was computed for.
184
+ if (this.owedReplies > 0) {
185
+ this.owedReplies -= 1;
186
+ continue;
187
+ }
188
+ const waiting = this.pending;
189
+ // A reply nobody is owed and nobody is waiting for means the stream
190
+ // desynchronised; dropping it is safer than delivering it.
191
+ if (!waiting)
192
+ continue;
193
+ this.pending = undefined;
194
+ clearTimeout(waiting.timer);
195
+ try {
196
+ waiting.resolve(JSON.parse(line));
197
+ }
198
+ catch {
199
+ waiting.reject(new RuntimeError("INTERNAL", `The strategy engine returned invalid JSON: ${line.slice(0, 200)}`));
200
+ }
201
+ }
202
+ }
203
+ fail(error) {
204
+ const waiting = this.pending;
205
+ if (!waiting)
206
+ return;
207
+ this.pending = undefined;
208
+ clearTimeout(waiting.timer);
209
+ waiting.reject(error);
210
+ }
211
+ describeExit() {
212
+ if (!this.exited)
213
+ return "still running";
214
+ const detail = this.stderrText.trim().split("\n").at(-1);
215
+ const status = this.exited.signal ? `signal ${this.exited.signal}` : `exit code ${this.exited.code}`;
216
+ return detail ? `${status}: ${detail}` : status;
217
+ }
218
+ }
219
+ function toPayload(bar) {
220
+ return {
221
+ openTimeMs: bar.openTimeMs,
222
+ closeTimeMs: bar.closeTimeMs,
223
+ open: Number(bar.open),
224
+ high: Number(bar.high),
225
+ low: Number(bar.low),
226
+ close: Number(bar.close),
227
+ volume: Number(bar.volume)
228
+ };
229
+ }
230
+ function pythonError(response) {
231
+ const error = (response.error ?? {});
232
+ return new RuntimeError(error.code === "policy_violation" ? "VALIDATION" : "INTERNAL", error.message ?? "The strategy engine reported an error", false, error.violations ? { violations: error.violations } : undefined);
233
+ }
234
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/live/session.ts"],"names":[],"mappings":"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"}