@tigeropenapi/tigeropen 0.5.0 → 0.5.4

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 (33) hide show
  1. package/dist/cjs/client/api-response.js +17 -15
  2. package/dist/cjs/client/token-methods.js +42 -0
  3. package/dist/cjs/config/client-config.js +59 -11
  4. package/dist/cjs/config/config-parser.js +19 -9
  5. package/dist/cjs/config/index.js +2 -1
  6. package/dist/cjs/index.js +2 -1
  7. package/dist/cjs/model/enums.js +4 -0
  8. package/dist/cjs/model/order-helpers.js +126 -21
  9. package/dist/cjs/quote/quote-client.js +112 -21
  10. package/dist/cjs/trade/trade-client.js +74 -16
  11. package/dist/esm/client/api-response.js +17 -15
  12. package/dist/esm/client/token-methods.js +37 -0
  13. package/dist/esm/config/client-config.js +59 -12
  14. package/dist/esm/config/config-parser.js +19 -9
  15. package/dist/esm/config/index.js +1 -1
  16. package/dist/esm/index.js +1 -1
  17. package/dist/esm/model/enums.js +4 -0
  18. package/dist/esm/model/order-helpers.js +119 -20
  19. package/dist/esm/quote/quote-client.js +79 -21
  20. package/dist/esm/trade/trade-client.js +41 -16
  21. package/dist/types/client/api-response.d.ts +6 -5
  22. package/dist/types/client/token-methods.d.ts +34 -0
  23. package/dist/types/config/client-config.d.ts +2 -0
  24. package/dist/types/config/index.d.ts +1 -1
  25. package/dist/types/index.d.ts +3 -3
  26. package/dist/types/model/enums.d.ts +5 -1
  27. package/dist/types/model/order-helpers.d.ts +48 -8
  28. package/dist/types/model/order.d.ts +52 -1
  29. package/dist/types/model/quote.d.ts +56 -0
  30. package/dist/types/model/trade-requests.d.ts +41 -0
  31. package/dist/types/quote/quote-client.d.ts +31 -8
  32. package/dist/types/trade/trade-client.d.ts +19 -3
  33. package/package.json +1 -1
@@ -7,29 +7,33 @@ exports.unmarshalData = unmarshalData;
7
7
  */
8
8
  const errors_1 = require("./errors");
9
9
  /**
10
- * Replace bare integers (not inside JSON string values) that exceed
11
- * Number.MAX_SAFE_INTEGER (> 15 digits) with quoted string equivalents,
12
- * so that int64 order IDs survive JSON.parse without precision loss.
10
+ * Replaces bare int64 values (≥17 digits, not part of a float) with quoted
11
+ * strings so they survive JSON.parse without precision loss.
12
+ * 17+ digits are guaranteed to exceed Number.MAX_SAFE_INTEGER (≈9.007e15),
13
+ * so 16-digit microsecond timestamps remain as numbers.
13
14
  */
14
15
  function patchLargeIntegers(text) {
15
- // Mask all existing JSON string values so their content isn't touched.
16
16
  const strings = [];
17
17
  const masked = text.replace(/"(?:[^"\\]|\\.)*"/g, (match) => {
18
18
  strings.push(match);
19
19
  return '\x01' + (strings.length - 1) + '\x01';
20
20
  });
21
- // Quote any bare integer with >15 digits.
22
- const patched = masked.replace(/-?\d{16,}/g, (m) => '"' + m + '"');
23
- // Restore original string values.
21
+ // Match bare integers preceded by a JSON structural character or line start,
22
+ // not followed by decimal point or exponent (avoids matching float components).
23
+ // The `m` flag makes `^` match line-start for multi-line JSON.
24
+ const patched = masked.replace(/([:,\[{]|^)\s*(-?\d{17,})(?![.\deE])/gm, (full, prefix, num) => {
25
+ return prefix + '"' + num + '"';
26
+ });
24
27
  return patched.replace(/\x01(\d+)\x01/g, (_, i) => strings[parseInt(i, 10)]);
25
28
  }
26
29
  /**
27
- * Parse the raw HTTP response body.
28
- * Throws TigerError when code != 0.
29
- * Large integers in the outer envelope are not an issue (only inner `data` contains int64 IDs).
30
+ * Parse the raw HTTP response body. Throws TigerError when code != 0.
31
+ *
32
+ * patchLargeIntegers is applied before JSON.parse to preserve int64 order IDs
33
+ * (e.g. 28868646234578944) as strings rather than losing precision.
30
34
  */
31
35
  function parseApiResponse(body) {
32
- const resp = JSON.parse(body);
36
+ const resp = JSON.parse(patchLargeIntegers(body));
33
37
  if (resp.code !== 0) {
34
38
  throw new errors_1.TigerError(resp.code, resp.message);
35
39
  }
@@ -37,15 +41,13 @@ function parseApiResponse(body) {
37
41
  }
38
42
  /**
39
43
  * Decode an ApiResponse.data payload into a typed value.
40
- * Handles the server's occasional double-encoded JSON (where `data` is a
41
- * JSON string that itself wraps JSON), seen on some trade endpoints.
44
+ * Handles double-encoded JSON (where `data` is a JSON string wrapping JSON),
45
+ * seen on some trade endpoints.
42
46
  */
43
47
  function unmarshalData(data) {
44
48
  if (data == null)
45
49
  return undefined;
46
50
  if (typeof data === 'string') {
47
- // Attempt to parse JSON string (double-encoded case).
48
- // Patch large integers before parsing so int64 IDs survive as strings.
49
51
  try {
50
52
  return JSON.parse(patchLargeIntegers(data));
51
53
  }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.queryToken = queryToken;
4
+ exports.refreshToken = refreshToken;
5
+ exports.startTokenAutoRefresh = startTokenAutoRefresh;
6
+ /**
7
+ * Call the `user_token_refresh` API and return the new token string.
8
+ * Read-only: does NOT update the stored token.
9
+ * Use `refreshToken()` to also update the in-memory config and optionally persist.
10
+ */
11
+ async function queryToken(httpClient) {
12
+ return httpClient.queryToken();
13
+ }
14
+ /**
15
+ * Refresh the token: call the API, update the in-memory config token, and
16
+ * optionally persist via the provided TokenManager (file + writer callback).
17
+ *
18
+ * @param httpClient - The underlying HTTP client
19
+ * @param tokenManager - optional TokenManager for file persistence and writer callback
20
+ */
21
+ async function refreshToken(httpClient, tokenManager) {
22
+ return httpClient.refreshToken(tokenManager);
23
+ }
24
+ /**
25
+ * Start background token auto-refresh. Returns a TokenManager that can be
26
+ * used to stop the background task via `tm.stopAutoRefresh()`.
27
+ *
28
+ * @param httpClient - The underlying HTTP client
29
+ * @param refreshDurationSecs - Refresh threshold in seconds (minimum 30).
30
+ * When the token age exceeds this value the refresh is triggered.
31
+ * @param checkIntervalMs - How often to check whether a refresh is needed,
32
+ * in milliseconds. Defaults to 300 000 ms (5 minutes).
33
+ * @param tokenWriter - Optional callback invoked with the new token string
34
+ * after each successful refresh.
35
+ */
36
+ function startTokenAutoRefresh(httpClient, refreshDurationSecs, checkIntervalMs, tokenWriter) {
37
+ return httpClient.startTokenAutoRefresh(null, {
38
+ refreshDuration: refreshDurationSecs,
39
+ refreshInterval: checkIntervalMs,
40
+ tokenWriter,
41
+ });
42
+ }
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SANDBOX_TIGER_PUBLIC_KEY = void 0;
3
4
  exports.loadPropertiesFile = loadPropertiesFile;
4
5
  exports.createClientConfig = createClientConfig;
5
6
  /**
@@ -26,6 +27,10 @@ const DEFAULT_TIMEOUT = 15;
26
27
  const DEFAULT_SERVER_URL = 'https://openapi.tigerfintech.com/gateway';
27
28
  /** Tiger public key for response signature verification (Base64-encoded) */
28
29
  const TIGER_PUBLIC_KEY = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNF3G8SoEcCZh2rshUbayDgLLrj6rKgzNMxDL2HSnKcB0+GPOsndqSv+a4IBu9+I3fyBp5hkyMMG2+AXugd9pMpy6VxJxlNjhX1MYbNTZJUT4nudki4uh+LMOkIBHOceGNXjgB+cXqmlUnjlqha/HgboeHSnSgpM3dKSJQlIOsDwIDAQAB';
30
+ /** Sandbox / QA tiger public key for response signature verification */
31
+ exports.SANDBOX_TIGER_PUBLIC_KEY = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCbm21i11hgAENGd3/f280PSe4g9YGkS3TEXBYMidihTvHHf' +
32
+ '+tJ0PYD0o3PruI0hl3qhEjHTAxb75T5YD3SGK4IBhHn/Rk6mhqlGgI+bBrBVYaXixmHfRo75RpUUuWACyeqQk' +
33
+ 'ZckgR0McxuW9xRMIa2cXZOoL1E4SL4lXKGhKoWbwIDAQAB';
29
34
  /** Environment variable names */
30
35
  const ENV_TIGER_ID = 'TIGEROPEN_TIGER_ID';
31
36
  const ENV_PRIVATE_KEY = 'TIGEROPEN_PRIVATE_KEY';
@@ -33,6 +38,9 @@ const ENV_ACCOUNT = 'TIGEROPEN_ACCOUNT';
33
38
  const ENV_SECRET_KEY = 'TIGEROPEN_SECRET_KEY';
34
39
  const ENV_TOKEN = 'TIGEROPEN_TOKEN';
35
40
  const ENV_TOKEN_FILE = 'TIGEROPEN_TOKEN_FILE';
41
+ const ENV_TIGER_PUBLIC_KEY = 'TIGEROPEN_TIGER_PUBLIC_KEY';
42
+ const ENV_SERVER_URL = 'TIGEROPEN_SERVER_URL';
43
+ const ENV_QUOTE_SERVER_URL = 'TIGEROPEN_QUOTE_SERVER_URL';
36
44
  /**
37
45
  * Load and parse a Java-style properties file.
38
46
  *
@@ -105,23 +113,32 @@ function createClientConfig(options) {
105
113
  const opts = options ?? {};
106
114
  // Load properties: explicit path > auto-discovery
107
115
  let fileProps = {};
116
+ let configFileDir;
108
117
  if (opts.propertiesFilePath) {
109
118
  // Support both directory path and full file path
110
119
  let filePath = opts.propertiesFilePath;
111
120
  if ((0, fs_1.existsSync)(filePath) && (0, fs_1.statSync)(filePath).isDirectory()) {
121
+ configFileDir = filePath;
112
122
  filePath = (0, path_1.join)(filePath, CONFIG_FILE_NAME);
113
123
  }
114
124
  else if (!(0, fs_1.existsSync)(filePath) && !filePath.endsWith('.properties')) {
115
125
  // Path doesn't exist as-is; try treating it as a directory
116
126
  const asDir = (0, path_1.join)(filePath, CONFIG_FILE_NAME);
117
- if ((0, fs_1.existsSync)(asDir))
127
+ if ((0, fs_1.existsSync)(asDir)) {
128
+ configFileDir = filePath;
118
129
  filePath = asDir;
130
+ }
131
+ }
132
+ else {
133
+ // It's a file path: derive directory from it
134
+ configFileDir = (0, path_1.dirname)(filePath);
119
135
  }
120
136
  fileProps = loadPropertiesFile(filePath);
121
137
  }
122
138
  else {
123
139
  const discovered = discoverConfigFile();
124
140
  if (discovered) {
141
+ configFileDir = (0, path_1.dirname)(discovered);
125
142
  fileProps = loadPropertiesFile(discovered);
126
143
  }
127
144
  }
@@ -134,12 +151,15 @@ function createClientConfig(options) {
134
151
  const license = opts.license || fileProps['license'];
135
152
  const language = opts.language || fileProps['language'] || DEFAULT_LANGUAGE;
136
153
  const timezone = opts.timezone || fileProps['timezone'];
154
+ // tiger_public_key from properties file (e.g. for QA/sandbox environments)
155
+ let tigerPublicKey = opts.tigerPublicKey || fileProps['tiger_public_key'] || TIGER_PUBLIC_KEY;
137
156
  // Environment variables override everything
138
157
  const envTigerId = process.env[ENV_TIGER_ID];
139
158
  const envPrivateKey = process.env[ENV_PRIVATE_KEY];
140
159
  const envAccount = process.env[ENV_ACCOUNT];
141
160
  const envSecretKey = process.env[ENV_SECRET_KEY];
142
161
  const envToken = process.env[ENV_TOKEN];
162
+ const envTigerPublicKey = process.env[ENV_TIGER_PUBLIC_KEY];
143
163
  if (envTigerId) {
144
164
  tigerId = envTigerId;
145
165
  }
@@ -155,6 +175,9 @@ function createClientConfig(options) {
155
175
  if (envToken) {
156
176
  token = envToken;
157
177
  }
178
+ if (envTigerPublicKey) {
179
+ tigerPublicKey = envTigerPublicKey;
180
+ }
158
181
  // Token loading priority: env TIGEROPEN_TOKEN > tokenLoader > token file (TIGEROPEN_TOKEN_FILE or default)
159
182
  if (!token) {
160
183
  if (opts.tokenLoader) {
@@ -175,8 +198,9 @@ function createClientConfig(options) {
175
198
  }
176
199
  }
177
200
  else {
178
- // Load from token file: TIGEROPEN_TOKEN_FILE env var > default file
179
- const tokenFilePath = process.env[ENV_TOKEN_FILE] || 'tiger_openapi_token.properties';
201
+ // Load from token file: TIGEROPEN_TOKEN_FILE env var > config dir > default
202
+ const tokenFilePath = process.env[ENV_TOKEN_FILE]
203
+ || (configFileDir ? (0, path_1.join)(configFileDir, 'tiger_openapi_token.properties') : 'tiger_openapi_token.properties');
180
204
  const tm = new token_manager_1.TokenManager({ filePath: tokenFilePath });
181
205
  try {
182
206
  token = tm.loadTokenSync();
@@ -193,29 +217,53 @@ function createClientConfig(options) {
193
217
  if (!privateKey) {
194
218
  throw new Error(`privateKey is required. Set it via options.privateKey, a config file, or the ${ENV_PRIVATE_KEY} environment variable.`);
195
219
  }
196
- // Resolve server URL: explicit > dynamic domain > default
220
+ // Resolve server URL: env var > explicit opts > file > dynamic domain > default
197
221
  const enableDynamicDomain = opts.enableDynamicDomain ?? true;
198
222
  let serverUrl;
199
223
  let quoteServerUrl;
200
224
  // Query dynamic domains once (may spawn a child process); reuse for both URLs.
201
225
  let domainConf = {};
202
- if (enableDynamicDomain && (!opts.serverUrl || !opts.quoteServerUrl)) {
226
+ const envServerUrl = process.env[ENV_SERVER_URL];
227
+ const envQuoteServerUrl = process.env[ENV_QUOTE_SERVER_URL];
228
+ const needServerUrl = !envServerUrl && !opts.serverUrl && !fileProps['server_url'];
229
+ const needQuoteServerUrl = !envQuoteServerUrl && !opts.quoteServerUrl && !fileProps['quote_server_url'];
230
+ if (enableDynamicDomain && (needServerUrl || needQuoteServerUrl)) {
203
231
  domainConf = (0, domain_1.queryDomains)(license);
204
232
  }
205
- if (opts.serverUrl) {
233
+ if (envServerUrl) {
234
+ serverUrl = envServerUrl;
235
+ }
236
+ else if (opts.serverUrl) {
206
237
  serverUrl = opts.serverUrl;
207
238
  }
239
+ else if (fileProps['server_url']) {
240
+ serverUrl = fileProps['server_url'];
241
+ }
208
242
  else {
209
243
  const dynamicUrl = (0, domain_1.resolveDynamicServerUrl)(domainConf, license);
210
244
  serverUrl = dynamicUrl || DEFAULT_SERVER_URL;
211
245
  }
212
- // Resolve quote server URL: explicit > dynamic domain > serverUrl fallback
213
- if (opts.quoteServerUrl) {
246
+ // Resolve quote server URL: env var > explicit opts > file > dynamic domain > serverUrl fallback
247
+ if (envQuoteServerUrl) {
248
+ quoteServerUrl = envQuoteServerUrl;
249
+ }
250
+ else if (opts.quoteServerUrl) {
214
251
  quoteServerUrl = opts.quoteServerUrl;
215
252
  }
253
+ else if (fileProps['quote_server_url']) {
254
+ quoteServerUrl = fileProps['quote_server_url'];
255
+ }
216
256
  else {
217
- const dynamicQuoteUrl = (0, domain_1.resolveDynamicQuoteServerUrl)(domainConf, license);
218
- quoteServerUrl = dynamicQuoteUrl || serverUrl;
257
+ // Only use dynamic domain if serverUrl itself came from dynamic resolution (not pinned by env/opts/file).
258
+ // When serverUrl is pinned, prefer it as the quote fallback to avoid mixing environments.
259
+ const serverUrlPinned = !!(envServerUrl || opts.serverUrl || fileProps['server_url']);
260
+ if (!serverUrlPinned) {
261
+ const dynamicQuoteUrl = (0, domain_1.resolveDynamicQuoteServerUrl)(domainConf, license);
262
+ quoteServerUrl = dynamicQuoteUrl || serverUrl;
263
+ }
264
+ else {
265
+ quoteServerUrl = serverUrl;
266
+ }
219
267
  }
220
268
  return {
221
269
  tigerId,
@@ -234,7 +282,7 @@ function createClientConfig(options) {
234
282
  serverUrl,
235
283
  quoteServerUrl,
236
284
  deviceId: detectDeviceId(),
237
- tigerPublicKey: opts.tigerPublicKey ?? TIGER_PUBLIC_KEY,
285
+ tigerPublicKey,
238
286
  };
239
287
  }
240
288
  /**
@@ -70,34 +70,34 @@ function parsePropertiesString(content) {
70
70
  let continuation = false;
71
71
  for (const rawLine of lines) {
72
72
  if (continuation) {
73
- // 续行:去除前导空格后拼接
74
73
  const trimmedLeft = rawLine.replace(/^[ \t]+/, '');
75
- if (trimmedLeft.endsWith('\\')) {
74
+ if (trimmedLeft === '' || trimmedLeft.startsWith('#') || trimmedLeft.startsWith('!')) {
75
+ continuation = false;
76
+ }
77
+ else if (endsWithContinuation(trimmedLeft)) {
76
78
  currentLine += trimmedLeft.slice(0, -1);
77
79
  continue;
78
80
  }
79
- currentLine += trimmedLeft;
80
- continuation = false;
81
+ else {
82
+ currentLine += trimmedLeft;
83
+ continuation = false;
84
+ }
81
85
  }
82
86
  else {
83
87
  const trimmed = rawLine.trim();
84
- // 跳过空行
85
88
  if (trimmed === '') {
86
89
  continue;
87
90
  }
88
- // 跳过注释行
89
91
  if (trimmed.startsWith('#') || trimmed.startsWith('!')) {
90
92
  continue;
91
93
  }
92
- // 检查续行
93
- if (trimmed.endsWith('\\')) {
94
+ if (endsWithContinuation(trimmed)) {
94
95
  currentLine = trimmed.slice(0, -1);
95
96
  continuation = true;
96
97
  continue;
97
98
  }
98
99
  currentLine = trimmed;
99
100
  }
100
- // 解析键值对
101
101
  const result = parseKeyValue(currentLine);
102
102
  if (result) {
103
103
  props[result[0]] = result[1];
@@ -113,6 +113,16 @@ function parsePropertiesString(content) {
113
113
  }
114
114
  return props;
115
115
  }
116
+ /**
117
+ * 判断一行是否以续行标记结尾(奇数个末尾反斜杠)。
118
+ */
119
+ function endsWithContinuation(line) {
120
+ let count = 0;
121
+ for (let i = line.length - 1; i >= 0 && line[i] === '\\'; i--) {
122
+ count++;
123
+ }
124
+ return count % 2 === 1;
125
+ }
116
126
  /**
117
127
  * 解析单行键值对,支持 = 和 : 分隔符。
118
128
  * 值中可以包含 = 或 :,只按第一个分隔符拆分。
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolveDynamicQuoteServerUrl = exports.resolveDynamicServerUrl = exports.queryDomains = exports.TokenManager = exports.loadPropertiesFile = exports.createClientConfig = exports.parsePropertiesString = exports.parsePropertiesFile = void 0;
3
+ exports.resolveDynamicQuoteServerUrl = exports.resolveDynamicServerUrl = exports.queryDomains = exports.TokenManager = exports.SANDBOX_TIGER_PUBLIC_KEY = exports.loadPropertiesFile = exports.createClientConfig = exports.parsePropertiesString = exports.parsePropertiesFile = void 0;
4
4
  /**
5
5
  * 配置层导出
6
6
  */
@@ -10,6 +10,7 @@ Object.defineProperty(exports, "parsePropertiesString", { enumerable: true, get:
10
10
  var client_config_1 = require("./client-config");
11
11
  Object.defineProperty(exports, "createClientConfig", { enumerable: true, get: function () { return client_config_1.createClientConfig; } });
12
12
  Object.defineProperty(exports, "loadPropertiesFile", { enumerable: true, get: function () { return client_config_1.loadPropertiesFile; } });
13
+ Object.defineProperty(exports, "SANDBOX_TIGER_PUBLIC_KEY", { enumerable: true, get: function () { return client_config_1.SANDBOX_TIGER_PUBLIC_KEY; } });
13
14
  var token_manager_1 = require("./token-manager");
14
15
  Object.defineProperty(exports, "TokenManager", { enumerable: true, get: function () { return token_manager_1.TokenManager; } });
15
16
  var domain_1 = require("./domain");
package/dist/cjs/index.js CHANGED
@@ -16,7 +16,7 @@
16
16
  * @packageDocumentation
17
17
  */
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.orderStatusCode = exports.BarPeriod = exports.License = exports.Language = exports.FinancialReportPeriod = exports.OptionAnalysisPeriod = exports.SortDirection = exports.IndustryLevel = exports.CorporateActionType = exports.OrderSortBy = exports.SegmentType = exports.QuoteRight = exports.Currency = exports.Market = exports.TimeInForce = exports.OrderType = exports.SecurityType = exports.OrderStatus = exports.normalizeOrderStatus = exports.stopLimitOrder = exports.stopOrder = exports.limitOrder = exports.marketOrder = exports.SubjectType = exports.ConnectionState = exports.PushClient = exports.TradeClient = exports.parseOptionIdentifier = exports.QuoteClient = exports.classifyErrorCode = exports.TigerError = exports.HttpClient = exports.TokenManager = exports.loadPropertiesFile = exports.createClientConfig = exports.VERSION = void 0;
19
+ exports.orderStatusCode = exports.BarPeriod = exports.License = exports.Language = exports.FinancialReportPeriod = exports.OptionAnalysisPeriod = exports.SortDirection = exports.IndustryLevel = exports.CorporateActionType = exports.OrderSortBy = exports.SegmentType = exports.QuoteRight = exports.Currency = exports.Market = exports.TimeInForce = exports.OrderType = exports.SecurityType = exports.OrderStatus = exports.normalizeOrderStatus = exports.stopLimitOrder = exports.stopOrder = exports.limitOrder = exports.marketOrder = exports.SubjectType = exports.ConnectionState = exports.PushClient = exports.TradeClient = exports.parseOptionIdentifier = exports.QuoteClient = exports.classifyErrorCode = exports.TigerError = exports.HttpClient = exports.TokenManager = exports.SANDBOX_TIGER_PUBLIC_KEY = exports.loadPropertiesFile = exports.createClientConfig = exports.VERSION = void 0;
20
20
  /** SDK 版本号 */
21
21
  var version_1 = require("./version");
22
22
  Object.defineProperty(exports, "VERSION", { enumerable: true, get: function () { return version_1.SDK_VERSION; } });
@@ -24,6 +24,7 @@ Object.defineProperty(exports, "VERSION", { enumerable: true, get: function () {
24
24
  var config_1 = require("./config");
25
25
  Object.defineProperty(exports, "createClientConfig", { enumerable: true, get: function () { return config_1.createClientConfig; } });
26
26
  Object.defineProperty(exports, "loadPropertiesFile", { enumerable: true, get: function () { return config_1.loadPropertiesFile; } });
27
+ Object.defineProperty(exports, "SANDBOX_TIGER_PUBLIC_KEY", { enumerable: true, get: function () { return config_1.SANDBOX_TIGER_PUBLIC_KEY; } });
27
28
  var config_2 = require("./config");
28
29
  Object.defineProperty(exports, "TokenManager", { enumerable: true, get: function () { return config_2.TokenManager; } });
29
30
  // Transport
@@ -150,6 +150,7 @@ var TimeInForce;
150
150
  (function (TimeInForce) {
151
151
  TimeInForce["DAY"] = "DAY";
152
152
  TimeInForce["GTC"] = "GTC";
153
+ TimeInForce["GTD"] = "GTD";
153
154
  TimeInForce["OPG"] = "OPG";
154
155
  })(TimeInForce || (exports.TimeInForce = TimeInForce = {}));
155
156
  /** 订单排序字段 */
@@ -172,6 +173,9 @@ var CorporateActionType;
172
173
  CorporateActionType["Split"] = "split";
173
174
  CorporateActionType["Dividend"] = "dividend";
174
175
  CorporateActionType["Earning"] = "earning";
176
+ CorporateActionType["SymbolChange"] = "symbol_change";
177
+ CorporateActionType["Delisting"] = "delisting";
178
+ CorporateActionType["IPO"] = "ipo";
175
179
  })(CorporateActionType || (exports.CorporateActionType = CorporateActionType = {}));
176
180
  /** 行业级别 (1~4 级) */
177
181
  var IndustryLevel;
@@ -9,8 +9,14 @@ exports.auctionLimitOrder = auctionLimitOrder;
9
9
  exports.auctionMarketOrder = auctionMarketOrder;
10
10
  exports.algoOrder = algoOrder;
11
11
  exports.icebergOrder = icebergOrder;
12
- exports.icebergOrderFull = icebergOrderFull;
13
12
  exports.orderLeg = orderLeg;
13
+ exports.marketOrderByAmount = marketOrderByAmount;
14
+ exports.limitOrderByAmount = limitOrderByAmount;
15
+ exports.trailOrderByPrice = trailOrderByPrice;
16
+ exports.limitOrderWithLegs = limitOrderWithLegs;
17
+ exports.comboOrder = comboOrder;
18
+ exports.ocaOrder = ocaOrder;
19
+ exports.contractLeg = contractLeg;
14
20
  const enums_1 = require("./enums");
15
21
  /** Market order */
16
22
  function marketOrder(account, symbol, secType, action, quantity) {
@@ -101,26 +107,11 @@ function algoOrder(account, symbol, secType, action, quantity, limitPrice, algoT
101
107
  };
102
108
  }
103
109
  /**
104
- * Iceberg order (basic) — only displaySize required.
105
- */
106
- function icebergOrder(account, symbol, secType, action, quantity, limitPrice, displaySize) {
107
- return {
108
- account, symbol, secType, action,
109
- orderType: enums_1.OrderType.ICEBERG,
110
- totalQuantity: quantity,
111
- limitPrice,
112
- timeInForce: enums_1.TimeInForce.DAY,
113
- outsideRth: false,
114
- displaySize,
115
- priceType: enums_1.PriceType.LIMIT_PRICE,
116
- };
117
- }
118
- /**
119
- * Iceberg order (full params).
120
- * Pass `priceType` as empty string to use server default (LIMIT_PRICE).
110
+ * Iceberg order. `displaySize` is required; all other parameters are optional.
111
+ * Pass `priceType` as empty string to use the server default (LIMIT_PRICE).
121
112
  * Pass `startTime` / `endTime` as 0 to omit the time window.
122
113
  */
123
- function icebergOrderFull(account, symbol, secType, action, quantity, limitPrice, displaySize, minDisplaySize, checkIntervals, priceType, startTime, endTime) {
114
+ function icebergOrder(account, symbol, secType, action, quantity, limitPrice, displaySize, minDisplaySize, checkIntervals, priceType, startTime, endTime) {
124
115
  const req = {
125
116
  account, symbol, secType, action,
126
117
  orderType: enums_1.OrderType.ICEBERG,
@@ -129,13 +120,12 @@ function icebergOrderFull(account, symbol, secType, action, quantity, limitPrice
129
120
  timeInForce: enums_1.TimeInForce.DAY,
130
121
  outsideRth: false,
131
122
  displaySize,
123
+ priceType: priceType || enums_1.PriceType.LIMIT_PRICE,
132
124
  };
133
125
  if (minDisplaySize !== undefined && minDisplaySize > 0)
134
126
  req.minDisplaySize = minDisplaySize;
135
127
  if (checkIntervals !== undefined && checkIntervals > 0)
136
128
  req.checkIntervals = checkIntervals;
137
- if (priceType)
138
- req.priceType = priceType;
139
129
  if (startTime && startTime > 0)
140
130
  req.startTime = startTime;
141
131
  if (endTime && endTime > 0)
@@ -146,3 +136,118 @@ function icebergOrderFull(account, symbol, secType, action, quantity, limitPrice
146
136
  function orderLeg(legType, price, timeInForce) {
147
137
  return { legType, price, timeInForce };
148
138
  }
139
+ /**
140
+ * Market order by cash amount (instead of quantity).
141
+ * Server uses `isQuantityByAmount=true` + `amount` field.
142
+ */
143
+ function marketOrderByAmount(account, symbol, secType, action, amount) {
144
+ return {
145
+ account, symbol, secType, action,
146
+ orderType: enums_1.OrderType.MKT,
147
+ totalQuantity: 0,
148
+ cashAmount: amount,
149
+ timeInForce: enums_1.TimeInForce.DAY,
150
+ outsideRth: false,
151
+ };
152
+ }
153
+ /**
154
+ * Limit order by cash amount (instead of quantity).
155
+ */
156
+ function limitOrderByAmount(account, symbol, secType, action, amount, limitPrice) {
157
+ return {
158
+ account, symbol, secType, action,
159
+ orderType: enums_1.OrderType.LMT,
160
+ totalQuantity: 0,
161
+ cashAmount: amount,
162
+ limitPrice,
163
+ timeInForce: enums_1.TimeInForce.DAY,
164
+ outsideRth: false,
165
+ };
166
+ }
167
+ /**
168
+ * Trailing stop order using a fixed price offset (auxPrice) instead of a percentage.
169
+ * Use `trailOrder()` for percentage-based trailing stops.
170
+ */
171
+ function trailOrderByPrice(account, symbol, secType, action, quantity, auxPrice) {
172
+ return {
173
+ account, symbol, secType, action,
174
+ orderType: enums_1.OrderType.TRAIL,
175
+ totalQuantity: quantity,
176
+ auxPrice,
177
+ timeInForce: enums_1.TimeInForce.DAY,
178
+ outsideRth: false,
179
+ };
180
+ }
181
+ /**
182
+ * Limit order with attached profit/loss legs (bracket order).
183
+ * At most 2 legs are supported.
184
+ *
185
+ * @param orderLegs - Array of `OrderLegRequest` (use `orderLeg()` helper)
186
+ */
187
+ function limitOrderWithLegs(account, symbol, secType, action, quantity, limitPrice, orderLegs) {
188
+ if (orderLegs.length === 0) {
189
+ throw new Error('At least 1 order leg is required for a bracket order');
190
+ }
191
+ if (orderLegs.length > 2) {
192
+ throw new Error('At most 2 order legs are supported');
193
+ }
194
+ return {
195
+ account, symbol, secType, action,
196
+ orderType: enums_1.OrderType.LMT,
197
+ totalQuantity: quantity,
198
+ limitPrice,
199
+ orderLegs,
200
+ timeInForce: enums_1.TimeInForce.DAY,
201
+ outsideRth: false,
202
+ };
203
+ }
204
+ /**
205
+ * Multi-leg (combo/MLEG) order for options combos.
206
+ *
207
+ * @param contractLegs - Array of `ContractLegRequest` (use `contractLeg()` helper)
208
+ * @param comboType - e.g. `'MLEG'`
209
+ */
210
+ function comboOrder(account, action, quantity, orderType, contractLegs, comboType = 'MLEG', limitPrice, auxPrice, trailingPercent) {
211
+ return {
212
+ account,
213
+ symbol: '', // not used for combo orders
214
+ secType: 'MLEG',
215
+ action,
216
+ orderType,
217
+ totalQuantity: quantity,
218
+ limitPrice,
219
+ auxPrice,
220
+ trailingPercent,
221
+ contractLegs,
222
+ comboType,
223
+ timeInForce: enums_1.TimeInForce.DAY,
224
+ outsideRth: false,
225
+ };
226
+ }
227
+ /**
228
+ * OCA (One-Cancels-All) order.
229
+ * Server expects `orderType: 'OCA'` and an `ocaOrders` list.
230
+ *
231
+ * @param ocaOrders - The contained orders (use any order helper to build each)
232
+ */
233
+ function ocaOrder(account, symbol, secType, action, quantity, ocaOrders) {
234
+ return {
235
+ account, symbol, secType, action,
236
+ orderType: enums_1.OrderType.OCA,
237
+ totalQuantity: quantity,
238
+ ocaOrders,
239
+ timeInForce: enums_1.TimeInForce.DAY,
240
+ outsideRth: false,
241
+ };
242
+ }
243
+ /**
244
+ * Build a single leg for a multi-leg (MLEG / combo) order.
245
+ *
246
+ * @param symbol - Underlying symbol
247
+ * @param secType - Security type (e.g. `'OPT'`)
248
+ * @param action - `'BUY'` or `'SELL'`
249
+ * @param ratio - Number of contracts for this leg
250
+ */
251
+ function contractLeg(symbol, secType, action, ratio, expiry, strike, right) {
252
+ return { symbol, secType, action, ratio, expiry, strike, right };
253
+ }