@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.
- package/README.md +2 -2
- package/dist/cjs/client/api-response.js +21 -2
- package/dist/cjs/client/case-convert.js +4 -3
- package/dist/cjs/client/http-client.js +122 -5
- package/dist/cjs/config/client-config.js +56 -12
- package/dist/cjs/config/index.js +3 -1
- package/dist/cjs/config/token-manager.js +54 -4
- package/dist/cjs/index.js +51 -2
- package/dist/cjs/model/enums.js +14 -1
- package/dist/cjs/model/order-helpers.js +44 -0
- package/dist/cjs/push/push-client.js +40 -15
- package/dist/cjs/quote/quote-client.js +14 -2
- package/dist/cjs/trade/trade-client.js +57 -8
- package/dist/esm/client/api-request.js +1 -1
- package/dist/esm/client/api-response.js +22 -3
- package/dist/esm/client/case-convert.js +4 -3
- package/dist/esm/client/http-client.js +127 -10
- package/dist/esm/client/index.js +6 -6
- package/dist/esm/config/client-config.js +59 -15
- package/dist/esm/config/index.js +4 -3
- package/dist/esm/config/token-manager.js +55 -5
- package/dist/esm/index.js +15 -1
- package/dist/esm/logger/index.js +1 -1
- package/dist/esm/model/contract-helpers.js +1 -1
- package/dist/esm/model/enums.js +13 -0
- package/dist/esm/model/order-helpers.js +43 -1
- package/dist/esm/model/order-status.js +1 -1
- package/dist/esm/push/index.js +5 -5
- package/dist/esm/push/pb/PushData.js +11 -11
- package/dist/esm/push/pb/QuoteBasicData.js +1 -1
- package/dist/esm/push/pb/Response.js +1 -1
- package/dist/esm/push/pb/index.js +17 -17
- package/dist/esm/push/proto-message.js +3 -3
- package/dist/esm/push/push-client.js +47 -22
- package/dist/esm/quote/index.js +1 -1
- package/dist/esm/quote/quote-client.js +16 -4
- package/dist/esm/signer/index.js +2 -2
- package/dist/esm/trade/index.js +1 -1
- package/dist/esm/trade/trade-client.js +60 -11
- package/dist/types/client/api-response.d.ts +1 -0
- package/dist/types/client/case-convert.d.ts +3 -1
- package/dist/types/client/http-client.d.ts +32 -1
- package/dist/types/config/client-config.d.ts +16 -1
- package/dist/types/config/index.d.ts +2 -0
- package/dist/types/config/token-manager.d.ts +27 -3
- package/dist/types/index.d.ts +22 -1
- package/dist/types/model/enums.d.ts +13 -1
- package/dist/types/model/order-helpers.d.ts +11 -0
- package/dist/types/model/order.d.ts +17 -2
- package/dist/types/model/trade-requests.d.ts +65 -0
- package/dist/types/model/trade.d.ts +108 -15
- package/dist/types/push/push-client.d.ts +5 -3
- package/dist/types/quote/quote-client.d.ts +3 -1
- package/dist/types/trade/trade-client.d.ts +19 -7
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
/**
|
package/dist/esm/config/index.js
CHANGED
|
@@ -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 {
|
|
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
|
-
/**
|
|
30
|
-
|
|
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
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* 设置 Token;若启用了文件持久化(显式传入 filePath),则同步写文件。
|
|
77
|
+
* 成功后触发 tokenWriter 回调(如有)。
|
|
78
|
+
*/
|
|
45
79
|
setToken(token) {
|
|
46
80
|
this.token = token;
|
|
47
|
-
this.
|
|
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.
|
|
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';
|
package/dist/esm/logger/index.js
CHANGED
package/dist/esm/model/enums.js
CHANGED
|
@@ -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 };
|
package/dist/esm/push/index.js
CHANGED
|
@@ -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
|
|
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:
|
|
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,
|
|
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
|
-
|
|
534
|
-
this.
|
|
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
|
-
|
|
544
|
-
this.
|
|
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
|
-
|
|
554
|
-
this.
|
|
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
|
-
|
|
564
|
-
this.
|
|
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
|
|
607
|
+
return new Map(this.accountSubs);
|
|
583
608
|
}
|
|
584
609
|
}
|
package/dist/esm/quote/index.js
CHANGED