@tigeropenapi/tigeropen 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +475 -104
- package/dist/cjs/client/api-request.js +16 -10
- package/dist/cjs/client/api-response.js +23 -3
- package/dist/cjs/client/case-convert.js +35 -0
- package/dist/cjs/client/http-client.js +46 -20
- package/dist/cjs/client/index.js +6 -2
- package/dist/cjs/config/client-config.js +130 -49
- package/dist/cjs/config/domain.js +21 -2
- package/dist/cjs/config/index.js +3 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/model/contract.js +1 -2
- package/dist/cjs/model/order-helpers.js +9 -9
- package/dist/cjs/model/order.js +5 -2
- package/dist/cjs/model/position.js +1 -2
- package/dist/cjs/model/quote.js +7 -0
- package/dist/cjs/model/trade.js +2 -0
- package/dist/cjs/push/index.js +6 -3
- package/dist/cjs/push/pb/AssetData.js +208 -0
- package/dist/cjs/push/pb/KlineData.js +184 -0
- package/dist/cjs/push/pb/OptionTopData.js +427 -0
- package/dist/cjs/push/pb/OrderStatusData.js +568 -0
- package/dist/cjs/push/pb/OrderTransactionData.js +256 -0
- package/dist/cjs/push/pb/PositionData.js +316 -0
- package/dist/cjs/push/pb/PushData.js +219 -0
- package/dist/cjs/push/pb/QuoteBBOData.js +160 -0
- package/dist/cjs/push/pb/QuoteBasicData.js +329 -0
- package/dist/cjs/push/pb/QuoteData.js +507 -0
- package/dist/cjs/push/pb/QuoteDepthData.js +220 -0
- package/dist/cjs/push/pb/Request.js +278 -0
- package/dist/cjs/push/pb/Response.js +97 -0
- package/dist/cjs/push/pb/SocketCommon.js +86 -0
- package/dist/cjs/push/pb/StockTopData.js +199 -0
- package/dist/cjs/push/pb/TickData.js +202 -0
- package/dist/cjs/push/pb/TradeTickData.js +308 -0
- package/dist/cjs/push/pb/index.js +49 -0
- package/dist/cjs/push/proto-message.js +129 -0
- package/dist/cjs/push/push-client.js +292 -164
- package/dist/cjs/push/push-message.js +4 -60
- package/dist/cjs/push/varint.js +76 -0
- package/dist/cjs/quote/index.js +3 -2
- package/dist/cjs/quote/quote-client.js +128 -94
- package/dist/cjs/signer/signer.js +10 -10
- package/dist/cjs/trade/trade-client.js +56 -62
- package/dist/esm/client/api-request.js +14 -8
- package/dist/esm/client/api-response.js +22 -3
- package/dist/esm/client/case-convert.js +31 -0
- package/dist/esm/client/http-client.js +47 -21
- package/dist/esm/client/index.js +3 -2
- package/dist/esm/config/client-config.js +131 -51
- package/dist/esm/config/domain.js +20 -2
- package/dist/esm/config/index.js +2 -2
- package/dist/esm/index.js +1 -1
- package/dist/esm/model/contract.js +1 -2
- package/dist/esm/model/order-helpers.js +9 -9
- package/dist/esm/model/order.js +5 -2
- package/dist/esm/model/position.js +1 -2
- package/dist/esm/model/quote.js +6 -0
- package/dist/esm/model/trade.js +1 -0
- package/dist/esm/push/index.js +4 -2
- package/dist/esm/push/pb/AssetData.js +205 -0
- package/dist/esm/push/pb/KlineData.js +181 -0
- package/dist/esm/push/pb/OptionTopData.js +424 -0
- package/dist/esm/push/pb/OrderStatusData.js +565 -0
- package/dist/esm/push/pb/OrderTransactionData.js +253 -0
- package/dist/esm/push/pb/PositionData.js +313 -0
- package/dist/esm/push/pb/PushData.js +216 -0
- package/dist/esm/push/pb/QuoteBBOData.js +157 -0
- package/dist/esm/push/pb/QuoteBasicData.js +326 -0
- package/dist/esm/push/pb/QuoteData.js +504 -0
- package/dist/esm/push/pb/QuoteDepthData.js +217 -0
- package/dist/esm/push/pb/Request.js +275 -0
- package/dist/esm/push/pb/Response.js +94 -0
- package/dist/esm/push/pb/SocketCommon.js +83 -0
- package/dist/esm/push/pb/StockTopData.js +196 -0
- package/dist/esm/push/pb/TickData.js +199 -0
- package/dist/esm/push/pb/TradeTickData.js +305 -0
- package/dist/esm/push/pb/index.js +25 -0
- package/dist/esm/push/proto-message.js +121 -0
- package/dist/esm/push/push-client.js +260 -165
- package/dist/esm/push/push-message.js +3 -56
- package/dist/esm/push/varint.js +72 -0
- package/dist/esm/quote/index.js +2 -2
- package/dist/esm/quote/quote-client.js +127 -94
- package/dist/esm/signer/signer.js +10 -10
- package/dist/esm/trade/trade-client.js +56 -62
- package/dist/types/client/api-request.d.ts +12 -11
- package/dist/types/client/api-response.d.ts +15 -7
- package/dist/types/client/case-convert.d.ts +16 -0
- package/dist/types/client/http-client.d.ts +26 -14
- package/dist/types/client/index.d.ts +3 -2
- package/dist/types/config/client-config.d.ts +29 -13
- package/dist/types/config/domain.d.ts +9 -0
- package/dist/types/config/index.d.ts +2 -2
- package/dist/types/index.d.ts +1 -1
- package/dist/types/model/contract.d.ts +13 -29
- package/dist/types/model/order-helpers.d.ts +21 -20
- package/dist/types/model/order.d.ts +101 -55
- package/dist/types/model/position.d.ts +26 -24
- package/dist/types/model/quote.d.ts +311 -0
- package/dist/types/model/trade.d.ts +113 -0
- package/dist/types/push/callbacks.d.ts +32 -51
- package/dist/types/push/index.d.ts +5 -3
- package/dist/types/push/pb/AssetData.d.ts +47 -0
- package/dist/types/push/pb/KlineData.d.ts +43 -0
- package/dist/types/push/pb/OptionTopData.d.ts +76 -0
- package/dist/types/push/pb/OrderStatusData.d.ts +96 -0
- package/dist/types/push/pb/OrderTransactionData.d.ts +51 -0
- package/dist/types/push/pb/PositionData.d.ts +63 -0
- package/dist/types/push/pb/PushData.d.ts +46 -0
- package/dist/types/push/pb/QuoteBBOData.d.ts +35 -0
- package/dist/types/push/pb/QuoteBasicData.d.ts +66 -0
- package/dist/types/push/pb/QuoteData.d.ts +94 -0
- package/dist/types/push/pb/QuoteDepthData.d.ts +37 -0
- package/dist/types/push/pb/Request.d.ts +44 -0
- package/dist/types/push/pb/Response.d.ts +30 -0
- package/dist/types/push/pb/SocketCommon.d.ts +64 -0
- package/dist/types/push/pb/StockTopData.d.ts +37 -0
- package/dist/types/push/pb/TickData.d.ts +44 -0
- package/dist/types/push/pb/TradeTickData.d.ts +53 -0
- package/dist/types/push/pb/index.d.ts +39 -0
- package/dist/types/push/proto-message.d.ts +33 -0
- package/dist/types/push/push-client.d.ts +69 -68
- package/dist/types/push/push-message.d.ts +3 -147
- package/dist/types/push/varint.d.ts +23 -0
- package/dist/types/quote/index.d.ts +2 -2
- package/dist/types/quote/quote-client.d.ts +41 -47
- package/dist/types/signer/signer.d.ts +6 -6
- package/dist/types/trade/trade-client.d.ts +34 -38
- package/package.json +11 -6
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Case conversion utilities.
|
|
3
|
+
*
|
|
4
|
+
* TypeScript SDK uses camelCase idiomatically. The server expects
|
|
5
|
+
* snake_case for request field names and returns camelCase for responses.
|
|
6
|
+
* These helpers convert camelCase objects to snake_case before
|
|
7
|
+
* serialization; response side needs no conversion.
|
|
8
|
+
*/
|
|
9
|
+
/** Convert a single camelCase key to snake_case. */
|
|
10
|
+
export function camelToSnake(key) {
|
|
11
|
+
return key.replace(/[A-Z]/g, (c) => '_' + c.toLowerCase());
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Recursively convert all object keys from camelCase to snake_case.
|
|
15
|
+
* Preserves arrays, primitives, and Date/null/undefined.
|
|
16
|
+
* Keys already containing an underscore are kept as-is (already snake_case).
|
|
17
|
+
*/
|
|
18
|
+
export function keysToSnakeCase(value) {
|
|
19
|
+
if (Array.isArray(value)) {
|
|
20
|
+
return value.map((v) => keysToSnakeCase(v));
|
|
21
|
+
}
|
|
22
|
+
if (value && typeof value === 'object' && value.constructor === Object) {
|
|
23
|
+
const out = {};
|
|
24
|
+
for (const [k, v] of Object.entries(value)) {
|
|
25
|
+
const newKey = k.includes('_') ? k : camelToSnake(k);
|
|
26
|
+
out[newKey] = keysToSnakeCase(v);
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getSignContent } from '../signer/sign-content';
|
|
2
|
-
import { signWithRSA } from '../signer/signer';
|
|
2
|
+
import { signWithRSA, verifyWithRSA } from '../signer/signer';
|
|
3
3
|
import { TigerError } from './errors';
|
|
4
4
|
import { defaultRetryPolicy } from './retry';
|
|
5
5
|
import { parseApiResponse } from './api-response';
|
|
@@ -14,29 +14,33 @@ const DEFAULT_SIGN_TYPE = 'RSA';
|
|
|
14
14
|
/** 默认 API 版本 */
|
|
15
15
|
const DEFAULT_VERSION = '2.0';
|
|
16
16
|
/**
|
|
17
|
-
* HttpClient
|
|
17
|
+
* HttpClient handles HTTP requests, signing, retry, and timeout
|
|
18
18
|
*/
|
|
19
19
|
export class HttpClient {
|
|
20
|
-
constructor(config, retryPolicy) {
|
|
20
|
+
constructor(config, retryPolicy, options) {
|
|
21
21
|
this.config = config;
|
|
22
22
|
this.retryPolicy = retryPolicy ?? defaultRetryPolicy();
|
|
23
|
+
this.targetUrl = options?.useQuoteServerUrl ? config.quoteServerUrl : config.serverUrl;
|
|
23
24
|
}
|
|
24
25
|
/**
|
|
25
|
-
*
|
|
26
|
+
* Build common request parameters
|
|
26
27
|
*/
|
|
27
|
-
buildCommonParams(apiMethod, bizContent) {
|
|
28
|
+
buildCommonParams(apiMethod, bizContent, version) {
|
|
28
29
|
const params = {
|
|
29
30
|
tiger_id: this.config.tigerId,
|
|
30
31
|
method: apiMethod,
|
|
31
32
|
charset: DEFAULT_CHARSET,
|
|
32
33
|
sign_type: DEFAULT_SIGN_TYPE,
|
|
33
34
|
timestamp: new Date().toISOString().replace('T', ' ').substring(0, 19),
|
|
34
|
-
version: DEFAULT_VERSION,
|
|
35
|
+
version: version || DEFAULT_VERSION,
|
|
35
36
|
biz_content: bizContent,
|
|
36
37
|
};
|
|
37
38
|
if (this.config.language) {
|
|
38
39
|
params['language'] = this.config.language;
|
|
39
40
|
}
|
|
41
|
+
if (this.config.deviceId) {
|
|
42
|
+
params['device_id'] = this.config.deviceId;
|
|
43
|
+
}
|
|
40
44
|
return params;
|
|
41
45
|
}
|
|
42
46
|
/**
|
|
@@ -47,7 +51,7 @@ export class HttpClient {
|
|
|
47
51
|
return signWithRSA(this.config.privateKey, content);
|
|
48
52
|
}
|
|
49
53
|
/**
|
|
50
|
-
*
|
|
54
|
+
* Send HTTP POST request
|
|
51
55
|
*/
|
|
52
56
|
async doHttpPost(params) {
|
|
53
57
|
const body = JSON.stringify(params);
|
|
@@ -58,7 +62,7 @@ export class HttpClient {
|
|
|
58
62
|
if (this.config.token) {
|
|
59
63
|
headers['Authorization'] = this.config.token;
|
|
60
64
|
}
|
|
61
|
-
const response = await fetch(this.
|
|
65
|
+
const response = await fetch(this.targetUrl, {
|
|
62
66
|
method: 'POST',
|
|
63
67
|
headers,
|
|
64
68
|
body,
|
|
@@ -67,11 +71,11 @@ export class HttpClient {
|
|
|
67
71
|
return response.text();
|
|
68
72
|
}
|
|
69
73
|
/**
|
|
70
|
-
*
|
|
71
|
-
*
|
|
74
|
+
* Execute a structured API request, returning a parsed ApiResponse.
|
|
75
|
+
* Used internally by QuoteClient/TradeClient.
|
|
72
76
|
*/
|
|
73
77
|
async executeRequest(request) {
|
|
74
|
-
const params = this.buildCommonParams(request.method, request.bizContent);
|
|
78
|
+
const params = this.buildCommonParams(request.method, request.bizContent, request.version);
|
|
75
79
|
const sign = this.signParams(params);
|
|
76
80
|
params['sign'] = sign;
|
|
77
81
|
let lastErr;
|
|
@@ -85,7 +89,9 @@ export class HttpClient {
|
|
|
85
89
|
}
|
|
86
90
|
try {
|
|
87
91
|
const body = await this.doHttpPost(params);
|
|
88
|
-
|
|
92
|
+
const response = parseApiResponse(body);
|
|
93
|
+
this.verifyResponseSign(params['timestamp'], response.sign);
|
|
94
|
+
return response;
|
|
89
95
|
}
|
|
90
96
|
catch (err) {
|
|
91
97
|
lastErr = err;
|
|
@@ -97,22 +103,22 @@ export class HttpClient {
|
|
|
97
103
|
throw lastErr;
|
|
98
104
|
}
|
|
99
105
|
/**
|
|
100
|
-
*
|
|
106
|
+
* General-purpose API call method.
|
|
101
107
|
*
|
|
102
|
-
* @param apiMethod - API
|
|
103
|
-
* @param requestJson -
|
|
104
|
-
* @returns
|
|
108
|
+
* @param apiMethod - API method name (e.g. "market_state", "place_order")
|
|
109
|
+
* @param requestJson - Raw biz_content JSON string
|
|
110
|
+
* @returns Raw response JSON string, unparsed
|
|
105
111
|
*/
|
|
106
112
|
async execute(apiMethod, requestJson) {
|
|
107
|
-
//
|
|
113
|
+
// Parameter validation
|
|
108
114
|
if (!apiMethod) {
|
|
109
|
-
throw new TigerError(-1, 'api_method
|
|
115
|
+
throw new TigerError(-1, 'api_method must not be empty');
|
|
110
116
|
}
|
|
111
117
|
try {
|
|
112
118
|
JSON.parse(requestJson);
|
|
113
119
|
}
|
|
114
120
|
catch {
|
|
115
|
-
throw new TigerError(-1, 'request_json
|
|
121
|
+
throw new TigerError(-1, 'request_json is not valid JSON');
|
|
116
122
|
}
|
|
117
123
|
const params = this.buildCommonParams(apiMethod, requestJson);
|
|
118
124
|
const sign = this.signParams(params);
|
|
@@ -127,7 +133,11 @@ export class HttpClient {
|
|
|
127
133
|
await this.sleep(backoff);
|
|
128
134
|
}
|
|
129
135
|
try {
|
|
130
|
-
|
|
136
|
+
const body = await this.doHttpPost(params);
|
|
137
|
+
// Verify response signature even for raw responses
|
|
138
|
+
const parsed = JSON.parse(body);
|
|
139
|
+
this.verifyResponseSign(params['timestamp'], parsed.sign);
|
|
140
|
+
return body;
|
|
131
141
|
}
|
|
132
142
|
catch (err) {
|
|
133
143
|
lastErr = err;
|
|
@@ -138,8 +148,24 @@ export class HttpClient {
|
|
|
138
148
|
}
|
|
139
149
|
throw lastErr;
|
|
140
150
|
}
|
|
141
|
-
/**
|
|
151
|
+
/** Delay for the specified milliseconds */
|
|
142
152
|
sleep(ms) {
|
|
143
153
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
144
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* Verify the response signature using the tiger public key.
|
|
157
|
+
*
|
|
158
|
+
* @param timestamp - The timestamp sent in the request params
|
|
159
|
+
* @param signBase64 - The `sign` field from the response body
|
|
160
|
+
* @throws TigerError if the signature is missing or invalid
|
|
161
|
+
*/
|
|
162
|
+
verifyResponseSign(timestamp, signBase64) {
|
|
163
|
+
if (!signBase64) {
|
|
164
|
+
throw new TigerError(-1, 'Response signature is missing');
|
|
165
|
+
}
|
|
166
|
+
const valid = verifyWithRSA(this.config.tigerPublicKey, timestamp, signBase64);
|
|
167
|
+
if (!valid) {
|
|
168
|
+
throw new TigerError(-1, 'Response signature verification failed');
|
|
169
|
+
}
|
|
170
|
+
}
|
|
145
171
|
}
|
package/dist/esm/client/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Transport layer exports.
|
|
3
3
|
*/
|
|
4
4
|
export { HttpClient } from './http-client';
|
|
5
5
|
export { TigerError, classifyErrorCode } from './errors';
|
|
6
6
|
export { RetryPolicy, defaultRetryPolicy, isTradeOperation } from './retry';
|
|
7
7
|
export { createApiRequest } from './api-request';
|
|
8
|
-
export { parseApiResponse } from './api-response';
|
|
8
|
+
export { parseApiResponse, unmarshalData } from './api-response';
|
|
9
|
+
export { camelToSnake, keysToSnakeCase } from './case-convert';
|
|
@@ -1,58 +1,125 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ClientConfig -
|
|
2
|
+
* ClientConfig - Client configuration management
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* -
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
4
|
+
* Aggregates all configuration parameters with support for:
|
|
5
|
+
* - Direct code configuration
|
|
6
|
+
* - Properties file loading (explicit path or auto-discovery)
|
|
7
|
+
* - Environment variable overrides
|
|
8
8
|
*
|
|
9
|
-
*
|
|
9
|
+
* Priority: environment variables > code options (incl. config file) > defaults
|
|
10
10
|
*/
|
|
11
|
-
import { readFileSync } from 'fs';
|
|
11
|
+
import { readFileSync, existsSync } from 'fs';
|
|
12
|
+
import { join } from 'path';
|
|
13
|
+
import { homedir, networkInterfaces } from 'os';
|
|
12
14
|
import { parsePropertiesString } from './config-parser';
|
|
13
|
-
import { queryDomains, resolveDynamicServerUrl } from './domain';
|
|
14
|
-
/**
|
|
15
|
+
import { queryDomains, resolveDynamicServerUrl, resolveDynamicQuoteServerUrl } from './domain';
|
|
16
|
+
/** Default config file name */
|
|
17
|
+
const CONFIG_FILE_NAME = 'tiger_openapi_config.properties';
|
|
18
|
+
/** Defaults */
|
|
15
19
|
const DEFAULT_LANGUAGE = 'zh_CN';
|
|
16
20
|
const DEFAULT_TIMEOUT = 15;
|
|
17
21
|
const DEFAULT_SERVER_URL = 'https://openapi.tigerfintech.com/gateway';
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
/** Tiger public key for response signature verification (Base64-encoded) */
|
|
23
|
+
const TIGER_PUBLIC_KEY = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNF3G8SoEcCZh2rshUbayDgLLrj6rKgzNMxDL2HSnKcB0+GPOsndqSv+a4IBu9+I3fyBp5hkyMMG2+AXugd9pMpy6VxJxlNjhX1MYbNTZJUT4nudki4uh+LMOkIBHOceGNXjgB+cXqmlUnjlqha/HgboeHSnSgpM3dKSJQlIOsDwIDAQAB';
|
|
24
|
+
/** Environment variable names */
|
|
20
25
|
const ENV_TIGER_ID = 'TIGEROPEN_TIGER_ID';
|
|
21
26
|
const ENV_PRIVATE_KEY = 'TIGEROPEN_PRIVATE_KEY';
|
|
22
27
|
const ENV_ACCOUNT = 'TIGEROPEN_ACCOUNT';
|
|
28
|
+
const ENV_TOKEN = 'TIGEROPEN_TOKEN';
|
|
23
29
|
/**
|
|
24
|
-
*
|
|
30
|
+
* Load and parse a Java-style properties file.
|
|
25
31
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
32
|
+
* @param filePath - Absolute or relative path to the properties file
|
|
33
|
+
* @returns Parsed key-value pairs, or empty object if the file cannot be read
|
|
34
|
+
*/
|
|
35
|
+
export function loadPropertiesFile(filePath) {
|
|
36
|
+
try {
|
|
37
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
38
|
+
return parsePropertiesString(content);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Discover the config file by searching well-known locations.
|
|
46
|
+
*
|
|
47
|
+
* Search order:
|
|
48
|
+
* 1. `./tiger_openapi_config.properties` (current working directory)
|
|
49
|
+
* 2. `~/.tigeropen/tiger_openapi_config.properties` (home directory)
|
|
50
|
+
*
|
|
51
|
+
* @returns The resolved file path, or `undefined` if not found
|
|
52
|
+
*/
|
|
53
|
+
function discoverConfigFile() {
|
|
54
|
+
const candidates = [
|
|
55
|
+
join(process.cwd(), CONFIG_FILE_NAME),
|
|
56
|
+
join(homedir(), '.tigeropen', CONFIG_FILE_NAME),
|
|
57
|
+
];
|
|
58
|
+
for (const candidate of candidates) {
|
|
59
|
+
if (existsSync(candidate)) {
|
|
60
|
+
return candidate;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Auto-detect device identifier from the first non-internal MAC address.
|
|
28
67
|
*
|
|
29
|
-
* @
|
|
30
|
-
|
|
31
|
-
|
|
68
|
+
* @returns MAC address string, or empty string if none found
|
|
69
|
+
*/
|
|
70
|
+
function detectDeviceId() {
|
|
71
|
+
const interfaces = networkInterfaces();
|
|
72
|
+
for (const [, addrs] of Object.entries(interfaces)) {
|
|
73
|
+
if (!addrs)
|
|
74
|
+
continue;
|
|
75
|
+
for (const addr of addrs) {
|
|
76
|
+
if (!addr.internal && addr.mac && addr.mac !== '00:00:00:00:00:00') {
|
|
77
|
+
return addr.mac;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return '';
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Create a client configuration.
|
|
85
|
+
*
|
|
86
|
+
* When called with no arguments (or without `propertiesFilePath`), the function
|
|
87
|
+
* automatically searches for `tiger_openapi_config.properties` in the current
|
|
88
|
+
* directory and `~/.tigeropen/`. Discovered values serve as defaults; explicit
|
|
89
|
+
* options and environment variables take precedence.
|
|
90
|
+
*
|
|
91
|
+
* Priority: environment variables > code options > config file > defaults
|
|
92
|
+
*
|
|
93
|
+
* @param options - Configuration options (optional)
|
|
94
|
+
* @returns Complete client configuration object
|
|
95
|
+
* @throws When tigerId or privateKey is empty after all resolution
|
|
32
96
|
*/
|
|
33
97
|
export function createClientConfig(options) {
|
|
34
|
-
|
|
98
|
+
const opts = options ?? {};
|
|
99
|
+
// Load properties: explicit path > auto-discovery
|
|
35
100
|
let fileProps = {};
|
|
36
|
-
if (
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
101
|
+
if (opts.propertiesFilePath) {
|
|
102
|
+
fileProps = loadPropertiesFile(opts.propertiesFilePath);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
const discovered = discoverConfigFile();
|
|
106
|
+
if (discovered) {
|
|
107
|
+
fileProps = loadPropertiesFile(discovered);
|
|
43
108
|
}
|
|
44
109
|
}
|
|
45
|
-
//
|
|
46
|
-
let tigerId =
|
|
47
|
-
let privateKey =
|
|
48
|
-
let account =
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
|
|
110
|
+
// Merge values (code options override config file)
|
|
111
|
+
let tigerId = opts.tigerId || fileProps['tiger_id'] || '';
|
|
112
|
+
let privateKey = opts.privateKey || resolvePrivateKey(fileProps) || '';
|
|
113
|
+
let account = opts.account || fileProps['account'] || '';
|
|
114
|
+
let token = opts.token || fileProps['token'];
|
|
115
|
+
const license = opts.license || fileProps['license'];
|
|
116
|
+
const language = opts.language || fileProps['language'] || DEFAULT_LANGUAGE;
|
|
117
|
+
const timezone = opts.timezone || fileProps['timezone'];
|
|
118
|
+
// Environment variables override everything
|
|
53
119
|
const envTigerId = process.env[ENV_TIGER_ID];
|
|
54
120
|
const envPrivateKey = process.env[ENV_PRIVATE_KEY];
|
|
55
121
|
const envAccount = process.env[ENV_ACCOUNT];
|
|
122
|
+
const envToken = process.env[ENV_TOKEN];
|
|
56
123
|
if (envTigerId) {
|
|
57
124
|
tigerId = envTigerId;
|
|
58
125
|
}
|
|
@@ -62,25 +129,24 @@ export function createClientConfig(options) {
|
|
|
62
129
|
if (envAccount) {
|
|
63
130
|
account = envAccount;
|
|
64
131
|
}
|
|
65
|
-
|
|
132
|
+
if (envToken) {
|
|
133
|
+
token = envToken;
|
|
134
|
+
}
|
|
135
|
+
// Validate required fields
|
|
66
136
|
if (!tigerId) {
|
|
67
|
-
throw new Error(`tigerId
|
|
137
|
+
throw new Error(`tigerId is required. Set it via options.tigerId, a config file, or the ${ENV_TIGER_ID} environment variable.`);
|
|
68
138
|
}
|
|
69
139
|
if (!privateKey) {
|
|
70
|
-
throw new Error(`privateKey
|
|
140
|
+
throw new Error(`privateKey is required. Set it via options.privateKey, a config file, or the ${ENV_PRIVATE_KEY} environment variable.`);
|
|
71
141
|
}
|
|
72
|
-
//
|
|
73
|
-
const
|
|
74
|
-
const enableDynamicDomain = options.enableDynamicDomain ?? true;
|
|
142
|
+
// Resolve server URL: explicit > dynamic domain > default
|
|
143
|
+
const enableDynamicDomain = opts.enableDynamicDomain ?? true;
|
|
75
144
|
let serverUrl;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
else if (options.serverUrl) {
|
|
80
|
-
serverUrl = options.serverUrl;
|
|
145
|
+
let quoteServerUrl;
|
|
146
|
+
if (opts.serverUrl) {
|
|
147
|
+
serverUrl = opts.serverUrl;
|
|
81
148
|
}
|
|
82
149
|
else {
|
|
83
|
-
// 尝试动态域名获取
|
|
84
150
|
let dynamicUrl = '';
|
|
85
151
|
if (enableDynamicDomain) {
|
|
86
152
|
const domainConf = queryDomains(license);
|
|
@@ -88,6 +154,18 @@ export function createClientConfig(options) {
|
|
|
88
154
|
}
|
|
89
155
|
serverUrl = dynamicUrl || DEFAULT_SERVER_URL;
|
|
90
156
|
}
|
|
157
|
+
// Resolve quote server URL: explicit > dynamic domain > serverUrl fallback
|
|
158
|
+
if (opts.quoteServerUrl) {
|
|
159
|
+
quoteServerUrl = opts.quoteServerUrl;
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
let dynamicQuoteUrl = '';
|
|
163
|
+
if (enableDynamicDomain) {
|
|
164
|
+
const domainConf = queryDomains(license);
|
|
165
|
+
dynamicQuoteUrl = resolveDynamicQuoteServerUrl(domainConf, license);
|
|
166
|
+
}
|
|
167
|
+
quoteServerUrl = dynamicQuoteUrl || serverUrl;
|
|
168
|
+
}
|
|
91
169
|
return {
|
|
92
170
|
tigerId,
|
|
93
171
|
privateKey,
|
|
@@ -95,16 +173,18 @@ export function createClientConfig(options) {
|
|
|
95
173
|
license,
|
|
96
174
|
language,
|
|
97
175
|
timezone,
|
|
98
|
-
timeout:
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
tokenRefreshDuration: options.tokenRefreshDuration,
|
|
176
|
+
timeout: opts.timeout ?? DEFAULT_TIMEOUT,
|
|
177
|
+
token,
|
|
178
|
+
tokenRefreshDuration: opts.tokenRefreshDuration,
|
|
102
179
|
serverUrl,
|
|
180
|
+
quoteServerUrl,
|
|
181
|
+
deviceId: detectDeviceId(),
|
|
182
|
+
tigerPublicKey: TIGER_PUBLIC_KEY,
|
|
103
183
|
};
|
|
104
184
|
}
|
|
105
185
|
/**
|
|
106
|
-
*
|
|
107
|
-
*
|
|
186
|
+
* Resolve private key from properties.
|
|
187
|
+
* Priority: private_key > private_key_pk8 > private_key_pk1
|
|
108
188
|
*/
|
|
109
189
|
function resolvePrivateKey(props) {
|
|
110
190
|
return props['private_key'] || props['private_key_pk8'] || props['private_key_pk1'] || '';
|
|
@@ -63,8 +63,26 @@ export function resolveDynamicServerUrl(domainConf, license) {
|
|
|
63
63
|
return '';
|
|
64
64
|
}
|
|
65
65
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
66
|
+
* Resolve the quote server URL from dynamic domain configuration.
|
|
67
|
+
* Uses the `{LICENSE}-QUOTE` key, falling back to empty string.
|
|
68
|
+
*
|
|
69
|
+
* @param domainConf - Domain configuration map from queryDomains
|
|
70
|
+
* @param license - License type (e.g. "TBNZ")
|
|
71
|
+
* @returns Quote server URL with gateway suffix, or empty string
|
|
72
|
+
*/
|
|
73
|
+
export function resolveDynamicQuoteServerUrl(domainConf, license) {
|
|
74
|
+
if (!domainConf || Object.keys(domainConf).length === 0) {
|
|
75
|
+
return '';
|
|
76
|
+
}
|
|
77
|
+
const key = license ? `${license}-QUOTE` : `${DOMAIN_KEY_COMMON}-QUOTE`;
|
|
78
|
+
if (typeof domainConf[key] === 'string') {
|
|
79
|
+
return domainConf[key] + GATEWAY_SUFFIX;
|
|
80
|
+
}
|
|
81
|
+
return '';
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Synchronous HTTP GET request (Node.js environment).
|
|
85
|
+
* Uses child_process.execSync for synchronous requests with fast timeout.
|
|
68
86
|
*/
|
|
69
87
|
function syncHttpGet(url, timeoutMs) {
|
|
70
88
|
try {
|
package/dist/esm/config/index.js
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
* 配置层导出
|
|
3
3
|
*/
|
|
4
4
|
export { parsePropertiesFile, parsePropertiesString } from './config-parser';
|
|
5
|
-
export { createClientConfig } from './client-config';
|
|
6
|
-
export { queryDomains, resolveDynamicServerUrl } from './domain';
|
|
5
|
+
export { createClientConfig, loadPropertiesFile } from './client-config';
|
|
6
|
+
export { queryDomains, resolveDynamicServerUrl, resolveDynamicQuoteServerUrl } from './domain';
|
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { OrderType, TimeInForce } from './enums';
|
|
2
|
-
/**
|
|
2
|
+
/** Market order */
|
|
3
3
|
export function marketOrder(account, symbol, secType, action, quantity) {
|
|
4
4
|
return {
|
|
5
5
|
account, symbol, secType, action,
|
|
@@ -9,7 +9,7 @@ export function marketOrder(account, symbol, secType, action, quantity) {
|
|
|
9
9
|
outsideRth: false,
|
|
10
10
|
};
|
|
11
11
|
}
|
|
12
|
-
/**
|
|
12
|
+
/** Limit order */
|
|
13
13
|
export function limitOrder(account, symbol, secType, action, quantity, limitPrice) {
|
|
14
14
|
return {
|
|
15
15
|
account, symbol, secType, action,
|
|
@@ -20,7 +20,7 @@ export function limitOrder(account, symbol, secType, action, quantity, limitPric
|
|
|
20
20
|
outsideRth: false,
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
|
-
/**
|
|
23
|
+
/** Stop order */
|
|
24
24
|
export function stopOrder(account, symbol, secType, action, quantity, auxPrice) {
|
|
25
25
|
return {
|
|
26
26
|
account, symbol, secType, action,
|
|
@@ -31,7 +31,7 @@ export function stopOrder(account, symbol, secType, action, quantity, auxPrice)
|
|
|
31
31
|
outsideRth: false,
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
|
-
/**
|
|
34
|
+
/** Stop limit order */
|
|
35
35
|
export function stopLimitOrder(account, symbol, secType, action, quantity, limitPrice, auxPrice) {
|
|
36
36
|
return {
|
|
37
37
|
account, symbol, secType, action,
|
|
@@ -43,7 +43,7 @@ export function stopLimitOrder(account, symbol, secType, action, quantity, limit
|
|
|
43
43
|
outsideRth: false,
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
|
-
/**
|
|
46
|
+
/** Trailing stop order */
|
|
47
47
|
export function trailOrder(account, symbol, secType, action, quantity, trailingPercent) {
|
|
48
48
|
return {
|
|
49
49
|
account, symbol, secType, action,
|
|
@@ -54,7 +54,7 @@ export function trailOrder(account, symbol, secType, action, quantity, trailingP
|
|
|
54
54
|
outsideRth: false,
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
|
-
/**
|
|
57
|
+
/** Auction limit order */
|
|
58
58
|
export function auctionLimitOrder(account, symbol, secType, action, quantity, limitPrice) {
|
|
59
59
|
return {
|
|
60
60
|
account, symbol, secType, action,
|
|
@@ -65,7 +65,7 @@ export function auctionLimitOrder(account, symbol, secType, action, quantity, li
|
|
|
65
65
|
outsideRth: false,
|
|
66
66
|
};
|
|
67
67
|
}
|
|
68
|
-
/**
|
|
68
|
+
/** Auction market order */
|
|
69
69
|
export function auctionMarketOrder(account, symbol, secType, action, quantity) {
|
|
70
70
|
return {
|
|
71
71
|
account, symbol, secType, action,
|
|
@@ -75,7 +75,7 @@ export function auctionMarketOrder(account, symbol, secType, action, quantity) {
|
|
|
75
75
|
outsideRth: false,
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
|
-
/**
|
|
78
|
+
/** Algo order (TWAP / VWAP) */
|
|
79
79
|
export function algoOrder(account, symbol, secType, action, quantity, limitPrice, algoType, params) {
|
|
80
80
|
return {
|
|
81
81
|
account, symbol, secType, action,
|
|
@@ -87,7 +87,7 @@ export function algoOrder(account, symbol, secType, action, quantity, limitPrice
|
|
|
87
87
|
outsideRth: false,
|
|
88
88
|
};
|
|
89
89
|
}
|
|
90
|
-
/**
|
|
90
|
+
/** Attached profit / loss leg */
|
|
91
91
|
export function orderLeg(legType, price, timeInForce) {
|
|
92
92
|
return { legType, price, timeInForce };
|
|
93
93
|
}
|
package/dist/esm/model/order.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Order
|
|
3
|
-
*
|
|
2
|
+
* Order response / request models.
|
|
3
|
+
*
|
|
4
|
+
* TypeScript uses camelCase field names idiomatically. The client layer
|
|
5
|
+
* transparently converts request camelCase to snake_case before sending;
|
|
6
|
+
* server responses are already camelCase so they decode directly.
|
|
4
7
|
*/
|
|
5
8
|
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/esm/push/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Push module exports
|
|
3
3
|
*/
|
|
4
|
-
export
|
|
4
|
+
export { SubjectType } from './push-message';
|
|
5
5
|
export * from './callbacks';
|
|
6
|
+
export * from './varint';
|
|
7
|
+
export * from './proto-message';
|
|
6
8
|
export { PushClient, ConnectionState } from './push-client';
|