@tigeropenapi/tigeropen 0.4.0 → 0.4.6

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 (55) hide show
  1. package/README.md +2 -2
  2. package/dist/cjs/client/api-response.js +21 -2
  3. package/dist/cjs/client/case-convert.js +4 -3
  4. package/dist/cjs/client/http-client.js +122 -5
  5. package/dist/cjs/config/client-config.js +56 -12
  6. package/dist/cjs/config/index.js +3 -1
  7. package/dist/cjs/config/token-manager.js +54 -4
  8. package/dist/cjs/index.js +51 -2
  9. package/dist/cjs/model/enums.js +14 -1
  10. package/dist/cjs/model/order-helpers.js +44 -0
  11. package/dist/cjs/push/push-client.js +40 -15
  12. package/dist/cjs/quote/quote-client.js +14 -2
  13. package/dist/cjs/trade/trade-client.js +57 -8
  14. package/dist/esm/client/api-request.js +1 -1
  15. package/dist/esm/client/api-response.js +22 -3
  16. package/dist/esm/client/case-convert.js +4 -3
  17. package/dist/esm/client/http-client.js +127 -10
  18. package/dist/esm/client/index.js +6 -6
  19. package/dist/esm/config/client-config.js +59 -15
  20. package/dist/esm/config/index.js +4 -3
  21. package/dist/esm/config/token-manager.js +55 -5
  22. package/dist/esm/index.js +15 -1
  23. package/dist/esm/logger/index.js +1 -1
  24. package/dist/esm/model/contract-helpers.js +1 -1
  25. package/dist/esm/model/enums.js +13 -0
  26. package/dist/esm/model/order-helpers.js +43 -1
  27. package/dist/esm/model/order-status.js +1 -1
  28. package/dist/esm/push/index.js +5 -5
  29. package/dist/esm/push/pb/PushData.js +11 -11
  30. package/dist/esm/push/pb/QuoteBasicData.js +1 -1
  31. package/dist/esm/push/pb/Response.js +1 -1
  32. package/dist/esm/push/pb/index.js +17 -17
  33. package/dist/esm/push/proto-message.js +3 -3
  34. package/dist/esm/push/push-client.js +47 -22
  35. package/dist/esm/quote/index.js +1 -1
  36. package/dist/esm/quote/quote-client.js +16 -4
  37. package/dist/esm/signer/index.js +2 -2
  38. package/dist/esm/trade/index.js +1 -1
  39. package/dist/esm/trade/trade-client.js +60 -11
  40. package/dist/types/client/api-response.d.ts +1 -0
  41. package/dist/types/client/case-convert.d.ts +3 -1
  42. package/dist/types/client/http-client.d.ts +32 -1
  43. package/dist/types/config/client-config.d.ts +16 -1
  44. package/dist/types/config/index.d.ts +2 -0
  45. package/dist/types/config/token-manager.d.ts +27 -3
  46. package/dist/types/index.d.ts +22 -1
  47. package/dist/types/model/enums.d.ts +13 -1
  48. package/dist/types/model/order-helpers.d.ts +11 -0
  49. package/dist/types/model/order.d.ts +17 -2
  50. package/dist/types/model/trade-requests.d.ts +65 -0
  51. package/dist/types/model/trade.d.ts +108 -15
  52. package/dist/types/push/push-client.d.ts +5 -3
  53. package/dist/types/quote/quote-client.d.ts +3 -1
  54. package/dist/types/trade/trade-client.d.ts +19 -7
  55. package/package.json +3 -2
@@ -8,11 +8,12 @@
8
8
  *
9
9
  * Priority: environment variables > code options (incl. config file) > defaults
10
10
  */
11
- import { readFileSync, existsSync } from 'fs';
11
+ import { readFileSync, existsSync, statSync } from 'fs';
12
12
  import { join } from 'path';
13
13
  import { homedir, networkInterfaces } from 'os';
14
- import { parsePropertiesString } from './config-parser';
15
- import { queryDomains, resolveDynamicServerUrl, resolveDynamicQuoteServerUrl } from './domain';
14
+ import { parsePropertiesString } from './config-parser.js';
15
+ import { queryDomains, resolveDynamicServerUrl, resolveDynamicQuoteServerUrl } from './domain.js';
16
+ import { TokenManager } from './token-manager.js';
16
17
  /** Default config file name */
17
18
  const CONFIG_FILE_NAME = 'tiger_openapi_config.properties';
18
19
  /** Defaults */
@@ -26,6 +27,7 @@ const ENV_TIGER_ID = 'TIGEROPEN_TIGER_ID';
26
27
  const ENV_PRIVATE_KEY = 'TIGEROPEN_PRIVATE_KEY';
27
28
  const ENV_ACCOUNT = 'TIGEROPEN_ACCOUNT';
28
29
  const ENV_TOKEN = 'TIGEROPEN_TOKEN';
30
+ const ENV_TOKEN_FILE = 'TIGEROPEN_TOKEN_FILE';
29
31
  /**
30
32
  * Load and parse a Java-style properties file.
31
33
  *
@@ -99,7 +101,18 @@ export function createClientConfig(options) {
99
101
  // Load properties: explicit path > auto-discovery
100
102
  let fileProps = {};
101
103
  if (opts.propertiesFilePath) {
102
- fileProps = loadPropertiesFile(opts.propertiesFilePath);
104
+ // Support both directory path and full file path
105
+ let filePath = opts.propertiesFilePath;
106
+ if (existsSync(filePath) && statSync(filePath).isDirectory()) {
107
+ filePath = join(filePath, CONFIG_FILE_NAME);
108
+ }
109
+ else if (!existsSync(filePath) && !filePath.endsWith('.properties')) {
110
+ // Path doesn't exist as-is; try treating it as a directory
111
+ const asDir = join(filePath, CONFIG_FILE_NAME);
112
+ if (existsSync(asDir))
113
+ filePath = asDir;
114
+ }
115
+ fileProps = loadPropertiesFile(filePath);
103
116
  }
104
117
  else {
105
118
  const discovered = discoverConfigFile();
@@ -132,6 +145,37 @@ export function createClientConfig(options) {
132
145
  if (envToken) {
133
146
  token = envToken;
134
147
  }
148
+ // Token loading priority: env TIGEROPEN_TOKEN > tokenLoader > token file (TIGEROPEN_TOKEN_FILE or default)
149
+ if (!token) {
150
+ if (opts.tokenLoader) {
151
+ // tokenLoader is async-capable; we do a best-effort synchronous call here.
152
+ // If the loader returns a Promise, the token will be set later via HttpClient.
153
+ try {
154
+ const result = opts.tokenLoader();
155
+ if (result instanceof Promise) {
156
+ // Cannot await in sync constructor; HttpClient.startTokenAutoRefresh() will
157
+ // resolve this Promise and sync the token after construction.
158
+ }
159
+ else if (result) {
160
+ token = result;
161
+ }
162
+ }
163
+ catch {
164
+ // loader failed; leave token undefined
165
+ }
166
+ }
167
+ else {
168
+ // Load from token file: TIGEROPEN_TOKEN_FILE env var > default file
169
+ const tokenFilePath = process.env[ENV_TOKEN_FILE] || 'tiger_openapi_token.properties';
170
+ const tm = new TokenManager({ filePath: tokenFilePath });
171
+ try {
172
+ token = tm.loadTokenSync();
173
+ }
174
+ catch {
175
+ // file not found or no token field; leave token undefined
176
+ }
177
+ }
178
+ }
135
179
  // Validate required fields
136
180
  if (!tigerId) {
137
181
  throw new Error(`tigerId is required. Set it via options.tigerId, a config file, or the ${ENV_TIGER_ID} environment variable.`);
@@ -143,15 +187,16 @@ export function createClientConfig(options) {
143
187
  const enableDynamicDomain = opts.enableDynamicDomain ?? true;
144
188
  let serverUrl;
145
189
  let quoteServerUrl;
190
+ // Query dynamic domains once (may spawn a child process); reuse for both URLs.
191
+ let domainConf = {};
192
+ if (enableDynamicDomain && (!opts.serverUrl || !opts.quoteServerUrl)) {
193
+ domainConf = queryDomains(license);
194
+ }
146
195
  if (opts.serverUrl) {
147
196
  serverUrl = opts.serverUrl;
148
197
  }
149
198
  else {
150
- let dynamicUrl = '';
151
- if (enableDynamicDomain) {
152
- const domainConf = queryDomains(license);
153
- dynamicUrl = resolveDynamicServerUrl(domainConf, license);
154
- }
199
+ const dynamicUrl = resolveDynamicServerUrl(domainConf, license);
155
200
  serverUrl = dynamicUrl || DEFAULT_SERVER_URL;
156
201
  }
157
202
  // Resolve quote server URL: explicit > dynamic domain > serverUrl fallback
@@ -159,11 +204,7 @@ export function createClientConfig(options) {
159
204
  quoteServerUrl = opts.quoteServerUrl;
160
205
  }
161
206
  else {
162
- let dynamicQuoteUrl = '';
163
- if (enableDynamicDomain) {
164
- const domainConf = queryDomains(license);
165
- dynamicQuoteUrl = resolveDynamicQuoteServerUrl(domainConf, license);
166
- }
207
+ const dynamicQuoteUrl = resolveDynamicQuoteServerUrl(domainConf, license);
167
208
  quoteServerUrl = dynamicQuoteUrl || serverUrl;
168
209
  }
169
210
  return {
@@ -176,10 +217,13 @@ export function createClientConfig(options) {
176
217
  timeout: opts.timeout ?? DEFAULT_TIMEOUT,
177
218
  token,
178
219
  tokenRefreshDuration: opts.tokenRefreshDuration,
220
+ tokenCheckInterval: opts.tokenCheckInterval,
221
+ tokenLoader: opts.tokenLoader,
222
+ tokenWriter: opts.tokenWriter,
179
223
  serverUrl,
180
224
  quoteServerUrl,
181
225
  deviceId: detectDeviceId(),
182
- tigerPublicKey: TIGER_PUBLIC_KEY,
226
+ tigerPublicKey: opts.tigerPublicKey ?? TIGER_PUBLIC_KEY,
183
227
  };
184
228
  }
185
229
  /**
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * 配置层导出
3
3
  */
4
- export { parsePropertiesFile, parsePropertiesString } from './config-parser';
5
- export { createClientConfig, loadPropertiesFile } from './client-config';
6
- export { queryDomains, resolveDynamicServerUrl, resolveDynamicQuoteServerUrl } from './domain';
4
+ export { parsePropertiesFile, parsePropertiesString } from './config-parser.js';
5
+ export { createClientConfig, loadPropertiesFile } from './client-config.js';
6
+ export { TokenManager } from './token-manager.js';
7
+ export { queryDomains, resolveDynamicServerUrl, resolveDynamicQuoteServerUrl } from './domain.js';
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import { readFileSync, writeFileSync, mkdirSync } from 'fs';
8
8
  import { dirname } from 'path';
9
- import { parsePropertiesString } from './config-parser';
9
+ import { parsePropertiesString } from './config-parser.js';
10
10
  /** 默认 Token 文件名 */
11
11
  const DEFAULT_TOKEN_FILE = 'tiger_openapi_token.properties';
12
12
  /** 默认刷新间隔(毫秒):24 小时 */
@@ -18,6 +18,7 @@ export class TokenManager {
18
18
  constructor(options) {
19
19
  this.token = '';
20
20
  this.timer = null;
21
+ this.fileEnabled = options?.filePath !== undefined;
21
22
  this.filePath = options?.filePath ?? DEFAULT_TOKEN_FILE;
22
23
  this.refreshInterval = options?.refreshInterval ?? DEFAULT_REFRESH_INTERVAL;
23
24
  let dur = options?.refreshDuration ?? 0;
@@ -25,9 +26,39 @@ export class TokenManager {
25
26
  dur = 30;
26
27
  }
27
28
  this.refreshDuration = dur;
29
+ this.tokenLoader = options?.tokenLoader;
30
+ this.tokenWriter = options?.tokenWriter;
28
31
  }
29
- /** 从 properties 文件加载 Token */
30
- loadToken() {
32
+ /**
33
+ * 从 properties 文件(或自定义 tokenLoader)加载 Token。
34
+ * 若设置了 tokenLoader,优先调用;否则从 properties 文件读取。
35
+ */
36
+ async loadToken() {
37
+ if (this.tokenLoader) {
38
+ const token = await this.tokenLoader();
39
+ if (!token) {
40
+ throw new Error('自定义 tokenLoader 返回空值');
41
+ }
42
+ this.token = token;
43
+ return token;
44
+ }
45
+ const content = readFileSync(this.filePath, 'utf-8');
46
+ const props = parsePropertiesString(content);
47
+ const token = props['token'];
48
+ if (!token) {
49
+ throw new Error('Token 文件中未找到 token 字段');
50
+ }
51
+ this.token = token;
52
+ return token;
53
+ }
54
+ /**
55
+ * 同步版文件加载(仅用于无 tokenLoader 时的兼容入口)。
56
+ * 建议优先使用 loadToken()(异步)。
57
+ */
58
+ loadTokenSync() {
59
+ if (this.tokenLoader) {
60
+ throw new Error('tokenLoader 是异步函数,请使用 loadToken()');
61
+ }
31
62
  const content = readFileSync(this.filePath, 'utf-8');
32
63
  const props = parsePropertiesString(content);
33
64
  const token = props['token'];
@@ -41,10 +72,25 @@ export class TokenManager {
41
72
  getToken() {
42
73
  return this.token;
43
74
  }
44
- /** 设置 Token 并更新文件 */
75
+ /**
76
+ * 设置 Token;若启用了文件持久化(显式传入 filePath),则同步写文件。
77
+ * 成功后触发 tokenWriter 回调(如有)。
78
+ */
45
79
  setToken(token) {
46
80
  this.token = token;
47
- this.saveTokenToFile(token);
81
+ if (this.fileEnabled) {
82
+ this.saveTokenToFile(token);
83
+ }
84
+ if (this.tokenWriter) {
85
+ this.tokenWriter(token);
86
+ }
87
+ }
88
+ /**
89
+ * 仅更新内存中的 token,不写文件,不触发 tokenWriter 回调。
90
+ * 用于多个组件共享 token 时的内部同步。
91
+ */
92
+ syncToken(token) {
93
+ this.token = token;
48
94
  }
49
95
  /** 将 Token 保存到 properties 文件 */
50
96
  saveTokenToFile(token) {
@@ -101,6 +147,10 @@ export class TokenManager {
101
147
  // 刷新失败时静默跳过
102
148
  }
103
149
  }, this.refreshInterval);
150
+ // 防止 timer 阻止 Node.js 进程退出
151
+ if (this.timer.unref) {
152
+ this.timer.unref();
153
+ }
104
154
  }
105
155
  /** 停止后台刷新 */
106
156
  stopAutoRefresh() {
package/dist/esm/index.js CHANGED
@@ -15,4 +15,18 @@
15
15
  * @packageDocumentation
16
16
  */
17
17
  /** SDK 版本号 */
18
- export const VERSION = '0.4.0';
18
+ export const VERSION = '0.4.6';
19
+ // Config
20
+ export { createClientConfig, loadPropertiesFile } from './config/index.js';
21
+ export { TokenManager } from './config/index.js';
22
+ // Transport
23
+ export { HttpClient } from './client/index.js';
24
+ export { TigerError, classifyErrorCode } from './client/index.js';
25
+ // Clients
26
+ export { QuoteClient, parseOptionIdentifier } from './quote/index.js';
27
+ export { TradeClient } from './trade/index.js';
28
+ export { PushClient, ConnectionState, SubjectType } from './push/index.js';
29
+ export { marketOrder, limitOrder, stopOrder, stopLimitOrder } from './model/order-helpers.js';
30
+ export { normalizeOrderStatus } from './model/order-status.js';
31
+ // Enums
32
+ export { OrderStatus, SecurityType, OrderType, TimeInForce, Market, Currency, QuoteRight, SegmentType, OrderSortBy, CorporateActionType, IndustryLevel, SortDirection, OptionAnalysisPeriod, FinancialReportPeriod, Language, License, BarPeriod, orderStatusCode, } from './model/enums.js';
@@ -1,4 +1,4 @@
1
1
  /**
2
2
  * 日志模块导出
3
3
  */
4
- export * from './logger';
4
+ export * from './logger.js';
@@ -1,4 +1,4 @@
1
- import { SecurityType } from './enums';
1
+ import { SecurityType } from './enums.js';
2
2
  /** 构造股票合约 */
3
3
  export function stockContract(symbol, currency) {
4
4
  return { symbol, secType: SecurityType.STK, currency };
@@ -47,7 +47,20 @@ export var OrderType;
47
47
  OrderType["TWAP"] = "TWAP";
48
48
  OrderType["VWAP"] = "VWAP";
49
49
  OrderType["OCA"] = "OCA";
50
+ OrderType["ICEBERG"] = "ICEBERG";
50
51
  })(OrderType || (OrderType = {}));
52
+ /** 冰山单价格类型 */
53
+ export var PriceType;
54
+ (function (PriceType) {
55
+ /** 限价 */
56
+ PriceType["LIMIT_PRICE"] = "LIMIT_PRICE";
57
+ /** 卖一价 */
58
+ PriceType["ASK_PRICE"] = "ASK_PRICE";
59
+ /** 买一价 */
60
+ PriceType["BID_PRICE"] = "BID_PRICE";
61
+ /** 最新价 */
62
+ PriceType["LATEST_PRICE"] = "LATEST_PRICE";
63
+ })(PriceType || (PriceType = {}));
51
64
  /**
52
65
  * 订单状态枚举。对齐 Java SDK OrderStatus 定义。
53
66
  *
@@ -1,4 +1,4 @@
1
- import { OrderType, TimeInForce } from './enums';
1
+ import { OrderType, TimeInForce, PriceType } from './enums.js';
2
2
  /** Market order */
3
3
  export function marketOrder(account, symbol, secType, action, quantity) {
4
4
  return {
@@ -87,6 +87,48 @@ export function algoOrder(account, symbol, secType, action, quantity, limitPrice
87
87
  outsideRth: false,
88
88
  };
89
89
  }
90
+ /**
91
+ * Iceberg order (basic) — only displaySize required.
92
+ */
93
+ export function icebergOrder(account, symbol, secType, action, quantity, limitPrice, displaySize) {
94
+ return {
95
+ account, symbol, secType, action,
96
+ orderType: OrderType.ICEBERG,
97
+ totalQuantity: quantity,
98
+ limitPrice,
99
+ timeInForce: TimeInForce.DAY,
100
+ outsideRth: false,
101
+ displaySize,
102
+ priceType: PriceType.LIMIT_PRICE,
103
+ };
104
+ }
105
+ /**
106
+ * Iceberg order (full params).
107
+ * Pass `priceType` as empty string to use server default (LIMIT_PRICE).
108
+ * Pass `startTime` / `endTime` as 0 to omit the time window.
109
+ */
110
+ export function icebergOrderFull(account, symbol, secType, action, quantity, limitPrice, displaySize, minDisplaySize, checkIntervals, priceType, startTime, endTime) {
111
+ const req = {
112
+ account, symbol, secType, action,
113
+ orderType: OrderType.ICEBERG,
114
+ totalQuantity: quantity,
115
+ limitPrice,
116
+ timeInForce: TimeInForce.DAY,
117
+ outsideRth: false,
118
+ displaySize,
119
+ };
120
+ if (minDisplaySize !== undefined && minDisplaySize > 0)
121
+ req.minDisplaySize = minDisplaySize;
122
+ if (checkIntervals !== undefined && checkIntervals > 0)
123
+ req.checkIntervals = checkIntervals;
124
+ if (priceType)
125
+ req.priceType = priceType;
126
+ if (startTime && startTime > 0)
127
+ req.startTime = startTime;
128
+ if (endTime && endTime > 0)
129
+ req.endTime = endTime;
130
+ return req;
131
+ }
90
132
  /** Attached profit / loss leg */
91
133
  export function orderLeg(legType, price, timeInForce) {
92
134
  return { legType, price, timeInForce };
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * 对齐 Java SDK OrderStatus.java。
9
9
  */
10
- import { OrderStatus } from './enums';
10
+ import { OrderStatus } from './enums.js';
11
11
  /** 把数字状态码映射到字符串,未识别返回空串。 */
12
12
  function statusIntToString(n) {
13
13
  switch (n) {
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Push module exports
3
3
  */
4
- export { SubjectType } from './push-message';
5
- export * from './callbacks';
6
- export * from './varint';
7
- export * from './proto-message';
8
- export { PushClient, ConnectionState } from './push-client';
4
+ export { SubjectType } from './push-message.js';
5
+ export * from './callbacks.js';
6
+ export * from './varint.js';
7
+ export * from './proto-message.js';
8
+ export { PushClient, ConnectionState } from './push-client.js';
@@ -5,17 +5,17 @@
5
5
  // source: PushData.proto
6
6
  /* eslint-disable */
7
7
  import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
8
- import { AssetData } from "./AssetData";
9
- import { KlineData } from "./KlineData";
10
- import { OptionTopData } from "./OptionTopData";
11
- import { OrderStatusData } from "./OrderStatusData";
12
- import { OrderTransactionData } from "./OrderTransactionData";
13
- import { PositionData } from "./PositionData";
14
- import { QuoteData } from "./QuoteData";
15
- import { QuoteDepthData } from "./QuoteDepthData";
16
- import { StockTopData } from "./StockTopData";
17
- import { TickData } from "./TickData";
18
- import { TradeTickData } from "./TradeTickData";
8
+ import { AssetData } from "./AssetData.js";
9
+ import { KlineData } from "./KlineData.js";
10
+ import { OptionTopData } from "./OptionTopData.js";
11
+ import { OrderStatusData } from "./OrderStatusData.js";
12
+ import { OrderTransactionData } from "./OrderTransactionData.js";
13
+ import { PositionData } from "./PositionData.js";
14
+ import { QuoteData } from "./QuoteData.js";
15
+ import { QuoteDepthData } from "./QuoteDepthData.js";
16
+ import { StockTopData } from "./StockTopData.js";
17
+ import { TickData } from "./TickData.js";
18
+ import { TradeTickData } from "./TradeTickData.js";
19
19
  export const protobufPackage = "com.tigerbrokers.stock.openapi.client.socket.data.pb";
20
20
  function createBasePushData() {
21
21
  return {
@@ -5,7 +5,7 @@
5
5
  // source: QuoteBasicData.proto
6
6
  /* eslint-disable */
7
7
  import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
8
- import { QuoteData_Minute } from "./QuoteData";
8
+ import { QuoteData_Minute } from "./QuoteData.js";
9
9
  export const protobufPackage = "com.tigerbrokers.stock.openapi.client.socket.data.pb";
10
10
  function createBaseQuoteBasicData() {
11
11
  return {
@@ -5,7 +5,7 @@
5
5
  // source: Response.proto
6
6
  /* eslint-disable */
7
7
  import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
8
- import { PushData } from "./PushData";
8
+ import { PushData } from "./PushData.js";
9
9
  export const protobufPackage = "com.tigerbrokers.stock.openapi.client.socket.data.pb";
10
10
  function createBaseResponse() {
11
11
  return { command: 0, id: undefined, code: undefined, msg: undefined, body: undefined };
@@ -4,22 +4,22 @@
4
4
  * 由 ts-proto 从 proto 文件生成的所有类型、枚举和编解码函数
5
5
  */
6
6
  // 基础层:命令和数据类型枚举
7
- export { SocketCommon_Command, SocketCommon_DataType, SocketCommon_QuoteType, } from './SocketCommon';
7
+ export { SocketCommon_Command, SocketCommon_DataType, SocketCommon_QuoteType, } from './SocketCommon.js';
8
8
  // 协议层:请求和响应消息
9
- export { Request, Request_Connect, Request_Subscribe } from './Request';
10
- export { Response } from './Response';
11
- export { PushData } from './PushData';
9
+ export { Request, Request_Connect, Request_Subscribe } from './Request.js';
10
+ export { Response } from './Response.js';
11
+ export { PushData } from './PushData.js';
12
12
  // 数据层:业务数据消息
13
- export { QuoteData } from './QuoteData';
14
- export { QuoteBasicData } from './QuoteBasicData';
15
- export { QuoteBBOData } from './QuoteBBOData';
16
- export { QuoteDepthData } from './QuoteDepthData';
17
- export { TradeTickData } from './TradeTickData';
18
- export { TickData } from './TickData';
19
- export { AssetData } from './AssetData';
20
- export { PositionData } from './PositionData';
21
- export { OrderStatusData } from './OrderStatusData';
22
- export { OrderTransactionData } from './OrderTransactionData';
23
- export { StockTopData } from './StockTopData';
24
- export { OptionTopData } from './OptionTopData';
25
- export { KlineData } from './KlineData';
13
+ export { QuoteData } from './QuoteData.js';
14
+ export { QuoteBasicData } from './QuoteBasicData.js';
15
+ export { QuoteBBOData } from './QuoteBBOData.js';
16
+ export { QuoteDepthData } from './QuoteDepthData.js';
17
+ export { TradeTickData } from './TradeTickData.js';
18
+ export { TickData } from './TickData.js';
19
+ export { AssetData } from './AssetData.js';
20
+ export { PositionData } from './PositionData.js';
21
+ export { OrderStatusData } from './OrderStatusData.js';
22
+ export { OrderTransactionData } from './OrderTransactionData.js';
23
+ export { StockTopData } from './StockTopData.js';
24
+ export { OptionTopData } from './OptionTopData.js';
25
+ export { KlineData } from './KlineData.js';
@@ -4,9 +4,9 @@
4
4
  * 对齐 Java SDK 的 ProtoMessageUtil,提供构建各种 Request 消息的工具函数。
5
5
  * 所有构建函数共享一个递增的 requestId 计数器。
6
6
  */
7
- import { Request, Request_Connect, Request_Subscribe, } from './pb/Request';
8
- import { SocketCommon_Command, SocketCommon_DataType, } from './pb/SocketCommon';
9
- import { SubjectType } from './push-message';
7
+ import { Request, Request_Connect, Request_Subscribe, } from './pb/Request.js';
8
+ import { SocketCommon_Command, SocketCommon_DataType, } from './pb/SocketCommon.js';
9
+ import { SubjectType } from './push-message.js';
10
10
  /** 递增请求 ID 计数器(单线程环境,简单递增变量) */
11
11
  let requestId = 0;
12
12
  /**
@@ -9,13 +9,13 @@
9
9
  * heartbeat keep-alive, and automatic reconnection.
10
10
  */
11
11
  import * as tls from 'tls';
12
- import { signWithRSA } from '../signer/signer';
13
- import { SubjectType } from './push-message';
14
- import { Request } from './pb/Request';
15
- import { Response } from './pb/Response';
16
- import { SocketCommon_Command, SocketCommon_DataType } from './pb/SocketCommon';
17
- import { encodeVarint32, decodeVarint32 } from './varint';
18
- import { buildConnectMessage, buildHeartBeatMessage, buildSubscribeMessage, buildUnSubscribeMessage, buildDisconnectMessage, subjectToDataType, } from './proto-message';
12
+ import { signWithRSA } from '../signer/signer.js';
13
+ import { SubjectType } from './push-message.js';
14
+ import { Request } from './pb/Request.js';
15
+ import { Response } from './pb/Response.js';
16
+ import { SocketCommon_Command, SocketCommon_DataType } from './pb/SocketCommon.js';
17
+ import { encodeVarint32, decodeVarint32 } from './varint.js';
18
+ import { buildConnectMessage, buildHeartBeatMessage, buildSubscribeMessage, buildUnSubscribeMessage, buildDisconnectMessage, subjectToDataType, } from './proto-message.js';
19
19
  /** Default push server host */
20
20
  const DEFAULT_PUSH_HOST = 'openapi.tigerfintech.com';
21
21
  /** Default push server port */
@@ -50,11 +50,13 @@ export class PushClient {
50
50
  constructor(config, options) {
51
51
  this.socket = null;
52
52
  this._state = ConnectionState.Disconnected;
53
+ /** Whether this connect attempt already fell back to unverified TLS */
54
+ this.tlsFallback = false;
53
55
  this.callbacks = {};
54
56
  /** Subscription state: subject -> Set<symbol> */
55
57
  this.subscriptions = new Map();
56
- /** Account-level subscriptions */
57
- this.accountSubs = new Set();
58
+ /** Account-level subscriptions: subject -> account string used at subscribe time */
59
+ this.accountSubs = new Map();
58
60
  this.heartbeatTimer = null;
59
61
  this.reconnectTimer = null;
60
62
  this.stopped = false;
@@ -88,6 +90,7 @@ export class PushClient {
88
90
  }
89
91
  this._state = ConnectionState.Connecting;
90
92
  this.stopped = false;
93
+ this.tlsFallback = false;
91
94
  this.recvBuffer = Buffer.alloc(0);
92
95
  const socket = this.createSocket(this.pushHost, this.pushPort);
93
96
  this.socket = socket;
@@ -144,6 +147,24 @@ export class PushClient {
144
147
  });
145
148
  socket.on('error', (err) => {
146
149
  if (!settled) {
150
+ // If this is a TLS certificate error, warn and retry without verification.
151
+ const isTlsCertError = err.message.includes('certificate') ||
152
+ err.message.includes('CERT') ||
153
+ err.code === 'DEPTH_ZERO_SELF_SIGNED_CERT' ||
154
+ err.code === 'UNABLE_TO_VERIFY_LEAF_SIGNATURE' ||
155
+ err.code === 'CERT_HAS_EXPIRED';
156
+ if (isTlsCertError && !this.tlsFallback) {
157
+ this.tlsFallback = true;
158
+ settled = true;
159
+ clearTimeout(timeout);
160
+ socket.destroy();
161
+ this.socket = null;
162
+ this._state = ConnectionState.Disconnected;
163
+ console.warn(`[TigerOpen] TLS certificate verification failed: ${err.message}. Retrying without verification.`);
164
+ // Retry immediately using unverified TLS
165
+ this.connect().then(resolve).catch(reject);
166
+ return;
167
+ }
147
168
  settled = true;
148
169
  clearTimeout(timeout);
149
170
  this._state = ConnectionState.Disconnected;
@@ -196,7 +217,7 @@ export class PushClient {
196
217
  createSocket(host, port) {
197
218
  if (this.socketFactory)
198
219
  return this.socketFactory(host, port);
199
- return tls.connect({ host, port, rejectUnauthorized: false });
220
+ return tls.connect({ host, port, rejectUnauthorized: !this.tlsFallback });
200
221
  }
201
222
  /** Handle incoming TCP data: buffer + decode varint32 frames */
202
223
  handleData(chunk, onConnected) {
@@ -295,8 +316,8 @@ export class PushClient {
295
316
  for (const [subject, symbols] of this.subscriptions) {
296
317
  this.doSubscribe(subject, Array.from(symbols));
297
318
  }
298
- for (const subject of this.accountSubs) {
299
- this.doSubscribe(subject, undefined, this.config.account);
319
+ for (const [subject, account] of this.accountSubs) {
320
+ this.doSubscribe(subject, undefined, account);
300
321
  }
301
322
  }
302
323
  /** Dispatch push data to the appropriate callback based on dataType */
@@ -530,8 +551,9 @@ export class PushClient {
530
551
  // ===== Account push subscribe/unsubscribe =====
531
552
  /** Subscribe to asset changes */
532
553
  subscribeAsset(account) {
533
- this.doSubscribe(SubjectType.Asset, undefined, account || this.config.account);
534
- this.accountSubs.add(SubjectType.Asset);
554
+ const acct = account || this.config.account;
555
+ this.doSubscribe(SubjectType.Asset, undefined, acct);
556
+ this.accountSubs.set(SubjectType.Asset, acct);
535
557
  }
536
558
  /** Unsubscribe from asset changes */
537
559
  unsubscribeAsset() {
@@ -540,8 +562,9 @@ export class PushClient {
540
562
  }
541
563
  /** Subscribe to position changes */
542
564
  subscribePosition(account) {
543
- this.doSubscribe(SubjectType.Position, undefined, account || this.config.account);
544
- this.accountSubs.add(SubjectType.Position);
565
+ const acct = account || this.config.account;
566
+ this.doSubscribe(SubjectType.Position, undefined, acct);
567
+ this.accountSubs.set(SubjectType.Position, acct);
545
568
  }
546
569
  /** Unsubscribe from position changes */
547
570
  unsubscribePosition() {
@@ -550,8 +573,9 @@ export class PushClient {
550
573
  }
551
574
  /** Subscribe to order status */
552
575
  subscribeOrder(account) {
553
- this.doSubscribe(SubjectType.Order, undefined, account || this.config.account);
554
- this.accountSubs.add(SubjectType.Order);
576
+ const acct = account || this.config.account;
577
+ this.doSubscribe(SubjectType.Order, undefined, acct);
578
+ this.accountSubs.set(SubjectType.Order, acct);
555
579
  }
556
580
  /** Unsubscribe from order status */
557
581
  unsubscribeOrder() {
@@ -560,8 +584,9 @@ export class PushClient {
560
584
  }
561
585
  /** Subscribe to transaction details */
562
586
  subscribeTransaction(account) {
563
- this.doSubscribe(SubjectType.Transaction, undefined, account || this.config.account);
564
- this.accountSubs.add(SubjectType.Transaction);
587
+ const acct = account || this.config.account;
588
+ this.doSubscribe(SubjectType.Transaction, undefined, acct);
589
+ this.accountSubs.set(SubjectType.Transaction, acct);
565
590
  }
566
591
  /** Unsubscribe from transaction details */
567
592
  unsubscribeTransaction() {
@@ -577,8 +602,8 @@ export class PushClient {
577
602
  }
578
603
  return result;
579
604
  }
580
- /** Get account-level subscriptions */
605
+ /** Get account-level subscriptions: subject -> account */
581
606
  getAccountSubscriptions() {
582
- return Array.from(this.accountSubs);
607
+ return new Map(this.accountSubs);
583
608
  }
584
609
  }
@@ -1,4 +1,4 @@
1
1
  /**
2
2
  * Quote module exports
3
3
  */
4
- export { QuoteClient, parseOptionIdentifier } from './quote-client';
4
+ export { QuoteClient, parseOptionIdentifier } from './quote-client.js';