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.
- package/README.en.md +90 -10
- package/README.md +76 -10
- package/dist/account/private-websocket.js +4 -4
- package/dist/account/private-websocket.js.map +1 -1
- package/dist/account/service.d.ts +12 -1
- package/dist/account/service.js +18 -0
- package/dist/account/service.js.map +1 -1
- package/dist/bars/rate-limiter.d.ts +18 -0
- package/dist/bars/rate-limiter.js +84 -0
- package/dist/bars/rate-limiter.js.map +1 -0
- package/dist/bars/schema.d.ts +36 -0
- package/dist/bars/schema.js +134 -0
- package/dist/bars/schema.js.map +1 -0
- package/dist/bars/service.d.ts +60 -0
- package/dist/bars/service.js +120 -0
- package/dist/bars/service.js.map +1 -0
- package/dist/bars/store.d.ts +105 -0
- package/dist/bars/store.js +415 -0
- package/dist/bars/store.js.map +1 -0
- package/dist/bars/timeframe.d.ts +40 -0
- package/dist/bars/timeframe.js +146 -0
- package/dist/bars/timeframe.js.map +1 -0
- package/dist/bars/types.d.ts +68 -0
- package/dist/bars/types.js +13 -0
- package/dist/bars/types.js.map +1 -0
- package/dist/cli/data-render.d.ts +37 -0
- package/dist/cli/data-render.js +143 -0
- package/dist/cli/data-render.js.map +1 -0
- package/dist/cli/doctor.js +7 -3
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.js +757 -26
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/live-render.d.ts +24 -0
- package/dist/cli/live-render.js +85 -0
- package/dist/cli/live-render.js.map +1 -0
- package/dist/cli/range.d.ts +28 -0
- package/dist/cli/range.js +63 -0
- package/dist/cli/range.js.map +1 -0
- package/dist/cli/render.js +3 -0
- package/dist/cli/render.js.map +1 -1
- package/dist/cli/strategy-render.d.ts +36 -0
- package/dist/cli/strategy-render.js +391 -0
- package/dist/cli/strategy-render.js.map +1 -0
- package/dist/cli/width.d.ts +18 -0
- package/dist/cli/width.js +71 -0
- package/dist/cli/width.js.map +1 -0
- package/dist/config/loader.js +1 -1
- package/dist/config/schema.d.ts +7 -0
- package/dist/config/schema.js +24 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/core/okx-client.d.ts +9 -1
- package/dist/core/okx-client.js +14 -5
- package/dist/core/okx-client.js.map +1 -1
- package/dist/i18n/locale.d.ts +24 -0
- package/dist/i18n/locale.js +65 -0
- package/dist/i18n/locale.js.map +1 -0
- package/dist/i18n/messages.d.ts +333 -0
- package/dist/i18n/messages.js +660 -0
- package/dist/i18n/messages.js.map +1 -0
- package/dist/live/account-snapshot.d.ts +30 -0
- package/dist/live/account-snapshot.js +130 -0
- package/dist/live/account-snapshot.js.map +1 -0
- package/dist/live/cutoff-queue.d.ts +42 -0
- package/dist/live/cutoff-queue.js +69 -0
- package/dist/live/cutoff-queue.js.map +1 -0
- package/dist/live/execution-key.d.ts +23 -0
- package/dist/live/execution-key.js +31 -0
- package/dist/live/execution-key.js.map +1 -0
- package/dist/live/failures.d.ts +37 -0
- package/dist/live/failures.js +57 -0
- package/dist/live/failures.js.map +1 -0
- package/dist/live/gates.d.ts +65 -0
- package/dist/live/gates.js +136 -0
- package/dist/live/gates.js.map +1 -0
- package/dist/live/loop.d.ts +56 -0
- package/dist/live/loop.js +197 -0
- package/dist/live/loop.js.map +1 -0
- package/dist/live/preconditions.d.ts +48 -0
- package/dist/live/preconditions.js +69 -0
- package/dist/live/preconditions.js.map +1 -0
- package/dist/live/reconcile.d.ts +46 -0
- package/dist/live/reconcile.js +104 -0
- package/dist/live/reconcile.js.map +1 -0
- package/dist/live/runner.d.ts +57 -0
- package/dist/live/runner.js +160 -0
- package/dist/live/runner.js.map +1 -0
- package/dist/live/schema.d.ts +18 -0
- package/dist/live/schema.js +91 -0
- package/dist/live/schema.js.map +1 -0
- package/dist/live/service.d.ts +144 -0
- package/dist/live/service.js +303 -0
- package/dist/live/service.js.map +1 -0
- package/dist/live/session.d.ts +85 -0
- package/dist/live/session.js +234 -0
- package/dist/live/session.js.map +1 -0
- package/dist/live/sizing.d.ts +62 -0
- package/dist/live/sizing.js +79 -0
- package/dist/live/sizing.js.map +1 -0
- package/dist/live/store.d.ts +123 -0
- package/dist/live/store.js +350 -0
- package/dist/live/store.js.map +1 -0
- package/dist/live/types.d.ts +82 -0
- package/dist/live/types.js +2 -0
- package/dist/live/types.js.map +1 -0
- package/dist/market/websocket.d.ts +16 -1
- package/dist/market/websocket.js +60 -5
- package/dist/market/websocket.js.map +1 -1
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +15 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/network/connectivity.d.ts +9 -1
- package/dist/network/connectivity.js +28 -1
- package/dist/network/connectivity.js.map +1 -1
- package/dist/report/chart-script.d.ts +12 -0
- package/dist/report/chart-script.js +146 -0
- package/dist/report/chart-script.js.map +1 -0
- package/dist/report/compare-html.d.ts +8 -0
- package/dist/report/compare-html.js +254 -0
- package/dist/report/compare-html.js.map +1 -0
- package/dist/report/compare-script.d.ts +12 -0
- package/dist/report/compare-script.js +109 -0
- package/dist/report/compare-script.js.map +1 -0
- package/dist/report/compare.d.ts +61 -0
- package/dist/report/compare.js +205 -0
- package/dist/report/compare.js.map +1 -0
- package/dist/report/fetch.d.ts +20 -0
- package/dist/report/fetch.js +56 -0
- package/dist/report/fetch.js.map +1 -0
- package/dist/report/html.d.ts +54 -0
- package/dist/report/html.js +641 -0
- package/dist/report/html.js.map +1 -0
- package/dist/report/open.d.ts +42 -0
- package/dist/report/open.js +114 -0
- package/dist/report/open.js.map +1 -0
- package/dist/runtime/server.d.ts +16 -1
- package/dist/runtime/server.js +112 -9
- package/dist/runtime/server.js.map +1 -1
- package/dist/setup/installer.d.ts +1 -0
- package/dist/setup/installer.js +8 -0
- package/dist/setup/installer.js.map +1 -1
- package/dist/setup/wizard.js +19 -22
- package/dist/setup/wizard.js.map +1 -1
- package/dist/strategy/constants.d.ts +23 -0
- package/dist/strategy/constants.js +24 -0
- package/dist/strategy/constants.js.map +1 -0
- package/dist/strategy/environment.d.ts +52 -0
- package/dist/strategy/environment.js +187 -0
- package/dist/strategy/environment.js.map +1 -0
- package/dist/strategy/instrument.d.ts +29 -0
- package/dist/strategy/instrument.js +39 -0
- package/dist/strategy/instrument.js.map +1 -0
- package/dist/strategy/optimize.d.ts +73 -0
- package/dist/strategy/optimize.js +113 -0
- package/dist/strategy/optimize.js.map +1 -0
- package/dist/strategy/parameter-space.d.ts +59 -0
- package/dist/strategy/parameter-space.js +221 -0
- package/dist/strategy/parameter-space.js.map +1 -0
- package/dist/strategy/python-bridge.d.ts +24 -0
- package/dist/strategy/python-bridge.js +114 -0
- package/dist/strategy/python-bridge.js.map +1 -0
- package/dist/strategy/schema.d.ts +11 -0
- package/dist/strategy/schema.js +94 -0
- package/dist/strategy/schema.js.map +1 -0
- package/dist/strategy/series-codec.d.ts +50 -0
- package/dist/strategy/series-codec.js +103 -0
- package/dist/strategy/series-codec.js.map +1 -0
- package/dist/strategy/service.d.ts +138 -0
- package/dist/strategy/service.js +745 -0
- package/dist/strategy/service.js.map +1 -0
- package/dist/strategy/settings.d.ts +162 -0
- package/dist/strategy/settings.js +243 -0
- package/dist/strategy/settings.js.map +1 -0
- package/dist/strategy/store.d.ts +96 -0
- package/dist/strategy/store.js +369 -0
- package/dist/strategy/store.js.map +1 -0
- package/dist/strategy/templates.d.ts +11 -0
- package/dist/strategy/templates.js +134 -0
- package/dist/strategy/templates.js.map +1 -0
- package/dist/strategy/types.d.ts +111 -0
- package/dist/strategy/types.js +2 -0
- package/dist/strategy/types.js.map +1 -0
- package/dist/tools/catalog.d.ts +16 -0
- package/dist/tools/catalog.js +118 -17
- package/dist/tools/catalog.js.map +1 -1
- package/dist/trade/service.d.ts +12 -0
- package/dist/trade/service.js +24 -7
- package/dist/trade/service.js.map +1 -1
- package/dist/tui/app.d.ts +23 -0
- package/dist/tui/app.js +322 -0
- package/dist/tui/app.js.map +1 -0
- package/dist/tui/commands.d.ts +70 -0
- package/dist/tui/commands.js +313 -0
- package/dist/tui/commands.js.map +1 -0
- package/dist/tui/entries.d.ts +17 -0
- package/dist/tui/entries.js +24 -0
- package/dist/tui/entries.js.map +1 -0
- package/dist/tui/execute.d.ts +26 -0
- package/dist/tui/execute.js +664 -0
- package/dist/tui/execute.js.map +1 -0
- package/dist/tui/history.d.ts +17 -0
- package/dist/tui/history.js +48 -0
- package/dist/tui/history.js.map +1 -0
- package/dist/tui/index.d.ts +8 -0
- package/dist/tui/index.js +48 -0
- package/dist/tui/index.js.map +1 -0
- package/dist/tui/line-editor.d.ts +44 -0
- package/dist/tui/line-editor.js +98 -0
- package/dist/tui/line-editor.js.map +1 -0
- package/dist/tui/progress.d.ts +23 -0
- package/dist/tui/progress.js +46 -0
- package/dist/tui/progress.js.map +1 -0
- package/dist/tui/settings-editor.d.ts +18 -0
- package/dist/tui/settings-editor.js +115 -0
- package/dist/tui/settings-editor.js.map +1 -0
- package/docs/live-trading.md +455 -0
- package/docs/strategy-research.md +608 -0
- package/package.json +10 -1
- package/python/desic_strategy/__init__.py +34 -0
- package/python/desic_strategy/actions.py +158 -0
- package/python/desic_strategy/context.py +171 -0
- package/python/desic_strategy/engine.py +614 -0
- package/python/desic_strategy/indicators.py +159 -0
- package/python/desic_strategy/live.py +253 -0
- package/python/desic_strategy/policy.py +193 -0
- package/python/desic_strategy/portfolio.py +152 -0
- package/python/desic_strategy/report.py +319 -0
- package/python/desic_strategy/runner.py +574 -0
- package/python/desic_strategy/timeframe.py +150 -0
- package/python/main.py +18 -0
- package/skills/okx-live-trading/SKILL.md +117 -0
- package/skills/okx-live-trading/agents/openai.yaml +9 -0
- package/skills/okx-live-trading/references/lifecycle.md +128 -0
- package/skills/okx-strategy-research/SKILL.md +113 -0
- package/skills/okx-strategy-research/agents/openai.yaml +9 -0
- package/skills/okx-strategy-research/references/execution-semantics.md +107 -0
- package/skills/okx-strategy-research/references/field-traps.md +142 -0
- package/skills/okx-strategy-research/references/python-api.md +125 -0
- package/skills/okx-strategy-research/references/tools-and-data.md +192 -0
- 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
|
+
}
|