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.
- package/LICENSE +21 -0
- package/README.md +242 -0
- package/dist/account/private-websocket.d.ts +13 -0
- package/dist/account/private-websocket.js +102 -0
- package/dist/account/private-websocket.js.map +1 -0
- package/dist/account/service.d.ts +22 -0
- package/dist/account/service.js +118 -0
- package/dist/account/service.js.map +1 -0
- package/dist/account/store.d.ts +12 -0
- package/dist/account/store.js +48 -0
- package/dist/account/store.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +122 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/prompts.d.ts +2 -0
- package/dist/cli/prompts.js +52 -0
- package/dist/cli/prompts.js.map +1 -0
- package/dist/config/loader.d.ts +9 -0
- package/dist/config/loader.js +87 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/paths.d.ts +7 -0
- package/dist/config/paths.js +11 -0
- package/dist/config/paths.js.map +1 -0
- package/dist/config/schema.d.ts +42 -0
- package/dist/config/schema.js +30 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/core/attribution.d.ts +2 -0
- package/dist/core/attribution.js +29 -0
- package/dist/core/attribution.js.map +1 -0
- package/dist/core/errors.d.ts +9 -0
- package/dist/core/errors.js +33 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/okx-client.d.ts +13 -0
- package/dist/core/okx-client.js +102 -0
- package/dist/core/okx-client.js.map +1 -0
- package/dist/core/sanitize.d.ts +2 -0
- package/dist/core/sanitize.js +19 -0
- package/dist/core/sanitize.js.map +1 -0
- package/dist/core/types.d.ts +38 -0
- package/dist/core/types.js +18 -0
- package/dist/core/types.js.map +1 -0
- package/dist/derivatives/service.d.ts +28 -0
- package/dist/derivatives/service.js +144 -0
- package/dist/derivatives/service.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/intelligence/service.d.ts +38 -0
- package/dist/intelligence/service.js +324 -0
- package/dist/intelligence/service.js.map +1 -0
- package/dist/market/indicators.d.ts +2 -0
- package/dist/market/indicators.js +74 -0
- package/dist/market/indicators.js.map +1 -0
- package/dist/market/service.d.ts +28 -0
- package/dist/market/service.js +232 -0
- package/dist/market/service.js.map +1 -0
- package/dist/market/store.d.ts +46 -0
- package/dist/market/store.js +158 -0
- package/dist/market/store.js.map +1 -0
- package/dist/market/websocket.d.ts +33 -0
- package/dist/market/websocket.js +213 -0
- package/dist/market/websocket.js.map +1 -0
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +28 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/runtime/client.d.ts +15 -0
- package/dist/runtime/client.js +106 -0
- package/dist/runtime/client.js.map +1 -0
- package/dist/runtime/server.d.ts +28 -0
- package/dist/runtime/server.js +140 -0
- package/dist/runtime/server.js.map +1 -0
- package/dist/runtime/state.d.ts +14 -0
- package/dist/runtime/state.js +98 -0
- package/dist/runtime/state.js.map +1 -0
- package/dist/storage/database.d.ts +23 -0
- package/dist/storage/database.js +106 -0
- package/dist/storage/database.js.map +1 -0
- package/dist/tools/catalog.d.ts +21 -0
- package/dist/tools/catalog.js +236 -0
- package/dist/tools/catalog.js.map +1 -0
- package/dist/trade/service.d.ts +128 -0
- package/dist/trade/service.js +470 -0
- package/dist/trade/service.js.map +1 -0
- package/examples/claude-code/mcp.json +8 -0
- package/examples/codex/config.toml +3 -0
- package/examples/config.example.json +18 -0
- package/package.json +71 -0
- package/skills/okx-account-analysis/SKILL.md +14 -0
- package/skills/okx-account-analysis/agents/openai.yaml +9 -0
- package/skills/okx-derivatives-analysis/SKILL.md +14 -0
- package/skills/okx-derivatives-analysis/agents/openai.yaml +9 -0
- package/skills/okx-market-analysis/SKILL.md +14 -0
- package/skills/okx-market-analysis/agents/openai.yaml +9 -0
- package/skills/okx-news-intelligence/SKILL.md +14 -0
- package/skills/okx-news-intelligence/agents/openai.yaml +9 -0
- package/skills/okx-smart-money-analysis/SKILL.md +15 -0
- package/skills/okx-smart-money-analysis/agents/openai.yaml +9 -0
- package/skills/okx-trading/SKILL.md +16 -0
- package/skills/okx-trading/agents/openai.yaml +9 -0
- package/skills/trading-philosophy/SKILL.md +14 -0
- 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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"}
|