hft-js 0.3.0 → 0.4.1
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/lib/bar.js +1 -1
- package/lib/broker.d.ts +3 -2
- package/lib/broker.js +9 -17
- package/lib/broker.js.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.test.js +8 -2
- package/lib/index.test.js.map +1 -1
- package/lib/interfaces.d.ts +5 -5
- package/lib/interfaces.js +1 -1
- package/lib/market.d.ts +6 -4
- package/lib/market.js +15 -9
- package/lib/market.js.map +1 -1
- package/lib/provider.d.ts +2 -2
- package/lib/provider.js +3 -3
- package/lib/provider.js.map +1 -1
- package/lib/tape.js +1 -1
- package/lib/trader.d.ts +13 -3
- package/lib/trader.js +48 -17
- package/lib/trader.js.map +1 -1
- package/lib/typedef.js +1 -1
- package/lib/utils.js +1 -1
- package/package.json +1 -1
- package/src/bar.ts +1 -1
- package/src/broker.ts +11 -21
- package/src/index.test.ts +10 -2
- package/src/index.ts +1 -1
- package/src/interfaces.ts +10 -6
- package/src/market.ts +23 -11
- package/src/provider.ts +4 -4
- package/src/tape.ts +1 -1
- package/src/trader.ts +78 -17
- package/src/typedef.ts +1 -1
- package/src/utils.ts +1 -1
package/src/market.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* market.ts
|
|
3
3
|
*
|
|
4
|
-
* Copyright (c) 2025 Xiongfei Shi
|
|
4
|
+
* Copyright (c) 2025-2026 Xiongfei Shi
|
|
5
5
|
*
|
|
6
6
|
* Author: Xiongfei Shi <xiongfei.shi(a)icloud.com>
|
|
7
7
|
* License: Apache-2.0
|
|
@@ -20,6 +20,7 @@ import type { InstrumentData, OrderBook, TickData } from "./typedef.js";
|
|
|
20
20
|
import { isValidPrice, isValidVolume, parseSymbol } from "./utils.js";
|
|
21
21
|
import { calcTapeData } from "./tape.js";
|
|
22
22
|
import type {
|
|
23
|
+
IErrorReceiver,
|
|
23
24
|
ILifecycleListener,
|
|
24
25
|
IMarketProvider,
|
|
25
26
|
IMarketRecorderProvider,
|
|
@@ -28,6 +29,8 @@ import type {
|
|
|
28
29
|
ITickReceiver,
|
|
29
30
|
} from "./interfaces.js";
|
|
30
31
|
|
|
32
|
+
type TickDataRaw = { tick: TickData; raw: DepthMarketDataField };
|
|
33
|
+
|
|
31
34
|
export interface IMarketListener {
|
|
32
35
|
onSubscribed: (symbol: string) => void;
|
|
33
36
|
onUnsubscribed: (symbol: string) => void;
|
|
@@ -48,7 +51,7 @@ export class Market
|
|
|
48
51
|
private readonly listener?: IMarketListener;
|
|
49
52
|
private readonly recordings: Set<string>;
|
|
50
53
|
private readonly symbols: Map<string, string>;
|
|
51
|
-
private readonly lastTicks: Map<string,
|
|
54
|
+
private readonly lastTicks: Map<string, TickDataRaw>;
|
|
52
55
|
private readonly subscribers: Map<string, ITickReceiver[]>;
|
|
53
56
|
|
|
54
57
|
constructor(
|
|
@@ -68,7 +71,11 @@ export class Market
|
|
|
68
71
|
}
|
|
69
72
|
}
|
|
70
73
|
|
|
71
|
-
|
|
74
|
+
getDepthMarketDataRaw() {
|
|
75
|
+
return Array.from(this.lastTicks.values().map((lastTick) => lastTick.raw));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
getRecorderProvider() {
|
|
72
79
|
return this;
|
|
73
80
|
}
|
|
74
81
|
|
|
@@ -85,10 +92,11 @@ export class Market
|
|
|
85
92
|
}
|
|
86
93
|
|
|
87
94
|
getLastTick(instrumentId: string) {
|
|
88
|
-
|
|
95
|
+
const lastTick = this.lastTicks.get(instrumentId);
|
|
96
|
+
return lastTick ? lastTick.tick : undefined;
|
|
89
97
|
}
|
|
90
98
|
|
|
91
|
-
open(lifecycle: ILifecycleListener) {
|
|
99
|
+
open(lifecycle: ILifecycleListener, errorReceiver: IErrorReceiver) {
|
|
92
100
|
if (this.marketApi) {
|
|
93
101
|
return true;
|
|
94
102
|
}
|
|
@@ -104,7 +112,7 @@ export class Market
|
|
|
104
112
|
this.marketApi.on<RspUserLoginField>(
|
|
105
113
|
ctp.MarketDataEvent.RspUserLogin,
|
|
106
114
|
(_, options) => {
|
|
107
|
-
if (this._isErrorResp(
|
|
115
|
+
if (this._isErrorResp(errorReceiver, options, "login-error")) {
|
|
108
116
|
return;
|
|
109
117
|
}
|
|
110
118
|
|
|
@@ -286,13 +294,13 @@ export class Market
|
|
|
286
294
|
orderBook: Object.freeze(orderBook),
|
|
287
295
|
});
|
|
288
296
|
|
|
289
|
-
|
|
290
|
-
const receivers = this.subscribers.get(instrumentId);
|
|
297
|
+
this.lastTicks.set(instrumentId, { tick, raw: depthMarketData });
|
|
291
298
|
|
|
292
|
-
this.
|
|
299
|
+
const receivers = this.subscribers.get(instrumentId);
|
|
293
300
|
|
|
294
301
|
if (receivers && receivers.length > 0) {
|
|
295
|
-
const
|
|
302
|
+
const lastTick = this.lastTicks.get(instrumentId);
|
|
303
|
+
const tape = calcTapeData(tick, lastTick ? lastTick.tick : undefined);
|
|
296
304
|
receivers.forEach((receiver) => receiver.onTick(tick, tape));
|
|
297
305
|
}
|
|
298
306
|
},
|
|
@@ -301,7 +309,7 @@ export class Market
|
|
|
301
309
|
return true;
|
|
302
310
|
}
|
|
303
311
|
|
|
304
|
-
close(lifecycle: ILifecycleListener) {
|
|
312
|
+
close(lifecycle: ILifecycleListener, _errorReceiver: IErrorReceiver) {
|
|
305
313
|
if (!this.marketApi) {
|
|
306
314
|
return;
|
|
307
315
|
}
|
|
@@ -340,6 +348,8 @@ export class Market
|
|
|
340
348
|
this.marketApi?.subscribeMarketData(Array.from(instrumentIds)),
|
|
341
349
|
);
|
|
342
350
|
}
|
|
351
|
+
|
|
352
|
+
this.recorder?.onOpen();
|
|
343
353
|
}
|
|
344
354
|
|
|
345
355
|
stopRecorder() {
|
|
@@ -363,6 +373,8 @@ export class Market
|
|
|
363
373
|
this.marketApi?.unsubscribeMarketData(Array.from(instrumentIds)),
|
|
364
374
|
);
|
|
365
375
|
}
|
|
376
|
+
|
|
377
|
+
this.recorder?.onClose();
|
|
366
378
|
}
|
|
367
379
|
|
|
368
380
|
subscribe(symbols: string[], receiver: ITickReceiver) {
|
package/src/provider.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* provider.ts
|
|
3
3
|
*
|
|
4
|
-
* Copyright (c) 2025 Xiongfei Shi
|
|
4
|
+
* Copyright (c) 2025-2026 Xiongfei Shi
|
|
5
5
|
*
|
|
6
6
|
* Author: Xiongfei Shi <xiongfei.shi(a)icloud.com>
|
|
7
7
|
* License: Apache-2.0
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import fs from "node:fs";
|
|
13
13
|
import ctp, { type CallbackOptions } from "napi-ctp";
|
|
14
|
-
import type { ErrorType,
|
|
14
|
+
import type { ErrorType, IErrorReceiver } from "./interfaces.js";
|
|
15
15
|
|
|
16
16
|
export class CTPProvider {
|
|
17
17
|
protected readonly flowPath: string;
|
|
@@ -52,7 +52,7 @@ export class CTPProvider {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
protected _isErrorResp(
|
|
55
|
-
|
|
55
|
+
errorReceiver: IErrorReceiver,
|
|
56
56
|
options: CallbackOptions,
|
|
57
57
|
error: ErrorType,
|
|
58
58
|
) {
|
|
@@ -60,7 +60,7 @@ export class CTPProvider {
|
|
|
60
60
|
return false;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
errorReceiver.onError(
|
|
64
64
|
error,
|
|
65
65
|
`${options.rspInfo.ErrorID}:${options.rspInfo.ErrorMsg}`,
|
|
66
66
|
);
|
package/src/tape.ts
CHANGED
package/src/trader.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* trader.ts
|
|
3
3
|
*
|
|
4
|
-
* Copyright (c) 2025 Xiongfei Shi
|
|
4
|
+
* Copyright (c) 2025-2026 Xiongfei Shi
|
|
5
5
|
*
|
|
6
6
|
* Author: Xiongfei Shi <xiongfei.shi(a)icloud.com>
|
|
7
7
|
* License: Apache-2.0
|
|
@@ -57,6 +57,7 @@ import type {
|
|
|
57
57
|
import type {
|
|
58
58
|
ICancelOrderResultReceiver,
|
|
59
59
|
ICommissionRateReceiver,
|
|
60
|
+
IErrorReceiver,
|
|
60
61
|
IInstrumentReceiver,
|
|
61
62
|
IInstrumentsReceiver,
|
|
62
63
|
ILifecycleListener,
|
|
@@ -122,6 +123,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
122
123
|
private readonly userInfo: CTPUserInfo;
|
|
123
124
|
private readonly receivers: IOrderReceiver[];
|
|
124
125
|
private readonly accounts: TradingAccountField[];
|
|
126
|
+
private readonly investorPositions: InvestorPositionField[];
|
|
125
127
|
private readonly positionDetails: InvestorPositionDetailField[];
|
|
126
128
|
private readonly instruments: Map<string, InstrumentField>;
|
|
127
129
|
private readonly positions: Map<string, PositionInfo>;
|
|
@@ -155,6 +157,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
155
157
|
this.userInfo = userInfo;
|
|
156
158
|
this.receivers = [];
|
|
157
159
|
this.accounts = [];
|
|
160
|
+
this.investorPositions = [];
|
|
158
161
|
this.positionDetails = [];
|
|
159
162
|
this.instruments = new Map();
|
|
160
163
|
this.positions = new Map();
|
|
@@ -177,7 +180,39 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
177
180
|
}
|
|
178
181
|
}
|
|
179
182
|
|
|
180
|
-
|
|
183
|
+
getTradingAccountsRaw() {
|
|
184
|
+
return this.accounts;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
getInvestorPositionsRaw() {
|
|
188
|
+
return this.investorPositions;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
getInvestorPositionDetailsRaw() {
|
|
192
|
+
return this.positionDetails;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
getInstrumentsRaw() {
|
|
196
|
+
return Array.from(this.instruments.values());
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
getOrdersRaw() {
|
|
200
|
+
return Array.from(this.orders.values());
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
getTradesRaw() {
|
|
204
|
+
return Array.from(this.trades.values());
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
getInstrumentMarginRatesRaw() {
|
|
208
|
+
return Array.from(this.marginRates.values());
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
getInstrumentCommissionRatesRaw() {
|
|
212
|
+
return Array.from(this.commRates.values());
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
open(lifecycle: ILifecycleListener, errorReceiver: IErrorReceiver) {
|
|
181
216
|
if (this.traderApi) {
|
|
182
217
|
return true;
|
|
183
218
|
}
|
|
@@ -197,7 +232,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
197
232
|
this.traderApi.on<RspAuthenticateField>(
|
|
198
233
|
ctp.TraderEvent.RspAuthenticate,
|
|
199
234
|
(_, options) => {
|
|
200
|
-
if (this._isErrorResp(
|
|
235
|
+
if (this._isErrorResp(errorReceiver, options, "login-error")) {
|
|
201
236
|
return;
|
|
202
237
|
}
|
|
203
238
|
|
|
@@ -208,7 +243,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
208
243
|
this.traderApi.on<RspUserLoginField>(
|
|
209
244
|
ctp.TraderEvent.RspUserLogin,
|
|
210
245
|
(rspUserLogin, options) => {
|
|
211
|
-
if (this._isErrorResp(
|
|
246
|
+
if (this._isErrorResp(errorReceiver, options, "login-error")) {
|
|
212
247
|
return;
|
|
213
248
|
}
|
|
214
249
|
|
|
@@ -235,7 +270,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
235
270
|
this.traderApi.on<SettlementInfoConfirmField>(
|
|
236
271
|
ctp.TraderEvent.RspSettlementInfoConfirm,
|
|
237
272
|
(_, options) => {
|
|
238
|
-
if (this._isErrorResp(
|
|
273
|
+
if (this._isErrorResp(errorReceiver, options, "login-error")) {
|
|
239
274
|
return;
|
|
240
275
|
}
|
|
241
276
|
|
|
@@ -247,7 +282,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
247
282
|
this.traderApi.on<OrderField>(
|
|
248
283
|
ctp.TraderEvent.RspQryOrder,
|
|
249
284
|
(order, options) => {
|
|
250
|
-
if (this._isErrorResp(
|
|
285
|
+
if (this._isErrorResp(errorReceiver, options, "query-order-error")) {
|
|
251
286
|
return;
|
|
252
287
|
}
|
|
253
288
|
|
|
@@ -266,7 +301,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
266
301
|
this.traderApi.on<TradeField>(
|
|
267
302
|
ctp.TraderEvent.RspQryTrade,
|
|
268
303
|
(trade, options) => {
|
|
269
|
-
if (this._isErrorResp(
|
|
304
|
+
if (this._isErrorResp(errorReceiver, options, "query-trade-error")) {
|
|
270
305
|
return;
|
|
271
306
|
}
|
|
272
307
|
|
|
@@ -291,7 +326,9 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
291
326
|
this.traderApi.on<InstrumentField>(
|
|
292
327
|
ctp.TraderEvent.RspQryInstrument,
|
|
293
328
|
(instrument, options) => {
|
|
294
|
-
if (
|
|
329
|
+
if (
|
|
330
|
+
this._isErrorResp(errorReceiver, options, "query-instrument-error")
|
|
331
|
+
) {
|
|
295
332
|
return;
|
|
296
333
|
}
|
|
297
334
|
|
|
@@ -306,6 +343,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
306
343
|
|
|
307
344
|
if (options.isLast) {
|
|
308
345
|
this.positions.clear();
|
|
346
|
+
this.investorPositions.splice(0, this.investorPositions.length);
|
|
309
347
|
|
|
310
348
|
this._withRetry(() =>
|
|
311
349
|
this.traderApi!.reqQryInvestorPosition(this.userInfo),
|
|
@@ -319,7 +357,9 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
319
357
|
this.traderApi.on<InvestorPositionField>(
|
|
320
358
|
ctp.TraderEvent.RspQryInvestorPosition,
|
|
321
359
|
(position, options) => {
|
|
322
|
-
if (
|
|
360
|
+
if (
|
|
361
|
+
this._isErrorResp(errorReceiver, options, "query-positions-error")
|
|
362
|
+
) {
|
|
323
363
|
return;
|
|
324
364
|
}
|
|
325
365
|
|
|
@@ -358,6 +398,8 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
358
398
|
break;
|
|
359
399
|
}
|
|
360
400
|
}
|
|
401
|
+
|
|
402
|
+
this.investorPositions.push(position);
|
|
361
403
|
}
|
|
362
404
|
|
|
363
405
|
if (options.isLast) {
|
|
@@ -367,12 +409,16 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
367
409
|
}
|
|
368
410
|
|
|
369
411
|
if (this.accountsQueue.size() > 0) {
|
|
412
|
+
this.accounts.splice(0, this.accounts.length);
|
|
413
|
+
|
|
370
414
|
this._withRetry(() =>
|
|
371
415
|
this.traderApi!.reqQryTradingAccount(this.userInfo),
|
|
372
416
|
);
|
|
373
417
|
}
|
|
374
418
|
|
|
375
419
|
if (this.positionDetailsQueue.size() > 0) {
|
|
420
|
+
this.positionDetails.splice(0, this.positionDetails.length);
|
|
421
|
+
|
|
376
422
|
this._withRetry(() =>
|
|
377
423
|
this.traderApi!.reqQryInvestorPositionDetail(this.userInfo),
|
|
378
424
|
);
|
|
@@ -530,7 +576,9 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
530
576
|
(marginRate, options) => {
|
|
531
577
|
const query = this.marginRatesQueue.shift();
|
|
532
578
|
|
|
533
|
-
if (
|
|
579
|
+
if (
|
|
580
|
+
this._isErrorResp(errorReceiver, options, "query-margin-rate-error")
|
|
581
|
+
) {
|
|
534
582
|
if (query) {
|
|
535
583
|
query.receiver.onMarginRate(undefined);
|
|
536
584
|
}
|
|
@@ -558,7 +606,11 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
558
606
|
const query = this.commRatesQueue.shift();
|
|
559
607
|
|
|
560
608
|
if (
|
|
561
|
-
this._isErrorResp(
|
|
609
|
+
this._isErrorResp(
|
|
610
|
+
errorReceiver,
|
|
611
|
+
options,
|
|
612
|
+
"query-commission-rate-error",
|
|
613
|
+
)
|
|
562
614
|
) {
|
|
563
615
|
if (query) {
|
|
564
616
|
query.receiver.onCommissionRate(undefined);
|
|
@@ -584,7 +636,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
584
636
|
this.traderApi.on<TradingAccountField>(
|
|
585
637
|
ctp.TraderEvent.RspQryTradingAccount,
|
|
586
638
|
(account, options) => {
|
|
587
|
-
if (this._isErrorResp(
|
|
639
|
+
if (this._isErrorResp(errorReceiver, options, "query-accounts-error")) {
|
|
588
640
|
const receivers = this.accountsQueue.toArray();
|
|
589
641
|
|
|
590
642
|
receivers.forEach((receiver) =>
|
|
@@ -615,7 +667,11 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
615
667
|
ctp.TraderEvent.RspQryInvestorPositionDetail,
|
|
616
668
|
(positionDetail, options) => {
|
|
617
669
|
if (
|
|
618
|
-
this._isErrorResp(
|
|
670
|
+
this._isErrorResp(
|
|
671
|
+
errorReceiver,
|
|
672
|
+
options,
|
|
673
|
+
"query-position-details-error",
|
|
674
|
+
)
|
|
619
675
|
) {
|
|
620
676
|
const receivers = this.positionDetailsQueue.toArray();
|
|
621
677
|
|
|
@@ -686,7 +742,11 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
686
742
|
ctp.TraderEvent.RspQryDepthMarketData,
|
|
687
743
|
(depthMarketData, options) => {
|
|
688
744
|
if (
|
|
689
|
-
this._isErrorResp(
|
|
745
|
+
this._isErrorResp(
|
|
746
|
+
errorReceiver,
|
|
747
|
+
options,
|
|
748
|
+
"query-depth-market-data-error",
|
|
749
|
+
)
|
|
690
750
|
) {
|
|
691
751
|
this._clearAllMarketOrders();
|
|
692
752
|
return;
|
|
@@ -754,7 +814,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
754
814
|
return true;
|
|
755
815
|
}
|
|
756
816
|
|
|
757
|
-
close(lifecycle: ILifecycleListener) {
|
|
817
|
+
close(lifecycle: ILifecycleListener, _errorReceiver: IErrorReceiver) {
|
|
758
818
|
if (!this.traderApi) {
|
|
759
819
|
return;
|
|
760
820
|
}
|
|
@@ -959,10 +1019,11 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
959
1019
|
}
|
|
960
1020
|
|
|
961
1021
|
this.positionDetailsQueue.push(receiver);
|
|
962
|
-
this.
|
|
1022
|
+
this.investorPositions.splice(0, this.investorPositions.length);
|
|
1023
|
+
this.positions.clear();
|
|
963
1024
|
|
|
964
1025
|
this._withRetry(() =>
|
|
965
|
-
this.traderApi
|
|
1026
|
+
this.traderApi!.reqQryInvestorPosition(this.userInfo),
|
|
966
1027
|
);
|
|
967
1028
|
}
|
|
968
1029
|
|
package/src/typedef.ts
CHANGED