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,94 @@
1
+ /**
2
+ * Storage for queued and completed strategy runs.
3
+ *
4
+ * The equity curve is stored as chunked binary float64 blocks rather than JSON rows.
5
+ * Every value round-trips bit-for-bit, which decimal JSON text does not guarantee, and
6
+ * a measured 344k-point curve is 2.4 MB here against roughly 40 MB as JSON objects.
7
+ *
8
+ * `codec` names the layout of each chunk's payload. It is read rather than inferred so
9
+ * that chunks written by an earlier build keep decoding correctly — see `series-codec.ts`.
10
+ */
11
+ export const STRATEGY_SCHEMA_SQL = `
12
+ CREATE TABLE IF NOT EXISTS strategy_runs (
13
+ id TEXT PRIMARY KEY,
14
+ kind TEXT NOT NULL,
15
+ status TEXT NOT NULL,
16
+ strategy_name TEXT NOT NULL,
17
+ strategy_hash TEXT NOT NULL,
18
+ strategy_source TEXT NOT NULL,
19
+ inst_id TEXT NOT NULL,
20
+ request_json TEXT NOT NULL,
21
+ data_snapshot_id TEXT,
22
+ bar_count INTEGER NOT NULL DEFAULT 0,
23
+ preload_bars INTEGER NOT NULL DEFAULT 0,
24
+ progress_pct REAL NOT NULL DEFAULT 0,
25
+ eta_ms INTEGER,
26
+ metrics_json TEXT,
27
+ -- The assumptions the run actually used. Stored per run because settings can
28
+ -- change between runs, and a report that cannot say what it assumed is not
29
+ -- comparable to any other report.
30
+ assumptions_json TEXT,
31
+ error TEXT,
32
+ created_at INTEGER NOT NULL,
33
+ started_at INTEGER,
34
+ finished_at INTEGER
35
+ );
36
+
37
+ CREATE INDEX IF NOT EXISTS idx_strategy_runs_created
38
+ ON strategy_runs(created_at DESC);
39
+ CREATE INDEX IF NOT EXISTS idx_strategy_runs_status
40
+ ON strategy_runs(status, created_at DESC);
41
+
42
+ CREATE TABLE IF NOT EXISTS strategy_run_series (
43
+ run_id TEXT NOT NULL,
44
+ chunk_index INTEGER NOT NULL,
45
+ from_point INTEGER NOT NULL,
46
+ to_point INTEGER NOT NULL,
47
+ start_ms INTEGER NOT NULL,
48
+ step_ms INTEGER NOT NULL,
49
+ payload BLOB NOT NULL,
50
+ codec TEXT NOT NULL DEFAULT 'f64x4',
51
+ PRIMARY KEY(run_id, chunk_index)
52
+ ) WITHOUT ROWID;
53
+
54
+ CREATE TABLE IF NOT EXISTS strategy_run_trades (
55
+ run_id TEXT NOT NULL,
56
+ seq INTEGER NOT NULL,
57
+ trade_json TEXT NOT NULL,
58
+ PRIMARY KEY(run_id, seq)
59
+ ) WITHOUT ROWID;
60
+
61
+ CREATE TABLE IF NOT EXISTS strategy_run_actions (
62
+ run_id TEXT NOT NULL,
63
+ seq INTEGER NOT NULL,
64
+ action_json TEXT NOT NULL,
65
+ PRIMARY KEY(run_id, seq)
66
+ ) WITHOUT ROWID;
67
+
68
+ -- One row per evaluated parameter combination of an optimize run.
69
+ --
70
+ -- Training and validation metrics are stored separately rather than merged into
71
+ -- one figure. The gap between them is the finding: a candidate that earned its
72
+ -- ranking and one that memorized the training segment are indistinguishable from
73
+ -- a single number.
74
+ CREATE TABLE IF NOT EXISTS strategy_optimization_candidates (
75
+ run_id TEXT NOT NULL,
76
+ candidate_index INTEGER NOT NULL,
77
+ params_json TEXT NOT NULL,
78
+ status TEXT NOT NULL,
79
+ train_metrics_json TEXT,
80
+ validation_metrics_json TEXT,
81
+ -- Validation-segment score. Null while queued, and for a candidate that failed.
82
+ rank_score REAL,
83
+ -- Which formula produced rank_score, because it is not always Calmar.
84
+ rank_basis TEXT,
85
+ verdict TEXT,
86
+ error TEXT,
87
+ PRIMARY KEY(run_id, candidate_index)
88
+ ) WITHOUT ROWID;
89
+
90
+ -- Ranking reads this in score order, so it is indexed rather than sorted per read.
91
+ CREATE INDEX IF NOT EXISTS idx_optimization_rank
92
+ ON strategy_optimization_candidates(run_id, rank_score DESC);
93
+ `;
94
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/strategy/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkFlC,CAAC"}
@@ -0,0 +1,50 @@
1
+ import type { EquityPoint } from "./types.js";
2
+ /**
3
+ * How one equity chunk's bytes are laid out.
4
+ *
5
+ * The name is stored beside the payload rather than inferred from its length, so a
6
+ * chunk written by an older build keeps being read the way it was written. A codec is
7
+ * append-only: once a name has been persisted its decoder stays.
8
+ */
9
+ export type SeriesCodec = "f64x4" | "f64x3z";
10
+ /** What every codec written before the column existed must be read as. */
11
+ export declare const LEGACY_CODEC: SeriesCodec;
12
+ /** What new chunks are written with. */
13
+ export declare const CURRENT_CODEC: SeriesCodec;
14
+ export interface ChunkPlan {
15
+ codec: SeriesCodec;
16
+ /** First point's timestamp, in epoch ms. */
17
+ startMs: number;
18
+ /** Spacing between consecutive points, in ms. Zero for a single-point chunk. */
19
+ stepMs: number;
20
+ payload: Buffer;
21
+ }
22
+ /**
23
+ * Encodes one chunk of the equity curve.
24
+ *
25
+ * Two things shrink it. The time column is dropped, because a backtest evaluates one
26
+ * closed bar at a time and those timestamps are an arithmetic sequence — `startMs +
27
+ * stepMs * i` reproduces them exactly, and this is verified rather than assumed.
28
+ * The remaining three f64 columns compress well because consecutive equity values
29
+ * share most of their bytes.
30
+ *
31
+ * When the spacing is not uniform the chunk falls back to `f64x4`, which stores every
32
+ * timestamp. A gap in the curve is a fact about the run, not something to smooth over.
33
+ */
34
+ export declare function encodeChunk(points: readonly EquityPoint[]): ChunkPlan;
35
+ /**
36
+ * Reads points `[fromIndex, toIndex)` of a chunk, where the indexes are relative to
37
+ * the chunk's own first point.
38
+ *
39
+ * A compressed chunk has to be inflated whole before any point can be addressed, so
40
+ * the range only narrows what is materialized, not what is decompressed. That is why
41
+ * chunks are bounded in size: the cost of reading one point is the cost of one chunk.
42
+ */
43
+ export declare function decodeChunk(chunk: {
44
+ codec: SeriesCodec;
45
+ startMs: number;
46
+ stepMs: number;
47
+ payload: Buffer;
48
+ }, fromIndex: number, toIndex: number): EquityPoint[];
49
+ /** Names a stored codec, treating anything written before the column existed as legacy. */
50
+ export declare function codecFrom(value: unknown): SeriesCodec;
@@ -0,0 +1,103 @@
1
+ import zlib from "node:zlib";
2
+ /** What every codec written before the column existed must be read as. */
3
+ export const LEGACY_CODEC = "f64x4";
4
+ /** What new chunks are written with. */
5
+ export const CURRENT_CODEC = "f64x3z";
6
+ /** Bytes per point in the uncompressed four-column layout. */
7
+ const F64X4_STRIDE = 32;
8
+ /** Bytes per point once the time column is dropped. */
9
+ const F64X3_STRIDE = 24;
10
+ /**
11
+ * Encodes one chunk of the equity curve.
12
+ *
13
+ * Two things shrink it. The time column is dropped, because a backtest evaluates one
14
+ * closed bar at a time and those timestamps are an arithmetic sequence — `startMs +
15
+ * stepMs * i` reproduces them exactly, and this is verified rather than assumed.
16
+ * The remaining three f64 columns compress well because consecutive equity values
17
+ * share most of their bytes.
18
+ *
19
+ * When the spacing is not uniform the chunk falls back to `f64x4`, which stores every
20
+ * timestamp. A gap in the curve is a fact about the run, not something to smooth over.
21
+ */
22
+ export function encodeChunk(points) {
23
+ if (points.length === 0) {
24
+ throw new Error("An equity chunk needs at least one point");
25
+ }
26
+ const first = points[0];
27
+ const stepMs = points.length > 1 ? points[1].timeMs - first.timeMs : 0;
28
+ if (!isUniform(points, first.timeMs, stepMs)) {
29
+ return { codec: "f64x4", startMs: first.timeMs, stepMs, payload: encodeF64x4(points) };
30
+ }
31
+ const raw = Buffer.allocUnsafe(points.length * F64X3_STRIDE);
32
+ points.forEach((point, index) => {
33
+ const base = index * F64X3_STRIDE;
34
+ raw.writeDoubleLE(point.equityUsdt, base);
35
+ raw.writeDoubleLE(point.realizedCashUsdt, base + 8);
36
+ raw.writeDoubleLE(point.unrealizedPnlUsdt, base + 16);
37
+ });
38
+ return {
39
+ codec: "f64x3z",
40
+ startMs: first.timeMs,
41
+ stepMs,
42
+ payload: zlib.deflateSync(raw)
43
+ };
44
+ }
45
+ /**
46
+ * Reads points `[fromIndex, toIndex)` of a chunk, where the indexes are relative to
47
+ * the chunk's own first point.
48
+ *
49
+ * A compressed chunk has to be inflated whole before any point can be addressed, so
50
+ * the range only narrows what is materialized, not what is decompressed. That is why
51
+ * chunks are bounded in size: the cost of reading one point is the cost of one chunk.
52
+ */
53
+ export function decodeChunk(chunk, fromIndex, toIndex) {
54
+ const rows = [];
55
+ if (chunk.codec === "f64x4") {
56
+ for (let index = fromIndex; index < toIndex; index += 1) {
57
+ const base = index * F64X4_STRIDE;
58
+ rows.push({
59
+ timeMs: chunk.payload.readDoubleLE(base),
60
+ equityUsdt: chunk.payload.readDoubleLE(base + 8),
61
+ realizedCashUsdt: chunk.payload.readDoubleLE(base + 16),
62
+ unrealizedPnlUsdt: chunk.payload.readDoubleLE(base + 24)
63
+ });
64
+ }
65
+ return rows;
66
+ }
67
+ const raw = zlib.inflateSync(chunk.payload);
68
+ for (let index = fromIndex; index < toIndex; index += 1) {
69
+ const base = index * F64X3_STRIDE;
70
+ rows.push({
71
+ timeMs: chunk.startMs + chunk.stepMs * index,
72
+ equityUsdt: raw.readDoubleLE(base),
73
+ realizedCashUsdt: raw.readDoubleLE(base + 8),
74
+ unrealizedPnlUsdt: raw.readDoubleLE(base + 16)
75
+ });
76
+ }
77
+ return rows;
78
+ }
79
+ /** Names a stored codec, treating anything written before the column existed as legacy. */
80
+ export function codecFrom(value) {
81
+ return value === "f64x3z" ? "f64x3z" : LEGACY_CODEC;
82
+ }
83
+ function isUniform(points, startMs, stepMs) {
84
+ // Checked rather than trusted: the decoder reconstructs every timestamp from this
85
+ // arithmetic, so one irregular bar would silently move the whole tail of the chunk.
86
+ for (let index = 0; index < points.length; index += 1) {
87
+ if (points[index].timeMs !== startMs + stepMs * index)
88
+ return false;
89
+ }
90
+ return true;
91
+ }
92
+ function encodeF64x4(points) {
93
+ const payload = Buffer.allocUnsafe(points.length * F64X4_STRIDE);
94
+ points.forEach((point, index) => {
95
+ const base = index * F64X4_STRIDE;
96
+ payload.writeDoubleLE(point.timeMs, base);
97
+ payload.writeDoubleLE(point.equityUsdt, base + 8);
98
+ payload.writeDoubleLE(point.realizedCashUsdt, base + 16);
99
+ payload.writeDoubleLE(point.unrealizedPnlUsdt, base + 24);
100
+ });
101
+ return payload;
102
+ }
103
+ //# sourceMappingURL=series-codec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"series-codec.js","sourceRoot":"","sources":["../../src/strategy/series-codec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,138 @@
1
+ import { type ToolResult } from "../core/types.js";
2
+ import type { BarService } from "../bars/service.js";
3
+ import type { MarketService } from "../market/service.js";
4
+ import { type PythonStatus } from "./environment.js";
5
+ import { type BacktestSettings } from "./settings.js";
6
+ import { StrategyStore } from "./store.js";
7
+ import { type ComparisonReport } from "../report/compare.js";
8
+ import type { BacktestRequest, EquityPoint, OptimizationReport, OptimizeRequest, Page, RunStatus, RunSummary, ValidationResult } from "./types.js";
9
+ export declare class StrategyService {
10
+ readonly store: StrategyStore;
11
+ private readonly bars;
12
+ /**
13
+ * Source of contract specifications. Optional so a test can construct the
14
+ * service without a market runtime; a run then fails with a clear message
15
+ * rather than falling back to a guessed contract size.
16
+ */
17
+ private readonly market?;
18
+ private readonly maxWorkers;
19
+ private readonly now;
20
+ /** Test seam: bypasses the settings file so a run is reproducible. */
21
+ private readonly injectedSettings?;
22
+ private readonly active;
23
+ private queue;
24
+ private running;
25
+ constructor(store: StrategyStore, bars: BarService,
26
+ /**
27
+ * Source of contract specifications. Optional so a test can construct the
28
+ * service without a market runtime; a run then fails with a clear message
29
+ * rather than falling back to a guessed contract size.
30
+ */
31
+ market?: MarketService | undefined, maxWorkers?: number, now?: () => number,
32
+ /** Test seam: bypasses the settings file so a run is reproducible. */
33
+ injectedSettings?: BacktestSettings | undefined);
34
+ /** Reports interpreter availability without changing anything. */
35
+ environment(): Promise<ToolResult<PythonStatus>>;
36
+ setupEnvironment(onLog?: (line: string) => void): Promise<ToolResult<Record<string, unknown>>>;
37
+ /** Saved settings, or the injected ones when a caller supplied them. */
38
+ settings(): BacktestSettings;
39
+ readSettings(): ToolResult<BacktestSettings>;
40
+ /**
41
+ * Applies saved settings. Unknown keys and out-of-range values are rejected
42
+ * rather than ignored: a silently dropped change means the next run uses
43
+ * assumptions the caller believes it changed.
44
+ */
45
+ updateSettings(values: Record<string, unknown>, reset?: boolean): ToolResult<BacktestSettings>;
46
+ /** Static policy check. Fast enough to run on every edit. */
47
+ validate(source: string): Promise<ToolResult<ValidationResult>>;
48
+ /**
49
+ * Queues a backtest and returns immediately.
50
+ *
51
+ * A run can take minutes, so the caller polls `getRun` rather than holding a
52
+ * request open and risking a client-side timeout.
53
+ */
54
+ startBacktest(request: BacktestRequest): Promise<ToolResult<{
55
+ runId: string;
56
+ status: RunStatus;
57
+ }>>;
58
+ /**
59
+ * Queues a parameter search and returns immediately.
60
+ *
61
+ * The space is validated against the strategy's own declared parameters before
62
+ * anything is queued, so a range naming a parameter the strategy never reads
63
+ * fails at the prompt rather than after every candidate has run identically.
64
+ */
65
+ startOptimize(request: OptimizeRequest): Promise<ToolResult<{
66
+ runId: string;
67
+ status: RunStatus;
68
+ }>>;
69
+ /**
70
+ * Ranked candidates for one optimize run.
71
+ *
72
+ * Ranking is by the held-back segment, never the searched one. The winner's
73
+ * ranking basis is reported alongside, because Calmar is unavailable on short
74
+ * windows and a column that silently changes meaning is worse than a labelled
75
+ * one.
76
+ */
77
+ optimization(id: string, top?: number): ToolResult<OptimizationReport>;
78
+ /**
79
+ * Compares completed runs.
80
+ *
81
+ * The equity curves are read whole here rather than paged: a comparison is
82
+ * summary-level, and the curves are downsampled before they leave. The response
83
+ * carries metrics, the assumptions that differ, and the warnings — not the
84
+ * curves, which would bury an agent's context for no benefit.
85
+ */
86
+ compareRuns(runIds: string[]): ToolResult<Omit<ComparisonReport, "series">>;
87
+ getRun(id: string): ToolResult<RunSummary>;
88
+ listRuns(options?: {
89
+ limit?: number;
90
+ status?: RunStatus;
91
+ instId?: string;
92
+ }): ToolResult<RunSummary[]>;
93
+ equity(id: string, offset?: number, limit?: number): ToolResult<Page<EquityPoint>>;
94
+ trades(id: string, offset?: number, limit?: number): ToolResult<Page<unknown>>;
95
+ actions(id: string, offset?: number, limit?: number): ToolResult<Page<unknown>>;
96
+ cancel(id: string): ToolResult<{
97
+ cancelled: boolean;
98
+ }>;
99
+ deleteRun(id: string): ToolResult<{
100
+ deleted: boolean;
101
+ }>;
102
+ /**
103
+ * Resolves the exact bar window a request describes.
104
+ *
105
+ * Preloaded history is context only: it warms up indicators and supplies the
106
+ * first decision point, and is excluded from every reported statistic.
107
+ */
108
+ resolveWindow(request: BacktestRequest): {
109
+ evaluationStart: number;
110
+ evaluationEnd: number;
111
+ preloadStart: number;
112
+ preloadBars: number;
113
+ totalBars: number;
114
+ };
115
+ private execute;
116
+ /**
117
+ * Runs one parameter search.
118
+ *
119
+ * The bars are loaded once and every candidate is measured against that single
120
+ * array. Batches go out one per worker rather than one per candidate: the
121
+ * payload is a small share of a candidate's cost, so nothing is gained by
122
+ * splitting further, while a process per candidate would hold the same
123
+ * multi-megabyte array once per candidate.
124
+ */
125
+ private executeOptimize;
126
+ /** Stores one batch's results, keyed by the candidate index the host assigned. */
127
+ private recordBatch;
128
+ /**
129
+ * Reads the instrument's contract specification.
130
+ *
131
+ * Refuses rather than defaulting: a backtest sized off a guessed contract value
132
+ * reports confident numbers for positions that could never have been taken.
133
+ */
134
+ private contractSpec;
135
+ private requireInterpreter;
136
+ private enqueue;
137
+ private drain;
138
+ }