desic-okx-agent 0.1.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 (101) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +242 -0
  3. package/dist/account/private-websocket.d.ts +13 -0
  4. package/dist/account/private-websocket.js +102 -0
  5. package/dist/account/private-websocket.js.map +1 -0
  6. package/dist/account/service.d.ts +22 -0
  7. package/dist/account/service.js +118 -0
  8. package/dist/account/service.js.map +1 -0
  9. package/dist/account/store.d.ts +12 -0
  10. package/dist/account/store.js +48 -0
  11. package/dist/account/store.js.map +1 -0
  12. package/dist/cli/index.d.ts +2 -0
  13. package/dist/cli/index.js +122 -0
  14. package/dist/cli/index.js.map +1 -0
  15. package/dist/cli/prompts.d.ts +2 -0
  16. package/dist/cli/prompts.js +52 -0
  17. package/dist/cli/prompts.js.map +1 -0
  18. package/dist/config/loader.d.ts +9 -0
  19. package/dist/config/loader.js +87 -0
  20. package/dist/config/loader.js.map +1 -0
  21. package/dist/config/paths.d.ts +7 -0
  22. package/dist/config/paths.js +11 -0
  23. package/dist/config/paths.js.map +1 -0
  24. package/dist/config/schema.d.ts +42 -0
  25. package/dist/config/schema.js +30 -0
  26. package/dist/config/schema.js.map +1 -0
  27. package/dist/core/attribution.d.ts +2 -0
  28. package/dist/core/attribution.js +29 -0
  29. package/dist/core/attribution.js.map +1 -0
  30. package/dist/core/errors.d.ts +9 -0
  31. package/dist/core/errors.js +33 -0
  32. package/dist/core/errors.js.map +1 -0
  33. package/dist/core/okx-client.d.ts +13 -0
  34. package/dist/core/okx-client.js +102 -0
  35. package/dist/core/okx-client.js.map +1 -0
  36. package/dist/core/sanitize.d.ts +2 -0
  37. package/dist/core/sanitize.js +19 -0
  38. package/dist/core/sanitize.js.map +1 -0
  39. package/dist/core/types.d.ts +38 -0
  40. package/dist/core/types.js +18 -0
  41. package/dist/core/types.js.map +1 -0
  42. package/dist/derivatives/service.d.ts +28 -0
  43. package/dist/derivatives/service.js +144 -0
  44. package/dist/derivatives/service.js.map +1 -0
  45. package/dist/index.d.ts +4 -0
  46. package/dist/index.js +5 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/intelligence/service.d.ts +38 -0
  49. package/dist/intelligence/service.js +324 -0
  50. package/dist/intelligence/service.js.map +1 -0
  51. package/dist/market/indicators.d.ts +2 -0
  52. package/dist/market/indicators.js +74 -0
  53. package/dist/market/indicators.js.map +1 -0
  54. package/dist/market/service.d.ts +28 -0
  55. package/dist/market/service.js +232 -0
  56. package/dist/market/service.js.map +1 -0
  57. package/dist/market/store.d.ts +46 -0
  58. package/dist/market/store.js +158 -0
  59. package/dist/market/store.js.map +1 -0
  60. package/dist/market/websocket.d.ts +33 -0
  61. package/dist/market/websocket.js +213 -0
  62. package/dist/market/websocket.js.map +1 -0
  63. package/dist/mcp/server.d.ts +1 -0
  64. package/dist/mcp/server.js +28 -0
  65. package/dist/mcp/server.js.map +1 -0
  66. package/dist/runtime/client.d.ts +15 -0
  67. package/dist/runtime/client.js +106 -0
  68. package/dist/runtime/client.js.map +1 -0
  69. package/dist/runtime/server.d.ts +28 -0
  70. package/dist/runtime/server.js +140 -0
  71. package/dist/runtime/server.js.map +1 -0
  72. package/dist/runtime/state.d.ts +14 -0
  73. package/dist/runtime/state.js +98 -0
  74. package/dist/runtime/state.js.map +1 -0
  75. package/dist/storage/database.d.ts +23 -0
  76. package/dist/storage/database.js +106 -0
  77. package/dist/storage/database.js.map +1 -0
  78. package/dist/tools/catalog.d.ts +21 -0
  79. package/dist/tools/catalog.js +236 -0
  80. package/dist/tools/catalog.js.map +1 -0
  81. package/dist/trade/service.d.ts +128 -0
  82. package/dist/trade/service.js +470 -0
  83. package/dist/trade/service.js.map +1 -0
  84. package/examples/claude-code/mcp.json +8 -0
  85. package/examples/codex/config.toml +3 -0
  86. package/examples/config.example.json +18 -0
  87. package/package.json +71 -0
  88. package/skills/okx-account-analysis/SKILL.md +14 -0
  89. package/skills/okx-account-analysis/agents/openai.yaml +9 -0
  90. package/skills/okx-derivatives-analysis/SKILL.md +14 -0
  91. package/skills/okx-derivatives-analysis/agents/openai.yaml +9 -0
  92. package/skills/okx-market-analysis/SKILL.md +14 -0
  93. package/skills/okx-market-analysis/agents/openai.yaml +9 -0
  94. package/skills/okx-news-intelligence/SKILL.md +14 -0
  95. package/skills/okx-news-intelligence/agents/openai.yaml +9 -0
  96. package/skills/okx-smart-money-analysis/SKILL.md +15 -0
  97. package/skills/okx-smart-money-analysis/agents/openai.yaml +9 -0
  98. package/skills/okx-trading/SKILL.md +16 -0
  99. package/skills/okx-trading/agents/openai.yaml +9 -0
  100. package/skills/trading-philosophy/SKILL.md +14 -0
  101. package/skills/trading-philosophy/agents/openai.yaml +4 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Xiazhi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,242 @@
1
+ # Desic OKX Agent
2
+
3
+ An independent local OKX runtime, MCP server, CLI, and reusable agent skills for Codex, Claude Code, and other MCP clients.
4
+
5
+ > [!IMPORTANT]
6
+ > Desic OKX Agent is an independent community project. It is not affiliated with, endorsed by, or an official product of OKX.
7
+
8
+ ## What it provides
9
+
10
+ - A shared local Runtime reused by multiple MCP and CLI clients
11
+ - In-memory ticker, order book, trades, candles, funding, mark price, and open-interest data
12
+ - Time-aligned decision snapshots with freshness and consistency metadata
13
+ - Public market and derivatives tools that work without API credentials
14
+ - Named OKX accounts for balances, positions, orders, fills, bills, and risk summaries
15
+ - Direct ordinary-order, algo-order, leverage, amend, cancel, and close-position operations
16
+ - Experimental News and Smart Money intelligence with SQLite history fallback
17
+ - Seven reusable analysis and trading skills
18
+ - SQLite WAL persistence for closed candles, intelligence history, derived events, and execution records
19
+
20
+ The project does not expose withdrawal, deposit, transfer, asset-movement, or API-key-management tools.
21
+
22
+ ## Requirements
23
+
24
+ - Node.js 22.12 or newer
25
+ - npm
26
+ - Network access to OKX, or an HTTP/HTTPS proxy that can reach OKX
27
+
28
+ ## Install from GitHub
29
+
30
+ The npm package is not published yet. Install the current source version with:
31
+
32
+ ```bash
33
+ git clone https://github.com/xiazhi88/desic-okx-agent.git
34
+ cd desic-okx-agent
35
+ npm ci
36
+ npm run build
37
+ npm link
38
+ ```
39
+
40
+ This registers the `desic-okx` command on the local machine.
41
+
42
+ After the package is published to npm, installation will be:
43
+
44
+ ```bash
45
+ npm install --global desic-okx-agent
46
+ ```
47
+
48
+ ## Quick start
49
+
50
+ Public market tools do not require an account:
51
+
52
+ ```bash
53
+ desic-okx start
54
+ desic-okx call market_get_ticker --json '{"instId":"BTC-USDT-SWAP"}'
55
+ desic-okx call market_get_decision_snapshot --json '{"instId":"BTC-USDT-SWAP","bar":"1m"}'
56
+ ```
57
+
58
+ The Runtime starts automatically when an MCP client or CLI call needs it. `desic-okx start` is optional.
59
+
60
+ Useful Runtime commands:
61
+
62
+ ```bash
63
+ desic-okx status
64
+ desic-okx tools
65
+ desic-okx stop
66
+ ```
67
+
68
+ ## Connect Codex
69
+
70
+ Register the local stdio MCP server:
71
+
72
+ ```bash
73
+ codex mcp add desic-okx -- desic-okx mcp
74
+ ```
75
+
76
+ Or add it to `~/.codex/config.toml`:
77
+
78
+ ```toml
79
+ [mcp_servers.desic-okx]
80
+ command = "desic-okx"
81
+ args = ["mcp"]
82
+ ```
83
+
84
+ ## Connect Claude Code
85
+
86
+ Register it for the current user:
87
+
88
+ ```bash
89
+ claude mcp add --transport stdio --scope user desic-okx -- desic-okx mcp
90
+ ```
91
+
92
+ Verify the connection with:
93
+
94
+ ```bash
95
+ claude mcp get desic-okx
96
+ ```
97
+
98
+ A JSON example is available in `examples/claude-code/mcp.json`.
99
+
100
+ ## Install the skills
101
+
102
+ The MCP server exposes the tools. Skills add reusable analysis and trading workflows. Install only the skills you want by copying their directories from `skills/`.
103
+
104
+ Codex personal skills:
105
+
106
+ ```text
107
+ ~/.agents/skills/<skill-name>/SKILL.md
108
+ ```
109
+
110
+ Claude Code personal skills:
111
+
112
+ ```text
113
+ ~/.claude/skills/<skill-name>/SKILL.md
114
+ ```
115
+
116
+ Project-scoped alternatives are `.agents/skills/` for Codex and `.claude/skills/` for Claude Code.
117
+
118
+ Included skills:
119
+
120
+ - `okx-market-analysis`
121
+ - `okx-derivatives-analysis`
122
+ - `okx-news-intelligence`
123
+ - `okx-smart-money-analysis`
124
+ - `okx-account-analysis`
125
+ - `okx-trading`
126
+ - `trading-philosophy`
127
+
128
+ Restart the client if a newly installed skill does not appear.
129
+
130
+ ## Configure an OKX account
131
+
132
+ Public tools work without credentials. Account and trading tools use a named account alias and never accept credentials as tool arguments.
133
+
134
+ Add and verify an account interactively:
135
+
136
+ ```bash
137
+ desic-okx account add --name demo --environment demo
138
+ desic-okx account verify --name demo
139
+ desic-okx account list
140
+ ```
141
+
142
+ The credential prompts hide input. Credentials are stored in the system configuration directory in `config.json`; Unix permissions are set to `0600`. The active path is shown by:
143
+
144
+ ```bash
145
+ desic-okx config-path
146
+ ```
147
+
148
+ Environment variables can override a named account:
149
+
150
+ ```text
151
+ OKX_ACCOUNT
152
+ OKX_API_KEY
153
+ OKX_API_SECRET
154
+ OKX_API_PASSPHRASE
155
+ OKX_ENVIRONMENT=demo|live
156
+ ```
157
+
158
+ The three credential values must be provided together. Whether an account can trade is determined by the permissions assigned to its API key in OKX.
159
+
160
+ ## Proxy
161
+
162
+ REST and WebSocket connections share the proxy configured in `config.json`:
163
+
164
+ ```json
165
+ {
166
+ "proxy": {
167
+ "url": "http://127.0.0.1:7890"
168
+ }
169
+ }
170
+ ```
171
+
172
+ See `examples/config.example.json` for a complete credential-free configuration template. Restart the Runtime after changing configuration.
173
+
174
+ ## Runtime behavior
175
+
176
+ - Binds a random port on `127.0.0.1`
177
+ - Stores PID, port, and a random access token in private local state files
178
+ - Prewarms `BTC-USDT-SWAP` and `ETH-USDT-SWAP`
179
+ - Subscribes to other instruments on demand and releases them after 15 minutes of inactivity
180
+ - Serves fresh data from memory and uses REST for cold or stale data
181
+ - Reconnects WebSockets automatically
182
+ - Rebuilds the order book after sequence gaps or checksum failures
183
+ - Restores persisted candle and intelligence history before live prewarming
184
+
185
+ `market_get_decision_snapshot` combines market components around one observation time. It reports every component's exchange timestamp, receive time, age, warnings, and maximum observed time skew. Trading prechecks reject inconsistent snapshots.
186
+
187
+ ## Trading behavior
188
+
189
+ Trading tools require a stable `executionKey`. Client order IDs are derived from that key, and execution state is recorded locally. When a write times out or returns an unclear result, the Runtime queries remote order state before deciding whether a retry is safe.
190
+
191
+ Supported order families include:
192
+
193
+ - `limit`
194
+ - `market`
195
+ - `post_only`
196
+ - `ioc`
197
+ - `fok`
198
+ - `trigger`
199
+ - `conditional`
200
+ - `trailing`
201
+
202
+ Ordinary and strategy orders are checked against instrument increments, minimum size, current account permission, and market snapshot consistency before submission.
203
+
204
+ Use an OKX Demo account first. Trading software can lose money, and the project provides no investment advice or guarantee of execution quality.
205
+
206
+ ## Experimental intelligence
207
+
208
+ News and Smart Money use upstream interfaces that may change without notice. Compatibility failures return `CAPABILITY_UNAVAILABLE`, or locally persisted history when available. A failure in these modules does not disable market, account, or trading tools.
209
+
210
+ ## Development
211
+
212
+ ```bash
213
+ npm ci
214
+ npm run check
215
+ ```
216
+
217
+ The check pipeline runs:
218
+
219
+ - TypeScript type checking
220
+ - Unit tests
221
+ - Skill validation
222
+ - Sensitive-information scanning
223
+ - Production build
224
+ - MCP SDK smoke tests
225
+
226
+ The optional end-to-end trading test is restricted to OKX Demo. Export the four environment variables listed in the account section, using `demo` as the environment, and then run:
227
+
228
+ ```bash
229
+ npm run test:demo
230
+ ```
231
+
232
+ Normal tests never submit orders.
233
+
234
+ ## Status
235
+
236
+ This repository is currently pre-release software at version `0.1.0`. Unit, packaging, Skill, and MCP transport checks are automated. Real OKX Demo trading should be validated in the target network environment before a `1.0.0` release.
237
+
238
+ ## License
239
+
240
+ MIT. See `LICENSE`.
241
+
242
+ OKX and related marks belong to their respective owners.
@@ -0,0 +1,13 @@
1
+ import type { RuntimeConfig } from "../config/schema.js";
2
+ import { AccountStore } from "./store.js";
3
+ export declare class PrivateAccountWebSockets {
4
+ private readonly store;
5
+ private readonly connections;
6
+ private readonly agent?;
7
+ constructor(config: RuntimeConfig, store: AccountStore);
8
+ start(): void;
9
+ stop(): void;
10
+ private connect;
11
+ private reconnect;
12
+ private message;
13
+ }
@@ -0,0 +1,102 @@
1
+ import crypto from "node:crypto";
2
+ import WebSocket from "ws";
3
+ import { HttpsProxyAgent } from "https-proxy-agent";
4
+ export class PrivateAccountWebSockets {
5
+ store;
6
+ connections;
7
+ agent;
8
+ constructor(config, store) {
9
+ this.store = store;
10
+ this.connections = Object.entries(config.accounts).map(([name, account]) => ({ account: { name, ...account }, stopping: false, attempt: 0 }));
11
+ this.agent = config.proxy.url ? new HttpsProxyAgent(config.proxy.url) : undefined;
12
+ }
13
+ start() {
14
+ for (const connection of this.connections)
15
+ this.connect(connection);
16
+ }
17
+ stop() {
18
+ for (const connection of this.connections) {
19
+ connection.stopping = true;
20
+ if (connection.reconnect)
21
+ clearTimeout(connection.reconnect);
22
+ if (connection.heartbeat)
23
+ clearInterval(connection.heartbeat);
24
+ connection.socket?.close();
25
+ }
26
+ }
27
+ connect(connection) {
28
+ if (connection.stopping)
29
+ return;
30
+ const url = connection.account.environment === "demo"
31
+ ? "wss://wspap.okx.com:8443/ws/v5/private"
32
+ : "wss://ws.okx.com:8443/ws/v5/private";
33
+ const socket = new WebSocket(url, this.agent ? { agent: this.agent } : undefined);
34
+ connection.socket = socket;
35
+ socket.on("open", () => socket.send(JSON.stringify({ op: "login", args: [loginArgs(connection.account)] })));
36
+ socket.on("message", (buffer) => this.message(connection, buffer.toString()));
37
+ socket.on("close", () => this.reconnect(connection));
38
+ socket.on("error", () => socket.close());
39
+ }
40
+ reconnect(connection) {
41
+ if (connection.stopping || connection.reconnect)
42
+ return;
43
+ if (connection.heartbeat)
44
+ clearInterval(connection.heartbeat);
45
+ const delay = Math.min(30_000, 500 * 2 ** connection.attempt) + Math.floor(Math.random() * 500);
46
+ connection.attempt += 1;
47
+ connection.reconnect = setTimeout(() => {
48
+ connection.reconnect = undefined;
49
+ this.connect(connection);
50
+ }, delay);
51
+ connection.reconnect.unref();
52
+ }
53
+ message(connection, text) {
54
+ if (text === "pong")
55
+ return;
56
+ let message;
57
+ try {
58
+ message = JSON.parse(text);
59
+ }
60
+ catch {
61
+ return;
62
+ }
63
+ if (message.event === "login" && message.code === "0") {
64
+ connection.attempt = 0;
65
+ connection.socket?.send(JSON.stringify({
66
+ op: "subscribe",
67
+ args: [
68
+ { channel: "account" },
69
+ { channel: "positions", instType: "ANY" },
70
+ { channel: "orders", instType: "ANY" },
71
+ { channel: "orders-algo", instType: "ANY" }
72
+ ]
73
+ }));
74
+ connection.heartbeat = setInterval(() => {
75
+ if (connection.socket?.readyState === WebSocket.OPEN)
76
+ connection.socket.send("ping");
77
+ }, 20_000);
78
+ connection.heartbeat.unref();
79
+ return;
80
+ }
81
+ const arg = message.arg;
82
+ const data = Array.isArray(message.data) ? message.data : [];
83
+ if (!arg?.channel || !data.length)
84
+ return;
85
+ if (arg.channel === "account")
86
+ this.store.setBalances(connection.account.name, data);
87
+ else if (arg.channel === "positions")
88
+ this.store.mergePositions(connection.account.name, data);
89
+ else if (arg.channel === "orders")
90
+ this.store.mergeOrders(connection.account.name, data);
91
+ else if (arg.channel === "orders-algo")
92
+ this.store.mergeOrders(connection.account.name, data, true);
93
+ }
94
+ }
95
+ function loginArgs(account) {
96
+ const timestamp = String(Math.floor(Date.now() / 1_000));
97
+ const sign = crypto.createHmac("sha256", account.secretKey)
98
+ .update(`${timestamp}GET/users/self/verify`)
99
+ .digest("base64");
100
+ return { apiKey: account.apiKey, passphrase: account.passphrase, timestamp, sign };
101
+ }
102
+ //# sourceMappingURL=private-websocket.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"private-websocket.js","sourceRoot":"","sources":["../../src/account/private-websocket.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { RuntimeConfig } from "../config/schema.js";
2
+ import { OkxClient } from "../core/okx-client.js";
3
+ import { type AccountCredentials, type ToolResult } from "../core/types.js";
4
+ import type { AccountStore } from "./store.js";
5
+ export declare class AccountService {
6
+ private readonly config;
7
+ private readonly client;
8
+ private readonly store?;
9
+ constructor(config: RuntimeConfig, client: OkxClient, store?: AccountStore | undefined);
10
+ account(name?: string): AccountCredentials;
11
+ verify(name?: string): Promise<ToolResult<Record<string, unknown>>>;
12
+ getBalances(name?: string, ccy?: string): Promise<ToolResult<unknown[]>>;
13
+ getPositions(name?: string, instId?: string): Promise<ToolResult<unknown[]>>;
14
+ getOpenOrders(name?: string, instId?: string, instType?: string): Promise<ToolResult<Record<string, unknown>>>;
15
+ getOrder(name: string | undefined, instId: string, ordId?: string, clOrdId?: string): Promise<ToolResult<unknown[]>>;
16
+ getOrderHistory(name?: string, query?: Record<string, unknown>): Promise<ToolResult<unknown[]>>;
17
+ getFills(name?: string, query?: Record<string, unknown>): Promise<ToolResult<unknown[]>>;
18
+ getBills(name?: string, query?: Record<string, unknown>): Promise<ToolResult<unknown[]>>;
19
+ getSnapshot(name?: string): Promise<ToolResult<Record<string, unknown>>>;
20
+ getRisk(name?: string): Promise<ToolResult<Record<string, unknown>>>;
21
+ private wrap;
22
+ }
@@ -0,0 +1,118 @@
1
+ import { resolveAccount } from "../config/loader.js";
2
+ import { resultMeta } from "../core/types.js";
3
+ export class AccountService {
4
+ config;
5
+ client;
6
+ store;
7
+ constructor(config, client, store) {
8
+ this.config = config;
9
+ this.client = client;
10
+ this.store = store;
11
+ }
12
+ account(name) {
13
+ return resolveAccount(this.config, name);
14
+ }
15
+ async verify(name) {
16
+ const account = this.account(name);
17
+ const [config] = await this.client.privateGet("/api/v5/account/config", account);
18
+ return this.wrap(account, config ?? {});
19
+ }
20
+ async getBalances(name, ccy) {
21
+ const account = this.account(name);
22
+ const cached = recent(this.store?.balances.get(account.name));
23
+ if (cached && !ccy)
24
+ return this.wrap(account, cached.value, "websocket");
25
+ return this.wrap(account, await this.client.privateGet("/api/v5/account/balance", account, { ccy }));
26
+ }
27
+ async getPositions(name, instId) {
28
+ const account = this.account(name);
29
+ const cached = recent(this.store?.positions.get(account.name));
30
+ if (cached) {
31
+ const rows = instId ? cached.value.filter((item) => item.instId === instId) : cached.value;
32
+ return this.wrap(account, rows, "websocket");
33
+ }
34
+ return this.wrap(account, await this.client.privateGet("/api/v5/account/positions", account, { instId }));
35
+ }
36
+ async getOpenOrders(name, instId, instType) {
37
+ const account = this.account(name);
38
+ const cachedOrders = recent(this.store?.orders.get(account.name));
39
+ const cachedAlgo = recent(this.store?.algoOrders.get(account.name));
40
+ if (cachedOrders && cachedAlgo) {
41
+ const filter = (rows) => instId ? rows.filter((item) => item.instId === instId) : rows;
42
+ return this.wrap(account, { orders: filter(cachedOrders.value), algoOrders: filter(cachedAlgo.value) }, "websocket");
43
+ }
44
+ const [orders, trigger, conditional, trailing] = await Promise.all([
45
+ this.client.privateGet("/api/v5/trade/orders-pending", account, { instType, instId }),
46
+ this.client.privateGet("/api/v5/trade/orders-algo-pending", account, { ordType: "trigger", instId }),
47
+ this.client.privateGet("/api/v5/trade/orders-algo-pending", account, { ordType: "conditional", instId }),
48
+ this.client.privateGet("/api/v5/trade/orders-algo-pending", account, { ordType: "move_order_stop", instId })
49
+ ]);
50
+ return this.wrap(account, { orders, algoOrders: [...trigger, ...conditional, ...trailing] }, "mixed");
51
+ }
52
+ async getOrder(name, instId, ordId, clOrdId) {
53
+ const account = this.account(name);
54
+ return this.wrap(account, await this.client.privateGet("/api/v5/trade/order", account, { instId, ordId, clOrdId }));
55
+ }
56
+ async getOrderHistory(name, query = {}) {
57
+ const account = this.account(name);
58
+ const path = query.archive === true ? "/api/v5/trade/orders-history-archive" : "/api/v5/trade/orders-history";
59
+ const { archive: _archive, ...params } = query;
60
+ return this.wrap(account, await this.client.privateGet(path, account, params));
61
+ }
62
+ async getFills(name, query = {}) {
63
+ const account = this.account(name);
64
+ const path = query.archive === true ? "/api/v5/trade/fills-history" : "/api/v5/trade/fills";
65
+ const { archive: _archive, ...params } = query;
66
+ return this.wrap(account, await this.client.privateGet(path, account, params));
67
+ }
68
+ async getBills(name, query = {}) {
69
+ const account = this.account(name);
70
+ const path = query.archive === true ? "/api/v5/account/bills-archive" : "/api/v5/account/bills";
71
+ const { archive: _archive, ...params } = query;
72
+ return this.wrap(account, await this.client.privateGet(path, account, params));
73
+ }
74
+ async getSnapshot(name) {
75
+ const account = this.account(name);
76
+ const [balances, positions, openOrders, config] = await Promise.all([
77
+ this.getBalances(account.name),
78
+ this.getPositions(account.name),
79
+ this.getOpenOrders(account.name),
80
+ this.verify(account.name)
81
+ ]);
82
+ return this.wrap(account, {
83
+ balances: balances.data,
84
+ positions: positions.data,
85
+ openOrders: openOrders.data,
86
+ config: config.data
87
+ }, "mixed");
88
+ }
89
+ async getRisk(name) {
90
+ const snapshot = await this.getSnapshot(name);
91
+ const balanceContainers = snapshot.data.balances;
92
+ const positions = snapshot.data.positions;
93
+ const firstBalance = balanceContainers[0] ?? {};
94
+ const details = Array.isArray(firstBalance.details) ? firstBalance.details : [];
95
+ const totalEquity = numeric(firstBalance.totalEq);
96
+ const available = details.reduce((sum, item) => sum + numeric(item.availEq ?? item.availBal), 0);
97
+ const maintenanceMargin = positions.reduce((sum, item) => sum + numeric(item.maintMargin ?? item.mmr), 0);
98
+ const notional = positions.reduce((sum, item) => sum + Math.abs(numeric(item.notionalUsd ?? item.notionalUsdForBorrow)), 0);
99
+ return {
100
+ data: { totalEquity, available, maintenanceMargin, grossNotionalUsd: notional, positionCount: positions.length },
101
+ meta: { ...snapshot.meta, source: "derived" }
102
+ };
103
+ }
104
+ wrap(account, data, source = "rest") {
105
+ return {
106
+ data,
107
+ meta: resultMeta({ source, environment: account.environment, account: account.name })
108
+ };
109
+ }
110
+ }
111
+ function recent(value) {
112
+ return value && Date.now() - value.receivedAt <= 15_000 ? value : undefined;
113
+ }
114
+ function numeric(value) {
115
+ const parsed = Number(value ?? 0);
116
+ return Number.isFinite(parsed) ? parsed : 0;
117
+ }
118
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/account/service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { TimedValue } from "../core/types.js";
2
+ type Row = Record<string, unknown>;
3
+ export declare class AccountStore {
4
+ readonly balances: Map<string, TimedValue<Row[]>>;
5
+ readonly positions: Map<string, TimedValue<Row[]>>;
6
+ readonly orders: Map<string, TimedValue<Row[]>>;
7
+ readonly algoOrders: Map<string, TimedValue<Row[]>>;
8
+ setBalances(account: string, rows: Row[]): void;
9
+ mergePositions(account: string, rows: Row[]): void;
10
+ mergeOrders(account: string, rows: Row[], algo?: boolean): void;
11
+ }
12
+ export {};
@@ -0,0 +1,48 @@
1
+ export class AccountStore {
2
+ balances = new Map();
3
+ positions = new Map();
4
+ orders = new Map();
5
+ algoOrders = new Map();
6
+ setBalances(account, rows) {
7
+ this.balances.set(account, timed(rows));
8
+ }
9
+ mergePositions(account, rows) {
10
+ const current = this.positions.get(account)?.value ?? [];
11
+ const map = new Map(current.map((row) => [positionKey(row), row]));
12
+ for (const row of rows) {
13
+ const key = positionKey(row);
14
+ if (Number(row.pos ?? 0) === 0)
15
+ map.delete(key);
16
+ else
17
+ map.set(key, row);
18
+ }
19
+ this.positions.set(account, timed([...map.values()]));
20
+ }
21
+ mergeOrders(account, rows, algo = false) {
22
+ const target = algo ? this.algoOrders : this.orders;
23
+ const current = target.get(account)?.value ?? [];
24
+ const map = new Map(current.map((row) => [orderKey(row), row]));
25
+ for (const row of rows) {
26
+ const key = orderKey(row);
27
+ if (terminal(String(row.state ?? "")))
28
+ map.delete(key);
29
+ else
30
+ map.set(key, row);
31
+ }
32
+ target.set(account, timed([...map.values()]));
33
+ }
34
+ }
35
+ function timed(value) {
36
+ const now = Date.now();
37
+ return { value, exchangeTs: now, receivedAt: now, source: "websocket" };
38
+ }
39
+ function positionKey(row) {
40
+ return `${String(row.instId ?? "")}:${String(row.posSide ?? "net")}:${String(row.mgnMode ?? "")}`;
41
+ }
42
+ function orderKey(row) {
43
+ return String(row.ordId ?? row.algoId ?? row.clOrdId ?? row.algoClOrdId ?? JSON.stringify(row));
44
+ }
45
+ function terminal(state) {
46
+ return ["filled", "canceled", "mmp_canceled", "order_failed", "effective"].includes(state);
47
+ }
48
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/account/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};