@tigeropenapi/tigeropen 0.5.4 → 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/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 +59 -12
- package/dist/cjs/trade/trade-client.js +43 -3
- 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 +59 -12
- package/dist/esm/trade/trade-client.js +43 -3
- package/dist/types/index.d.ts +1 -1
- package/dist/types/model/order.d.ts +62 -11
- 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 +27 -6
- package/dist/types/trade/trade-client.d.ts +15 -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
|
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
|
+
}
|
|
@@ -46,6 +46,7 @@ const http_client_1 = require("../client/http-client");
|
|
|
46
46
|
const api_request_1 = require("../client/api-request");
|
|
47
47
|
const api_response_1 = require("../client/api-response");
|
|
48
48
|
const tokenMethods = __importStar(require("../client/token-methods"));
|
|
49
|
+
const tick_util_1 = require("../push/tick-util");
|
|
49
50
|
/**
|
|
50
51
|
* resolveOptionTimezone returns the timezone string to use for expiry conversion.
|
|
51
52
|
* Explicit tz takes precedence; otherwise infer from symbol (.HK suffix → Asia/Hong_Kong, else America/New_York).
|
|
@@ -185,12 +186,22 @@ class QuoteClient {
|
|
|
185
186
|
async getKline(req) {
|
|
186
187
|
return this.callInto('kline', req);
|
|
187
188
|
}
|
|
188
|
-
async getTimeline(
|
|
189
|
-
|
|
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);
|
|
190
192
|
}
|
|
191
193
|
/** Tick-by-tick trades. wire: trade_tick */
|
|
192
194
|
async getTradeTick(req) {
|
|
193
|
-
|
|
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;
|
|
194
205
|
}
|
|
195
206
|
/** Depth snapshot. wire: quote_depth */
|
|
196
207
|
async getQuoteDepth(req) {
|
|
@@ -356,6 +367,7 @@ class QuoteClient {
|
|
|
356
367
|
while (acc.length < totalSize) {
|
|
357
368
|
const sub = {
|
|
358
369
|
symbols: req.symbol ? [req.symbol] : undefined,
|
|
370
|
+
secType: req.secType,
|
|
359
371
|
period: req.period,
|
|
360
372
|
right: req.right,
|
|
361
373
|
beginTime,
|
|
@@ -406,7 +418,7 @@ class QuoteClient {
|
|
|
406
418
|
async getStockIndustry(req) {
|
|
407
419
|
return this.callInto('stock_industry', req);
|
|
408
420
|
}
|
|
409
|
-
/**
|
|
421
|
+
/** Market data permission entries. wire: get_quote_permission */
|
|
410
422
|
async getQuotePermission(req) {
|
|
411
423
|
return this.callInto('get_quote_permission', req);
|
|
412
424
|
}
|
|
@@ -417,9 +429,16 @@ class QuoteClient {
|
|
|
417
429
|
// ==========================================================================
|
|
418
430
|
// Batch 4: option / future extensions
|
|
419
431
|
// ==========================================================================
|
|
420
|
-
/** 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
|
+
*/
|
|
421
440
|
async getOptionTradeTicks(req) {
|
|
422
|
-
return this.callInto('option_trade_tick', req);
|
|
441
|
+
return this.callInto('option_trade_tick', req.contracts ?? []);
|
|
423
442
|
}
|
|
424
443
|
/** Option intraday timeline. wire: option_timeline */
|
|
425
444
|
async getOptionTimeline(req) {
|
|
@@ -458,10 +477,16 @@ class QuoteClient {
|
|
|
458
477
|
return this.callInto('future_main_contract', req);
|
|
459
478
|
}
|
|
460
479
|
/** Futures K-line. wire: future_kline
|
|
461
|
-
* 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. */
|
|
462
483
|
async getFutureKline(req) {
|
|
484
|
+
const contractCodes = req.contractCodes
|
|
485
|
+
?? (req.contractCode ? [req.contractCode] : undefined);
|
|
486
|
+
const { contractCode: _drop, ...rest } = req;
|
|
463
487
|
const body = {
|
|
464
|
-
...
|
|
488
|
+
...rest,
|
|
489
|
+
...(contractCodes ? { contractCodes } : {}),
|
|
465
490
|
beginTime: req.beginTime ?? -1,
|
|
466
491
|
endTime: req.endTime ?? -1,
|
|
467
492
|
};
|
|
@@ -479,8 +504,9 @@ class QuoteClient {
|
|
|
479
504
|
endTime = -1;
|
|
480
505
|
const acc = [];
|
|
481
506
|
while (acc.length < totalSize) {
|
|
507
|
+
// Server rejects singular `contract_code` — send the plural form.
|
|
482
508
|
const sub = {
|
|
483
|
-
|
|
509
|
+
contractCodes: req.contractCode ? [req.contractCode] : undefined,
|
|
484
510
|
period: req.period,
|
|
485
511
|
beginTime,
|
|
486
512
|
endTime,
|
|
@@ -559,9 +585,30 @@ class QuoteClient {
|
|
|
559
585
|
async getWarrantFilter(req) {
|
|
560
586
|
return this.callInto('warrant_filter', req);
|
|
561
587
|
}
|
|
562
|
-
/** Industry list. wire: industry_list
|
|
563
|
-
|
|
564
|
-
|
|
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;
|
|
565
612
|
}
|
|
566
613
|
/** Stocks inside an industry. wire: industry_stock_list */
|
|
567
614
|
async getIndustryStocks(req) {
|
|
@@ -145,14 +145,54 @@ class TradeClient {
|
|
|
145
145
|
});
|
|
146
146
|
}
|
|
147
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
|
+
}
|
|
148
188
|
async placeOrder(order) {
|
|
149
|
-
return this.callInto('place_order',
|
|
189
|
+
return this.callInto('place_order', this.marshalOrderForWire(order));
|
|
150
190
|
}
|
|
151
191
|
async previewOrder(order) {
|
|
152
|
-
return this.callInto('preview_order',
|
|
192
|
+
return this.callInto('preview_order', this.marshalOrderForWire(order));
|
|
153
193
|
}
|
|
154
194
|
async modifyOrder(id, order) {
|
|
155
|
-
return this.callInto('modify_order', { ...
|
|
195
|
+
return this.callInto('modify_order', { ...this.marshalOrderForWire(order), id });
|
|
156
196
|
}
|
|
157
197
|
async cancelOrder(id, secretKey) {
|
|
158
198
|
const params = { account: this.account, id };
|
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;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
const PART_CODE_NAME_MAP = {
|
|
2
|
+
a: 'NYSE American, LLC (NYSE American)',
|
|
3
|
+
b: 'NASDAQ OMX BX, Inc. (NASDAQ OMX BX)',
|
|
4
|
+
c: 'NYSE National, Inc. (NYSE National)',
|
|
5
|
+
d: 'FINRA Alternative Display Facility (ADF)',
|
|
6
|
+
h: 'MIAX Pearl Exchange, LLC (MIAX)',
|
|
7
|
+
i: 'International Securities Exchange, LLC (ISE)',
|
|
8
|
+
j: 'Cboe EDGA Exchange, Inc. (Cboe EDGA)',
|
|
9
|
+
k: 'Cboe EDGX Exchange, Inc. (Cboe EDGX)',
|
|
10
|
+
l: 'Long-Term Stock Exchange, Inc. (LTSE)',
|
|
11
|
+
m: 'NYSE Chicago, Inc. (NYSE Chicago)',
|
|
12
|
+
n: 'New York Stock Exchange, LLC (NYSE)',
|
|
13
|
+
p: 'NYSE Arca, Inc. (NYSE Arca)',
|
|
14
|
+
s: 'Consolidated Tape System (CTS)',
|
|
15
|
+
t: 'NASDAQ Stock Market, LLC (NASDAQ)',
|
|
16
|
+
u: 'Members Exchange, LLC (MEMX)',
|
|
17
|
+
v: "Investors' Exchange, LLC. (IEX)",
|
|
18
|
+
w: 'CBOE Stock Exchange, Inc. (CBSX)',
|
|
19
|
+
x: 'NASDAQ OMX PSX, Inc. (NASDAQ OMX PSX)',
|
|
20
|
+
y: 'Cboe BYX Exchange, Inc. (Cboe BYX)',
|
|
21
|
+
z: 'Cboe BZX Exchange, Inc. (Cboe BZX)',
|
|
22
|
+
};
|
|
23
|
+
const PART_CODE_SHORT_NAME_MAP = {
|
|
24
|
+
a: 'AMEX',
|
|
25
|
+
b: 'BX',
|
|
26
|
+
c: 'NSX',
|
|
27
|
+
d: 'ADF',
|
|
28
|
+
h: 'MIAX',
|
|
29
|
+
i: 'ISE',
|
|
30
|
+
j: 'EDGA',
|
|
31
|
+
k: 'EDGX',
|
|
32
|
+
l: 'LTSE',
|
|
33
|
+
m: 'CHO',
|
|
34
|
+
n: 'NYSE',
|
|
35
|
+
p: 'ARCA',
|
|
36
|
+
s: 'CTS',
|
|
37
|
+
t: 'NSDQ',
|
|
38
|
+
u: 'MEMX',
|
|
39
|
+
v: 'IEX',
|
|
40
|
+
w: 'CBSX',
|
|
41
|
+
x: 'PSX',
|
|
42
|
+
y: 'BYX',
|
|
43
|
+
z: 'BZX',
|
|
44
|
+
};
|
|
45
|
+
const US_TRADE_COND = {
|
|
46
|
+
' ': 'US_REGULAR_SALE',
|
|
47
|
+
B: 'US_BUNCHED_TRADE',
|
|
48
|
+
C: 'US_CASH_TRADE',
|
|
49
|
+
F: 'US_INTERMARKET_SWEEP',
|
|
50
|
+
G: 'US_BUNCHED_SOLD_TRADE',
|
|
51
|
+
H: 'US_PRICE_VARIATION_TRADE',
|
|
52
|
+
I: 'US_ODD_LOT_TRADE',
|
|
53
|
+
K: 'US_RULE_127_OR_155_TRADE',
|
|
54
|
+
L: 'US_SOLD_LAST',
|
|
55
|
+
M: 'US_MARKET_CENTER_CLOSE_PRICE',
|
|
56
|
+
N: 'US_NEXT_DAY_TRADE',
|
|
57
|
+
O: 'US_MARKET_CENTER_OPENING_TRADE',
|
|
58
|
+
P: 'US_PRIOR_REFERENCE_PRICE',
|
|
59
|
+
Q: 'US_MARKET_CENTER_OPEN_PRICE',
|
|
60
|
+
R: 'US_SELLER',
|
|
61
|
+
T: 'US_FORM_T',
|
|
62
|
+
U: 'US_EXTENDED_TRADING_HOURS',
|
|
63
|
+
V: 'US_CONTINGENT_TRADE',
|
|
64
|
+
W: 'US_AVERAGE_PRICE_TRADE',
|
|
65
|
+
X: 'US_CROSS_TRADE',
|
|
66
|
+
Z: 'US_SOLD_OUT_OF_SEQUENCE',
|
|
67
|
+
'0': 'US_ODD_LOST_CROSS_TRADE',
|
|
68
|
+
'4': 'US_DERIVATIVELY_PRICED',
|
|
69
|
+
'5': 'US_MARKET_CENTER_RE_OPENING_TRADE',
|
|
70
|
+
'6': 'US_MARKET_CENTER_CLOSING_TRADE',
|
|
71
|
+
'7': 'US_QUALIFIED_CONTINGENT_TRADE',
|
|
72
|
+
'9': 'US_CONSOLIDATED_LAST_PRICE_PER_LISTING_PACKET',
|
|
73
|
+
};
|
|
74
|
+
const HK_TRADE_COND = {
|
|
75
|
+
' ': 'HK_AUTOMATCH_NORMAL',
|
|
76
|
+
D: 'HK_ODD_LOT_TRADE',
|
|
77
|
+
U: 'HK_AUCTION_TRADE',
|
|
78
|
+
'*': 'HK_OVERSEAS_TRADE',
|
|
79
|
+
P: 'HK_LATE_TRADE_OFF_EXCHG',
|
|
80
|
+
M: 'HK_NON_DIRECT_OFF_EXCHG_TRADE',
|
|
81
|
+
X: 'HK_DIRECT_OFF_EXCHG_TRADE',
|
|
82
|
+
Y: 'HK_AUTOMATIC_INTERNALIZED',
|
|
83
|
+
};
|
|
84
|
+
/** Returns the full exchange name for a partCode letter, or the raw code if unknown. */
|
|
85
|
+
export function getPartNameByCode(code) {
|
|
86
|
+
if (!code)
|
|
87
|
+
return code;
|
|
88
|
+
return PART_CODE_NAME_MAP[code] ?? code;
|
|
89
|
+
}
|
|
90
|
+
/** Returns the short exchange name for a partCode letter, or the raw code if unknown. */
|
|
91
|
+
export function getPartShortNameByCode(code) {
|
|
92
|
+
if (!code)
|
|
93
|
+
return code;
|
|
94
|
+
return PART_CODE_SHORT_NAME_MAP[code] ?? code;
|
|
95
|
+
}
|
|
96
|
+
/** Resolves a raw cond character to a readable string. */
|
|
97
|
+
export function getTradeCondByCode(isUs, ch) {
|
|
98
|
+
const map = isUs ? US_TRADE_COND : HK_TRADE_COND;
|
|
99
|
+
return map[ch] ?? ch;
|
|
100
|
+
}
|
|
101
|
+
const US_SYMBOL_RE = /^[A-Z]+(\.[A-Z0-9]+)?$/;
|
|
102
|
+
/** Reports whether symbol is a US stock (uppercase letters, optional dot suffix). */
|
|
103
|
+
export function isUsStockSymbol(symbol) {
|
|
104
|
+
return US_SYMBOL_RE.test(symbol);
|
|
105
|
+
}
|
|
106
|
+
function isUsSymbol(symbol) {
|
|
107
|
+
return US_SYMBOL_RE.test(symbol);
|
|
108
|
+
}
|
|
109
|
+
function getTradeCond(isUs, ch) {
|
|
110
|
+
const map = isUs ? US_TRADE_COND : HK_TRADE_COND;
|
|
111
|
+
return map[ch] ?? ch;
|
|
112
|
+
}
|
|
113
|
+
/** Decodes a TradeTickData pb message into a PushTradeTick with resolved partCode/partName. */
|
|
114
|
+
export function convertTradeTick(src) {
|
|
115
|
+
if (src.secType === 'FUT')
|
|
116
|
+
return convertFutureTick(src);
|
|
117
|
+
return convertStockTick(src);
|
|
118
|
+
}
|
|
119
|
+
function convertStockTick(src) {
|
|
120
|
+
const isUs = isUsSymbol(src.symbol);
|
|
121
|
+
const denominator = Math.pow(10, src.priceOffset);
|
|
122
|
+
const ticks = [];
|
|
123
|
+
let currentTime = 0;
|
|
124
|
+
for (let i = 0; i < src.time.length; i++) {
|
|
125
|
+
currentTime += src.time[i];
|
|
126
|
+
const condCh = src.cond.length > i ? src.cond[i] : ' ';
|
|
127
|
+
const rawCode = i < src.partCode.length ? src.partCode[i] : '';
|
|
128
|
+
ticks.push({
|
|
129
|
+
sn: src.sn + i,
|
|
130
|
+
time: currentTime,
|
|
131
|
+
price: (src.priceBase + src.price[i]) / denominator,
|
|
132
|
+
volume: src.volume[i],
|
|
133
|
+
tickType: src.type.length > i ? src.type[i] : '',
|
|
134
|
+
cond: getTradeCond(isUs, condCh),
|
|
135
|
+
partCode: getPartShortNameByCode(rawCode),
|
|
136
|
+
partName: getPartNameByCode(rawCode),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return { symbol: src.symbol, secType: src.secType, quoteLevel: src.quoteLevel, timestamp: src.timestamp, ticks };
|
|
140
|
+
}
|
|
141
|
+
function convertFutureTick(src) {
|
|
142
|
+
const denominator = Math.pow(10, src.priceOffset);
|
|
143
|
+
const ticks = [];
|
|
144
|
+
let currentTime = 0;
|
|
145
|
+
let startSn = src.sn;
|
|
146
|
+
for (let i = 0; i < src.time.length; i++) {
|
|
147
|
+
currentTime += src.time[i];
|
|
148
|
+
const curPrice = (src.priceBase + src.price[i]) / denominator;
|
|
149
|
+
const mv = src.mergedVols[i];
|
|
150
|
+
for (let j = 0; j < mv.vol.length; j++) {
|
|
151
|
+
ticks.push({
|
|
152
|
+
sn: startSn * 10 + j,
|
|
153
|
+
time: currentTime,
|
|
154
|
+
price: curPrice,
|
|
155
|
+
volume: mv.vol[j],
|
|
156
|
+
tickType: '',
|
|
157
|
+
cond: '',
|
|
158
|
+
partCode: '',
|
|
159
|
+
partName: '',
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
startSn++;
|
|
163
|
+
}
|
|
164
|
+
return { symbol: src.symbol, secType: src.secType, quoteLevel: '', timestamp: src.timestamp, ticks };
|
|
165
|
+
}
|