@tigeropenapi/tigeropen 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 (96) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +272 -0
  3. package/dist/cjs/client/api-request.js +26 -0
  4. package/dist/cjs/client/api-response.js +18 -0
  5. package/dist/cjs/client/errors.js +51 -0
  6. package/dist/cjs/client/http-client.js +149 -0
  7. package/dist/cjs/client/index.js +19 -0
  8. package/dist/cjs/client/retry.js +54 -0
  9. package/dist/cjs/config/client-config.js +114 -0
  10. package/dist/cjs/config/config-parser.js +142 -0
  11. package/dist/cjs/config/domain.js +97 -0
  12. package/dist/cjs/config/index.js +14 -0
  13. package/dist/cjs/config/token-manager.js +116 -0
  14. package/dist/cjs/index.js +21 -0
  15. package/dist/cjs/logger/index.js +20 -0
  16. package/dist/cjs/logger/logger.js +68 -0
  17. package/dist/cjs/model/contract-helpers.js +38 -0
  18. package/dist/cjs/model/contract.js +6 -0
  19. package/dist/cjs/model/enums.js +110 -0
  20. package/dist/cjs/model/order-helpers.js +104 -0
  21. package/dist/cjs/model/order.js +6 -0
  22. package/dist/cjs/model/position.js +6 -0
  23. package/dist/cjs/push/callbacks.js +2 -0
  24. package/dist/cjs/push/index.js +25 -0
  25. package/dist/cjs/push/push-client.js +423 -0
  26. package/dist/cjs/push/push-message.js +83 -0
  27. package/dist/cjs/quote/index.js +8 -0
  28. package/dist/cjs/quote/quote-client.js +110 -0
  29. package/dist/cjs/signer/index.js +12 -0
  30. package/dist/cjs/signer/sign-content.js +19 -0
  31. package/dist/cjs/signer/signer.js +93 -0
  32. package/dist/cjs/trade/index.js +8 -0
  33. package/dist/cjs/trade/trade-client.js +99 -0
  34. package/dist/esm/client/api-request.js +23 -0
  35. package/dist/esm/client/api-response.js +15 -0
  36. package/dist/esm/client/errors.js +46 -0
  37. package/dist/esm/client/http-client.js +145 -0
  38. package/dist/esm/client/index.js +8 -0
  39. package/dist/esm/client/retry.js +48 -0
  40. package/dist/esm/config/client-config.js +111 -0
  41. package/dist/esm/config/config-parser.js +105 -0
  42. package/dist/esm/config/domain.js +93 -0
  43. package/dist/esm/config/index.js +6 -0
  44. package/dist/esm/config/token-manager.js +112 -0
  45. package/dist/esm/index.js +18 -0
  46. package/dist/esm/logger/index.js +4 -0
  47. package/dist/esm/logger/logger.js +61 -0
  48. package/dist/esm/model/contract-helpers.js +29 -0
  49. package/dist/esm/model/contract.js +5 -0
  50. package/dist/esm/model/enums.js +107 -0
  51. package/dist/esm/model/order-helpers.js +93 -0
  52. package/dist/esm/model/order.js +5 -0
  53. package/dist/esm/model/position.js +5 -0
  54. package/dist/esm/push/callbacks.js +1 -0
  55. package/dist/esm/push/index.js +6 -0
  56. package/dist/esm/push/push-client.js +419 -0
  57. package/dist/esm/push/push-message.js +77 -0
  58. package/dist/esm/quote/index.js +4 -0
  59. package/dist/esm/quote/quote-client.js +106 -0
  60. package/dist/esm/signer/index.js +5 -0
  61. package/dist/esm/signer/sign-content.js +16 -0
  62. package/dist/esm/signer/signer.js +88 -0
  63. package/dist/esm/trade/index.js +4 -0
  64. package/dist/esm/trade/trade-client.js +95 -0
  65. package/dist/types/client/api-request.d.ts +18 -0
  66. package/dist/types/client/api-response.d.ts +16 -0
  67. package/dist/types/client/errors.d.ts +19 -0
  68. package/dist/types/client/http-client.d.ts +45 -0
  69. package/dist/types/client/index.d.ts +8 -0
  70. package/dist/types/client/retry.d.ts +37 -0
  71. package/dist/types/config/client-config.d.ts +43 -0
  72. package/dist/types/config/config-parser.d.ts +16 -0
  73. package/dist/types/config/domain.d.ts +18 -0
  74. package/dist/types/config/index.d.ts +7 -0
  75. package/dist/types/config/token-manager.d.ts +38 -0
  76. package/dist/types/index.d.ts +18 -0
  77. package/dist/types/logger/index.d.ts +4 -0
  78. package/dist/types/logger/logger.d.ts +42 -0
  79. package/dist/types/model/contract-helpers.d.ts +18 -0
  80. package/dist/types/model/contract.d.ts +60 -0
  81. package/dist/types/model/enums.d.ts +97 -0
  82. package/dist/types/model/order-helpers.d.ts +22 -0
  83. package/dist/types/model/order.d.ts +95 -0
  84. package/dist/types/model/position.d.ts +39 -0
  85. package/dist/types/push/callbacks.d.ts +57 -0
  86. package/dist/types/push/index.d.ts +7 -0
  87. package/dist/types/push/push-client.d.ts +139 -0
  88. package/dist/types/push/push-message.d.ts +167 -0
  89. package/dist/types/quote/index.d.ts +4 -0
  90. package/dist/types/quote/quote-client.d.ts +58 -0
  91. package/dist/types/signer/index.d.ts +5 -0
  92. package/dist/types/signer/sign-content.d.ts +13 -0
  93. package/dist/types/signer/signer.d.ts +36 -0
  94. package/dist/types/trade/index.d.ts +4 -0
  95. package/dist/types/trade/trade-client.d.ts +48 -0
  96. package/package.json +54 -0
@@ -0,0 +1,107 @@
1
+ /**
2
+ * 枚举类型定义
3
+ * 所有枚举值与 API JSON 保持一致
4
+ */
5
+ /** 市场枚举 */
6
+ export var Market;
7
+ (function (Market) {
8
+ Market["All"] = "ALL";
9
+ Market["US"] = "US";
10
+ Market["HK"] = "HK";
11
+ Market["CN"] = "CN";
12
+ Market["SG"] = "SG";
13
+ })(Market || (Market = {}));
14
+ /** 证券类型枚举 */
15
+ export var SecurityType;
16
+ (function (SecurityType) {
17
+ SecurityType["All"] = "ALL";
18
+ SecurityType["STK"] = "STK";
19
+ SecurityType["OPT"] = "OPT";
20
+ SecurityType["WAR"] = "WAR";
21
+ SecurityType["IOPT"] = "IOPT";
22
+ SecurityType["FUT"] = "FUT";
23
+ SecurityType["FOP"] = "FOP";
24
+ SecurityType["CASH"] = "CASH";
25
+ SecurityType["MLEG"] = "MLEG";
26
+ SecurityType["FUND"] = "FUND";
27
+ })(SecurityType || (SecurityType = {}));
28
+ /** 货币枚举 */
29
+ export var Currency;
30
+ (function (Currency) {
31
+ Currency["All"] = "ALL";
32
+ Currency["USD"] = "USD";
33
+ Currency["HKD"] = "HKD";
34
+ Currency["CNH"] = "CNH";
35
+ Currency["SGD"] = "SGD";
36
+ })(Currency || (Currency = {}));
37
+ /** 订单类型枚举 */
38
+ export var OrderType;
39
+ (function (OrderType) {
40
+ OrderType["MKT"] = "MKT";
41
+ OrderType["LMT"] = "LMT";
42
+ OrderType["STP"] = "STP";
43
+ OrderType["STP_LMT"] = "STP_LMT";
44
+ OrderType["TRAIL"] = "TRAIL";
45
+ OrderType["AM"] = "AM";
46
+ OrderType["AL"] = "AL";
47
+ OrderType["TWAP"] = "TWAP";
48
+ OrderType["VWAP"] = "VWAP";
49
+ OrderType["OCA"] = "OCA";
50
+ })(OrderType || (OrderType = {}));
51
+ /** 订单状态枚举 */
52
+ export var OrderStatus;
53
+ (function (OrderStatus) {
54
+ OrderStatus["PendingNew"] = "PendingNew";
55
+ OrderStatus["Initial"] = "Initial";
56
+ OrderStatus["Submitted"] = "Submitted";
57
+ OrderStatus["PartiallyFilled"] = "PartiallyFilled";
58
+ OrderStatus["Filled"] = "Filled";
59
+ OrderStatus["Cancelled"] = "Cancelled";
60
+ OrderStatus["PendingCancel"] = "PendingCancel";
61
+ OrderStatus["Inactive"] = "Inactive";
62
+ OrderStatus["Invalid"] = "Invalid";
63
+ })(OrderStatus || (OrderStatus = {}));
64
+ /** K 线周期枚举 */
65
+ export var BarPeriod;
66
+ (function (BarPeriod) {
67
+ BarPeriod["Day"] = "day";
68
+ BarPeriod["Week"] = "week";
69
+ BarPeriod["Month"] = "month";
70
+ BarPeriod["Year"] = "year";
71
+ BarPeriod["Min1"] = "1min";
72
+ BarPeriod["Min5"] = "5min";
73
+ BarPeriod["Min15"] = "15min";
74
+ BarPeriod["Min30"] = "30min";
75
+ BarPeriod["Min60"] = "60min";
76
+ })(BarPeriod || (BarPeriod = {}));
77
+ /** 语言枚举 */
78
+ export var Language;
79
+ (function (Language) {
80
+ Language["ZhCN"] = "zh_CN";
81
+ Language["ZhTW"] = "zh_TW";
82
+ Language["EnUS"] = "en_US";
83
+ })(Language || (Language = {}));
84
+ /** 复权类型枚举 */
85
+ export var QuoteRight;
86
+ (function (QuoteRight) {
87
+ /** 前复权 */
88
+ QuoteRight["Br"] = "br";
89
+ /** 不复权 */
90
+ QuoteRight["Nr"] = "nr";
91
+ })(QuoteRight || (QuoteRight = {}));
92
+ /** 牌照类型枚举 */
93
+ export var License;
94
+ (function (License) {
95
+ License["TBNZ"] = "TBNZ";
96
+ License["TBSG"] = "TBSG";
97
+ License["TBHK"] = "TBHK";
98
+ License["TBAU"] = "TBAU";
99
+ License["TBUS"] = "TBUS";
100
+ })(License || (License = {}));
101
+ /** 订单有效期枚举 */
102
+ export var TimeInForce;
103
+ (function (TimeInForce) {
104
+ TimeInForce["DAY"] = "DAY";
105
+ TimeInForce["GTC"] = "GTC";
106
+ TimeInForce["OPG"] = "OPG";
107
+ })(TimeInForce || (TimeInForce = {}));
@@ -0,0 +1,93 @@
1
+ import { OrderType, TimeInForce } from './enums';
2
+ /** 构造市价单 */
3
+ export function marketOrder(account, symbol, secType, action, quantity) {
4
+ return {
5
+ account, symbol, secType, action,
6
+ orderType: OrderType.MKT,
7
+ totalQuantity: quantity,
8
+ timeInForce: TimeInForce.DAY,
9
+ outsideRth: false,
10
+ };
11
+ }
12
+ /** 构造限价单 */
13
+ export function limitOrder(account, symbol, secType, action, quantity, limitPrice) {
14
+ return {
15
+ account, symbol, secType, action,
16
+ orderType: OrderType.LMT,
17
+ totalQuantity: quantity,
18
+ limitPrice,
19
+ timeInForce: TimeInForce.DAY,
20
+ outsideRth: false,
21
+ };
22
+ }
23
+ /** 构造止损单 */
24
+ export function stopOrder(account, symbol, secType, action, quantity, auxPrice) {
25
+ return {
26
+ account, symbol, secType, action,
27
+ orderType: OrderType.STP,
28
+ totalQuantity: quantity,
29
+ auxPrice,
30
+ timeInForce: TimeInForce.DAY,
31
+ outsideRth: false,
32
+ };
33
+ }
34
+ /** 构造止损限价单 */
35
+ export function stopLimitOrder(account, symbol, secType, action, quantity, limitPrice, auxPrice) {
36
+ return {
37
+ account, symbol, secType, action,
38
+ orderType: OrderType.STP_LMT,
39
+ totalQuantity: quantity,
40
+ limitPrice,
41
+ auxPrice,
42
+ timeInForce: TimeInForce.DAY,
43
+ outsideRth: false,
44
+ };
45
+ }
46
+ /** 构造跟踪止损单 */
47
+ export function trailOrder(account, symbol, secType, action, quantity, trailingPercent) {
48
+ return {
49
+ account, symbol, secType, action,
50
+ orderType: OrderType.TRAIL,
51
+ totalQuantity: quantity,
52
+ trailingPercent,
53
+ timeInForce: TimeInForce.DAY,
54
+ outsideRth: false,
55
+ };
56
+ }
57
+ /** 构造竞价限价单 */
58
+ export function auctionLimitOrder(account, symbol, secType, action, quantity, limitPrice) {
59
+ return {
60
+ account, symbol, secType, action,
61
+ orderType: OrderType.AL,
62
+ totalQuantity: quantity,
63
+ limitPrice,
64
+ timeInForce: TimeInForce.DAY,
65
+ outsideRth: false,
66
+ };
67
+ }
68
+ /** 构造竞价市价单 */
69
+ export function auctionMarketOrder(account, symbol, secType, action, quantity) {
70
+ return {
71
+ account, symbol, secType, action,
72
+ orderType: OrderType.AM,
73
+ totalQuantity: quantity,
74
+ timeInForce: TimeInForce.DAY,
75
+ outsideRth: false,
76
+ };
77
+ }
78
+ /** 构造算法订单(TWAP/VWAP) */
79
+ export function algoOrder(account, symbol, secType, action, quantity, limitPrice, algoType, params) {
80
+ return {
81
+ account, symbol, secType, action,
82
+ orderType: algoType,
83
+ totalQuantity: quantity,
84
+ limitPrice,
85
+ algoParams: params,
86
+ timeInForce: TimeInForce.DAY,
87
+ outsideRth: false,
88
+ };
89
+ }
90
+ /** 构造附加订单(止盈/止损) */
91
+ export function orderLeg(legType, price, timeInForce) {
92
+ return { legType, price, timeInForce };
93
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Order 订单接口
3
+ * 字段名 camelCase 与 API JSON 天然一致,不改名、不加封装层
4
+ */
5
+ export {};
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Position 持仓接口
3
+ * 字段名 camelCase 与 API JSON 天然一致,不改名、不加封装层
4
+ */
5
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ /**
2
+ * 推送模块导出
3
+ */
4
+ export * from './push-message';
5
+ export * from './callbacks';
6
+ export { PushClient, ConnectionState } from './push-client';
@@ -0,0 +1,419 @@
1
+ import { signWithRSA } from '../signer/signer';
2
+ import { MessageType, SubjectType, createPushMessage, serializeMessage, deserializeMessage, } from './push-message';
3
+ /** 默认推送服务器地址 */
4
+ const DEFAULT_PUSH_URL = 'wss://openapi-push.tigerfintech.com';
5
+ /** 默认心跳间隔(毫秒) */
6
+ const DEFAULT_HEARTBEAT_INTERVAL = 10000;
7
+ /** 默认重连间隔(毫秒) */
8
+ const DEFAULT_RECONNECT_INTERVAL = 5000;
9
+ /** 最大重连间隔(毫秒) */
10
+ const MAX_RECONNECT_INTERVAL = 60000;
11
+ /** 默认连接超时(毫秒) */
12
+ const DEFAULT_CONNECT_TIMEOUT = 30000;
13
+ /** 连接状态 */
14
+ export var ConnectionState;
15
+ (function (ConnectionState) {
16
+ ConnectionState[ConnectionState["Disconnected"] = 0] = "Disconnected";
17
+ ConnectionState[ConnectionState["Connecting"] = 1] = "Connecting";
18
+ ConnectionState[ConnectionState["Connected"] = 2] = "Connected";
19
+ })(ConnectionState || (ConnectionState = {}));
20
+ /**
21
+ * PushClient WebSocket 推送客户端
22
+ */
23
+ export class PushClient {
24
+ constructor(config, options) {
25
+ this.ws = null;
26
+ this._state = ConnectionState.Disconnected;
27
+ this.callbacks = {};
28
+ /** 订阅状态管理:subject -> Set<symbol> */
29
+ this.subscriptions = new Map();
30
+ /** 账户级别订阅 */
31
+ this.accountSubs = new Set();
32
+ this.heartbeatTimer = null;
33
+ this.reconnectTimer = null;
34
+ this.stopped = false;
35
+ /** 可注入的 WebSocket 工厂(用于测试) */
36
+ this.wsFactory = null;
37
+ this.config = config;
38
+ this.pushUrl = options?.pushUrl ?? DEFAULT_PUSH_URL;
39
+ this.heartbeatInterval = options?.heartbeatInterval ?? DEFAULT_HEARTBEAT_INTERVAL;
40
+ this.reconnectInterval = options?.reconnectInterval ?? DEFAULT_RECONNECT_INTERVAL;
41
+ this.autoReconnect = options?.autoReconnect ?? true;
42
+ this.connectTimeout = options?.connectTimeout ?? DEFAULT_CONNECT_TIMEOUT;
43
+ }
44
+ /** 获取当前连接状态 */
45
+ get state() {
46
+ return this._state;
47
+ }
48
+ /** 设置回调函数集合 */
49
+ setCallbacks(cb) {
50
+ this.callbacks = cb;
51
+ }
52
+ /** 连接到推送服务器并进行认证 */
53
+ connect() {
54
+ return new Promise((resolve, reject) => {
55
+ if (this._state !== ConnectionState.Disconnected) {
56
+ reject(new Error('客户端已连接或正在连接中'));
57
+ return;
58
+ }
59
+ this._state = ConnectionState.Connecting;
60
+ this.stopped = false;
61
+ const ws = this.createWebSocket(this.pushUrl);
62
+ this.ws = ws;
63
+ const timeout = setTimeout(() => {
64
+ ws.close();
65
+ this._state = ConnectionState.Disconnected;
66
+ this.ws = null;
67
+ reject(new Error('连接超时'));
68
+ }, this.connectTimeout);
69
+ ws.onopen = () => {
70
+ clearTimeout(timeout);
71
+ // 发送认证消息
72
+ try {
73
+ this.authenticate();
74
+ this._state = ConnectionState.Connected;
75
+ this.startHeartbeat();
76
+ this.callbacks.onConnect?.();
77
+ resolve();
78
+ }
79
+ catch (err) {
80
+ ws.close();
81
+ this._state = ConnectionState.Disconnected;
82
+ this.ws = null;
83
+ reject(err);
84
+ }
85
+ };
86
+ ws.onmessage = (ev) => {
87
+ this.handleMessage(String(ev.data));
88
+ };
89
+ ws.onclose = () => {
90
+ if (this.stopped)
91
+ return;
92
+ this.stopHeartbeat();
93
+ const wasConnected = this._state === ConnectionState.Connected;
94
+ this._state = ConnectionState.Disconnected;
95
+ this.ws = null;
96
+ if (wasConnected && this.autoReconnect && !this.stopped) {
97
+ this.scheduleReconnect();
98
+ }
99
+ };
100
+ ws.onerror = (ev) => {
101
+ if (this._state === ConnectionState.Connecting) {
102
+ clearTimeout(timeout);
103
+ this._state = ConnectionState.Disconnected;
104
+ this.ws = null;
105
+ reject(new Error('WebSocket 连接失败'));
106
+ }
107
+ else {
108
+ this.callbacks.onError?.(new Error('WebSocket 错误'));
109
+ }
110
+ };
111
+ });
112
+ }
113
+ /** 断开连接 */
114
+ disconnect() {
115
+ this.stopped = true;
116
+ this.stopHeartbeat();
117
+ this.clearReconnectTimer();
118
+ if (this.ws) {
119
+ this.ws.onclose = null;
120
+ this.ws.onerror = null;
121
+ this.ws.onmessage = null;
122
+ this.ws.close();
123
+ this.ws = null;
124
+ }
125
+ this._state = ConnectionState.Disconnected;
126
+ this.callbacks.onDisconnect?.();
127
+ }
128
+ /** 发送认证消息 */
129
+ authenticate() {
130
+ const ts = new Date().toISOString().replace('T', ' ').substring(0, 19);
131
+ const sign = signWithRSA(this.config.privateKey, this.config.tigerId);
132
+ const req = {
133
+ tigerId: this.config.tigerId,
134
+ sign,
135
+ timestamp: ts,
136
+ version: '2.0',
137
+ };
138
+ const msg = createPushMessage(MessageType.Connect, undefined, req);
139
+ this.sendMessage(msg);
140
+ }
141
+ /** 发送消息 */
142
+ sendMessage(msg) {
143
+ if (!this.ws)
144
+ throw new Error('WebSocket 连接未建立');
145
+ this.ws.send(serializeMessage(msg));
146
+ }
147
+ /** 创建 WebSocket 实例 */
148
+ createWebSocket(url) {
149
+ if (this.wsFactory)
150
+ return this.wsFactory(url);
151
+ // 动态导入 ws 库(Node.js 环境)
152
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
153
+ const WS = require('ws');
154
+ return new WS(url);
155
+ }
156
+ /** 心跳保活 */
157
+ startHeartbeat() {
158
+ this.stopHeartbeat();
159
+ this.heartbeatTimer = setInterval(() => {
160
+ try {
161
+ const msg = createPushMessage(MessageType.Heartbeat);
162
+ this.sendMessage(msg);
163
+ }
164
+ catch {
165
+ // 心跳发送失败,连接可能已断开
166
+ }
167
+ }, this.heartbeatInterval);
168
+ }
169
+ stopHeartbeat() {
170
+ if (this.heartbeatTimer) {
171
+ clearInterval(this.heartbeatTimer);
172
+ this.heartbeatTimer = null;
173
+ }
174
+ }
175
+ /** 自动重连 */
176
+ scheduleReconnect() {
177
+ let interval = this.reconnectInterval;
178
+ const attempt = () => {
179
+ if (this.stopped)
180
+ return;
181
+ this.connect().then(() => {
182
+ this.resubscribe();
183
+ }).catch(() => {
184
+ interval = Math.min(interval * 2, MAX_RECONNECT_INTERVAL);
185
+ if (!this.stopped) {
186
+ this.reconnectTimer = setTimeout(attempt, interval);
187
+ }
188
+ });
189
+ };
190
+ this.reconnectTimer = setTimeout(attempt, interval);
191
+ }
192
+ clearReconnectTimer() {
193
+ if (this.reconnectTimer) {
194
+ clearTimeout(this.reconnectTimer);
195
+ this.reconnectTimer = null;
196
+ }
197
+ }
198
+ /** 重连后恢复订阅 */
199
+ resubscribe() {
200
+ for (const [subject, symbols] of this.subscriptions) {
201
+ this.doSubscribe(subject, Array.from(symbols));
202
+ }
203
+ for (const subject of this.accountSubs) {
204
+ this.doSubscribe(subject, undefined, this.config.account);
205
+ }
206
+ }
207
+ /** 处理收到的消息 */
208
+ handleMessage(raw) {
209
+ let msg;
210
+ try {
211
+ msg = deserializeMessage(raw);
212
+ }
213
+ catch {
214
+ this.callbacks.onError?.(new Error('反序列化消息失败'));
215
+ return;
216
+ }
217
+ const cb = this.callbacks;
218
+ switch (msg.type) {
219
+ case MessageType.Kickout:
220
+ cb.onKickout?.(msg.data);
221
+ break;
222
+ case MessageType.Error:
223
+ cb.onError?.(new Error(`服务端错误: ${msg.data}`));
224
+ break;
225
+ case MessageType.Quote:
226
+ cb.onQuote?.(msg.data);
227
+ break;
228
+ case MessageType.Tick:
229
+ cb.onTick?.(msg.data);
230
+ break;
231
+ case MessageType.Depth:
232
+ cb.onDepth?.(msg.data);
233
+ break;
234
+ case MessageType.Option:
235
+ cb.onOption?.(msg.data);
236
+ break;
237
+ case MessageType.Future:
238
+ cb.onFuture?.(msg.data);
239
+ break;
240
+ case MessageType.Kline:
241
+ cb.onKline?.(msg.data);
242
+ break;
243
+ case MessageType.Asset:
244
+ cb.onAsset?.(msg.data);
245
+ break;
246
+ case MessageType.Position:
247
+ cb.onPosition?.(msg.data);
248
+ break;
249
+ case MessageType.Order:
250
+ cb.onOrder?.(msg.data);
251
+ break;
252
+ case MessageType.Transaction:
253
+ cb.onTransaction?.(msg.data);
254
+ break;
255
+ case MessageType.StockTop:
256
+ cb.onStockTop?.(msg.data);
257
+ break;
258
+ case MessageType.OptionTop:
259
+ cb.onOptionTop?.(msg.data);
260
+ break;
261
+ case MessageType.FullTick:
262
+ cb.onFullTick?.(msg.data);
263
+ break;
264
+ case MessageType.QuoteBBO:
265
+ cb.onQuoteBBO?.(msg.data);
266
+ break;
267
+ }
268
+ }
269
+ /** 内部订阅方法 */
270
+ doSubscribe(subject, symbols, account, market) {
271
+ const req = { subject, symbols, account, market };
272
+ const msg = createPushMessage(MessageType.Subscribe, subject, req);
273
+ this.sendMessage(msg);
274
+ }
275
+ /** 内部退订方法 */
276
+ doUnsubscribe(subject, symbols) {
277
+ const req = { subject, symbols };
278
+ const msg = createPushMessage(MessageType.Unsubscribe, subject, req);
279
+ this.sendMessage(msg);
280
+ }
281
+ /** 添加订阅记录 */
282
+ addSubscription(subject, symbols) {
283
+ if (!this.subscriptions.has(subject)) {
284
+ this.subscriptions.set(subject, new Set());
285
+ }
286
+ const set = this.subscriptions.get(subject);
287
+ for (const s of symbols)
288
+ set.add(s);
289
+ }
290
+ /** 移除订阅记录 */
291
+ removeSubscription(subject, symbols) {
292
+ if (!symbols) {
293
+ this.subscriptions.delete(subject);
294
+ return;
295
+ }
296
+ const set = this.subscriptions.get(subject);
297
+ if (set) {
298
+ for (const s of symbols)
299
+ set.delete(s);
300
+ if (set.size === 0)
301
+ this.subscriptions.delete(subject);
302
+ }
303
+ }
304
+ // ===== 行情订阅/退订 =====
305
+ /** 订阅行情 */
306
+ subscribeQuote(symbols) {
307
+ this.doSubscribe(SubjectType.Quote, symbols);
308
+ this.addSubscription(SubjectType.Quote, symbols);
309
+ }
310
+ /** 退订行情 */
311
+ unsubscribeQuote(symbols) {
312
+ this.doUnsubscribe(SubjectType.Quote, symbols);
313
+ this.removeSubscription(SubjectType.Quote, symbols);
314
+ }
315
+ /** 订阅逐笔成交 */
316
+ subscribeTick(symbols) {
317
+ this.doSubscribe(SubjectType.Tick, symbols);
318
+ this.addSubscription(SubjectType.Tick, symbols);
319
+ }
320
+ /** 退订逐笔成交 */
321
+ unsubscribeTick(symbols) {
322
+ this.doUnsubscribe(SubjectType.Tick, symbols);
323
+ this.removeSubscription(SubjectType.Tick, symbols);
324
+ }
325
+ /** 订阅深度行情 */
326
+ subscribeDepth(symbols) {
327
+ this.doSubscribe(SubjectType.Depth, symbols);
328
+ this.addSubscription(SubjectType.Depth, symbols);
329
+ }
330
+ /** 退订深度行情 */
331
+ unsubscribeDepth(symbols) {
332
+ this.doUnsubscribe(SubjectType.Depth, symbols);
333
+ this.removeSubscription(SubjectType.Depth, symbols);
334
+ }
335
+ /** 订阅期权行情 */
336
+ subscribeOption(symbols) {
337
+ this.doSubscribe(SubjectType.Option, symbols);
338
+ this.addSubscription(SubjectType.Option, symbols);
339
+ }
340
+ /** 退订期权行情 */
341
+ unsubscribeOption(symbols) {
342
+ this.doUnsubscribe(SubjectType.Option, symbols);
343
+ this.removeSubscription(SubjectType.Option, symbols);
344
+ }
345
+ /** 订阅期货行情 */
346
+ subscribeFuture(symbols) {
347
+ this.doSubscribe(SubjectType.Future, symbols);
348
+ this.addSubscription(SubjectType.Future, symbols);
349
+ }
350
+ /** 退订期货行情 */
351
+ unsubscribeFuture(symbols) {
352
+ this.doUnsubscribe(SubjectType.Future, symbols);
353
+ this.removeSubscription(SubjectType.Future, symbols);
354
+ }
355
+ /** 订阅 K 线 */
356
+ subscribeKline(symbols) {
357
+ this.doSubscribe(SubjectType.Kline, symbols);
358
+ this.addSubscription(SubjectType.Kline, symbols);
359
+ }
360
+ /** 退订 K 线 */
361
+ unsubscribeKline(symbols) {
362
+ this.doUnsubscribe(SubjectType.Kline, symbols);
363
+ this.removeSubscription(SubjectType.Kline, symbols);
364
+ }
365
+ // ===== 账户推送订阅/退订 =====
366
+ /** 订阅资产变动 */
367
+ subscribeAsset(account) {
368
+ this.doSubscribe(SubjectType.Asset, undefined, account || this.config.account);
369
+ this.accountSubs.add(SubjectType.Asset);
370
+ }
371
+ /** 退订资产变动 */
372
+ unsubscribeAsset() {
373
+ this.doUnsubscribe(SubjectType.Asset);
374
+ this.accountSubs.delete(SubjectType.Asset);
375
+ }
376
+ /** 订阅持仓变动 */
377
+ subscribePosition(account) {
378
+ this.doSubscribe(SubjectType.Position, undefined, account || this.config.account);
379
+ this.accountSubs.add(SubjectType.Position);
380
+ }
381
+ /** 退订持仓变动 */
382
+ unsubscribePosition() {
383
+ this.doUnsubscribe(SubjectType.Position);
384
+ this.accountSubs.delete(SubjectType.Position);
385
+ }
386
+ /** 订阅订单状态 */
387
+ subscribeOrder(account) {
388
+ this.doSubscribe(SubjectType.Order, undefined, account || this.config.account);
389
+ this.accountSubs.add(SubjectType.Order);
390
+ }
391
+ /** 退订订单状态 */
392
+ unsubscribeOrder() {
393
+ this.doUnsubscribe(SubjectType.Order);
394
+ this.accountSubs.delete(SubjectType.Order);
395
+ }
396
+ /** 订阅成交明细 */
397
+ subscribeTransaction(account) {
398
+ this.doSubscribe(SubjectType.Transaction, undefined, account || this.config.account);
399
+ this.accountSubs.add(SubjectType.Transaction);
400
+ }
401
+ /** 退订成交明细 */
402
+ unsubscribeTransaction() {
403
+ this.doUnsubscribe(SubjectType.Transaction);
404
+ this.accountSubs.delete(SubjectType.Transaction);
405
+ }
406
+ // ===== 查询订阅状态 =====
407
+ /** 获取当前行情订阅状态 */
408
+ getSubscriptions() {
409
+ const result = new Map();
410
+ for (const [subject, symbols] of this.subscriptions) {
411
+ result.set(subject, Array.from(symbols));
412
+ }
413
+ return result;
414
+ }
415
+ /** 获取账户级别订阅状态 */
416
+ getAccountSubscriptions() {
417
+ return Array.from(this.accountSubs);
418
+ }
419
+ }