@tigeropenapi/tigeropen 0.1.0 → 0.4.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 (141) hide show
  1. package/README.md +475 -104
  2. package/dist/cjs/client/api-request.js +16 -10
  3. package/dist/cjs/client/api-response.js +23 -3
  4. package/dist/cjs/client/case-convert.js +35 -0
  5. package/dist/cjs/client/http-client.js +47 -21
  6. package/dist/cjs/client/index.js +6 -2
  7. package/dist/cjs/config/client-config.js +130 -49
  8. package/dist/cjs/config/domain.js +21 -2
  9. package/dist/cjs/config/index.js +3 -1
  10. package/dist/cjs/index.js +1 -1
  11. package/dist/cjs/model/contract.js +1 -2
  12. package/dist/cjs/model/enums.js +89 -7
  13. package/dist/cjs/model/order-helpers.js +9 -9
  14. package/dist/cjs/model/order-status.js +46 -0
  15. package/dist/cjs/model/order.js +5 -2
  16. package/dist/cjs/model/position.js +1 -2
  17. package/dist/cjs/model/quote-requests.js +11 -0
  18. package/dist/cjs/model/quote.js +7 -0
  19. package/dist/cjs/model/trade-requests.js +13 -0
  20. package/dist/cjs/model/trade.js +2 -0
  21. package/dist/cjs/push/index.js +6 -3
  22. package/dist/cjs/push/pb/AssetData.js +208 -0
  23. package/dist/cjs/push/pb/KlineData.js +184 -0
  24. package/dist/cjs/push/pb/OptionTopData.js +427 -0
  25. package/dist/cjs/push/pb/OrderStatusData.js +592 -0
  26. package/dist/cjs/push/pb/OrderTransactionData.js +256 -0
  27. package/dist/cjs/push/pb/PositionData.js +316 -0
  28. package/dist/cjs/push/pb/PushData.js +219 -0
  29. package/dist/cjs/push/pb/QuoteBBOData.js +160 -0
  30. package/dist/cjs/push/pb/QuoteBasicData.js +329 -0
  31. package/dist/cjs/push/pb/QuoteData.js +507 -0
  32. package/dist/cjs/push/pb/QuoteDepthData.js +220 -0
  33. package/dist/cjs/push/pb/Request.js +278 -0
  34. package/dist/cjs/push/pb/Response.js +97 -0
  35. package/dist/cjs/push/pb/SocketCommon.js +86 -0
  36. package/dist/cjs/push/pb/StockTopData.js +199 -0
  37. package/dist/cjs/push/pb/TickData.js +202 -0
  38. package/dist/cjs/push/pb/TradeTickData.js +308 -0
  39. package/dist/cjs/push/pb/index.js +49 -0
  40. package/dist/cjs/push/proto-message.js +135 -0
  41. package/dist/cjs/push/push-client.js +361 -163
  42. package/dist/cjs/push/push-message.js +8 -60
  43. package/dist/cjs/push/varint.js +76 -0
  44. package/dist/cjs/quote/index.js +3 -2
  45. package/dist/cjs/quote/quote-client.js +440 -94
  46. package/dist/cjs/signer/signer.js +10 -10
  47. package/dist/cjs/trade/trade-client.js +197 -62
  48. package/dist/esm/client/api-request.js +14 -8
  49. package/dist/esm/client/api-response.js +22 -3
  50. package/dist/esm/client/case-convert.js +31 -0
  51. package/dist/esm/client/http-client.js +48 -22
  52. package/dist/esm/client/index.js +3 -2
  53. package/dist/esm/config/client-config.js +131 -51
  54. package/dist/esm/config/domain.js +20 -2
  55. package/dist/esm/config/index.js +2 -2
  56. package/dist/esm/index.js +1 -1
  57. package/dist/esm/model/contract.js +1 -2
  58. package/dist/esm/model/enums.js +87 -6
  59. package/dist/esm/model/order-helpers.js +9 -9
  60. package/dist/esm/model/order-status.js +43 -0
  61. package/dist/esm/model/order.js +5 -2
  62. package/dist/esm/model/position.js +1 -2
  63. package/dist/esm/model/quote-requests.js +10 -0
  64. package/dist/esm/model/quote.js +6 -0
  65. package/dist/esm/model/trade-requests.js +12 -0
  66. package/dist/esm/model/trade.js +1 -0
  67. package/dist/esm/push/index.js +4 -2
  68. package/dist/esm/push/pb/AssetData.js +205 -0
  69. package/dist/esm/push/pb/KlineData.js +181 -0
  70. package/dist/esm/push/pb/OptionTopData.js +424 -0
  71. package/dist/esm/push/pb/OrderStatusData.js +589 -0
  72. package/dist/esm/push/pb/OrderTransactionData.js +253 -0
  73. package/dist/esm/push/pb/PositionData.js +313 -0
  74. package/dist/esm/push/pb/PushData.js +216 -0
  75. package/dist/esm/push/pb/QuoteBBOData.js +157 -0
  76. package/dist/esm/push/pb/QuoteBasicData.js +326 -0
  77. package/dist/esm/push/pb/QuoteData.js +504 -0
  78. package/dist/esm/push/pb/QuoteDepthData.js +217 -0
  79. package/dist/esm/push/pb/Request.js +275 -0
  80. package/dist/esm/push/pb/Response.js +94 -0
  81. package/dist/esm/push/pb/SocketCommon.js +83 -0
  82. package/dist/esm/push/pb/StockTopData.js +196 -0
  83. package/dist/esm/push/pb/TickData.js +199 -0
  84. package/dist/esm/push/pb/TradeTickData.js +305 -0
  85. package/dist/esm/push/pb/index.js +25 -0
  86. package/dist/esm/push/proto-message.js +127 -0
  87. package/dist/esm/push/push-client.js +329 -164
  88. package/dist/esm/push/push-message.js +7 -56
  89. package/dist/esm/push/varint.js +72 -0
  90. package/dist/esm/quote/index.js +2 -2
  91. package/dist/esm/quote/quote-client.js +439 -94
  92. package/dist/esm/signer/signer.js +10 -10
  93. package/dist/esm/trade/trade-client.js +197 -62
  94. package/dist/types/client/api-request.d.ts +12 -11
  95. package/dist/types/client/api-response.d.ts +15 -7
  96. package/dist/types/client/case-convert.d.ts +16 -0
  97. package/dist/types/client/http-client.d.ts +26 -14
  98. package/dist/types/client/index.d.ts +3 -2
  99. package/dist/types/config/client-config.d.ts +29 -13
  100. package/dist/types/config/domain.d.ts +9 -0
  101. package/dist/types/config/index.d.ts +2 -2
  102. package/dist/types/index.d.ts +1 -1
  103. package/dist/types/model/contract.d.ts +13 -29
  104. package/dist/types/model/enums.d.ts +60 -6
  105. package/dist/types/model/order-helpers.d.ts +21 -20
  106. package/dist/types/model/order-status.d.ts +14 -0
  107. package/dist/types/model/order.d.ts +101 -55
  108. package/dist/types/model/position.d.ts +26 -24
  109. package/dist/types/model/quote-requests.d.ts +344 -0
  110. package/dist/types/model/quote.d.ts +572 -0
  111. package/dist/types/model/trade-requests.d.ts +195 -0
  112. package/dist/types/model/trade.d.ts +235 -0
  113. package/dist/types/push/callbacks.d.ts +32 -51
  114. package/dist/types/push/index.d.ts +5 -3
  115. package/dist/types/push/pb/AssetData.d.ts +47 -0
  116. package/dist/types/push/pb/KlineData.d.ts +43 -0
  117. package/dist/types/push/pb/OptionTopData.d.ts +76 -0
  118. package/dist/types/push/pb/OrderStatusData.d.ts +100 -0
  119. package/dist/types/push/pb/OrderTransactionData.d.ts +51 -0
  120. package/dist/types/push/pb/PositionData.d.ts +63 -0
  121. package/dist/types/push/pb/PushData.d.ts +46 -0
  122. package/dist/types/push/pb/QuoteBBOData.d.ts +35 -0
  123. package/dist/types/push/pb/QuoteBasicData.d.ts +66 -0
  124. package/dist/types/push/pb/QuoteData.d.ts +94 -0
  125. package/dist/types/push/pb/QuoteDepthData.d.ts +37 -0
  126. package/dist/types/push/pb/Request.d.ts +44 -0
  127. package/dist/types/push/pb/Response.d.ts +30 -0
  128. package/dist/types/push/pb/SocketCommon.d.ts +64 -0
  129. package/dist/types/push/pb/StockTopData.d.ts +37 -0
  130. package/dist/types/push/pb/TickData.d.ts +44 -0
  131. package/dist/types/push/pb/TradeTickData.d.ts +53 -0
  132. package/dist/types/push/pb/index.d.ts +39 -0
  133. package/dist/types/push/proto-message.d.ts +33 -0
  134. package/dist/types/push/push-client.d.ts +100 -68
  135. package/dist/types/push/push-message.d.ts +7 -147
  136. package/dist/types/push/varint.d.ts +23 -0
  137. package/dist/types/quote/index.d.ts +2 -2
  138. package/dist/types/quote/quote-client.d.ts +154 -47
  139. package/dist/types/signer/signer.d.ts +6 -6
  140. package/dist/types/trade/trade-client.d.ts +106 -38
  141. package/package.json +11 -6
@@ -1,19 +1,78 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.PushClient = exports.ConnectionState = void 0;
37
+ /**
38
+ * PushClient - TCP + TLS push client (Protobuf protocol)
39
+ *
40
+ * Connects to the push server via raw TCP + TLS to receive real-time
41
+ * market data and account push notifications.
42
+ * Uses varint32 length-prefixed + Protobuf binary frame format,
43
+ * aligned with the Java SDK's Netty + Protobuf implementation.
44
+ * Supports connection authentication, subscribe/unsubscribe, callbacks,
45
+ * heartbeat keep-alive, and automatic reconnection.
46
+ */
47
+ const tls = __importStar(require("tls"));
4
48
  const signer_1 = require("../signer/signer");
5
49
  const push_message_1 = require("./push-message");
6
- /** 默认推送服务器地址 */
7
- const DEFAULT_PUSH_URL = 'wss://openapi-push.tigerfintech.com';
8
- /** 默认心跳间隔(毫秒) */
50
+ const Request_1 = require("./pb/Request");
51
+ const Response_1 = require("./pb/Response");
52
+ const SocketCommon_1 = require("./pb/SocketCommon");
53
+ const varint_1 = require("./varint");
54
+ const proto_message_1 = require("./proto-message");
55
+ /** Default push server host */
56
+ const DEFAULT_PUSH_HOST = 'openapi.tigerfintech.com';
57
+ /** Default push server port */
58
+ const DEFAULT_PUSH_PORT = 9883;
59
+ /** Default heartbeat interval (ms) */
9
60
  const DEFAULT_HEARTBEAT_INTERVAL = 10000;
10
- /** 默认重连间隔(毫秒) */
61
+ /** Default reconnect interval (ms) */
11
62
  const DEFAULT_RECONNECT_INTERVAL = 5000;
12
- /** 最大重连间隔(毫秒) */
63
+ /** Max reconnect interval (ms) */
13
64
  const MAX_RECONNECT_INTERVAL = 60000;
14
- /** 默认连接超时(毫秒) */
65
+ /** Default connect timeout (ms) */
15
66
  const DEFAULT_CONNECT_TIMEOUT = 30000;
16
- /** 连接状态 */
67
+ /** SDK version identifier */
68
+ const SDK_VERSION = 'typescript/0.4.0';
69
+ /** Protocol version */
70
+ const ACCEPT_VERSION = '1.0';
71
+ /** Default send heartbeat interval (ms) */
72
+ const DEFAULT_SEND_INTERVAL = 10000;
73
+ /** Default receive heartbeat interval (ms) */
74
+ const DEFAULT_RECEIVE_INTERVAL = 10000;
75
+ /** Connection state */
17
76
  var ConnectionState;
18
77
  (function (ConnectionState) {
19
78
  ConnectionState[ConnectionState["Disconnected"] = 0] = "Disconnected";
@@ -21,151 +80,220 @@ var ConnectionState;
21
80
  ConnectionState[ConnectionState["Connected"] = 2] = "Connected";
22
81
  })(ConnectionState || (exports.ConnectionState = ConnectionState = {}));
23
82
  /**
24
- * PushClient WebSocket 推送客户端
83
+ * PushClient - TCP + TLS push client (Protobuf protocol)
25
84
  */
26
85
  class PushClient {
27
86
  constructor(config, options) {
28
- this.ws = null;
87
+ this.socket = null;
29
88
  this._state = ConnectionState.Disconnected;
30
89
  this.callbacks = {};
31
- /** 订阅状态管理:subject -> Set<symbol> */
90
+ /** Subscription state: subject -> Set<symbol> */
32
91
  this.subscriptions = new Map();
33
- /** 账户级别订阅 */
92
+ /** Account-level subscriptions */
34
93
  this.accountSubs = new Set();
35
94
  this.heartbeatTimer = null;
36
95
  this.reconnectTimer = null;
37
96
  this.stopped = false;
38
- /** 可注入的 WebSocket 工厂(用于测试) */
39
- this.wsFactory = null;
97
+ /** TCP stream receive buffer */
98
+ this.recvBuffer = Buffer.alloc(0);
99
+ /** Injectable TLS socket factory (for testing) */
100
+ this.socketFactory = null;
40
101
  this.config = config;
41
- this.pushUrl = options?.pushUrl ?? DEFAULT_PUSH_URL;
102
+ this.pushHost = options?.pushHost ?? DEFAULT_PUSH_HOST;
103
+ this.pushPort = options?.pushPort ?? DEFAULT_PUSH_PORT;
42
104
  this.heartbeatInterval = options?.heartbeatInterval ?? DEFAULT_HEARTBEAT_INTERVAL;
43
105
  this.reconnectInterval = options?.reconnectInterval ?? DEFAULT_RECONNECT_INTERVAL;
44
106
  this.autoReconnect = options?.autoReconnect ?? true;
45
107
  this.connectTimeout = options?.connectTimeout ?? DEFAULT_CONNECT_TIMEOUT;
108
+ this.useFullTick = options?.useFullTick ?? false;
46
109
  }
47
- /** 获取当前连接状态 */
110
+ /** Get current connection state */
48
111
  get state() {
49
112
  return this._state;
50
113
  }
51
- /** 设置回调函数集合 */
114
+ /** Set callback functions */
52
115
  setCallbacks(cb) {
53
116
  this.callbacks = cb;
54
117
  }
55
- /** 连接到推送服务器并进行认证 */
118
+ /** Connect to the push server and authenticate */
56
119
  connect() {
57
120
  return new Promise((resolve, reject) => {
58
121
  if (this._state !== ConnectionState.Disconnected) {
59
- reject(new Error('客户端已连接或正在连接中'));
122
+ reject(new Error('Client is already connected or connecting'));
60
123
  return;
61
124
  }
62
125
  this._state = ConnectionState.Connecting;
63
126
  this.stopped = false;
64
- const ws = this.createWebSocket(this.pushUrl);
65
- this.ws = ws;
127
+ this.recvBuffer = Buffer.alloc(0);
128
+ const socket = this.createSocket(this.pushHost, this.pushPort);
129
+ this.socket = socket;
130
+ let settled = false;
66
131
  const timeout = setTimeout(() => {
67
- ws.close();
68
- this._state = ConnectionState.Disconnected;
69
- this.ws = null;
70
- reject(new Error('连接超时'));
132
+ if (!settled) {
133
+ settled = true;
134
+ socket.destroy();
135
+ this._state = ConnectionState.Disconnected;
136
+ this.socket = null;
137
+ reject(new Error('Connect timeout'));
138
+ }
71
139
  }, this.connectTimeout);
72
- ws.onopen = () => {
73
- clearTimeout(timeout);
74
- // 发送认证消息
75
- try {
76
- this.authenticate();
140
+ // Resolve once we receive CONNECTED response from server
141
+ const onConnected = () => {
142
+ if (!settled) {
143
+ settled = true;
144
+ clearTimeout(timeout);
77
145
  this._state = ConnectionState.Connected;
78
146
  this.startHeartbeat();
79
147
  this.callbacks.onConnect?.();
80
148
  resolve();
81
149
  }
150
+ };
151
+ socket.on('connect', () => {
152
+ // TLS handshake complete, send authentication message
153
+ try {
154
+ this.authenticate();
155
+ }
82
156
  catch (err) {
83
- ws.close();
84
- this._state = ConnectionState.Disconnected;
85
- this.ws = null;
86
- reject(err);
157
+ if (!settled) {
158
+ settled = true;
159
+ clearTimeout(timeout);
160
+ socket.destroy();
161
+ this._state = ConnectionState.Disconnected;
162
+ this.socket = null;
163
+ reject(err);
164
+ }
87
165
  }
88
- };
89
- ws.onmessage = (ev) => {
90
- this.handleMessage(String(ev.data));
91
- };
92
- ws.onclose = () => {
166
+ });
167
+ socket.on('data', (chunk) => {
168
+ this.handleData(chunk, onConnected);
169
+ });
170
+ socket.on('close', () => {
93
171
  if (this.stopped)
94
172
  return;
95
173
  this.stopHeartbeat();
96
174
  const wasConnected = this._state === ConnectionState.Connected;
97
175
  this._state = ConnectionState.Disconnected;
98
- this.ws = null;
176
+ this.socket = null;
99
177
  if (wasConnected && this.autoReconnect && !this.stopped) {
100
178
  this.scheduleReconnect();
101
179
  }
102
- };
103
- ws.onerror = (ev) => {
104
- if (this._state === ConnectionState.Connecting) {
180
+ });
181
+ socket.on('error', (err) => {
182
+ if (!settled) {
183
+ settled = true;
105
184
  clearTimeout(timeout);
106
185
  this._state = ConnectionState.Disconnected;
107
- this.ws = null;
108
- reject(new Error('WebSocket 连接失败'));
186
+ this.socket = null;
187
+ reject(new Error(`TLS connection failed: ${err.message}`));
109
188
  }
110
189
  else {
111
- this.callbacks.onError?.(new Error('WebSocket 错误'));
190
+ this.callbacks.onError?.(new Error(`TLS error: ${err.message}`));
112
191
  }
113
- };
192
+ });
114
193
  });
115
194
  }
116
- /** 断开连接 */
195
+ /** Disconnect from the push server */
117
196
  disconnect() {
118
197
  this.stopped = true;
119
198
  this.stopHeartbeat();
120
199
  this.clearReconnectTimer();
121
- if (this.ws) {
122
- this.ws.onclose = null;
123
- this.ws.onerror = null;
124
- this.ws.onmessage = null;
125
- this.ws.close();
126
- this.ws = null;
200
+ if (this.socket) {
201
+ // Send DISCONNECT message before closing
202
+ try {
203
+ const req = (0, proto_message_1.buildDisconnectMessage)();
204
+ this.sendMessage(req);
205
+ }
206
+ catch {
207
+ // Ignore send failure, continue closing
208
+ }
209
+ this.socket.destroy();
210
+ this.socket = null;
127
211
  }
128
212
  this._state = ConnectionState.Disconnected;
213
+ this.recvBuffer = Buffer.alloc(0);
129
214
  this.callbacks.onDisconnect?.();
130
215
  }
131
- /** 发送认证消息 */
216
+ /** Send authentication message */
132
217
  authenticate() {
133
- const ts = new Date().toISOString().replace('T', ' ').substring(0, 19);
218
+ // Sign content is the tigerId string itself (aligned with Java SDK)
134
219
  const sign = (0, signer_1.signWithRSA)(this.config.privateKey, this.config.tigerId);
135
- const req = {
136
- tigerId: this.config.tigerId,
137
- sign,
138
- timestamp: ts,
139
- version: '2.0',
140
- };
141
- const msg = (0, push_message_1.createPushMessage)(push_message_1.MessageType.Connect, undefined, req);
142
- this.sendMessage(msg);
220
+ const req = (0, proto_message_1.buildConnectMessage)(this.config.tigerId, sign, SDK_VERSION, ACCEPT_VERSION, DEFAULT_SEND_INTERVAL, DEFAULT_RECEIVE_INTERVAL, this.useFullTick);
221
+ this.sendMessage(req);
143
222
  }
144
- /** 发送消息 */
223
+ /** Send a Protobuf Request message (varint32 length prefix + binary) */
145
224
  sendMessage(msg) {
146
- if (!this.ws)
147
- throw new Error('WebSocket 连接未建立');
148
- this.ws.send((0, push_message_1.serializeMessage)(msg));
149
- }
150
- /** 创建 WebSocket 实例 */
151
- createWebSocket(url) {
152
- if (this.wsFactory)
153
- return this.wsFactory(url);
154
- // 动态导入 ws 库(Node.js 环境)
155
- // eslint-disable-next-line @typescript-eslint/no-require-imports
156
- const WS = require('ws');
157
- return new WS(url);
158
- }
159
- /** 心跳保活 */
225
+ if (!this.socket)
226
+ throw new Error('TLS connection not established');
227
+ const encoded = Request_1.Request.encode(msg).finish();
228
+ const framed = (0, varint_1.encodeVarint32)(encoded);
229
+ this.socket.write(framed);
230
+ }
231
+ /** Create a TLS socket instance */
232
+ createSocket(host, port) {
233
+ if (this.socketFactory)
234
+ return this.socketFactory(host, port);
235
+ return tls.connect({ host, port, rejectUnauthorized: false });
236
+ }
237
+ /** Handle incoming TCP data: buffer + decode varint32 frames */
238
+ handleData(chunk, onConnected) {
239
+ this.recvBuffer = Buffer.concat([this.recvBuffer, chunk]);
240
+ while (this.recvBuffer.length > 0) {
241
+ const frame = (0, varint_1.decodeVarint32)(new Uint8Array(this.recvBuffer));
242
+ if (!frame)
243
+ break; // Incomplete frame, wait for more data
244
+ this.recvBuffer = Buffer.from(frame.remaining);
245
+ // Deserialize Protobuf Response
246
+ let response;
247
+ try {
248
+ response = Response_1.Response.decode(frame.message);
249
+ }
250
+ catch {
251
+ this.callbacks.onError?.(new Error('Protobuf deserialization failed'));
252
+ continue;
253
+ }
254
+ this.handleResponse(response, onConnected);
255
+ }
256
+ }
257
+ /** Handle a decoded Response message */
258
+ handleResponse(response, onConnected) {
259
+ const cb = this.callbacks;
260
+ switch (response.command) {
261
+ case SocketCommon_1.SocketCommon_Command.CONNECTED:
262
+ // Server confirmed authentication
263
+ onConnected?.();
264
+ break;
265
+ case SocketCommon_1.SocketCommon_Command.HEARTBEAT:
266
+ // Heartbeat response, ignore
267
+ break;
268
+ case SocketCommon_1.SocketCommon_Command.MESSAGE:
269
+ if (response.body) {
270
+ this.dispatchPushData(response.body);
271
+ }
272
+ break;
273
+ case SocketCommon_1.SocketCommon_Command.ERROR:
274
+ cb.onError?.(new Error(`Server error: ${response.msg ?? 'unknown error'}`));
275
+ // Check for kickout
276
+ if (response.msg && response.msg.toLowerCase().includes('kickout')) {
277
+ cb.onKickout?.(response.msg);
278
+ }
279
+ break;
280
+ case SocketCommon_1.SocketCommon_Command.DISCONNECT:
281
+ cb.onDisconnect?.();
282
+ break;
283
+ default:
284
+ break;
285
+ }
286
+ }
287
+ /** Heartbeat keep-alive */
160
288
  startHeartbeat() {
161
289
  this.stopHeartbeat();
162
290
  this.heartbeatTimer = setInterval(() => {
163
291
  try {
164
- const msg = (0, push_message_1.createPushMessage)(push_message_1.MessageType.Heartbeat);
165
- this.sendMessage(msg);
292
+ const req = (0, proto_message_1.buildHeartBeatMessage)();
293
+ this.sendMessage(req);
166
294
  }
167
295
  catch {
168
- // 心跳发送失败,连接可能已断开
296
+ // Heartbeat send failed, connection may be broken
169
297
  }
170
298
  }, this.heartbeatInterval);
171
299
  }
@@ -175,7 +303,7 @@ class PushClient {
175
303
  this.heartbeatTimer = null;
176
304
  }
177
305
  }
178
- /** 自动重连 */
306
+ /** Auto reconnect with exponential backoff */
179
307
  scheduleReconnect() {
180
308
  let interval = this.reconnectInterval;
181
309
  const attempt = () => {
@@ -198,7 +326,7 @@ class PushClient {
198
326
  this.reconnectTimer = null;
199
327
  }
200
328
  }
201
- /** 重连后恢复订阅 */
329
+ /** Resubscribe after reconnection */
202
330
  resubscribe() {
203
331
  for (const [subject, symbols] of this.subscriptions) {
204
332
  this.doSubscribe(subject, Array.from(symbols));
@@ -207,81 +335,86 @@ class PushClient {
207
335
  this.doSubscribe(subject, undefined, this.config.account);
208
336
  }
209
337
  }
210
- /** 处理收到的消息 */
211
- handleMessage(raw) {
212
- let msg;
213
- try {
214
- msg = (0, push_message_1.deserializeMessage)(raw);
215
- }
216
- catch {
217
- this.callbacks.onError?.(new Error('反序列化消息失败'));
218
- return;
219
- }
338
+ /** Dispatch push data to the appropriate callback based on dataType */
339
+ dispatchPushData(pushData) {
220
340
  const cb = this.callbacks;
221
- switch (msg.type) {
222
- case push_message_1.MessageType.Kickout:
223
- cb.onKickout?.(msg.data);
224
- break;
225
- case push_message_1.MessageType.Error:
226
- cb.onError?.(new Error(`服务端错误: ${msg.data}`));
227
- break;
228
- case push_message_1.MessageType.Quote:
229
- cb.onQuote?.(msg.data);
341
+ switch (pushData.dataType) {
342
+ case SocketCommon_1.SocketCommon_DataType.Quote:
343
+ if (pushData.quoteData)
344
+ cb.onQuote?.(pushData.quoteData);
230
345
  break;
231
- case push_message_1.MessageType.Tick:
232
- cb.onTick?.(msg.data);
346
+ case SocketCommon_1.SocketCommon_DataType.Option:
347
+ if (pushData.quoteData)
348
+ cb.onOption?.(pushData.quoteData);
233
349
  break;
234
- case push_message_1.MessageType.Depth:
235
- cb.onDepth?.(msg.data);
350
+ case SocketCommon_1.SocketCommon_DataType.Future:
351
+ if (pushData.quoteData)
352
+ cb.onFuture?.(pushData.quoteData);
236
353
  break;
237
- case push_message_1.MessageType.Option:
238
- cb.onOption?.(msg.data);
354
+ case SocketCommon_1.SocketCommon_DataType.QuoteDepth:
355
+ if (pushData.quoteDepthData)
356
+ cb.onDepth?.(pushData.quoteDepthData);
239
357
  break;
240
- case push_message_1.MessageType.Future:
241
- cb.onFuture?.(msg.data);
358
+ case SocketCommon_1.SocketCommon_DataType.TradeTick:
359
+ if (pushData.tradeTickData)
360
+ cb.onTick?.(pushData.tradeTickData);
242
361
  break;
243
- case push_message_1.MessageType.Kline:
244
- cb.onKline?.(msg.data);
362
+ case SocketCommon_1.SocketCommon_DataType.Asset:
363
+ if (pushData.assetData)
364
+ cb.onAsset?.(pushData.assetData);
245
365
  break;
246
- case push_message_1.MessageType.Asset:
247
- cb.onAsset?.(msg.data);
366
+ case SocketCommon_1.SocketCommon_DataType.Position:
367
+ if (pushData.positionData)
368
+ cb.onPosition?.(pushData.positionData);
248
369
  break;
249
- case push_message_1.MessageType.Position:
250
- cb.onPosition?.(msg.data);
370
+ case SocketCommon_1.SocketCommon_DataType.OrderStatus:
371
+ if (pushData.orderStatusData)
372
+ cb.onOrder?.(pushData.orderStatusData);
251
373
  break;
252
- case push_message_1.MessageType.Order:
253
- cb.onOrder?.(msg.data);
374
+ case SocketCommon_1.SocketCommon_DataType.OrderTransaction:
375
+ if (pushData.orderTransactionData)
376
+ cb.onTransaction?.(pushData.orderTransactionData);
254
377
  break;
255
- case push_message_1.MessageType.Transaction:
256
- cb.onTransaction?.(msg.data);
378
+ case SocketCommon_1.SocketCommon_DataType.StockTop:
379
+ if (pushData.stockTopData)
380
+ cb.onStockTop?.(pushData.stockTopData);
257
381
  break;
258
- case push_message_1.MessageType.StockTop:
259
- cb.onStockTop?.(msg.data);
382
+ case SocketCommon_1.SocketCommon_DataType.OptionTop:
383
+ if (pushData.optionTopData)
384
+ cb.onOptionTop?.(pushData.optionTopData);
260
385
  break;
261
- case push_message_1.MessageType.OptionTop:
262
- cb.onOptionTop?.(msg.data);
386
+ case SocketCommon_1.SocketCommon_DataType.Kline:
387
+ if (pushData.klineData)
388
+ cb.onKline?.(pushData.klineData);
263
389
  break;
264
- case push_message_1.MessageType.FullTick:
265
- cb.onFullTick?.(msg.data);
390
+ case SocketCommon_1.SocketCommon_DataType.Cc:
391
+ // 加密货币(Cc)推送复用 QuoteData body,路由到 onQuote 回调
392
+ if (pushData.quoteData)
393
+ cb.onQuote?.(pushData.quoteData);
266
394
  break;
267
- case push_message_1.MessageType.QuoteBBO:
268
- cb.onQuoteBBO?.(msg.data);
395
+ default:
396
+ // FullTick and QuoteBBO use TradeTick/Quote dataType
397
+ // TickData (fullTick) via tickData field
398
+ if (pushData.tickData)
399
+ cb.onFullTick?.(pushData.tickData);
269
400
  break;
270
401
  }
271
402
  }
272
- /** 内部订阅方法 */
403
+ /** Internal subscribe method */
273
404
  doSubscribe(subject, symbols, account, market) {
274
- const req = { subject, symbols, account, market };
275
- const msg = (0, push_message_1.createPushMessage)(push_message_1.MessageType.Subscribe, subject, req);
276
- this.sendMessage(msg);
405
+ const dataType = (0, proto_message_1.subjectToDataType)(subject);
406
+ const symbolsStr = symbols ? symbols.join(',') : undefined;
407
+ const req = (0, proto_message_1.buildSubscribeMessage)(dataType, symbolsStr, account, market);
408
+ this.sendMessage(req);
277
409
  }
278
- /** 内部退订方法 */
410
+ /** Internal unsubscribe method */
279
411
  doUnsubscribe(subject, symbols) {
280
- const req = { subject, symbols };
281
- const msg = (0, push_message_1.createPushMessage)(push_message_1.MessageType.Unsubscribe, subject, req);
282
- this.sendMessage(msg);
412
+ const dataType = (0, proto_message_1.subjectToDataType)(subject);
413
+ const symbolsStr = symbols ? symbols.join(',') : undefined;
414
+ const req = (0, proto_message_1.buildUnSubscribeMessage)(dataType, symbolsStr);
415
+ this.sendMessage(req);
283
416
  }
284
- /** 添加订阅记录 */
417
+ /** Add subscription record */
285
418
  addSubscription(subject, symbols) {
286
419
  if (!this.subscriptions.has(subject)) {
287
420
  this.subscriptions.set(subject, new Set());
@@ -290,7 +423,7 @@ class PushClient {
290
423
  for (const s of symbols)
291
424
  set.add(s);
292
425
  }
293
- /** 移除订阅记录 */
426
+ /** Remove subscription record */
294
427
  removeSubscription(subject, symbols) {
295
428
  if (!symbols) {
296
429
  this.subscriptions.delete(subject);
@@ -304,110 +437,175 @@ class PushClient {
304
437
  this.subscriptions.delete(subject);
305
438
  }
306
439
  }
307
- // ===== 行情订阅/退订 =====
308
- /** 订阅行情 */
440
+ // ===== Market data subscribe/unsubscribe =====
441
+ /** Subscribe to quotes */
309
442
  subscribeQuote(symbols) {
310
443
  this.doSubscribe(push_message_1.SubjectType.Quote, symbols);
311
444
  this.addSubscription(push_message_1.SubjectType.Quote, symbols);
312
445
  }
313
- /** 退订行情 */
446
+ /** Unsubscribe from quotes */
314
447
  unsubscribeQuote(symbols) {
315
448
  this.doUnsubscribe(push_message_1.SubjectType.Quote, symbols);
316
449
  this.removeSubscription(push_message_1.SubjectType.Quote, symbols);
317
450
  }
318
- /** 订阅逐笔成交 */
451
+ /** Subscribe to trade ticks */
319
452
  subscribeTick(symbols) {
320
453
  this.doSubscribe(push_message_1.SubjectType.Tick, symbols);
321
454
  this.addSubscription(push_message_1.SubjectType.Tick, symbols);
322
455
  }
323
- /** 退订逐笔成交 */
456
+ /** Unsubscribe from trade ticks */
324
457
  unsubscribeTick(symbols) {
325
458
  this.doUnsubscribe(push_message_1.SubjectType.Tick, symbols);
326
459
  this.removeSubscription(push_message_1.SubjectType.Tick, symbols);
327
460
  }
328
- /** 订阅深度行情 */
461
+ /** Subscribe to depth data */
329
462
  subscribeDepth(symbols) {
330
463
  this.doSubscribe(push_message_1.SubjectType.Depth, symbols);
331
464
  this.addSubscription(push_message_1.SubjectType.Depth, symbols);
332
465
  }
333
- /** 退订深度行情 */
466
+ /** Unsubscribe from depth data */
334
467
  unsubscribeDepth(symbols) {
335
468
  this.doUnsubscribe(push_message_1.SubjectType.Depth, symbols);
336
469
  this.removeSubscription(push_message_1.SubjectType.Depth, symbols);
337
470
  }
338
- /** 订阅期权行情 */
471
+ /** Subscribe to option quotes */
339
472
  subscribeOption(symbols) {
340
473
  this.doSubscribe(push_message_1.SubjectType.Option, symbols);
341
474
  this.addSubscription(push_message_1.SubjectType.Option, symbols);
342
475
  }
343
- /** 退订期权行情 */
476
+ /** Unsubscribe from option quotes */
344
477
  unsubscribeOption(symbols) {
345
478
  this.doUnsubscribe(push_message_1.SubjectType.Option, symbols);
346
479
  this.removeSubscription(push_message_1.SubjectType.Option, symbols);
347
480
  }
348
- /** 订阅期货行情 */
481
+ /** Subscribe to futures quotes */
349
482
  subscribeFuture(symbols) {
350
483
  this.doSubscribe(push_message_1.SubjectType.Future, symbols);
351
484
  this.addSubscription(push_message_1.SubjectType.Future, symbols);
352
485
  }
353
- /** 退订期货行情 */
486
+ /** Unsubscribe from futures quotes */
354
487
  unsubscribeFuture(symbols) {
355
488
  this.doUnsubscribe(push_message_1.SubjectType.Future, symbols);
356
489
  this.removeSubscription(push_message_1.SubjectType.Future, symbols);
357
490
  }
358
- /** 订阅 K 线 */
491
+ /** Subscribe to kline data */
359
492
  subscribeKline(symbols) {
360
493
  this.doSubscribe(push_message_1.SubjectType.Kline, symbols);
361
494
  this.addSubscription(push_message_1.SubjectType.Kline, symbols);
362
495
  }
363
- /** 退订 K 线 */
496
+ /** Unsubscribe from kline data */
364
497
  unsubscribeKline(symbols) {
365
498
  this.doUnsubscribe(push_message_1.SubjectType.Kline, symbols);
366
499
  this.removeSubscription(push_message_1.SubjectType.Kline, symbols);
367
500
  }
368
- // ===== 账户推送订阅/退订 =====
369
- /** 订阅资产变动 */
501
+ // ===== v0.4.0: stock_top / option_top / cc / market =====
502
+ /**
503
+ * Subscribe to stock top rankings (v0.4.0).
504
+ * @param market US / HK / CN
505
+ * @param indicators LATEST_PRICE / HIGHER / LOWER / VOLUME / AMOUNT / AMPLITUDE / TURNOVER_RATE
506
+ */
507
+ subscribeStockTop(market, indicators) {
508
+ this.doSubscribe(push_message_1.SubjectType.StockTop, indicators, undefined, market);
509
+ this.addSubscription(push_message_1.SubjectType.StockTop, indicators);
510
+ }
511
+ /** Unsubscribe from stock top rankings (v0.4.0). */
512
+ unsubscribeStockTop(market, indicators) {
513
+ const dataType = (0, proto_message_1.subjectToDataType)(push_message_1.SubjectType.StockTop);
514
+ const symbolsStr = indicators.join(',');
515
+ const req = (0, proto_message_1.buildUnSubscribeMessage)(dataType, symbolsStr, undefined, market);
516
+ this.sendMessage(req);
517
+ this.removeSubscription(push_message_1.SubjectType.StockTop, indicators);
518
+ }
519
+ /**
520
+ * Subscribe to option top rankings (v0.4.0).
521
+ * @param market US / HK / CN
522
+ */
523
+ subscribeOptionTop(market, indicators) {
524
+ this.doSubscribe(push_message_1.SubjectType.OptionTop, indicators, undefined, market);
525
+ this.addSubscription(push_message_1.SubjectType.OptionTop, indicators);
526
+ }
527
+ /** Unsubscribe from option top rankings (v0.4.0). */
528
+ unsubscribeOptionTop(market, indicators) {
529
+ const dataType = (0, proto_message_1.subjectToDataType)(push_message_1.SubjectType.OptionTop);
530
+ const symbolsStr = indicators.join(',');
531
+ const req = (0, proto_message_1.buildUnSubscribeMessage)(dataType, symbolsStr, undefined, market);
532
+ this.sendMessage(req);
533
+ this.removeSubscription(push_message_1.SubjectType.OptionTop, indicators);
534
+ }
535
+ /**
536
+ * Subscribe to crypto realtime quotes (v0.4.0).
537
+ * Data is delivered via the `onQuote` callback (same QuoteData body).
538
+ * @param symbols e.g. ["BTCUSD", "ETHUSD"]
539
+ */
540
+ subscribeCc(symbols) {
541
+ this.doSubscribe(push_message_1.SubjectType.Cc, symbols);
542
+ this.addSubscription(push_message_1.SubjectType.Cc, symbols);
543
+ }
544
+ /** Unsubscribe from crypto realtime quotes (v0.4.0). */
545
+ unsubscribeCc(symbols) {
546
+ this.doUnsubscribe(push_message_1.SubjectType.Cc, symbols);
547
+ this.removeSubscription(push_message_1.SubjectType.Cc, symbols);
548
+ }
549
+ /**
550
+ * Subscribe to market status updates (v0.4.0).
551
+ * Protocol uses dataType=Quote with only the `market` field set.
552
+ * Data is delivered via the `onQuote` callback.
553
+ */
554
+ subscribeMarket(market) {
555
+ // symbols 为空,只带 market 字段
556
+ this.doSubscribe(push_message_1.SubjectType.Market, undefined, undefined, market);
557
+ this.addSubscription(push_message_1.SubjectType.Market, [market]);
558
+ }
559
+ /** Unsubscribe from market status (v0.4.0). */
560
+ unsubscribeMarket(market) {
561
+ const dataType = (0, proto_message_1.subjectToDataType)(push_message_1.SubjectType.Market);
562
+ const req = (0, proto_message_1.buildUnSubscribeMessage)(dataType, undefined, undefined, market);
563
+ this.sendMessage(req);
564
+ this.removeSubscription(push_message_1.SubjectType.Market, [market]);
565
+ }
566
+ // ===== Account push subscribe/unsubscribe =====
567
+ /** Subscribe to asset changes */
370
568
  subscribeAsset(account) {
371
569
  this.doSubscribe(push_message_1.SubjectType.Asset, undefined, account || this.config.account);
372
570
  this.accountSubs.add(push_message_1.SubjectType.Asset);
373
571
  }
374
- /** 退订资产变动 */
572
+ /** Unsubscribe from asset changes */
375
573
  unsubscribeAsset() {
376
574
  this.doUnsubscribe(push_message_1.SubjectType.Asset);
377
575
  this.accountSubs.delete(push_message_1.SubjectType.Asset);
378
576
  }
379
- /** 订阅持仓变动 */
577
+ /** Subscribe to position changes */
380
578
  subscribePosition(account) {
381
579
  this.doSubscribe(push_message_1.SubjectType.Position, undefined, account || this.config.account);
382
580
  this.accountSubs.add(push_message_1.SubjectType.Position);
383
581
  }
384
- /** 退订持仓变动 */
582
+ /** Unsubscribe from position changes */
385
583
  unsubscribePosition() {
386
584
  this.doUnsubscribe(push_message_1.SubjectType.Position);
387
585
  this.accountSubs.delete(push_message_1.SubjectType.Position);
388
586
  }
389
- /** 订阅订单状态 */
587
+ /** Subscribe to order status */
390
588
  subscribeOrder(account) {
391
589
  this.doSubscribe(push_message_1.SubjectType.Order, undefined, account || this.config.account);
392
590
  this.accountSubs.add(push_message_1.SubjectType.Order);
393
591
  }
394
- /** 退订订单状态 */
592
+ /** Unsubscribe from order status */
395
593
  unsubscribeOrder() {
396
594
  this.doUnsubscribe(push_message_1.SubjectType.Order);
397
595
  this.accountSubs.delete(push_message_1.SubjectType.Order);
398
596
  }
399
- /** 订阅成交明细 */
597
+ /** Subscribe to transaction details */
400
598
  subscribeTransaction(account) {
401
599
  this.doSubscribe(push_message_1.SubjectType.Transaction, undefined, account || this.config.account);
402
600
  this.accountSubs.add(push_message_1.SubjectType.Transaction);
403
601
  }
404
- /** 退订成交明细 */
602
+ /** Unsubscribe from transaction details */
405
603
  unsubscribeTransaction() {
406
604
  this.doUnsubscribe(push_message_1.SubjectType.Transaction);
407
605
  this.accountSubs.delete(push_message_1.SubjectType.Transaction);
408
606
  }
409
- // ===== 查询订阅状态 =====
410
- /** 获取当前行情订阅状态 */
607
+ // ===== Query subscription state =====
608
+ /** Get current market data subscriptions */
411
609
  getSubscriptions() {
412
610
  const result = new Map();
413
611
  for (const [subject, symbols] of this.subscriptions) {
@@ -415,7 +613,7 @@ class PushClient {
415
613
  }
416
614
  return result;
417
615
  }
418
- /** 获取账户级别订阅状态 */
616
+ /** Get account-level subscriptions */
419
617
  getAccountSubscriptions() {
420
618
  return Array.from(this.accountSubs);
421
619
  }