hft-js 0.2.0 → 0.4.0
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.d.ts +2 -2
- package/lib/bar.js +43 -67
- package/lib/bar.js.map +1 -1
- package/lib/broker.d.ts +5 -4
- package/lib/broker.js +92 -106
- package/lib/broker.js.map +1 -1
- package/lib/index.js +9 -25
- package/lib/index.js.map +1 -1
- package/lib/index.test.d.ts +1 -1
- package/lib/index.test.js +82 -111
- package/lib/index.test.js.map +1 -1
- package/lib/interfaces.d.ts +5 -5
- package/lib/interfaces.js +2 -3
- package/lib/interfaces.js.map +1 -1
- package/lib/market.d.ts +4 -4
- package/lib/market.js +135 -163
- package/lib/market.js.map +1 -1
- package/lib/provider.d.ts +3 -3
- package/lib/provider.js +34 -86
- package/lib/provider.js.map +1 -1
- package/lib/tape.d.ts +1 -1
- package/lib/tape.js +19 -24
- package/lib/tape.js.map +1 -1
- package/lib/trader.d.ts +5 -4
- package/lib/trader.js +501 -510
- package/lib/trader.js.map +1 -1
- package/lib/typedef.d.ts +5 -1
- package/lib/typedef.js +2 -3
- package/lib/typedef.js.map +1 -1
- package/lib/utils.d.ts +2 -2
- package/lib/utils.js +28 -78
- package/lib/utils.js.map +1 -1
- package/package.json +9 -8
- package/src/bar.ts +7 -13
- package/src/broker.ts +11 -21
- package/src/index.test.ts +29 -21
- package/src/index.ts +1 -1
- package/src/interfaces.ts +10 -6
- package/src/market.ts +25 -16
- package/src/provider.ts +8 -8
- package/src/tape.ts +2 -2
- package/src/trader.ts +127 -60
- package/src/typedef.ts +6 -2
- package/src/utils.ts +25 -35
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
|
|
@@ -10,14 +10,36 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import Denque from "denque";
|
|
13
|
-
import ctp from "napi-ctp";
|
|
13
|
+
import ctp, { type Trader as TraderApi } from "napi-ctp";
|
|
14
|
+
import type {
|
|
15
|
+
DepthMarketDataField,
|
|
16
|
+
DirectionType,
|
|
17
|
+
InputOrderActionField,
|
|
18
|
+
InputOrderField,
|
|
19
|
+
InstrumentCommissionRateField,
|
|
20
|
+
InstrumentField,
|
|
21
|
+
InstrumentMarginRateField,
|
|
22
|
+
InvestorPositionDetailField,
|
|
23
|
+
InvestorPositionField,
|
|
24
|
+
OffsetFlagType,
|
|
25
|
+
OptionsTypeType,
|
|
26
|
+
OrderField,
|
|
27
|
+
OrderPriceTypeType,
|
|
28
|
+
ProductClassType,
|
|
29
|
+
RspAuthenticateField,
|
|
30
|
+
RspUserLoginField,
|
|
31
|
+
SettlementInfoConfirmField,
|
|
32
|
+
TradeField,
|
|
33
|
+
TradingAccountField,
|
|
34
|
+
} from "@napi-ctp/types";
|
|
14
35
|
import { CTPProvider } from "./provider.js";
|
|
15
36
|
import { isValidPrice, parseSymbol } from "./utils.js";
|
|
16
|
-
import {
|
|
37
|
+
import type {
|
|
17
38
|
CommissionRate,
|
|
18
39
|
InstrumentData,
|
|
19
40
|
MarginRate,
|
|
20
41
|
OffsetType,
|
|
42
|
+
OptionsType,
|
|
21
43
|
OrderData,
|
|
22
44
|
OrderFlag,
|
|
23
45
|
OrderStatistic,
|
|
@@ -32,9 +54,10 @@ import {
|
|
|
32
54
|
TradingAccount,
|
|
33
55
|
Writeable,
|
|
34
56
|
} from "./typedef.js";
|
|
35
|
-
import {
|
|
57
|
+
import type {
|
|
36
58
|
ICancelOrderResultReceiver,
|
|
37
59
|
ICommissionRateReceiver,
|
|
60
|
+
IErrorReceiver,
|
|
38
61
|
IInstrumentReceiver,
|
|
39
62
|
IInstrumentsReceiver,
|
|
40
63
|
ILifecycleListener,
|
|
@@ -89,7 +112,7 @@ export type TraderOptions = {
|
|
|
89
112
|
};
|
|
90
113
|
|
|
91
114
|
export class Trader extends CTPProvider implements ITraderProvider {
|
|
92
|
-
private traderApi?:
|
|
115
|
+
private traderApi?: TraderApi;
|
|
93
116
|
private tradingDay: number;
|
|
94
117
|
private frontId: number;
|
|
95
118
|
private sessionId: number;
|
|
@@ -99,14 +122,14 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
99
122
|
private readonly fastQueryLastTick?: FastQueryLastTickFunc;
|
|
100
123
|
private readonly userInfo: CTPUserInfo;
|
|
101
124
|
private readonly receivers: IOrderReceiver[];
|
|
102
|
-
private readonly accounts:
|
|
103
|
-
private readonly positionDetails:
|
|
104
|
-
private readonly instruments: Map<string,
|
|
125
|
+
private readonly accounts: TradingAccountField[];
|
|
126
|
+
private readonly positionDetails: InvestorPositionDetailField[];
|
|
127
|
+
private readonly instruments: Map<string, InstrumentField>;
|
|
105
128
|
private readonly positions: Map<string, PositionInfo>;
|
|
106
|
-
private readonly orders: Map<string,
|
|
107
|
-
private readonly trades: Map<string,
|
|
108
|
-
private readonly marginRates: Map<string,
|
|
109
|
-
private readonly commRates: Map<string,
|
|
129
|
+
private readonly orders: Map<string, OrderField>;
|
|
130
|
+
private readonly trades: Map<string, TradeField[]>;
|
|
131
|
+
private readonly marginRates: Map<string, InstrumentMarginRateField>;
|
|
132
|
+
private readonly commRates: Map<string, InstrumentCommissionRateField>;
|
|
110
133
|
private readonly placeOrders: Map<number, IPlaceOrderResultReceiver>;
|
|
111
134
|
private readonly cancelOrders: Map<number, ICancelOrderResultReceiver>;
|
|
112
135
|
private readonly marketOrdersQueue: Map<string, Denque<MarketOrder>>;
|
|
@@ -155,7 +178,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
155
178
|
}
|
|
156
179
|
}
|
|
157
180
|
|
|
158
|
-
open(lifecycle: ILifecycleListener) {
|
|
181
|
+
open(lifecycle: ILifecycleListener, errorReceiver: IErrorReceiver) {
|
|
159
182
|
if (this.traderApi) {
|
|
160
183
|
return true;
|
|
161
184
|
}
|
|
@@ -172,10 +195,10 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
172
195
|
this.cancelOrders.clear();
|
|
173
196
|
});
|
|
174
197
|
|
|
175
|
-
this.traderApi.on<
|
|
198
|
+
this.traderApi.on<RspAuthenticateField>(
|
|
176
199
|
ctp.TraderEvent.RspAuthenticate,
|
|
177
200
|
(_, options) => {
|
|
178
|
-
if (this._isErrorResp(
|
|
201
|
+
if (this._isErrorResp(errorReceiver, options, "login-error")) {
|
|
179
202
|
return;
|
|
180
203
|
}
|
|
181
204
|
|
|
@@ -183,10 +206,10 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
183
206
|
},
|
|
184
207
|
);
|
|
185
208
|
|
|
186
|
-
this.traderApi.on<
|
|
209
|
+
this.traderApi.on<RspUserLoginField>(
|
|
187
210
|
ctp.TraderEvent.RspUserLogin,
|
|
188
211
|
(rspUserLogin, options) => {
|
|
189
|
-
if (this._isErrorResp(
|
|
212
|
+
if (this._isErrorResp(errorReceiver, options, "login-error")) {
|
|
190
213
|
return;
|
|
191
214
|
}
|
|
192
215
|
|
|
@@ -210,10 +233,10 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
210
233
|
},
|
|
211
234
|
);
|
|
212
235
|
|
|
213
|
-
this.traderApi.on<
|
|
236
|
+
this.traderApi.on<SettlementInfoConfirmField>(
|
|
214
237
|
ctp.TraderEvent.RspSettlementInfoConfirm,
|
|
215
238
|
(_, options) => {
|
|
216
|
-
if (this._isErrorResp(
|
|
239
|
+
if (this._isErrorResp(errorReceiver, options, "login-error")) {
|
|
217
240
|
return;
|
|
218
241
|
}
|
|
219
242
|
|
|
@@ -222,10 +245,10 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
222
245
|
},
|
|
223
246
|
);
|
|
224
247
|
|
|
225
|
-
this.traderApi.on<
|
|
248
|
+
this.traderApi.on<OrderField>(
|
|
226
249
|
ctp.TraderEvent.RspQryOrder,
|
|
227
250
|
(order, options) => {
|
|
228
|
-
if (this._isErrorResp(
|
|
251
|
+
if (this._isErrorResp(errorReceiver, options, "query-order-error")) {
|
|
229
252
|
return;
|
|
230
253
|
}
|
|
231
254
|
|
|
@@ -241,10 +264,10 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
241
264
|
},
|
|
242
265
|
);
|
|
243
266
|
|
|
244
|
-
this.traderApi.on<
|
|
267
|
+
this.traderApi.on<TradeField>(
|
|
245
268
|
ctp.TraderEvent.RspQryTrade,
|
|
246
269
|
(trade, options) => {
|
|
247
|
-
if (this._isErrorResp(
|
|
270
|
+
if (this._isErrorResp(errorReceiver, options, "query-trade-error")) {
|
|
248
271
|
return;
|
|
249
272
|
}
|
|
250
273
|
|
|
@@ -266,10 +289,12 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
266
289
|
},
|
|
267
290
|
);
|
|
268
291
|
|
|
269
|
-
this.traderApi.on<
|
|
292
|
+
this.traderApi.on<InstrumentField>(
|
|
270
293
|
ctp.TraderEvent.RspQryInstrument,
|
|
271
294
|
(instrument, options) => {
|
|
272
|
-
if (
|
|
295
|
+
if (
|
|
296
|
+
this._isErrorResp(errorReceiver, options, "query-instrument-error")
|
|
297
|
+
) {
|
|
273
298
|
return;
|
|
274
299
|
}
|
|
275
300
|
|
|
@@ -294,10 +319,12 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
294
319
|
|
|
295
320
|
let fired = false;
|
|
296
321
|
|
|
297
|
-
this.traderApi.on<
|
|
322
|
+
this.traderApi.on<InvestorPositionField>(
|
|
298
323
|
ctp.TraderEvent.RspQryInvestorPosition,
|
|
299
324
|
(position, options) => {
|
|
300
|
-
if (
|
|
325
|
+
if (
|
|
326
|
+
this._isErrorResp(errorReceiver, options, "query-positions-error")
|
|
327
|
+
) {
|
|
301
328
|
return;
|
|
302
329
|
}
|
|
303
330
|
|
|
@@ -362,7 +389,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
362
389
|
},
|
|
363
390
|
);
|
|
364
391
|
|
|
365
|
-
this.traderApi.on<
|
|
392
|
+
this.traderApi.on<OrderField>(ctp.TraderEvent.RtnOrder, (order) => {
|
|
366
393
|
const orderId = this._calcOrderId(order);
|
|
367
394
|
const current = this.orders.get(orderId);
|
|
368
395
|
|
|
@@ -469,7 +496,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
469
496
|
}
|
|
470
497
|
});
|
|
471
498
|
|
|
472
|
-
this.traderApi.on<
|
|
499
|
+
this.traderApi.on<TradeField>(ctp.TraderEvent.RtnTrade, (trade) => {
|
|
473
500
|
const orderId = this._calcOrderId(trade);
|
|
474
501
|
const trades = this.trades.get(orderId);
|
|
475
502
|
|
|
@@ -503,12 +530,14 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
503
530
|
}
|
|
504
531
|
});
|
|
505
532
|
|
|
506
|
-
this.traderApi.on<
|
|
533
|
+
this.traderApi.on<InstrumentMarginRateField>(
|
|
507
534
|
ctp.TraderEvent.RspQryInstrumentMarginRate,
|
|
508
535
|
(marginRate, options) => {
|
|
509
536
|
const query = this.marginRatesQueue.shift();
|
|
510
537
|
|
|
511
|
-
if (
|
|
538
|
+
if (
|
|
539
|
+
this._isErrorResp(errorReceiver, options, "query-margin-rate-error")
|
|
540
|
+
) {
|
|
512
541
|
if (query) {
|
|
513
542
|
query.receiver.onMarginRate(undefined);
|
|
514
543
|
}
|
|
@@ -530,13 +559,17 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
530
559
|
},
|
|
531
560
|
);
|
|
532
561
|
|
|
533
|
-
this.traderApi.on<
|
|
562
|
+
this.traderApi.on<InstrumentCommissionRateField>(
|
|
534
563
|
ctp.TraderEvent.RspQryInstrumentCommissionRate,
|
|
535
564
|
(commRate, options) => {
|
|
536
565
|
const query = this.commRatesQueue.shift();
|
|
537
566
|
|
|
538
567
|
if (
|
|
539
|
-
this._isErrorResp(
|
|
568
|
+
this._isErrorResp(
|
|
569
|
+
errorReceiver,
|
|
570
|
+
options,
|
|
571
|
+
"query-commission-rate-error",
|
|
572
|
+
)
|
|
540
573
|
) {
|
|
541
574
|
if (query) {
|
|
542
575
|
query.receiver.onCommissionRate(undefined);
|
|
@@ -559,10 +592,10 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
559
592
|
},
|
|
560
593
|
);
|
|
561
594
|
|
|
562
|
-
this.traderApi.on<
|
|
595
|
+
this.traderApi.on<TradingAccountField>(
|
|
563
596
|
ctp.TraderEvent.RspQryTradingAccount,
|
|
564
597
|
(account, options) => {
|
|
565
|
-
if (this._isErrorResp(
|
|
598
|
+
if (this._isErrorResp(errorReceiver, options, "query-accounts-error")) {
|
|
566
599
|
const receivers = this.accountsQueue.toArray();
|
|
567
600
|
|
|
568
601
|
receivers.forEach((receiver) =>
|
|
@@ -589,11 +622,15 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
589
622
|
},
|
|
590
623
|
);
|
|
591
624
|
|
|
592
|
-
this.traderApi.on<
|
|
625
|
+
this.traderApi.on<InvestorPositionDetailField>(
|
|
593
626
|
ctp.TraderEvent.RspQryInvestorPositionDetail,
|
|
594
627
|
(positionDetail, options) => {
|
|
595
628
|
if (
|
|
596
|
-
this._isErrorResp(
|
|
629
|
+
this._isErrorResp(
|
|
630
|
+
errorReceiver,
|
|
631
|
+
options,
|
|
632
|
+
"query-position-details-error",
|
|
633
|
+
)
|
|
597
634
|
) {
|
|
598
635
|
const receivers = this.positionDetailsQueue.toArray();
|
|
599
636
|
|
|
@@ -626,7 +663,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
626
663
|
},
|
|
627
664
|
);
|
|
628
665
|
|
|
629
|
-
this.traderApi.on<
|
|
666
|
+
this.traderApi.on<InputOrderField>(
|
|
630
667
|
ctp.TraderEvent.RspOrderInsert,
|
|
631
668
|
(order, options) => {
|
|
632
669
|
if (options.rspInfo && order && options.requestId && options.isLast) {
|
|
@@ -643,7 +680,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
643
680
|
},
|
|
644
681
|
);
|
|
645
682
|
|
|
646
|
-
this.traderApi.on<
|
|
683
|
+
this.traderApi.on<InputOrderActionField>(
|
|
647
684
|
ctp.TraderEvent.RspOrderAction,
|
|
648
685
|
(order, options) => {
|
|
649
686
|
if (options.rspInfo && order && options.requestId && options.isLast) {
|
|
@@ -660,11 +697,15 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
660
697
|
},
|
|
661
698
|
);
|
|
662
699
|
|
|
663
|
-
this.traderApi.on<
|
|
700
|
+
this.traderApi.on<DepthMarketDataField>(
|
|
664
701
|
ctp.TraderEvent.RspQryDepthMarketData,
|
|
665
702
|
(depthMarketData, options) => {
|
|
666
703
|
if (
|
|
667
|
-
this._isErrorResp(
|
|
704
|
+
this._isErrorResp(
|
|
705
|
+
errorReceiver,
|
|
706
|
+
options,
|
|
707
|
+
"query-depth-market-data-error",
|
|
708
|
+
)
|
|
668
709
|
) {
|
|
669
710
|
this._clearAllMarketOrders();
|
|
670
711
|
return;
|
|
@@ -732,7 +773,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
732
773
|
return true;
|
|
733
774
|
}
|
|
734
775
|
|
|
735
|
-
close(lifecycle: ILifecycleListener) {
|
|
776
|
+
close(lifecycle: ILifecycleListener, _errorReceiver: IErrorReceiver) {
|
|
736
777
|
if (!this.traderApi) {
|
|
737
778
|
return;
|
|
738
779
|
}
|
|
@@ -1179,6 +1220,11 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
1179
1220
|
flag: OrderFlag,
|
|
1180
1221
|
receiver: IPlaceOrderResultReceiver,
|
|
1181
1222
|
) {
|
|
1223
|
+
if (volume <= 0) {
|
|
1224
|
+
receiver.onPlaceOrderError("Invalid Volume");
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1182
1228
|
switch (flag) {
|
|
1183
1229
|
case "limit":
|
|
1184
1230
|
return this._placeLimitOrder(
|
|
@@ -1241,19 +1287,16 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
1241
1287
|
return `${instrument.InstrumentID}.${instrument.ExchangeID}`;
|
|
1242
1288
|
}
|
|
1243
1289
|
|
|
1244
|
-
private _calcOrderId(orderOrTrade:
|
|
1290
|
+
private _calcOrderId(orderOrTrade: OrderField | TradeField) {
|
|
1245
1291
|
const { ExchangeID, TraderID, OrderLocalID } = orderOrTrade;
|
|
1246
1292
|
return `${ExchangeID}:${TraderID}:${OrderLocalID}`;
|
|
1247
1293
|
}
|
|
1248
1294
|
|
|
1249
|
-
private _calcReceiptId(order:
|
|
1295
|
+
private _calcReceiptId(order: OrderField | InputOrderActionField) {
|
|
1250
1296
|
return `${order.FrontID}:${order.SessionID}:${parseInt(order.OrderRef)}`;
|
|
1251
1297
|
}
|
|
1252
1298
|
|
|
1253
|
-
private _calcOrderStatus(
|
|
1254
|
-
order: ctp.OrderField,
|
|
1255
|
-
traded?: number,
|
|
1256
|
-
): OrderStatus {
|
|
1299
|
+
private _calcOrderStatus(order: OrderField, traded?: number): OrderStatus {
|
|
1257
1300
|
switch (order.OrderStatus) {
|
|
1258
1301
|
case ctp.OrderStatusType.Unknown:
|
|
1259
1302
|
return "submitted";
|
|
@@ -1279,7 +1322,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
1279
1322
|
}
|
|
1280
1323
|
}
|
|
1281
1324
|
|
|
1282
|
-
private _calcOrderFlag(orderPriceType:
|
|
1325
|
+
private _calcOrderFlag(orderPriceType: OrderPriceTypeType): OrderFlag {
|
|
1283
1326
|
switch (orderPriceType) {
|
|
1284
1327
|
case ctp.OrderPriceTypeType.LimitPrice:
|
|
1285
1328
|
return "limit";
|
|
@@ -1289,7 +1332,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
1289
1332
|
}
|
|
1290
1333
|
}
|
|
1291
1334
|
|
|
1292
|
-
private _calcSideType(direction:
|
|
1335
|
+
private _calcSideType(direction: DirectionType): SideType {
|
|
1293
1336
|
switch (direction) {
|
|
1294
1337
|
case ctp.DirectionType.Buy:
|
|
1295
1338
|
return "long";
|
|
@@ -1309,7 +1352,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
1309
1352
|
}
|
|
1310
1353
|
}
|
|
1311
1354
|
|
|
1312
|
-
private _calcOffsetType(offset:
|
|
1355
|
+
private _calcOffsetType(offset: OffsetFlagType): OffsetType {
|
|
1313
1356
|
switch (offset) {
|
|
1314
1357
|
case ctp.OffsetFlagType.Open:
|
|
1315
1358
|
return "open";
|
|
@@ -1335,7 +1378,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
1335
1378
|
}
|
|
1336
1379
|
}
|
|
1337
1380
|
|
|
1338
|
-
private _calcProductType(productClass:
|
|
1381
|
+
private _calcProductType(productClass: ProductClassType): ProductType {
|
|
1339
1382
|
switch (productClass) {
|
|
1340
1383
|
case ctp.ProductClassType.Futures:
|
|
1341
1384
|
return "futures";
|
|
@@ -1343,11 +1386,32 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
1343
1386
|
case ctp.ProductClassType.Options:
|
|
1344
1387
|
return "options";
|
|
1345
1388
|
|
|
1389
|
+
case ctp.ProductClassType.Spot:
|
|
1390
|
+
return "spot";
|
|
1391
|
+
|
|
1392
|
+
case ctp.ProductClassType.SpotOption:
|
|
1393
|
+
return "spot-options";
|
|
1394
|
+
|
|
1346
1395
|
default:
|
|
1347
1396
|
throw new Error(`Unsupported product class: ${productClass}`);
|
|
1348
1397
|
}
|
|
1349
1398
|
}
|
|
1350
1399
|
|
|
1400
|
+
private _calcOptionsType(
|
|
1401
|
+
optionsType: OptionsTypeType,
|
|
1402
|
+
): OptionsType | undefined {
|
|
1403
|
+
switch (optionsType) {
|
|
1404
|
+
case ctp.OptionsTypeType.CallOptions:
|
|
1405
|
+
return "call";
|
|
1406
|
+
|
|
1407
|
+
case ctp.OptionsTypeType.PutOptions:
|
|
1408
|
+
return "put";
|
|
1409
|
+
|
|
1410
|
+
default:
|
|
1411
|
+
return undefined;
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1351
1415
|
private _ensurePositionInfo(symbol: string): PositionInfo {
|
|
1352
1416
|
let position = this.positions.get(symbol);
|
|
1353
1417
|
|
|
@@ -1671,7 +1735,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
1671
1735
|
}
|
|
1672
1736
|
}
|
|
1673
1737
|
|
|
1674
|
-
private _toTradeData(trade:
|
|
1738
|
+
private _toTradeData(trade: TradeField): TradeData {
|
|
1675
1739
|
return Object.freeze({
|
|
1676
1740
|
id: trade.TradeID,
|
|
1677
1741
|
date: parseInt(trade.TradeDate),
|
|
@@ -1681,7 +1745,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
1681
1745
|
});
|
|
1682
1746
|
}
|
|
1683
1747
|
|
|
1684
|
-
private _toOrderData(order:
|
|
1748
|
+
private _toOrderData(order: OrderField): OrderData {
|
|
1685
1749
|
const orderId = this._calcOrderId(order);
|
|
1686
1750
|
const trades = this.trades.get(orderId) ?? [];
|
|
1687
1751
|
|
|
@@ -1697,7 +1761,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
1697
1761
|
time: this._parseTime(order.InsertTime),
|
|
1698
1762
|
flag: this._calcOrderFlag(order.OrderPriceType),
|
|
1699
1763
|
side: this._calcSideType(order.Direction),
|
|
1700
|
-
offset: this._calcOffsetType(order.CombOffsetFlag as
|
|
1764
|
+
offset: this._calcOffsetType(order.CombOffsetFlag as OffsetFlagType),
|
|
1701
1765
|
price: order.LimitPrice,
|
|
1702
1766
|
volume: order.VolumeTotalOriginal,
|
|
1703
1767
|
traded: traded,
|
|
@@ -1708,7 +1772,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
1708
1772
|
});
|
|
1709
1773
|
}
|
|
1710
1774
|
|
|
1711
|
-
private _toInstrumentData(instrument:
|
|
1775
|
+
private _toInstrumentData(instrument: InstrumentField): InstrumentData {
|
|
1712
1776
|
return Object.freeze({
|
|
1713
1777
|
symbol: `${instrument.InstrumentID}.${instrument.ExchangeID}`,
|
|
1714
1778
|
id: instrument.InstrumentID,
|
|
@@ -1724,12 +1788,14 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
1724
1788
|
priceTick: instrument.PriceTick,
|
|
1725
1789
|
maxLimitOrderVolume: instrument.MaxLimitOrderVolume,
|
|
1726
1790
|
minLimitOrderVolume: instrument.MinLimitOrderVolume,
|
|
1791
|
+
strikePrice: instrument.StrikePrice,
|
|
1792
|
+
optionsType: this._calcOptionsType(instrument.OptionsType),
|
|
1727
1793
|
});
|
|
1728
1794
|
}
|
|
1729
1795
|
|
|
1730
1796
|
private _toCommissionRate(
|
|
1731
1797
|
symbol: string,
|
|
1732
|
-
commRate:
|
|
1798
|
+
commRate: InstrumentCommissionRateField,
|
|
1733
1799
|
): CommissionRate {
|
|
1734
1800
|
return Object.freeze({
|
|
1735
1801
|
symbol: symbol,
|
|
@@ -1750,7 +1816,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
1750
1816
|
|
|
1751
1817
|
private _toMarginRate(
|
|
1752
1818
|
symbol: string,
|
|
1753
|
-
marginRate:
|
|
1819
|
+
marginRate: InstrumentMarginRateField,
|
|
1754
1820
|
): MarginRate {
|
|
1755
1821
|
return Object.freeze({
|
|
1756
1822
|
symbol: symbol,
|
|
@@ -1765,11 +1831,12 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
1765
1831
|
});
|
|
1766
1832
|
}
|
|
1767
1833
|
|
|
1768
|
-
private _toTradingAccount(account:
|
|
1834
|
+
private _toTradingAccount(account: TradingAccountField): TradingAccount {
|
|
1769
1835
|
return Object.freeze({
|
|
1770
1836
|
id: account.AccountID,
|
|
1771
1837
|
currency: account.CurrencyID,
|
|
1772
1838
|
preBalance: account.PreBalance - account.Withdraw + account.Deposit,
|
|
1839
|
+
preMargin: account.PreMargin,
|
|
1773
1840
|
balance: account.Balance,
|
|
1774
1841
|
cash: account.Available,
|
|
1775
1842
|
margin: account.CurrMargin,
|
|
@@ -1781,7 +1848,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
1781
1848
|
}
|
|
1782
1849
|
|
|
1783
1850
|
private _toPositionDetail(
|
|
1784
|
-
positionDetail:
|
|
1851
|
+
positionDetail: InvestorPositionDetailField,
|
|
1785
1852
|
): PositionDetail {
|
|
1786
1853
|
return Object.freeze({
|
|
1787
1854
|
symbol: this._toSymbol(positionDetail.InstrumentID)!,
|
package/src/typedef.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* typedef.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
|
|
@@ -168,6 +168,7 @@ export type TradingAccount = Readonly<{
|
|
|
168
168
|
id: string;
|
|
169
169
|
currency: string;
|
|
170
170
|
preBalance: number;
|
|
171
|
+
preMargin: number;
|
|
171
172
|
balance: number;
|
|
172
173
|
cash: number;
|
|
173
174
|
margin: number;
|
|
@@ -177,7 +178,8 @@ export type TradingAccount = Readonly<{
|
|
|
177
178
|
frozenCommission: number;
|
|
178
179
|
}>;
|
|
179
180
|
|
|
180
|
-
export type ProductType = "futures" | "options";
|
|
181
|
+
export type ProductType = "futures" | "options" | "spot" | "spot-options";
|
|
182
|
+
export type OptionsType = "call" | "put";
|
|
181
183
|
|
|
182
184
|
export type InstrumentData = Readonly<{
|
|
183
185
|
symbol: string;
|
|
@@ -194,6 +196,8 @@ export type InstrumentData = Readonly<{
|
|
|
194
196
|
priceTick: number;
|
|
195
197
|
maxLimitOrderVolume: number;
|
|
196
198
|
minLimitOrderVolume: number;
|
|
199
|
+
strikePrice: number;
|
|
200
|
+
optionsType?: OptionsType;
|
|
197
201
|
}>;
|
|
198
202
|
|
|
199
203
|
export type PriceVolume = Readonly<{
|
package/src/utils.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* utils.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
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
* https://github.com/shixiongfei/hft.js
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { BarData, OrderFlag } from "./typedef.js";
|
|
13
|
-
import { BarInfo } from "./bar.js";
|
|
14
|
-
import {
|
|
12
|
+
import type { BarData, OrderFlag } from "./typedef.js";
|
|
13
|
+
import type { BarInfo } from "./bar.js";
|
|
14
|
+
import type {
|
|
15
15
|
IPlaceOrderResultReceiver,
|
|
16
16
|
IRuntimeEngine,
|
|
17
17
|
IStrategy,
|
|
@@ -21,7 +21,7 @@ export const isValidPrice = (x: number) => x !== Number.MAX_VALUE && x !== 0;
|
|
|
21
21
|
export const isValidVolume = (x: number) => x !== Number.MAX_VALUE && x !== 0;
|
|
22
22
|
|
|
23
23
|
export const parseSymbol = (symbol: string): [string, string] => {
|
|
24
|
-
const [instrumentId, exchangeId] = symbol.split(".");
|
|
24
|
+
const [instrumentId = "", exchangeId = ""] = symbol.split(".");
|
|
25
25
|
return [instrumentId, exchangeId];
|
|
26
26
|
};
|
|
27
27
|
|
|
@@ -40,28 +40,28 @@ export const mergeBarData = (bars: BarData[]): BarData => {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
if (bars.length === 1) {
|
|
43
|
-
return bars[1]
|
|
43
|
+
return bars[1]!;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
const bar: BarInfo = {
|
|
47
|
-
symbol: bars[0]
|
|
48
|
-
date: bars[0]
|
|
49
|
-
time: bars[0]
|
|
50
|
-
openInterest: bars[0]
|
|
51
|
-
openPrice: bars[0]
|
|
52
|
-
highPrice: bars[0]
|
|
53
|
-
lowPrice: bars[0]
|
|
54
|
-
closePrice: bars[0]
|
|
55
|
-
volume: bars[0]
|
|
56
|
-
amount: bars[0]
|
|
57
|
-
delta: bars[0]
|
|
58
|
-
poc: bars[0]
|
|
59
|
-
buyVolumes: { ...bars[0]
|
|
60
|
-
sellVolumes: { ...bars[0]
|
|
47
|
+
symbol: bars[0]!.symbol,
|
|
48
|
+
date: bars[0]!.date,
|
|
49
|
+
time: bars[0]!.time,
|
|
50
|
+
openInterest: bars[0]!.openInterest,
|
|
51
|
+
openPrice: bars[0]!.openPrice,
|
|
52
|
+
highPrice: bars[0]!.highPrice,
|
|
53
|
+
lowPrice: bars[0]!.lowPrice,
|
|
54
|
+
closePrice: bars[0]!.closePrice,
|
|
55
|
+
volume: bars[0]!.volume,
|
|
56
|
+
amount: bars[0]!.volume,
|
|
57
|
+
delta: bars[0]!.delta,
|
|
58
|
+
poc: bars[0]!.poc,
|
|
59
|
+
buyVolumes: { ...bars[0]!.buyVolumes },
|
|
60
|
+
sellVolumes: { ...bars[0]!.sellVolumes },
|
|
61
61
|
};
|
|
62
62
|
|
|
63
63
|
for (let i = 1; i < bars.length; ++i) {
|
|
64
|
-
const nextBar = bars[i]
|
|
64
|
+
const nextBar = bars[i]!;
|
|
65
65
|
|
|
66
66
|
bar.openInterest = nextBar.openInterest;
|
|
67
67
|
bar.closePrice = nextBar.closePrice;
|
|
@@ -73,14 +73,9 @@ export const mergeBarData = (bars: BarData[]): BarData => {
|
|
|
73
73
|
bar.amount += nextBar.amount;
|
|
74
74
|
|
|
75
75
|
for (const price in nextBar.buyVolumes) {
|
|
76
|
-
const volumeDelta = nextBar.buyVolumes[price]
|
|
77
|
-
|
|
78
|
-
if (price in bar.buyVolumes) {
|
|
79
|
-
bar.buyVolumes[price] += volumeDelta;
|
|
80
|
-
} else {
|
|
81
|
-
bar.buyVolumes[price] = volumeDelta;
|
|
82
|
-
}
|
|
76
|
+
const volumeDelta = nextBar.buyVolumes[price]!;
|
|
83
77
|
|
|
78
|
+
bar.buyVolumes[price] = volumeDelta + (bar.buyVolumes[price] ?? 0);
|
|
84
79
|
bar.delta += volumeDelta;
|
|
85
80
|
|
|
86
81
|
const priceVP = bar.buyVolumes[price] + (bar.sellVolumes[price] ?? 0);
|
|
@@ -92,14 +87,9 @@ export const mergeBarData = (bars: BarData[]): BarData => {
|
|
|
92
87
|
}
|
|
93
88
|
|
|
94
89
|
for (const price in nextBar.sellVolumes) {
|
|
95
|
-
const volumeDelta = nextBar.sellVolumes[price]
|
|
96
|
-
|
|
97
|
-
if (price in bar.sellVolumes) {
|
|
98
|
-
bar.sellVolumes[price] += volumeDelta;
|
|
99
|
-
} else {
|
|
100
|
-
bar.sellVolumes[price] = volumeDelta;
|
|
101
|
-
}
|
|
90
|
+
const volumeDelta = nextBar.sellVolumes[price]!;
|
|
102
91
|
|
|
92
|
+
bar.sellVolumes[price] = volumeDelta + (bar.sellVolumes[price] ?? 0);
|
|
103
93
|
bar.delta -= volumeDelta;
|
|
104
94
|
|
|
105
95
|
const priceVP = bar.sellVolumes[price] + (bar.buyVolumes[price] ?? 0);
|