@tigeropenapi/tigeropen 0.5.3 → 0.5.5
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 +53 -0
- package/dist/cjs/client/token-methods.js +42 -0
- package/dist/cjs/config/client-config.js +13 -3
- package/dist/cjs/model/order-helpers.js +126 -21
- package/dist/cjs/push/index.js +1 -0
- package/dist/cjs/push/push-client.js +5 -3
- package/dist/cjs/push/tick-util.js +172 -0
- package/dist/cjs/quote/quote-client.js +112 -12
- package/dist/cjs/trade/trade-client.js +102 -3
- package/dist/esm/client/token-methods.js +37 -0
- package/dist/esm/config/client-config.js +14 -4
- package/dist/esm/model/order-helpers.js +119 -20
- package/dist/esm/push/index.js +1 -0
- package/dist/esm/push/push-client.js +5 -3
- package/dist/esm/push/tick-util.js +165 -0
- package/dist/esm/quote/quote-client.js +79 -12
- package/dist/esm/trade/trade-client.js +69 -3
- package/dist/types/client/token-methods.d.ts +34 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/model/order-helpers.d.ts +48 -8
- package/dist/types/model/order.d.ts +70 -12
- package/dist/types/model/quote-requests.d.ts +21 -2
- package/dist/types/model/quote.d.ts +71 -14
- package/dist/types/model/trade-requests.d.ts +12 -4
- package/dist/types/model/trade.d.ts +11 -1
- package/dist/types/push/callbacks.d.ts +3 -7
- package/dist/types/push/index.d.ts +1 -0
- package/dist/types/push/tick-util.d.ts +30 -0
- package/dist/types/quote/quote-client.d.ts +40 -6
- package/dist/types/trade/trade-client.d.ts +34 -0
- package/package.json +3 -1
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.QuoteClient = void 0;
|
|
4
37
|
exports.parseOptionIdentifier = parseOptionIdentifier;
|
|
@@ -12,6 +45,8 @@ exports.parseOptionIdentifier = parseOptionIdentifier;
|
|
|
12
45
|
const http_client_1 = require("../client/http-client");
|
|
13
46
|
const api_request_1 = require("../client/api-request");
|
|
14
47
|
const api_response_1 = require("../client/api-response");
|
|
48
|
+
const tokenMethods = __importStar(require("../client/token-methods"));
|
|
49
|
+
const tick_util_1 = require("../push/tick-util");
|
|
15
50
|
/**
|
|
16
51
|
* resolveOptionTimezone returns the timezone string to use for expiry conversion.
|
|
17
52
|
* Explicit tz takes precedence; otherwise infer from symbol (.HK suffix → Asia/Hong_Kong, else America/New_York).
|
|
@@ -94,6 +129,25 @@ class QuoteClient {
|
|
|
94
129
|
static fromConfig(config) {
|
|
95
130
|
return new QuoteClient(new http_client_1.HttpClient(config, undefined, { useQuoteServerUrl: true }));
|
|
96
131
|
}
|
|
132
|
+
// ── Token management ──────────────────────────────────────────────────────
|
|
133
|
+
/** Call the `user_token_refresh` API and return the new token. Read-only. */
|
|
134
|
+
async queryToken() {
|
|
135
|
+
return tokenMethods.queryToken(this.httpClient);
|
|
136
|
+
}
|
|
137
|
+
/** Refresh the token and update the in-memory config token. */
|
|
138
|
+
async refreshToken(tokenManager) {
|
|
139
|
+
return tokenMethods.refreshToken(this.httpClient, tokenManager);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Start background token auto-refresh. Returns a TokenManager to stop with `tm.stopAutoRefresh()`.
|
|
143
|
+
*
|
|
144
|
+
* @param refreshDurationSecs - Refresh threshold in seconds (minimum 30).
|
|
145
|
+
* @param checkIntervalMs - How often to check, in milliseconds (default 300 000 / 5 min).
|
|
146
|
+
* @param tokenWriter - Optional callback invoked after each successful refresh.
|
|
147
|
+
*/
|
|
148
|
+
startTokenAutoRefresh(refreshDurationSecs, checkIntervalMs, tokenWriter) {
|
|
149
|
+
return tokenMethods.startTokenAutoRefresh(this.httpClient, refreshDurationSecs, checkIntervalMs, tokenWriter);
|
|
150
|
+
}
|
|
97
151
|
async callInto(method, bizParams, version) {
|
|
98
152
|
const request = (0, api_request_1.createApiRequest)(method, bizParams, version);
|
|
99
153
|
const response = await this.httpClient.executeRequest(request);
|
|
@@ -132,12 +186,22 @@ class QuoteClient {
|
|
|
132
186
|
async getKline(req) {
|
|
133
187
|
return this.callInto('kline', req);
|
|
134
188
|
}
|
|
135
|
-
async getTimeline(
|
|
136
|
-
|
|
189
|
+
async getTimeline(symbolsOrRequest) {
|
|
190
|
+
const req = Array.isArray(symbolsOrRequest) ? { symbols: symbolsOrRequest } : symbolsOrRequest;
|
|
191
|
+
return this.callInto('timeline', req, req.secType === 'CC' ? '3.0' : undefined);
|
|
137
192
|
}
|
|
138
193
|
/** Tick-by-tick trades. wire: trade_tick */
|
|
139
194
|
async getTradeTick(req) {
|
|
140
|
-
|
|
195
|
+
const out = await this.callInto('trade_tick', req);
|
|
196
|
+
for (const tick of out) {
|
|
197
|
+
const isUs = (0, tick_util_1.isUsStockSymbol)(tick.symbol);
|
|
198
|
+
for (const item of tick.items) {
|
|
199
|
+
if (item.cond && item.cond.length > 0) {
|
|
200
|
+
item.cond = (0, tick_util_1.getTradeCondByCode)(isUs, item.cond[0]);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return out;
|
|
141
205
|
}
|
|
142
206
|
/** Depth snapshot. wire: quote_depth */
|
|
143
207
|
async getQuoteDepth(req) {
|
|
@@ -303,6 +367,7 @@ class QuoteClient {
|
|
|
303
367
|
while (acc.length < totalSize) {
|
|
304
368
|
const sub = {
|
|
305
369
|
symbols: req.symbol ? [req.symbol] : undefined,
|
|
370
|
+
secType: req.secType,
|
|
306
371
|
period: req.period,
|
|
307
372
|
right: req.right,
|
|
308
373
|
beginTime,
|
|
@@ -353,7 +418,7 @@ class QuoteClient {
|
|
|
353
418
|
async getStockIndustry(req) {
|
|
354
419
|
return this.callInto('stock_industry', req);
|
|
355
420
|
}
|
|
356
|
-
/**
|
|
421
|
+
/** Market data permission entries. wire: get_quote_permission */
|
|
357
422
|
async getQuotePermission(req) {
|
|
358
423
|
return this.callInto('get_quote_permission', req);
|
|
359
424
|
}
|
|
@@ -364,9 +429,16 @@ class QuoteClient {
|
|
|
364
429
|
// ==========================================================================
|
|
365
430
|
// Batch 4: option / future extensions
|
|
366
431
|
// ==========================================================================
|
|
367
|
-
/** Option tick trades. wire: option_trade_tick
|
|
432
|
+
/** Option tick trades. wire: option_trade_tick
|
|
433
|
+
*
|
|
434
|
+
* Server expects `biz_content` as a top-level JSON array (matches Java's
|
|
435
|
+
* `BatchApiModel` and Python's `MultipleContractParams.to_openapi_dict`).
|
|
436
|
+
* We unwrap the request here so the array is emitted directly instead of
|
|
437
|
+
* `{"contracts": [...]}`. Sending it as an object triggers
|
|
438
|
+
* `biz param error(failed to parse parameters in 'biz_content')`.
|
|
439
|
+
*/
|
|
368
440
|
async getOptionTradeTicks(req) {
|
|
369
|
-
return this.callInto('option_trade_tick', req);
|
|
441
|
+
return this.callInto('option_trade_tick', req.contracts ?? []);
|
|
370
442
|
}
|
|
371
443
|
/** Option intraday timeline. wire: option_timeline */
|
|
372
444
|
async getOptionTimeline(req) {
|
|
@@ -405,10 +477,16 @@ class QuoteClient {
|
|
|
405
477
|
return this.callInto('future_main_contract', req);
|
|
406
478
|
}
|
|
407
479
|
/** Futures K-line. wire: future_kline
|
|
408
|
-
* beginTime / endTime default to -1 when unset (server requires them present).
|
|
480
|
+
* beginTime / endTime default to -1 when unset (server requires them present).
|
|
481
|
+
* Server expects `contract_codes` (plural) — normalize singular `contractCode`
|
|
482
|
+
* to the plural form so callers can pass either shape. */
|
|
409
483
|
async getFutureKline(req) {
|
|
484
|
+
const contractCodes = req.contractCodes
|
|
485
|
+
?? (req.contractCode ? [req.contractCode] : undefined);
|
|
486
|
+
const { contractCode: _drop, ...rest } = req;
|
|
410
487
|
const body = {
|
|
411
|
-
...
|
|
488
|
+
...rest,
|
|
489
|
+
...(contractCodes ? { contractCodes } : {}),
|
|
412
490
|
beginTime: req.beginTime ?? -1,
|
|
413
491
|
endTime: req.endTime ?? -1,
|
|
414
492
|
};
|
|
@@ -426,8 +504,9 @@ class QuoteClient {
|
|
|
426
504
|
endTime = -1;
|
|
427
505
|
const acc = [];
|
|
428
506
|
while (acc.length < totalSize) {
|
|
507
|
+
// Server rejects singular `contract_code` — send the plural form.
|
|
429
508
|
const sub = {
|
|
430
|
-
|
|
509
|
+
contractCodes: req.contractCode ? [req.contractCode] : undefined,
|
|
431
510
|
period: req.period,
|
|
432
511
|
beginTime,
|
|
433
512
|
endTime,
|
|
@@ -506,9 +585,30 @@ class QuoteClient {
|
|
|
506
585
|
async getWarrantFilter(req) {
|
|
507
586
|
return this.callInto('warrant_filter', req);
|
|
508
587
|
}
|
|
509
|
-
/** Industry list. wire: industry_list
|
|
510
|
-
|
|
511
|
-
|
|
588
|
+
/** Industry list. wire: industry_list
|
|
589
|
+
*
|
|
590
|
+
* `industryLevel` is required by the server — omitting it returns
|
|
591
|
+
* `industry level error, all supported is: [GSECTOR, GGROUP, GIND, GSUBIND]`.
|
|
592
|
+
* Defaults to `GGROUP` when unset, matching Python SDK's
|
|
593
|
+
* `IndustryLevel.GGROUP` default.
|
|
594
|
+
*
|
|
595
|
+
* Wire fields differ from other endpoints (see `IndustryItem`): server sends
|
|
596
|
+
* `nameCN` / `nameEN` / `industryLevel`. This method hydrates the
|
|
597
|
+
* backwards-compat `name` (from `nameEN || nameCN`) and mirrors
|
|
598
|
+
* `industryLevel` to `level` so existing callers keep working.
|
|
599
|
+
*/
|
|
600
|
+
async getIndustryList(req = {}) {
|
|
601
|
+
const payload = { industryLevel: 'GGROUP', ...req };
|
|
602
|
+
const items = await this.callInto('industry_list', payload);
|
|
603
|
+
if (!Array.isArray(items))
|
|
604
|
+
return [];
|
|
605
|
+
for (const it of items) {
|
|
606
|
+
if (it.name === undefined)
|
|
607
|
+
it.name = it.nameEN || it.nameCN;
|
|
608
|
+
if (it.level === undefined)
|
|
609
|
+
it.level = it.industryLevel;
|
|
610
|
+
}
|
|
611
|
+
return items;
|
|
512
612
|
}
|
|
513
613
|
/** Stocks inside an industry. wire: industry_stock_list */
|
|
514
614
|
async getIndustryStocks(req) {
|
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.TradeClient = void 0;
|
|
4
37
|
/**
|
|
@@ -13,6 +46,7 @@ exports.TradeClient = void 0;
|
|
|
13
46
|
const http_client_1 = require("../client/http-client");
|
|
14
47
|
const api_request_1 = require("../client/api-request");
|
|
15
48
|
const api_response_1 = require("../client/api-response");
|
|
49
|
+
const tokenMethods = __importStar(require("../client/token-methods"));
|
|
16
50
|
const order_status_1 = require("../model/order-status");
|
|
17
51
|
/** Normalize `Order.status` field in-place (handles int → string). */
|
|
18
52
|
function normalizeOrderInPlace(o) {
|
|
@@ -44,6 +78,31 @@ class TradeClient {
|
|
|
44
78
|
static fromConfig(config, account, secretKey) {
|
|
45
79
|
return new TradeClient(new http_client_1.HttpClient(config), account, secretKey);
|
|
46
80
|
}
|
|
81
|
+
// ── Token management ──────────────────────────────────────────────────────
|
|
82
|
+
/** Call the `user_token_refresh` API and return the new token. Read-only. */
|
|
83
|
+
async queryToken() {
|
|
84
|
+
return tokenMethods.queryToken(this.httpClient);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Refresh the token: call the API, update the in-memory config token, and
|
|
88
|
+
* optionally persist via the provided TokenManager (file + writer callback).
|
|
89
|
+
*
|
|
90
|
+
* @param tokenManager - optional TokenManager for file persistence and writer callback
|
|
91
|
+
*/
|
|
92
|
+
async refreshToken(tokenManager) {
|
|
93
|
+
return tokenMethods.refreshToken(this.httpClient, tokenManager);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Start background token auto-refresh. Returns a TokenManager that can be
|
|
97
|
+
* used to stop the background task via `tm.stopAutoRefresh()`.
|
|
98
|
+
*
|
|
99
|
+
* @param refreshDurationSecs - Refresh threshold in seconds (minimum 30).
|
|
100
|
+
* @param checkIntervalMs - How often to check, in milliseconds (default 300 000 / 5 min).
|
|
101
|
+
* @param tokenWriter - Optional callback invoked after each successful refresh.
|
|
102
|
+
*/
|
|
103
|
+
startTokenAutoRefresh(refreshDurationSecs, checkIntervalMs, tokenWriter) {
|
|
104
|
+
return tokenMethods.startTokenAutoRefresh(this.httpClient, refreshDurationSecs, checkIntervalMs, tokenWriter);
|
|
105
|
+
}
|
|
47
106
|
async callInto(method, bizParams) {
|
|
48
107
|
// Inject secretKey into every request when available (institution accounts require it).
|
|
49
108
|
// Individual methods that already set secretKey will not be overwritten (|| not &&).
|
|
@@ -86,14 +145,54 @@ class TradeClient {
|
|
|
86
145
|
});
|
|
87
146
|
}
|
|
88
147
|
// === Order operations ===
|
|
148
|
+
/**
|
|
149
|
+
* Prepare an order request for the wire.
|
|
150
|
+
*
|
|
151
|
+
* The gateway expects `algo_params` as a `[{tag, value}, ...]` array
|
|
152
|
+
* (matching the Python SDK's AlgoParams.to_dict serialization), not the
|
|
153
|
+
* plain object shape that reads naturally in TypeScript. This helper
|
|
154
|
+
* normalizes the object form into the array form on outbound payloads
|
|
155
|
+
* so callers can pass the natural object and TWAP/VWAP orders work end
|
|
156
|
+
* to end without wire-format workarounds.
|
|
157
|
+
*
|
|
158
|
+
* Fields with undefined/null values are dropped; empty algoParams
|
|
159
|
+
* becomes an empty array rather than being stripped, matching the
|
|
160
|
+
* Python SDK behavior when the user passes an empty AlgoParams().
|
|
161
|
+
*/
|
|
162
|
+
marshalOrderForWire(order) {
|
|
163
|
+
const { algoParams, ...rest } = order;
|
|
164
|
+
const withAccount = { ...rest, account: this.account };
|
|
165
|
+
if (algoParams === undefined)
|
|
166
|
+
return withAccount;
|
|
167
|
+
// Already a tag/value array (advanced callers who want raw control)
|
|
168
|
+
if (Array.isArray(algoParams)) {
|
|
169
|
+
withAccount.algoParams = algoParams;
|
|
170
|
+
return withAccount;
|
|
171
|
+
}
|
|
172
|
+
// Object shape → tag/value array.
|
|
173
|
+
// Keys are converted to snake_case explicitly here (e.g. `startTime` →
|
|
174
|
+
// `start_time`) so they land correctly in the `tag` string that the
|
|
175
|
+
// gateway reads. `keysToSnakeCase` only rewrites object *keys*, not
|
|
176
|
+
// string *values*, so `tag` would be left camelCase if we didn't do
|
|
177
|
+
// this conversion ourselves.
|
|
178
|
+
const tagValues = [];
|
|
179
|
+
for (const [key, value] of Object.entries(algoParams)) {
|
|
180
|
+
if (value === undefined || value === null)
|
|
181
|
+
continue;
|
|
182
|
+
const snakeKey = key.replace(/[A-Z]/g, (c) => '_' + c.toLowerCase());
|
|
183
|
+
tagValues.push({ tag: snakeKey, value });
|
|
184
|
+
}
|
|
185
|
+
withAccount.algoParams = tagValues;
|
|
186
|
+
return withAccount;
|
|
187
|
+
}
|
|
89
188
|
async placeOrder(order) {
|
|
90
|
-
return this.callInto('place_order',
|
|
189
|
+
return this.callInto('place_order', this.marshalOrderForWire(order));
|
|
91
190
|
}
|
|
92
191
|
async previewOrder(order) {
|
|
93
|
-
return this.callInto('preview_order',
|
|
192
|
+
return this.callInto('preview_order', this.marshalOrderForWire(order));
|
|
94
193
|
}
|
|
95
194
|
async modifyOrder(id, order) {
|
|
96
|
-
return this.callInto('modify_order', { ...
|
|
195
|
+
return this.callInto('modify_order', { ...this.marshalOrderForWire(order), id });
|
|
97
196
|
}
|
|
98
197
|
async cancelOrder(id, secretKey) {
|
|
99
198
|
const params = { account: this.account, id };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Call the `user_token_refresh` API and return the new token string.
|
|
3
|
+
* Read-only: does NOT update the stored token.
|
|
4
|
+
* Use `refreshToken()` to also update the in-memory config and optionally persist.
|
|
5
|
+
*/
|
|
6
|
+
export async function queryToken(httpClient) {
|
|
7
|
+
return httpClient.queryToken();
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Refresh the token: call the API, update the in-memory config token, and
|
|
11
|
+
* optionally persist via the provided TokenManager (file + writer callback).
|
|
12
|
+
*
|
|
13
|
+
* @param httpClient - The underlying HTTP client
|
|
14
|
+
* @param tokenManager - optional TokenManager for file persistence and writer callback
|
|
15
|
+
*/
|
|
16
|
+
export async function refreshToken(httpClient, tokenManager) {
|
|
17
|
+
return httpClient.refreshToken(tokenManager);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Start background token auto-refresh. Returns a TokenManager that can be
|
|
21
|
+
* used to stop the background task via `tm.stopAutoRefresh()`.
|
|
22
|
+
*
|
|
23
|
+
* @param httpClient - The underlying HTTP client
|
|
24
|
+
* @param refreshDurationSecs - Refresh threshold in seconds (minimum 30).
|
|
25
|
+
* When the token age exceeds this value the refresh is triggered.
|
|
26
|
+
* @param checkIntervalMs - How often to check whether a refresh is needed,
|
|
27
|
+
* in milliseconds. Defaults to 300 000 ms (5 minutes).
|
|
28
|
+
* @param tokenWriter - Optional callback invoked with the new token string
|
|
29
|
+
* after each successful refresh.
|
|
30
|
+
*/
|
|
31
|
+
export function startTokenAutoRefresh(httpClient, refreshDurationSecs, checkIntervalMs, tokenWriter) {
|
|
32
|
+
return httpClient.startTokenAutoRefresh(null, {
|
|
33
|
+
refreshDuration: refreshDurationSecs,
|
|
34
|
+
refreshInterval: checkIntervalMs,
|
|
35
|
+
tokenWriter,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Priority: environment variables > code options (incl. config file) > defaults
|
|
10
10
|
*/
|
|
11
11
|
import { readFileSync, existsSync, statSync } from 'fs';
|
|
12
|
-
import { join } from 'path';
|
|
12
|
+
import { join, dirname } from 'path';
|
|
13
13
|
import { homedir, networkInterfaces } from 'os';
|
|
14
14
|
import { parsePropertiesString } from './config-parser.js';
|
|
15
15
|
import { queryDomains, resolveDynamicServerUrl, resolveDynamicQuoteServerUrl } from './domain.js';
|
|
@@ -108,23 +108,32 @@ export function createClientConfig(options) {
|
|
|
108
108
|
const opts = options ?? {};
|
|
109
109
|
// Load properties: explicit path > auto-discovery
|
|
110
110
|
let fileProps = {};
|
|
111
|
+
let configFileDir;
|
|
111
112
|
if (opts.propertiesFilePath) {
|
|
112
113
|
// Support both directory path and full file path
|
|
113
114
|
let filePath = opts.propertiesFilePath;
|
|
114
115
|
if (existsSync(filePath) && statSync(filePath).isDirectory()) {
|
|
116
|
+
configFileDir = filePath;
|
|
115
117
|
filePath = join(filePath, CONFIG_FILE_NAME);
|
|
116
118
|
}
|
|
117
119
|
else if (!existsSync(filePath) && !filePath.endsWith('.properties')) {
|
|
118
120
|
// Path doesn't exist as-is; try treating it as a directory
|
|
119
121
|
const asDir = join(filePath, CONFIG_FILE_NAME);
|
|
120
|
-
if (existsSync(asDir))
|
|
122
|
+
if (existsSync(asDir)) {
|
|
123
|
+
configFileDir = filePath;
|
|
121
124
|
filePath = asDir;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
// It's a file path: derive directory from it
|
|
129
|
+
configFileDir = dirname(filePath);
|
|
122
130
|
}
|
|
123
131
|
fileProps = loadPropertiesFile(filePath);
|
|
124
132
|
}
|
|
125
133
|
else {
|
|
126
134
|
const discovered = discoverConfigFile();
|
|
127
135
|
if (discovered) {
|
|
136
|
+
configFileDir = dirname(discovered);
|
|
128
137
|
fileProps = loadPropertiesFile(discovered);
|
|
129
138
|
}
|
|
130
139
|
}
|
|
@@ -184,8 +193,9 @@ export function createClientConfig(options) {
|
|
|
184
193
|
}
|
|
185
194
|
}
|
|
186
195
|
else {
|
|
187
|
-
// Load from token file: TIGEROPEN_TOKEN_FILE env var > default
|
|
188
|
-
const tokenFilePath = process.env[ENV_TOKEN_FILE]
|
|
196
|
+
// Load from token file: TIGEROPEN_TOKEN_FILE env var > config dir > default
|
|
197
|
+
const tokenFilePath = process.env[ENV_TOKEN_FILE]
|
|
198
|
+
|| (configFileDir ? join(configFileDir, 'tiger_openapi_token.properties') : 'tiger_openapi_token.properties');
|
|
189
199
|
const tm = new TokenManager({ filePath: tokenFilePath });
|
|
190
200
|
try {
|
|
191
201
|
token = tm.loadTokenSync();
|
|
@@ -88,26 +88,11 @@ export function algoOrder(account, symbol, secType, action, quantity, limitPrice
|
|
|
88
88
|
};
|
|
89
89
|
}
|
|
90
90
|
/**
|
|
91
|
-
* Iceberg order
|
|
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).
|
|
91
|
+
* Iceberg order. `displaySize` is required; all other parameters are optional.
|
|
92
|
+
* Pass `priceType` as empty string to use the server default (LIMIT_PRICE).
|
|
108
93
|
* Pass `startTime` / `endTime` as 0 to omit the time window.
|
|
109
94
|
*/
|
|
110
|
-
export function
|
|
95
|
+
export function icebergOrder(account, symbol, secType, action, quantity, limitPrice, displaySize, minDisplaySize, checkIntervals, priceType, startTime, endTime) {
|
|
111
96
|
const req = {
|
|
112
97
|
account, symbol, secType, action,
|
|
113
98
|
orderType: OrderType.ICEBERG,
|
|
@@ -116,13 +101,12 @@ export function icebergOrderFull(account, symbol, secType, action, quantity, lim
|
|
|
116
101
|
timeInForce: TimeInForce.DAY,
|
|
117
102
|
outsideRth: false,
|
|
118
103
|
displaySize,
|
|
104
|
+
priceType: priceType || PriceType.LIMIT_PRICE,
|
|
119
105
|
};
|
|
120
106
|
if (minDisplaySize !== undefined && minDisplaySize > 0)
|
|
121
107
|
req.minDisplaySize = minDisplaySize;
|
|
122
108
|
if (checkIntervals !== undefined && checkIntervals > 0)
|
|
123
109
|
req.checkIntervals = checkIntervals;
|
|
124
|
-
if (priceType)
|
|
125
|
-
req.priceType = priceType;
|
|
126
110
|
if (startTime && startTime > 0)
|
|
127
111
|
req.startTime = startTime;
|
|
128
112
|
if (endTime && endTime > 0)
|
|
@@ -133,3 +117,118 @@ export function icebergOrderFull(account, symbol, secType, action, quantity, lim
|
|
|
133
117
|
export function orderLeg(legType, price, timeInForce) {
|
|
134
118
|
return { legType, price, timeInForce };
|
|
135
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Market order by cash amount (instead of quantity).
|
|
122
|
+
* Server uses `isQuantityByAmount=true` + `amount` field.
|
|
123
|
+
*/
|
|
124
|
+
export function marketOrderByAmount(account, symbol, secType, action, amount) {
|
|
125
|
+
return {
|
|
126
|
+
account, symbol, secType, action,
|
|
127
|
+
orderType: OrderType.MKT,
|
|
128
|
+
totalQuantity: 0,
|
|
129
|
+
cashAmount: amount,
|
|
130
|
+
timeInForce: TimeInForce.DAY,
|
|
131
|
+
outsideRth: false,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Limit order by cash amount (instead of quantity).
|
|
136
|
+
*/
|
|
137
|
+
export function limitOrderByAmount(account, symbol, secType, action, amount, limitPrice) {
|
|
138
|
+
return {
|
|
139
|
+
account, symbol, secType, action,
|
|
140
|
+
orderType: OrderType.LMT,
|
|
141
|
+
totalQuantity: 0,
|
|
142
|
+
cashAmount: amount,
|
|
143
|
+
limitPrice,
|
|
144
|
+
timeInForce: TimeInForce.DAY,
|
|
145
|
+
outsideRth: false,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Trailing stop order using a fixed price offset (auxPrice) instead of a percentage.
|
|
150
|
+
* Use `trailOrder()` for percentage-based trailing stops.
|
|
151
|
+
*/
|
|
152
|
+
export function trailOrderByPrice(account, symbol, secType, action, quantity, auxPrice) {
|
|
153
|
+
return {
|
|
154
|
+
account, symbol, secType, action,
|
|
155
|
+
orderType: OrderType.TRAIL,
|
|
156
|
+
totalQuantity: quantity,
|
|
157
|
+
auxPrice,
|
|
158
|
+
timeInForce: TimeInForce.DAY,
|
|
159
|
+
outsideRth: false,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Limit order with attached profit/loss legs (bracket order).
|
|
164
|
+
* At most 2 legs are supported.
|
|
165
|
+
*
|
|
166
|
+
* @param orderLegs - Array of `OrderLegRequest` (use `orderLeg()` helper)
|
|
167
|
+
*/
|
|
168
|
+
export function limitOrderWithLegs(account, symbol, secType, action, quantity, limitPrice, orderLegs) {
|
|
169
|
+
if (orderLegs.length === 0) {
|
|
170
|
+
throw new Error('At least 1 order leg is required for a bracket order');
|
|
171
|
+
}
|
|
172
|
+
if (orderLegs.length > 2) {
|
|
173
|
+
throw new Error('At most 2 order legs are supported');
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
account, symbol, secType, action,
|
|
177
|
+
orderType: OrderType.LMT,
|
|
178
|
+
totalQuantity: quantity,
|
|
179
|
+
limitPrice,
|
|
180
|
+
orderLegs,
|
|
181
|
+
timeInForce: TimeInForce.DAY,
|
|
182
|
+
outsideRth: false,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Multi-leg (combo/MLEG) order for options combos.
|
|
187
|
+
*
|
|
188
|
+
* @param contractLegs - Array of `ContractLegRequest` (use `contractLeg()` helper)
|
|
189
|
+
* @param comboType - e.g. `'MLEG'`
|
|
190
|
+
*/
|
|
191
|
+
export function comboOrder(account, action, quantity, orderType, contractLegs, comboType = 'MLEG', limitPrice, auxPrice, trailingPercent) {
|
|
192
|
+
return {
|
|
193
|
+
account,
|
|
194
|
+
symbol: '', // not used for combo orders
|
|
195
|
+
secType: 'MLEG',
|
|
196
|
+
action,
|
|
197
|
+
orderType,
|
|
198
|
+
totalQuantity: quantity,
|
|
199
|
+
limitPrice,
|
|
200
|
+
auxPrice,
|
|
201
|
+
trailingPercent,
|
|
202
|
+
contractLegs,
|
|
203
|
+
comboType,
|
|
204
|
+
timeInForce: TimeInForce.DAY,
|
|
205
|
+
outsideRth: false,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* OCA (One-Cancels-All) order.
|
|
210
|
+
* Server expects `orderType: 'OCA'` and an `ocaOrders` list.
|
|
211
|
+
*
|
|
212
|
+
* @param ocaOrders - The contained orders (use any order helper to build each)
|
|
213
|
+
*/
|
|
214
|
+
export function ocaOrder(account, symbol, secType, action, quantity, ocaOrders) {
|
|
215
|
+
return {
|
|
216
|
+
account, symbol, secType, action,
|
|
217
|
+
orderType: OrderType.OCA,
|
|
218
|
+
totalQuantity: quantity,
|
|
219
|
+
ocaOrders,
|
|
220
|
+
timeInForce: TimeInForce.DAY,
|
|
221
|
+
outsideRth: false,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Build a single leg for a multi-leg (MLEG / combo) order.
|
|
226
|
+
*
|
|
227
|
+
* @param symbol - Underlying symbol
|
|
228
|
+
* @param secType - Security type (e.g. `'OPT'`)
|
|
229
|
+
* @param action - `'BUY'` or `'SELL'`
|
|
230
|
+
* @param ratio - Number of contracts for this leg
|
|
231
|
+
*/
|
|
232
|
+
export function contractLeg(symbol, secType, action, ratio, expiry, strike, right) {
|
|
233
|
+
return { symbol, secType, action, ratio, expiry, strike, right };
|
|
234
|
+
}
|
package/dist/esm/push/index.js
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import * as tls from 'tls';
|
|
12
12
|
import { signWithRSA } from '../signer/signer.js';
|
|
13
13
|
import { SubjectType } from './push-message.js';
|
|
14
|
+
import { convertTradeTick } from './tick-util.js';
|
|
14
15
|
import { Request } from './pb/Request.js';
|
|
15
16
|
import { Response } from './pb/Response.js';
|
|
16
17
|
import { SocketCommon_Command, SocketCommon_DataType } from './pb/SocketCommon.js';
|
|
@@ -342,8 +343,10 @@ export class PushClient {
|
|
|
342
343
|
cb.onDepth?.(pushData.quoteDepthData);
|
|
343
344
|
break;
|
|
344
345
|
case SocketCommon_DataType.TradeTick:
|
|
346
|
+
if (pushData.tickData)
|
|
347
|
+
cb.onFullTick?.(pushData.tickData);
|
|
345
348
|
if (pushData.tradeTickData)
|
|
346
|
-
cb.onTick?.(pushData.tradeTickData);
|
|
349
|
+
cb.onTick?.(convertTradeTick(pushData.tradeTickData));
|
|
347
350
|
break;
|
|
348
351
|
case SocketCommon_DataType.Asset:
|
|
349
352
|
if (pushData.assetData)
|
|
@@ -379,8 +382,7 @@ export class PushClient {
|
|
|
379
382
|
cb.onQuote?.(pushData.quoteData);
|
|
380
383
|
break;
|
|
381
384
|
default:
|
|
382
|
-
//
|
|
383
|
-
// TickData (fullTick) via tickData field
|
|
385
|
+
// QuoteBBO and some full-tick payloads reuse an existing dataType.
|
|
384
386
|
if (pushData.tickData)
|
|
385
387
|
cb.onFullTick?.(pushData.tickData);
|
|
386
388
|
break;
|