@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
package/README.md
CHANGED
|
@@ -486,8 +486,61 @@ const chain = await qc.optionChain('AAPL', '2024-01-19');
|
|
|
486
486
|
|
|
487
487
|
// 期货交易所
|
|
488
488
|
const exchanges = await qc.futureExchange();
|
|
489
|
+
|
|
490
|
+
// 逐笔成交
|
|
491
|
+
const ticks = await qc.getTradeTick({ symbols: ['AAPL'] });
|
|
492
|
+
// ticks[0].items[i].partCode — 交易所短名,如 "NYSE"、"NSDQ"
|
|
493
|
+
// ticks[0].items[i].partName — 交易所全名,如 "New York Stock Exchange, LLC (NYSE)"
|
|
494
|
+
// ticks[0].items[i].cond — 已转换的成交条件,如 "US_REGULAR_SALE",参见下方说明
|
|
489
495
|
```
|
|
490
496
|
|
|
497
|
+
### 逐笔成交 `cond` 字段说明
|
|
498
|
+
|
|
499
|
+
`trade_tick` 接口和 push 层返回的 `cond` 字段已由 SDK 转换为可读字符串,含义如下:
|
|
500
|
+
|
|
501
|
+
**美股(US)**
|
|
502
|
+
|
|
503
|
+
| 字符 | 含义 |
|
|
504
|
+
|------|------|
|
|
505
|
+
| ` `(空格)| 常规交易(Regular Sale) |
|
|
506
|
+
| `B` | 批量交易(Bunched Trade) |
|
|
507
|
+
| `C` | 现金交易(Cash Trade) |
|
|
508
|
+
| `F` | 跨市场交易(Intermarket Sweep) |
|
|
509
|
+
| `G` | 批量卖出(Bunched Sold Trade) |
|
|
510
|
+
| `H` | 离价交易(Price Variation Trade) |
|
|
511
|
+
| `I` | 碎股交易(Odd Lot Trade) |
|
|
512
|
+
| `K` | 纽交所第 127/155 条交易 |
|
|
513
|
+
| `L` | 延迟交易(Sold Last) |
|
|
514
|
+
| `M` | 中央收市价(Market Center Close Price) |
|
|
515
|
+
| `N` | 隔日交易(Next Day Trade) |
|
|
516
|
+
| `O` | 中央开盘价交易(Market Center Opening Trade) |
|
|
517
|
+
| `P` | 前参考价(Prior Reference Price) |
|
|
518
|
+
| `Q` | 中央开盘价(Market Center Open Price) |
|
|
519
|
+
| `R` | 卖方(Seller) |
|
|
520
|
+
| `T` | 盘前盘后交易(Form T) |
|
|
521
|
+
| `U` | 延长交易时段(Extended Trading Hours) |
|
|
522
|
+
| `V` | 合单交易(Contingent Trade) |
|
|
523
|
+
| `W` | 均价交易(Average Price Trade) |
|
|
524
|
+
| `X` | 跨市场交易(Cross Trade) |
|
|
525
|
+
| `Z` | 场外售出(Sold Out of Sequence) |
|
|
526
|
+
| `4` | 衍生工具定价(Derivatively Priced) |
|
|
527
|
+
| `7` | 合单交易(Qualified Contingent Trade) |
|
|
528
|
+
|
|
529
|
+
**港股(HK)**
|
|
530
|
+
|
|
531
|
+
| 字符 | 含义 |
|
|
532
|
+
|------|------|
|
|
533
|
+
| ` `(空格)| 自动对盘(Automatch Normal) |
|
|
534
|
+
| `D` | 碎股交易(Odd Lot Trade) |
|
|
535
|
+
| `U` | 竞价交易(Auction Trade) |
|
|
536
|
+
| `*` | 场外交易(Overseas Trade) |
|
|
537
|
+
| `P` | 开市前成交(Late Trade Off Exchange) |
|
|
538
|
+
| `M` | 非自动对盘(Non-Direct Off Exchange Trade) |
|
|
539
|
+
| `X` | 同券商自动对盘(Direct Off Exchange Trade) |
|
|
540
|
+
| `Y` | 同券商非自动对盘(Automatic Internalized) |
|
|
541
|
+
|
|
542
|
+
如上表所示,`cond` 值因市场(美股/港股)不同而有所区别。
|
|
543
|
+
|
|
491
544
|
## 交易
|
|
492
545
|
|
|
493
546
|
```typescript
|
|
@@ -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
|
+
}
|
|
@@ -113,23 +113,32 @@ function createClientConfig(options) {
|
|
|
113
113
|
const opts = options ?? {};
|
|
114
114
|
// Load properties: explicit path > auto-discovery
|
|
115
115
|
let fileProps = {};
|
|
116
|
+
let configFileDir;
|
|
116
117
|
if (opts.propertiesFilePath) {
|
|
117
118
|
// Support both directory path and full file path
|
|
118
119
|
let filePath = opts.propertiesFilePath;
|
|
119
120
|
if ((0, fs_1.existsSync)(filePath) && (0, fs_1.statSync)(filePath).isDirectory()) {
|
|
121
|
+
configFileDir = filePath;
|
|
120
122
|
filePath = (0, path_1.join)(filePath, CONFIG_FILE_NAME);
|
|
121
123
|
}
|
|
122
124
|
else if (!(0, fs_1.existsSync)(filePath) && !filePath.endsWith('.properties')) {
|
|
123
125
|
// Path doesn't exist as-is; try treating it as a directory
|
|
124
126
|
const asDir = (0, path_1.join)(filePath, CONFIG_FILE_NAME);
|
|
125
|
-
if ((0, fs_1.existsSync)(asDir))
|
|
127
|
+
if ((0, fs_1.existsSync)(asDir)) {
|
|
128
|
+
configFileDir = filePath;
|
|
126
129
|
filePath = asDir;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
// It's a file path: derive directory from it
|
|
134
|
+
configFileDir = (0, path_1.dirname)(filePath);
|
|
127
135
|
}
|
|
128
136
|
fileProps = loadPropertiesFile(filePath);
|
|
129
137
|
}
|
|
130
138
|
else {
|
|
131
139
|
const discovered = discoverConfigFile();
|
|
132
140
|
if (discovered) {
|
|
141
|
+
configFileDir = (0, path_1.dirname)(discovered);
|
|
133
142
|
fileProps = loadPropertiesFile(discovered);
|
|
134
143
|
}
|
|
135
144
|
}
|
|
@@ -189,8 +198,9 @@ function createClientConfig(options) {
|
|
|
189
198
|
}
|
|
190
199
|
}
|
|
191
200
|
else {
|
|
192
|
-
// Load from token file: TIGEROPEN_TOKEN_FILE env var > default
|
|
193
|
-
const tokenFilePath = process.env[ENV_TOKEN_FILE]
|
|
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');
|
|
194
204
|
const tm = new token_manager_1.TokenManager({ filePath: tokenFilePath });
|
|
195
205
|
try {
|
|
196
206
|
token = tm.loadTokenSync();
|
|
@@ -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
|
|
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
|
|
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
|
+
}
|
package/dist/cjs/push/index.js
CHANGED
|
@@ -22,6 +22,7 @@ var push_message_1 = require("./push-message");
|
|
|
22
22
|
Object.defineProperty(exports, "SubjectType", { enumerable: true, get: function () { return push_message_1.SubjectType; } });
|
|
23
23
|
__exportStar(require("./callbacks"), exports);
|
|
24
24
|
__exportStar(require("./varint"), exports);
|
|
25
|
+
__exportStar(require("./tick-util"), exports);
|
|
25
26
|
__exportStar(require("./proto-message"), exports);
|
|
26
27
|
var push_client_1 = require("./push-client");
|
|
27
28
|
Object.defineProperty(exports, "PushClient", { enumerable: true, get: function () { return push_client_1.PushClient; } });
|
|
@@ -47,6 +47,7 @@ exports.PushClient = exports.ConnectionState = void 0;
|
|
|
47
47
|
const tls = __importStar(require("tls"));
|
|
48
48
|
const signer_1 = require("../signer/signer");
|
|
49
49
|
const push_message_1 = require("./push-message");
|
|
50
|
+
const tick_util_1 = require("./tick-util");
|
|
50
51
|
const Request_1 = require("./pb/Request");
|
|
51
52
|
const Response_1 = require("./pb/Response");
|
|
52
53
|
const SocketCommon_1 = require("./pb/SocketCommon");
|
|
@@ -378,8 +379,10 @@ class PushClient {
|
|
|
378
379
|
cb.onDepth?.(pushData.quoteDepthData);
|
|
379
380
|
break;
|
|
380
381
|
case SocketCommon_1.SocketCommon_DataType.TradeTick:
|
|
382
|
+
if (pushData.tickData)
|
|
383
|
+
cb.onFullTick?.(pushData.tickData);
|
|
381
384
|
if (pushData.tradeTickData)
|
|
382
|
-
cb.onTick?.(pushData.tradeTickData);
|
|
385
|
+
cb.onTick?.((0, tick_util_1.convertTradeTick)(pushData.tradeTickData));
|
|
383
386
|
break;
|
|
384
387
|
case SocketCommon_1.SocketCommon_DataType.Asset:
|
|
385
388
|
if (pushData.assetData)
|
|
@@ -415,8 +418,7 @@ class PushClient {
|
|
|
415
418
|
cb.onQuote?.(pushData.quoteData);
|
|
416
419
|
break;
|
|
417
420
|
default:
|
|
418
|
-
//
|
|
419
|
-
// TickData (fullTick) via tickData field
|
|
421
|
+
// QuoteBBO and some full-tick payloads reuse an existing dataType.
|
|
420
422
|
if (pushData.tickData)
|
|
421
423
|
cb.onFullTick?.(pushData.tickData);
|
|
422
424
|
break;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getPartNameByCode = getPartNameByCode;
|
|
4
|
+
exports.getPartShortNameByCode = getPartShortNameByCode;
|
|
5
|
+
exports.getTradeCondByCode = getTradeCondByCode;
|
|
6
|
+
exports.isUsStockSymbol = isUsStockSymbol;
|
|
7
|
+
exports.convertTradeTick = convertTradeTick;
|
|
8
|
+
const PART_CODE_NAME_MAP = {
|
|
9
|
+
a: 'NYSE American, LLC (NYSE American)',
|
|
10
|
+
b: 'NASDAQ OMX BX, Inc. (NASDAQ OMX BX)',
|
|
11
|
+
c: 'NYSE National, Inc. (NYSE National)',
|
|
12
|
+
d: 'FINRA Alternative Display Facility (ADF)',
|
|
13
|
+
h: 'MIAX Pearl Exchange, LLC (MIAX)',
|
|
14
|
+
i: 'International Securities Exchange, LLC (ISE)',
|
|
15
|
+
j: 'Cboe EDGA Exchange, Inc. (Cboe EDGA)',
|
|
16
|
+
k: 'Cboe EDGX Exchange, Inc. (Cboe EDGX)',
|
|
17
|
+
l: 'Long-Term Stock Exchange, Inc. (LTSE)',
|
|
18
|
+
m: 'NYSE Chicago, Inc. (NYSE Chicago)',
|
|
19
|
+
n: 'New York Stock Exchange, LLC (NYSE)',
|
|
20
|
+
p: 'NYSE Arca, Inc. (NYSE Arca)',
|
|
21
|
+
s: 'Consolidated Tape System (CTS)',
|
|
22
|
+
t: 'NASDAQ Stock Market, LLC (NASDAQ)',
|
|
23
|
+
u: 'Members Exchange, LLC (MEMX)',
|
|
24
|
+
v: "Investors' Exchange, LLC. (IEX)",
|
|
25
|
+
w: 'CBOE Stock Exchange, Inc. (CBSX)',
|
|
26
|
+
x: 'NASDAQ OMX PSX, Inc. (NASDAQ OMX PSX)',
|
|
27
|
+
y: 'Cboe BYX Exchange, Inc. (Cboe BYX)',
|
|
28
|
+
z: 'Cboe BZX Exchange, Inc. (Cboe BZX)',
|
|
29
|
+
};
|
|
30
|
+
const PART_CODE_SHORT_NAME_MAP = {
|
|
31
|
+
a: 'AMEX',
|
|
32
|
+
b: 'BX',
|
|
33
|
+
c: 'NSX',
|
|
34
|
+
d: 'ADF',
|
|
35
|
+
h: 'MIAX',
|
|
36
|
+
i: 'ISE',
|
|
37
|
+
j: 'EDGA',
|
|
38
|
+
k: 'EDGX',
|
|
39
|
+
l: 'LTSE',
|
|
40
|
+
m: 'CHO',
|
|
41
|
+
n: 'NYSE',
|
|
42
|
+
p: 'ARCA',
|
|
43
|
+
s: 'CTS',
|
|
44
|
+
t: 'NSDQ',
|
|
45
|
+
u: 'MEMX',
|
|
46
|
+
v: 'IEX',
|
|
47
|
+
w: 'CBSX',
|
|
48
|
+
x: 'PSX',
|
|
49
|
+
y: 'BYX',
|
|
50
|
+
z: 'BZX',
|
|
51
|
+
};
|
|
52
|
+
const US_TRADE_COND = {
|
|
53
|
+
' ': 'US_REGULAR_SALE',
|
|
54
|
+
B: 'US_BUNCHED_TRADE',
|
|
55
|
+
C: 'US_CASH_TRADE',
|
|
56
|
+
F: 'US_INTERMARKET_SWEEP',
|
|
57
|
+
G: 'US_BUNCHED_SOLD_TRADE',
|
|
58
|
+
H: 'US_PRICE_VARIATION_TRADE',
|
|
59
|
+
I: 'US_ODD_LOT_TRADE',
|
|
60
|
+
K: 'US_RULE_127_OR_155_TRADE',
|
|
61
|
+
L: 'US_SOLD_LAST',
|
|
62
|
+
M: 'US_MARKET_CENTER_CLOSE_PRICE',
|
|
63
|
+
N: 'US_NEXT_DAY_TRADE',
|
|
64
|
+
O: 'US_MARKET_CENTER_OPENING_TRADE',
|
|
65
|
+
P: 'US_PRIOR_REFERENCE_PRICE',
|
|
66
|
+
Q: 'US_MARKET_CENTER_OPEN_PRICE',
|
|
67
|
+
R: 'US_SELLER',
|
|
68
|
+
T: 'US_FORM_T',
|
|
69
|
+
U: 'US_EXTENDED_TRADING_HOURS',
|
|
70
|
+
V: 'US_CONTINGENT_TRADE',
|
|
71
|
+
W: 'US_AVERAGE_PRICE_TRADE',
|
|
72
|
+
X: 'US_CROSS_TRADE',
|
|
73
|
+
Z: 'US_SOLD_OUT_OF_SEQUENCE',
|
|
74
|
+
'0': 'US_ODD_LOST_CROSS_TRADE',
|
|
75
|
+
'4': 'US_DERIVATIVELY_PRICED',
|
|
76
|
+
'5': 'US_MARKET_CENTER_RE_OPENING_TRADE',
|
|
77
|
+
'6': 'US_MARKET_CENTER_CLOSING_TRADE',
|
|
78
|
+
'7': 'US_QUALIFIED_CONTINGENT_TRADE',
|
|
79
|
+
'9': 'US_CONSOLIDATED_LAST_PRICE_PER_LISTING_PACKET',
|
|
80
|
+
};
|
|
81
|
+
const HK_TRADE_COND = {
|
|
82
|
+
' ': 'HK_AUTOMATCH_NORMAL',
|
|
83
|
+
D: 'HK_ODD_LOT_TRADE',
|
|
84
|
+
U: 'HK_AUCTION_TRADE',
|
|
85
|
+
'*': 'HK_OVERSEAS_TRADE',
|
|
86
|
+
P: 'HK_LATE_TRADE_OFF_EXCHG',
|
|
87
|
+
M: 'HK_NON_DIRECT_OFF_EXCHG_TRADE',
|
|
88
|
+
X: 'HK_DIRECT_OFF_EXCHG_TRADE',
|
|
89
|
+
Y: 'HK_AUTOMATIC_INTERNALIZED',
|
|
90
|
+
};
|
|
91
|
+
/** Returns the full exchange name for a partCode letter, or the raw code if unknown. */
|
|
92
|
+
function getPartNameByCode(code) {
|
|
93
|
+
if (!code)
|
|
94
|
+
return code;
|
|
95
|
+
return PART_CODE_NAME_MAP[code] ?? code;
|
|
96
|
+
}
|
|
97
|
+
/** Returns the short exchange name for a partCode letter, or the raw code if unknown. */
|
|
98
|
+
function getPartShortNameByCode(code) {
|
|
99
|
+
if (!code)
|
|
100
|
+
return code;
|
|
101
|
+
return PART_CODE_SHORT_NAME_MAP[code] ?? code;
|
|
102
|
+
}
|
|
103
|
+
/** Resolves a raw cond character to a readable string. */
|
|
104
|
+
function getTradeCondByCode(isUs, ch) {
|
|
105
|
+
const map = isUs ? US_TRADE_COND : HK_TRADE_COND;
|
|
106
|
+
return map[ch] ?? ch;
|
|
107
|
+
}
|
|
108
|
+
const US_SYMBOL_RE = /^[A-Z]+(\.[A-Z0-9]+)?$/;
|
|
109
|
+
/** Reports whether symbol is a US stock (uppercase letters, optional dot suffix). */
|
|
110
|
+
function isUsStockSymbol(symbol) {
|
|
111
|
+
return US_SYMBOL_RE.test(symbol);
|
|
112
|
+
}
|
|
113
|
+
function isUsSymbol(symbol) {
|
|
114
|
+
return US_SYMBOL_RE.test(symbol);
|
|
115
|
+
}
|
|
116
|
+
function getTradeCond(isUs, ch) {
|
|
117
|
+
const map = isUs ? US_TRADE_COND : HK_TRADE_COND;
|
|
118
|
+
return map[ch] ?? ch;
|
|
119
|
+
}
|
|
120
|
+
/** Decodes a TradeTickData pb message into a PushTradeTick with resolved partCode/partName. */
|
|
121
|
+
function convertTradeTick(src) {
|
|
122
|
+
if (src.secType === 'FUT')
|
|
123
|
+
return convertFutureTick(src);
|
|
124
|
+
return convertStockTick(src);
|
|
125
|
+
}
|
|
126
|
+
function convertStockTick(src) {
|
|
127
|
+
const isUs = isUsSymbol(src.symbol);
|
|
128
|
+
const denominator = Math.pow(10, src.priceOffset);
|
|
129
|
+
const ticks = [];
|
|
130
|
+
let currentTime = 0;
|
|
131
|
+
for (let i = 0; i < src.time.length; i++) {
|
|
132
|
+
currentTime += src.time[i];
|
|
133
|
+
const condCh = src.cond.length > i ? src.cond[i] : ' ';
|
|
134
|
+
const rawCode = i < src.partCode.length ? src.partCode[i] : '';
|
|
135
|
+
ticks.push({
|
|
136
|
+
sn: src.sn + i,
|
|
137
|
+
time: currentTime,
|
|
138
|
+
price: (src.priceBase + src.price[i]) / denominator,
|
|
139
|
+
volume: src.volume[i],
|
|
140
|
+
tickType: src.type.length > i ? src.type[i] : '',
|
|
141
|
+
cond: getTradeCond(isUs, condCh),
|
|
142
|
+
partCode: getPartShortNameByCode(rawCode),
|
|
143
|
+
partName: getPartNameByCode(rawCode),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
return { symbol: src.symbol, secType: src.secType, quoteLevel: src.quoteLevel, timestamp: src.timestamp, ticks };
|
|
147
|
+
}
|
|
148
|
+
function convertFutureTick(src) {
|
|
149
|
+
const denominator = Math.pow(10, src.priceOffset);
|
|
150
|
+
const ticks = [];
|
|
151
|
+
let currentTime = 0;
|
|
152
|
+
let startSn = src.sn;
|
|
153
|
+
for (let i = 0; i < src.time.length; i++) {
|
|
154
|
+
currentTime += src.time[i];
|
|
155
|
+
const curPrice = (src.priceBase + src.price[i]) / denominator;
|
|
156
|
+
const mv = src.mergedVols[i];
|
|
157
|
+
for (let j = 0; j < mv.vol.length; j++) {
|
|
158
|
+
ticks.push({
|
|
159
|
+
sn: startSn * 10 + j,
|
|
160
|
+
time: currentTime,
|
|
161
|
+
price: curPrice,
|
|
162
|
+
volume: mv.vol[j],
|
|
163
|
+
tickType: '',
|
|
164
|
+
cond: '',
|
|
165
|
+
partCode: '',
|
|
166
|
+
partName: '',
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
startSn++;
|
|
170
|
+
}
|
|
171
|
+
return { symbol: src.symbol, secType: src.secType, quoteLevel: '', timestamp: src.timestamp, ticks };
|
|
172
|
+
}
|