hft-js 0.0.0 → 0.1.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/trader.js ADDED
@@ -0,0 +1,1109 @@
1
+ "use strict";
2
+ /*
3
+ * trader.ts
4
+ *
5
+ * Copyright (c) 2025 Xiongfei Shi
6
+ *
7
+ * Author: Xiongfei Shi <xiongfei.shi(a)icloud.com>
8
+ * License: Apache-2.0
9
+ *
10
+ * https://github.com/shixiongfei/hft.js
11
+ */
12
+ var __extends = (this && this.__extends) || (function () {
13
+ var extendStatics = function (d, b) {
14
+ extendStatics = Object.setPrototypeOf ||
15
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
16
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
17
+ return extendStatics(d, b);
18
+ };
19
+ return function (d, b) {
20
+ if (typeof b !== "function" && b !== null)
21
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
22
+ extendStatics(d, b);
23
+ function __() { this.constructor = d; }
24
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
25
+ };
26
+ })();
27
+ var __assign = (this && this.__assign) || function () {
28
+ __assign = Object.assign || function(t) {
29
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
30
+ s = arguments[i];
31
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
32
+ t[p] = s[p];
33
+ }
34
+ return t;
35
+ };
36
+ return __assign.apply(this, arguments);
37
+ };
38
+ var __importDefault = (this && this.__importDefault) || function (mod) {
39
+ return (mod && mod.__esModule) ? mod : { "default": mod };
40
+ };
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.createTrader = exports.Trader = void 0;
43
+ var denque_1 = __importDefault(require("denque"));
44
+ var napi_ctp_1 = __importDefault(require("napi-ctp"));
45
+ var provider_js_1 = require("./provider.js");
46
+ var Trader = /** @class */ (function (_super) {
47
+ __extends(Trader, _super);
48
+ function Trader(flowTdPath, frontTdAddrs, userInfo) {
49
+ var _this = _super.call(this, flowTdPath, frontTdAddrs, userInfo) || this;
50
+ _this.tradingDay = 0;
51
+ _this.frontId = 0;
52
+ _this.sessionId = 0;
53
+ _this.orderRef = 0;
54
+ _this.accountsQueryTime = 0;
55
+ _this.positionDetailsChanged = true;
56
+ _this.receivers = [];
57
+ _this.instruments = [];
58
+ _this.accounts = [];
59
+ _this.positionDetails = [];
60
+ _this.symbols = new Map();
61
+ _this.positions = new Map();
62
+ _this.orders = new Map();
63
+ _this.trades = new Map();
64
+ _this.marginRates = new Map();
65
+ _this.commRates = new Map();
66
+ _this.placeOrders = new Map();
67
+ _this.cancelOrders = new Map();
68
+ _this.marginRatesQueue = new denque_1.default();
69
+ _this.commRatesQueue = new denque_1.default();
70
+ _this.accountsQueue = new denque_1.default();
71
+ _this.positionDetailsQueue = new denque_1.default();
72
+ return _this;
73
+ }
74
+ Trader.prototype.open = function (lifecycle) {
75
+ var _this = this;
76
+ if (this.traderApi) {
77
+ return true;
78
+ }
79
+ this.traderApi = napi_ctp_1.default.createTrader(this.flowPath, this.frontAddrs);
80
+ this.traderApi.on(napi_ctp_1.default.TraderEvent.FrontConnected, function () {
81
+ _this._withRetry(function () { return _this.traderApi.reqAuthenticate(_this.userInfo); });
82
+ });
83
+ this.traderApi.on(napi_ctp_1.default.TraderEvent.FrontDisconnected, function () {
84
+ _this.placeOrders.clear();
85
+ _this.cancelOrders.clear();
86
+ });
87
+ this.traderApi.on(napi_ctp_1.default.TraderEvent.RspAuthenticate, function (_, options) {
88
+ if (_this._isErrorResp(lifecycle, options, "login-error")) {
89
+ return;
90
+ }
91
+ _this._withRetry(function () { return _this.traderApi.reqUserLogin(_this.userInfo); });
92
+ });
93
+ this.traderApi.on(napi_ctp_1.default.TraderEvent.RspUserLogin, function (rspUserLogin, options) {
94
+ if (_this._isErrorResp(lifecycle, options, "login-error")) {
95
+ return;
96
+ }
97
+ _this.frontId = rspUserLogin.FrontID;
98
+ _this.sessionId = rspUserLogin.SessionID;
99
+ _this.orderRef = parseInt(rspUserLogin.MaxOrderRef);
100
+ var tradingDay = parseInt(_this.traderApi.getTradingDay());
101
+ if (_this.tradingDay !== tradingDay) {
102
+ _this.marginRates.clear();
103
+ _this.commRates.clear();
104
+ _this.tradingDay = tradingDay;
105
+ }
106
+ _this._withRetry(function () {
107
+ return _this.traderApi.reqSettlementInfoConfirm(_this.userInfo);
108
+ });
109
+ });
110
+ this.traderApi.on(napi_ctp_1.default.TraderEvent.RspSettlementInfoConfirm, function (_, options) {
111
+ if (_this._isErrorResp(lifecycle, options, "login-error")) {
112
+ return;
113
+ }
114
+ _this.orders.clear();
115
+ _this._withRetry(function () { return _this.traderApi.reqQryOrder(_this.userInfo); });
116
+ });
117
+ this.traderApi.on(napi_ctp_1.default.TraderEvent.RspQryOrder, function (order, options) {
118
+ if (_this._isErrorResp(lifecycle, options, "query-order-error")) {
119
+ return;
120
+ }
121
+ if (order) {
122
+ var orderId = _this._calcOrderId(order);
123
+ _this.orders.set(orderId, order);
124
+ }
125
+ if (options.isLast) {
126
+ _this.trades.clear();
127
+ _this._withRetry(function () { return _this.traderApi.reqQryTrade(_this.userInfo); });
128
+ }
129
+ });
130
+ this.traderApi.on(napi_ctp_1.default.TraderEvent.RspQryTrade, function (trade, options) {
131
+ if (_this._isErrorResp(lifecycle, options, "query-trade-error")) {
132
+ return;
133
+ }
134
+ if (trade) {
135
+ var orderId = _this._calcOrderId(trade);
136
+ var trades = _this.trades.get(orderId);
137
+ if (trades) {
138
+ trades.push(trade);
139
+ }
140
+ else {
141
+ _this.trades.set(orderId, [trade]);
142
+ }
143
+ }
144
+ if (options.isLast) {
145
+ _this.symbols.clear();
146
+ _this.instruments.splice(0, _this.instruments.length);
147
+ _this._withRetry(function () { return _this.traderApi.reqQryInstrument(); });
148
+ }
149
+ });
150
+ this.traderApi.on(napi_ctp_1.default.TraderEvent.RspQryInstrument, function (instrument, options) {
151
+ if (_this._isErrorResp(lifecycle, options, "query-instrument-error")) {
152
+ return;
153
+ }
154
+ if (instrument) {
155
+ if (instrument.ProductClass === napi_ctp_1.default.ProductClassType.Futures ||
156
+ instrument.ProductClass === napi_ctp_1.default.ProductClassType.Options) {
157
+ _this.symbols.set(instrument.InstrumentID, "".concat(instrument.InstrumentID, ".").concat(instrument.ExchangeID));
158
+ _this.instruments.push(instrument);
159
+ }
160
+ }
161
+ if (options.isLast) {
162
+ _this.positions.clear();
163
+ _this._withRetry(function () {
164
+ return _this.traderApi.reqQryInvestorPosition(_this.userInfo);
165
+ });
166
+ }
167
+ });
168
+ var fired = false;
169
+ this.traderApi.on(napi_ctp_1.default.TraderEvent.RspQryInvestorPosition, function (position, options) {
170
+ if (_this._isErrorResp(lifecycle, options, "query-positions-error")) {
171
+ return;
172
+ }
173
+ if (position) {
174
+ var symbol = _this.symbols.get(position.InstrumentID);
175
+ if (symbol) {
176
+ var posInfo = _this._ensurePositionInfo(symbol);
177
+ var ExchangeSH = ["SHFE", "INE"];
178
+ switch (position.PosiDirection) {
179
+ case napi_ctp_1.default.PosiDirectionType.Long:
180
+ if (position.PositionDate === napi_ctp_1.default.PositionDateType.Today) {
181
+ if (ExchangeSH.includes(position.ExchangeID)) {
182
+ posInfo.today.long.position += position.TodayPosition;
183
+ }
184
+ else {
185
+ posInfo.today.long.position += position.Position;
186
+ }
187
+ }
188
+ else {
189
+ posInfo.history.long.position +=
190
+ position.Position - position.TodayPosition;
191
+ }
192
+ break;
193
+ case napi_ctp_1.default.PosiDirectionType.Short:
194
+ if (position.PositionDate === napi_ctp_1.default.PositionDateType.Today) {
195
+ if (ExchangeSH.includes(position.ExchangeID)) {
196
+ posInfo.today.short.position += position.TodayPosition;
197
+ }
198
+ else {
199
+ posInfo.today.short.position += position.Position;
200
+ }
201
+ }
202
+ else {
203
+ posInfo.history.short.position +=
204
+ position.Position - position.TodayPosition;
205
+ }
206
+ break;
207
+ }
208
+ }
209
+ }
210
+ if (options.isLast) {
211
+ if (!fired) {
212
+ fired = true;
213
+ lifecycle.onOpen();
214
+ }
215
+ if (_this.accountsQueue.size() > 0) {
216
+ _this._withRetry(function () {
217
+ return _this.traderApi.reqQryTradingAccount(_this.userInfo);
218
+ });
219
+ }
220
+ if (_this.positionDetailsQueue.size() > 0) {
221
+ _this._withRetry(function () {
222
+ return _this.traderApi.reqQryInvestorPositionDetail(_this.userInfo);
223
+ });
224
+ }
225
+ _this._processMarginRatesQueue();
226
+ _this._processCommissionRatesQueue();
227
+ }
228
+ });
229
+ this.traderApi.on(napi_ctp_1.default.TraderEvent.RtnOrder, function (order) {
230
+ var orderId = _this._calcOrderId(order);
231
+ var current = _this.orders.get(orderId);
232
+ if (current) {
233
+ if (order.OrderSubmitStatus === current.OrderSubmitStatus &&
234
+ order.OrderStatus === current.OrderStatus) {
235
+ return;
236
+ }
237
+ }
238
+ _this.orders.set(orderId, order);
239
+ switch (_this._calcOrderStatus(order)) {
240
+ case "submitted":
241
+ {
242
+ var orderData_1 = _this._toOrderData(order);
243
+ var symbol = _this.symbols.get(order.InstrumentID);
244
+ if (symbol) {
245
+ if (orderData_1.offset === "open") {
246
+ _this._recordPending(symbol, orderData_1.side, orderData_1.offset, orderData_1.volume);
247
+ }
248
+ else {
249
+ _this._freezePosition(symbol, orderData_1.side, orderData_1.offset, orderData_1.volume);
250
+ }
251
+ }
252
+ _this.receivers.forEach(function (receiver) { return receiver.onEntrust(orderData_1); });
253
+ }
254
+ break;
255
+ case "canceled":
256
+ {
257
+ var orderData_2 = _this._toOrderData(order);
258
+ var symbol = _this.symbols.get(order.InstrumentID);
259
+ if (symbol) {
260
+ if (orderData_2.offset === "open") {
261
+ _this._recoverPending(symbol, orderData_2.side, orderData_2.offset, orderData_2.volume);
262
+ }
263
+ else {
264
+ _this._unfreezePosition(symbol, orderData_2.side, orderData_2.offset, orderData_2.volume);
265
+ }
266
+ }
267
+ _this.receivers.forEach(function (receiver) { return receiver.onCancel(orderData_2); });
268
+ }
269
+ break;
270
+ case "rejected":
271
+ {
272
+ var orderData_3 = _this._toOrderData(order);
273
+ _this.receivers.forEach(function (receiver) { return receiver.onReject(orderData_3); });
274
+ }
275
+ break;
276
+ }
277
+ });
278
+ this.traderApi.on(napi_ctp_1.default.TraderEvent.RtnTrade, function (trade) {
279
+ var orderId = _this._calcOrderId(trade);
280
+ var trades = _this.trades.get(orderId);
281
+ if (trades) {
282
+ trades.push(trade);
283
+ }
284
+ else {
285
+ _this.trades.set(orderId, [trade]);
286
+ }
287
+ _this.positionDetailsChanged = true;
288
+ var order = _this.orders.get(orderId);
289
+ if (order) {
290
+ var orderData_4 = _this._toOrderData(order);
291
+ var tradeData_1 = _this._toTradeData(trade);
292
+ var symbol = _this.symbols.get(order.InstrumentID);
293
+ if (symbol) {
294
+ _this._calcPosition(symbol, orderData_4.side, orderData_4.offset, orderData_4.volume);
295
+ }
296
+ _this.receivers.forEach(function (receiver) {
297
+ return receiver.onTrade(orderData_4, tradeData_1);
298
+ });
299
+ }
300
+ });
301
+ this.traderApi.on(napi_ctp_1.default.TraderEvent.RspQryInstrumentMarginRate, function (marginRate, options) {
302
+ var query = _this.marginRatesQueue.shift();
303
+ if (_this._isErrorResp(lifecycle, options, "query-margin-rate-error")) {
304
+ if (query) {
305
+ query.receiver.onMarginRate(undefined);
306
+ }
307
+ return;
308
+ }
309
+ if (marginRate) {
310
+ _this.marginRates.set(marginRate.InstrumentID, marginRate);
311
+ if (query) {
312
+ query.receiver.onMarginRate(_this._toMarginRate(query.symbol, marginRate));
313
+ }
314
+ }
315
+ _this._processMarginRatesQueue();
316
+ });
317
+ this.traderApi.on(napi_ctp_1.default.TraderEvent.RspQryInstrumentCommissionRate, function (commRate, options) {
318
+ var query = _this.commRatesQueue.shift();
319
+ if (_this._isErrorResp(lifecycle, options, "query-commission-rate-error")) {
320
+ if (query) {
321
+ query.receiver.onCommissionRate(undefined);
322
+ }
323
+ return;
324
+ }
325
+ if (commRate) {
326
+ _this.commRates.set(commRate.InstrumentID, commRate);
327
+ if (query) {
328
+ query.receiver.onCommissionRate(_this._toCommissionRate(query.symbol, commRate));
329
+ }
330
+ }
331
+ _this._processCommissionRatesQueue();
332
+ });
333
+ this.traderApi.on(napi_ctp_1.default.TraderEvent.RspQryTradingAccount, function (account, options) {
334
+ if (_this._isErrorResp(lifecycle, options, "query-accounts-error")) {
335
+ var receivers = _this.accountsQueue.toArray();
336
+ receivers.forEach(function (receiver) {
337
+ return receiver.onTradingAccounts(undefined);
338
+ });
339
+ _this.accountsQueue.clear();
340
+ return;
341
+ }
342
+ if (account) {
343
+ _this.accounts.push(account);
344
+ }
345
+ if (options.isLast) {
346
+ var accounts_1 = _this.accounts.map(_this._toTradingAccount, _this);
347
+ var receivers = _this.accountsQueue.toArray();
348
+ receivers.forEach(function (receiver) { return receiver.onTradingAccounts(accounts_1); });
349
+ _this.accountsQueue.clear();
350
+ _this.accountsQueryTime = Date.now();
351
+ }
352
+ });
353
+ this.traderApi.on(napi_ctp_1.default.TraderEvent.RspQryInvestorPositionDetail, function (positionDetail, options) {
354
+ if (_this._isErrorResp(lifecycle, options, "query-position-details-error")) {
355
+ var receivers = _this.positionDetailsQueue.toArray();
356
+ receivers.forEach(function (receiver) {
357
+ return receiver.onPositionDetails(undefined);
358
+ });
359
+ _this.positionDetailsQueue.clear();
360
+ return;
361
+ }
362
+ if (positionDetail) {
363
+ _this.positionDetails.push(positionDetail);
364
+ }
365
+ if (options.isLast) {
366
+ var positionDetails_1 = _this.positionDetails.map(_this._toPositionDetail);
367
+ var receivers = _this.positionDetailsQueue.toArray();
368
+ _this.positionDetailsChanged = false;
369
+ receivers.forEach(function (receiver) {
370
+ return receiver.onPositionDetails(positionDetails_1);
371
+ });
372
+ _this.positionDetailsQueue.clear();
373
+ }
374
+ });
375
+ this.traderApi.on(napi_ctp_1.default.TraderEvent.RspOrderInsert, function (order, options) {
376
+ if (options.rspInfo && order && options.requestId && options.isLast) {
377
+ var receiver = _this.placeOrders.get(options.requestId);
378
+ if (receiver) {
379
+ _this.placeOrders.delete(options.requestId);
380
+ receiver.onPlaceOrderError("".concat(options.rspInfo.ErrorID, ": ").concat(options.rspInfo.ErrorMsg));
381
+ }
382
+ }
383
+ });
384
+ this.traderApi.on(napi_ctp_1.default.TraderEvent.RspOrderAction, function (order, options) {
385
+ if (options.rspInfo && order && options.requestId && options.isLast) {
386
+ var receiver = _this.cancelOrders.get(options.requestId);
387
+ if (receiver) {
388
+ _this.cancelOrders.delete(options.requestId);
389
+ receiver.onCancelOrderError("".concat(options.rspInfo.ErrorID, ": ").concat(options.rspInfo.ErrorMsg));
390
+ }
391
+ }
392
+ });
393
+ return true;
394
+ };
395
+ Trader.prototype.close = function (lifecycle) {
396
+ if (!this.traderApi) {
397
+ return;
398
+ }
399
+ this.traderApi.close();
400
+ this.traderApi = undefined;
401
+ lifecycle.onClose();
402
+ };
403
+ Trader.prototype.addReceiver = function (receiver) {
404
+ if (!this.receivers.includes(receiver)) {
405
+ this.receivers.push(receiver);
406
+ }
407
+ };
408
+ Trader.prototype.removeReceiver = function (receiver) {
409
+ var index = this.receivers.indexOf(receiver);
410
+ if (index < 0) {
411
+ return;
412
+ }
413
+ this.receivers.splice(index, 1);
414
+ };
415
+ Trader.prototype.getTradingDay = function () {
416
+ return this.tradingDay;
417
+ };
418
+ Trader.prototype.queryCommissionRate = function (symbol, receiver) {
419
+ var _this = this;
420
+ var instrumentId = this._parseSymbol(symbol)[0];
421
+ var commRate = this.commRates.get(instrumentId);
422
+ if (commRate) {
423
+ receiver.onCommissionRate(this._toCommissionRate(symbol, commRate));
424
+ return;
425
+ }
426
+ this.commRatesQueue.push({ symbol: symbol, receiver: receiver });
427
+ if (this.commRatesQueue.size() === 1) {
428
+ this._withRetry(function () {
429
+ var _a;
430
+ return (_a = _this.traderApi) === null || _a === void 0 ? void 0 : _a.reqQryInstrumentCommissionRate(__assign(__assign({}, _this.userInfo), { InstrumentID: instrumentId }));
431
+ });
432
+ }
433
+ };
434
+ Trader.prototype.queryMarginRate = function (symbol, receiver) {
435
+ var _this = this;
436
+ var instrumentId = this._parseSymbol(symbol)[0];
437
+ var marginRate = this.marginRates.get(instrumentId);
438
+ if (marginRate) {
439
+ receiver.onMarginRate(this._toMarginRate(symbol, marginRate));
440
+ return;
441
+ }
442
+ this.marginRatesQueue.push({ symbol: symbol, receiver: receiver });
443
+ if (this.marginRatesQueue.size() === 1) {
444
+ this._withRetry(function () {
445
+ var _a;
446
+ return (_a = _this.traderApi) === null || _a === void 0 ? void 0 : _a.reqQryInstrumentMarginRate(__assign(__assign({}, _this.userInfo), { HedgeFlag: napi_ctp_1.default.HedgeFlagType.Speculation, InstrumentID: instrumentId }));
447
+ });
448
+ }
449
+ };
450
+ Trader.prototype.queryInstrument = function (symbol, receiver) {
451
+ var _a = this._parseSymbol(symbol), instrumentId = _a[0], exchangeId = _a[1];
452
+ var instrument = this.instruments.find(function (instrument) {
453
+ return instrument.InstrumentID === instrumentId &&
454
+ instrument.ExchangeID === exchangeId;
455
+ });
456
+ receiver.onInstrument(instrument ? this._toInstrumentData(instrument) : undefined);
457
+ };
458
+ Trader.prototype.queryPosition = function (symbol, receiver) {
459
+ var position = this.positions.get(symbol);
460
+ if (position) {
461
+ receiver.onPosition(this._toPositionData(position));
462
+ return;
463
+ }
464
+ var instrumentId = this._parseSymbol(symbol)[0];
465
+ if (!this.symbols.has(instrumentId)) {
466
+ receiver.onPosition(undefined);
467
+ return;
468
+ }
469
+ receiver.onPosition(Object.freeze({
470
+ symbol: symbol,
471
+ today: Object.freeze({
472
+ long: Object.freeze({ position: 0, frozen: 0 }),
473
+ short: Object.freeze({ position: 0, frozen: 0 }),
474
+ }),
475
+ history: Object.freeze({
476
+ long: Object.freeze({ position: 0, frozen: 0 }),
477
+ short: Object.freeze({ position: 0, frozen: 0 }),
478
+ }),
479
+ pending: Object.freeze({ long: 0, short: 0 }),
480
+ }));
481
+ };
482
+ Trader.prototype.queryInstruments = function (receiver, type) {
483
+ switch (type) {
484
+ case "future":
485
+ receiver.onInstruments(this.instruments
486
+ .filter(function (instrument) {
487
+ return instrument.ProductClass === napi_ctp_1.default.ProductClassType.Futures;
488
+ })
489
+ .map(this._toInstrumentData, this));
490
+ break;
491
+ case "option":
492
+ receiver.onInstruments(this.instruments
493
+ .filter(function (instrument) {
494
+ return instrument.ProductClass === napi_ctp_1.default.ProductClassType.Options;
495
+ })
496
+ .map(this._toInstrumentData, this));
497
+ break;
498
+ default:
499
+ receiver.onInstruments(this.instruments.map(this._toInstrumentData, this));
500
+ break;
501
+ }
502
+ };
503
+ Trader.prototype.queryTradingAccounts = function (receiver) {
504
+ var _this = this;
505
+ if (this.accountsQueue.size() > 0) {
506
+ this.accountsQueue.push(receiver);
507
+ return;
508
+ }
509
+ var elapsed = Date.now() - this.accountsQueryTime;
510
+ if (elapsed < 3000) {
511
+ receiver.onTradingAccounts(this.accounts.map(this._toTradingAccount, this));
512
+ return;
513
+ }
514
+ this.accountsQueue.push(receiver);
515
+ this.accounts.splice(0, this.accounts.length);
516
+ this._withRetry(function () { var _a; return (_a = _this.traderApi) === null || _a === void 0 ? void 0 : _a.reqQryTradingAccount(_this.userInfo); });
517
+ };
518
+ Trader.prototype.queryPositionDetails = function (receiver) {
519
+ var _this = this;
520
+ if (this.positionDetailsQueue.size() > 0) {
521
+ this.positionDetailsQueue.push(receiver);
522
+ return;
523
+ }
524
+ if (!this.positionDetailsChanged) {
525
+ receiver.onPositionDetails(this.positionDetails.map(this._toPositionDetail, this));
526
+ return;
527
+ }
528
+ this.positionDetailsQueue.push(receiver);
529
+ this.positionDetails.splice(0, this.positionDetails.length);
530
+ this._withRetry(function () { var _a; return (_a = _this.traderApi) === null || _a === void 0 ? void 0 : _a.reqQryInvestorPositionDetail(_this.userInfo); });
531
+ };
532
+ Trader.prototype.queryPositions = function (receiver) {
533
+ var _this = this;
534
+ var positions = [];
535
+ this.positions.forEach(function (position) {
536
+ return positions.push(_this._toPositionData(position));
537
+ });
538
+ receiver.onPositions(positions);
539
+ };
540
+ Trader.prototype.queryOrders = function (receiver) {
541
+ var _this = this;
542
+ var orders = [];
543
+ this.orders.forEach(function (order) {
544
+ orders.push(_this._toOrderData(order));
545
+ });
546
+ receiver.onOrders(orders);
547
+ };
548
+ Trader.prototype.placeOrder = function (symbol, offset, side, volume, price, flag, receiver) {
549
+ var _this = this;
550
+ if (flag !== "limit") {
551
+ receiver.onPlaceOrderError("Only Supports Limit Order");
552
+ return;
553
+ }
554
+ var instrumentId = this._parseSymbol(symbol)[0];
555
+ var instrument = this.instruments.find(function (instrument) { return instrument.InstrumentID === instrumentId; });
556
+ if (!instrument) {
557
+ receiver.onPlaceOrderError("Instrument Not Found");
558
+ return;
559
+ }
560
+ var orderRef = 0;
561
+ this._withRetry(function () {
562
+ var _a;
563
+ orderRef = ++_this.orderRef;
564
+ return (_a = _this.traderApi) === null || _a === void 0 ? void 0 : _a.reqOrderInsert(__assign(__assign({}, _this.userInfo), { OrderRef: "".concat(orderRef), InstrumentID: instrumentId, ExchangeID: instrument.ExchangeID, LimitPrice: price, VolumeTotalOriginal: volume, VolumeCondition: napi_ctp_1.default.VolumeConditionType.AV, TimeCondition: napi_ctp_1.default.TimeConditionType.GFD, Direction: _this._toDirection(side), OrderPriceType: napi_ctp_1.default.OrderPriceTypeType.LimitPrice, CombOffsetFlag: _this._toOffsetFlag(offset), CombHedgeFlag: napi_ctp_1.default.HedgeFlagType.Speculation, ContingentCondition: napi_ctp_1.default.ContingentConditionType.Immediately, ForceCloseReason: napi_ctp_1.default.ForceCloseReasonType.NotForceClose, IsAutoSuspend: 0, UserForceClose: 0 }));
565
+ }).then(function (requestId) {
566
+ if (!requestId) {
567
+ receiver.onPlaceOrderError("Request Failed");
568
+ return;
569
+ }
570
+ if (requestId < 0) {
571
+ receiver.onPlaceOrderError("Request Error");
572
+ return;
573
+ }
574
+ _this.placeOrders.set(requestId, receiver);
575
+ var receiptId = "".concat(_this.frontId, ":").concat(_this.sessionId, ":").concat(orderRef);
576
+ receiver.onPlaceOrderSent(receiptId);
577
+ return receiptId;
578
+ });
579
+ };
580
+ Trader.prototype.cancelOrder = function (order, receiver) {
581
+ var _this = this;
582
+ var current = this.orders.get(order.id);
583
+ if (!current) {
584
+ receiver.onCancelOrderError("Order Not Found");
585
+ return;
586
+ }
587
+ if (order.cancelTime) {
588
+ receiver.onCancelOrderError("Already Canceled");
589
+ return;
590
+ }
591
+ this._withRetry(function () {
592
+ var _a;
593
+ return (_a = _this.traderApi) === null || _a === void 0 ? void 0 : _a.reqOrderAction(__assign(__assign({}, _this.userInfo), { InstrumentID: current.InstrumentID, FrontID: current.FrontID, SessionID: current.SessionID, OrderRef: current.OrderRef, ExchangeID: current.ExchangeID, OrderSysID: current.OrderSysID, ActionFlag: napi_ctp_1.default.ActionFlagType.Delete }));
594
+ }).then(function (requestId) {
595
+ if (!requestId) {
596
+ receiver.onCancelOrderError("Request Failed");
597
+ return;
598
+ }
599
+ if (requestId < 0) {
600
+ receiver.onCancelOrderError("Request Error");
601
+ return;
602
+ }
603
+ _this.cancelOrders.set(requestId, receiver);
604
+ receiver.onCancelOrderSent();
605
+ });
606
+ };
607
+ Trader.prototype._calcOrderId = function (orderOrTrade) {
608
+ var ExchangeID = orderOrTrade.ExchangeID, TraderID = orderOrTrade.TraderID, OrderLocalID = orderOrTrade.OrderLocalID;
609
+ return "".concat(ExchangeID, ":").concat(TraderID, ":").concat(OrderLocalID);
610
+ };
611
+ Trader.prototype._calcReceiptId = function (order) {
612
+ return "".concat(order.FrontID, ":").concat(order.SessionID, ":").concat(parseInt(order.OrderRef));
613
+ };
614
+ Trader.prototype._calcOrderStatus = function (order, traded) {
615
+ switch (order.OrderStatus) {
616
+ case napi_ctp_1.default.OrderStatusType.Unknown:
617
+ return "submitted";
618
+ case napi_ctp_1.default.OrderStatusType.AllTraded:
619
+ return "filled";
620
+ case napi_ctp_1.default.OrderStatusType.Canceled:
621
+ switch (order.OrderSubmitStatus) {
622
+ case napi_ctp_1.default.OrderSubmitStatusType.InsertRejected:
623
+ case napi_ctp_1.default.OrderSubmitStatusType.CancelRejected:
624
+ case napi_ctp_1.default.OrderSubmitStatusType.ModifyRejected:
625
+ return "rejected";
626
+ default:
627
+ return "canceled";
628
+ }
629
+ default:
630
+ return traded && order.VolumeTotalOriginal === traded
631
+ ? "filled"
632
+ : "partially-filled";
633
+ }
634
+ };
635
+ Trader.prototype._calcOrderFlag = function (orderPriceType) {
636
+ switch (orderPriceType) {
637
+ case napi_ctp_1.default.OrderPriceTypeType.LimitPrice:
638
+ return "limit";
639
+ default:
640
+ return "market";
641
+ }
642
+ };
643
+ Trader.prototype._calcSideType = function (direction) {
644
+ switch (direction) {
645
+ case napi_ctp_1.default.DirectionType.Buy:
646
+ return "long";
647
+ case napi_ctp_1.default.DirectionType.Sell:
648
+ return "short";
649
+ }
650
+ };
651
+ Trader.prototype._toDirection = function (side) {
652
+ switch (side) {
653
+ case "long":
654
+ return napi_ctp_1.default.DirectionType.Buy;
655
+ case "short":
656
+ return napi_ctp_1.default.DirectionType.Sell;
657
+ }
658
+ };
659
+ Trader.prototype._calcOffsetType = function (offset) {
660
+ switch (offset) {
661
+ case napi_ctp_1.default.OffsetFlagType.Open:
662
+ return "open";
663
+ case napi_ctp_1.default.OffsetFlagType.CloseToday:
664
+ return "close-today";
665
+ default:
666
+ return "close";
667
+ }
668
+ };
669
+ Trader.prototype._toOffsetFlag = function (offset) {
670
+ switch (offset) {
671
+ case "open":
672
+ return napi_ctp_1.default.OffsetFlagType.Open;
673
+ case "close":
674
+ return napi_ctp_1.default.OffsetFlagType.Close;
675
+ case "close-today":
676
+ return napi_ctp_1.default.OffsetFlagType.CloseToday;
677
+ }
678
+ };
679
+ Trader.prototype._calcProductType = function (productClass) {
680
+ switch (productClass) {
681
+ case napi_ctp_1.default.ProductClassType.Futures:
682
+ return "future";
683
+ case napi_ctp_1.default.ProductClassType.Options:
684
+ return "option";
685
+ default:
686
+ throw new Error("Unsupported product class: ".concat(productClass));
687
+ }
688
+ };
689
+ Trader.prototype._ensurePositionInfo = function (symbol) {
690
+ var position = this.positions.get(symbol);
691
+ if (!position) {
692
+ position = {
693
+ symbol: symbol,
694
+ today: {
695
+ long: { position: 0, frozen: 0 },
696
+ short: { position: 0, frozen: 0 },
697
+ },
698
+ history: {
699
+ long: { position: 0, frozen: 0 },
700
+ short: { position: 0, frozen: 0 },
701
+ },
702
+ pending: { long: 0, short: 0 },
703
+ };
704
+ this.positions.set(symbol, position);
705
+ }
706
+ return position;
707
+ };
708
+ Trader.prototype._calcPosition = function (symbol, side, offset, volume) {
709
+ var position = this._ensurePositionInfo(symbol);
710
+ switch (offset) {
711
+ case "open":
712
+ switch (side) {
713
+ case "long":
714
+ position.today.long.position += volume;
715
+ if (position.pending.long > volume) {
716
+ position.pending.long -= volume;
717
+ }
718
+ else {
719
+ position.pending.long = 0;
720
+ }
721
+ break;
722
+ case "short":
723
+ position.today.short.position += volume;
724
+ if (position.pending.short > volume) {
725
+ position.pending.short -= volume;
726
+ }
727
+ else {
728
+ position.pending.short = 0;
729
+ }
730
+ break;
731
+ }
732
+ break;
733
+ case "close":
734
+ switch (side) {
735
+ case "long":
736
+ if (position.history.long.position > volume) {
737
+ position.history.long.position -= volume;
738
+ }
739
+ else {
740
+ var rest = volume - position.history.long.position;
741
+ position.history.long.position -= position.history.long.position;
742
+ if (rest > 0) {
743
+ if (position.today.long.position > rest) {
744
+ position.today.long.position -= rest;
745
+ }
746
+ else {
747
+ position.today.long.position = 0;
748
+ }
749
+ }
750
+ }
751
+ if (position.history.long.frozen > volume) {
752
+ position.history.long.frozen -= volume;
753
+ }
754
+ else {
755
+ var rest = volume - position.history.long.frozen;
756
+ position.history.long.frozen -= position.history.long.frozen;
757
+ if (rest > 0) {
758
+ if (position.today.long.frozen > rest) {
759
+ position.today.long.frozen -= rest;
760
+ }
761
+ else {
762
+ position.today.long.frozen = 0;
763
+ }
764
+ }
765
+ }
766
+ break;
767
+ case "short":
768
+ if (position.history.short.position > volume) {
769
+ position.history.short.position -= volume;
770
+ }
771
+ else {
772
+ var rest = volume - position.history.short.position;
773
+ position.history.short.position -=
774
+ position.history.short.position;
775
+ if (rest > 0) {
776
+ if (position.today.short.position > rest) {
777
+ position.today.short.position -= rest;
778
+ }
779
+ else {
780
+ position.today.short.position = 0;
781
+ }
782
+ }
783
+ }
784
+ if (position.history.short.frozen > volume) {
785
+ position.history.short.frozen -= volume;
786
+ }
787
+ else {
788
+ var rest = volume - position.history.short.frozen;
789
+ position.history.short.frozen -= position.history.short.frozen;
790
+ if (rest > 0) {
791
+ if (position.today.short.frozen > rest) {
792
+ position.today.short.frozen -= rest;
793
+ }
794
+ else {
795
+ position.today.short.frozen = 0;
796
+ }
797
+ }
798
+ }
799
+ break;
800
+ }
801
+ break;
802
+ case "close-today":
803
+ switch (side) {
804
+ case "long":
805
+ if (position.today.long.position > volume) {
806
+ position.today.long.position -= volume;
807
+ }
808
+ else {
809
+ position.today.long.position = 0;
810
+ }
811
+ if (position.today.long.frozen > volume) {
812
+ position.today.long.frozen -= volume;
813
+ }
814
+ else {
815
+ position.today.long.frozen = 0;
816
+ }
817
+ break;
818
+ case "short":
819
+ if (position.today.short.position > volume) {
820
+ position.today.short.position -= volume;
821
+ }
822
+ else {
823
+ position.today.short.position = 0;
824
+ }
825
+ if (position.today.short.frozen > volume) {
826
+ position.today.short.frozen -= volume;
827
+ }
828
+ else {
829
+ position.today.short.frozen = 0;
830
+ }
831
+ break;
832
+ }
833
+ break;
834
+ }
835
+ };
836
+ Trader.prototype._recordPending = function (symbol, side, offset, volume) {
837
+ if (offset !== "open") {
838
+ return;
839
+ }
840
+ var position = this._ensurePositionInfo(symbol);
841
+ switch (side) {
842
+ case "long":
843
+ position.pending.long += volume;
844
+ break;
845
+ case "short":
846
+ position.pending.short += volume;
847
+ break;
848
+ }
849
+ };
850
+ Trader.prototype._recoverPending = function (symbol, side, offset, volume) {
851
+ if (offset !== "open") {
852
+ return;
853
+ }
854
+ var position = this.positions.get(symbol);
855
+ if (!position) {
856
+ return;
857
+ }
858
+ switch (side) {
859
+ case "long":
860
+ position.pending.long -= volume;
861
+ break;
862
+ case "short":
863
+ position.pending.short -= volume;
864
+ break;
865
+ }
866
+ };
867
+ Trader.prototype._freezePosition = function (symbol, side, offset, volume) {
868
+ var position = this.positions.get(symbol);
869
+ if (!position) {
870
+ return;
871
+ }
872
+ switch (offset) {
873
+ case "close":
874
+ switch (side) {
875
+ case "long":
876
+ position.history.long.frozen += volume;
877
+ break;
878
+ case "short":
879
+ position.history.short.frozen += volume;
880
+ break;
881
+ }
882
+ break;
883
+ case "close-today":
884
+ switch (side) {
885
+ case "long":
886
+ position.today.long.frozen += volume;
887
+ break;
888
+ case "short":
889
+ position.today.short.frozen += volume;
890
+ break;
891
+ }
892
+ break;
893
+ }
894
+ };
895
+ Trader.prototype._unfreezePosition = function (symbol, side, offset, volume) {
896
+ var position = this.positions.get(symbol);
897
+ if (!position) {
898
+ return;
899
+ }
900
+ switch (offset) {
901
+ case "close":
902
+ switch (side) {
903
+ case "long":
904
+ if (position.history.long.frozen > volume) {
905
+ position.history.long.frozen -= volume;
906
+ }
907
+ else {
908
+ position.history.long.frozen = 0;
909
+ }
910
+ break;
911
+ case "short":
912
+ if (position.history.short.frozen > volume) {
913
+ position.history.short.frozen -= volume;
914
+ }
915
+ else {
916
+ position.history.short.frozen = 0;
917
+ }
918
+ break;
919
+ }
920
+ break;
921
+ case "close-today":
922
+ switch (side) {
923
+ case "long":
924
+ if (position.today.long.frozen > volume) {
925
+ position.today.long.frozen -= volume;
926
+ }
927
+ else {
928
+ position.today.long.frozen = 0;
929
+ }
930
+ break;
931
+ case "short":
932
+ if (position.today.short.frozen > volume) {
933
+ position.today.short.frozen -= volume;
934
+ }
935
+ else {
936
+ position.today.short.frozen = 0;
937
+ }
938
+ break;
939
+ }
940
+ break;
941
+ }
942
+ };
943
+ Trader.prototype._toTradeData = function (trade) {
944
+ return Object.freeze({
945
+ id: trade.TradeID,
946
+ date: parseInt(trade.TradeDate),
947
+ time: this._parseTime(trade.TradeTime),
948
+ price: trade.Price,
949
+ volume: trade.Volume,
950
+ });
951
+ };
952
+ Trader.prototype._toOrderData = function (order) {
953
+ var _a;
954
+ var orderId = this._calcOrderId(order);
955
+ var trades = (_a = this.trades.get(orderId)) !== null && _a !== void 0 ? _a : [];
956
+ var traded = trades
957
+ .map(function (trade) { return trade.Volume; })
958
+ .reduce(function (a, b) { return a + b; }, 0);
959
+ return Object.freeze({
960
+ id: orderId,
961
+ receiptId: this._calcReceiptId(order),
962
+ symbol: "".concat(order.InstrumentID, ".").concat(order.ExchangeID),
963
+ date: parseInt(order.InsertDate),
964
+ time: this._parseTime(order.InsertTime),
965
+ flag: this._calcOrderFlag(order.OrderPriceType),
966
+ side: this._calcSideType(order.Direction),
967
+ offset: this._calcOffsetType(order.CombOffsetFlag),
968
+ price: order.LimitPrice,
969
+ volume: order.VolumeTotalOriginal,
970
+ traded: traded,
971
+ status: this._calcOrderStatus(order, traded),
972
+ trades: trades.map(this._toTradeData, this),
973
+ cancelTime: order.CancelTime !== "" ? this._parseTime(order.CancelTime) : undefined,
974
+ });
975
+ };
976
+ Trader.prototype._toInstrumentData = function (instrument) {
977
+ return Object.freeze({
978
+ symbol: "".concat(instrument.InstrumentID, ".").concat(instrument.ExchangeID),
979
+ id: instrument.InstrumentID,
980
+ name: instrument.InstrumentName,
981
+ exchangeId: instrument.ExchangeID,
982
+ productId: instrument.ProductID,
983
+ productType: this._calcProductType(instrument.ProductClass),
984
+ deliveryTime: instrument.DeliveryYear * 100 + instrument.DeliveryMonth,
985
+ createDate: parseInt(instrument.CreateDate),
986
+ openDate: parseInt(instrument.OpenDate),
987
+ expireDate: parseInt(instrument.ExpireDate),
988
+ multiple: instrument.VolumeMultiple,
989
+ priceTick: instrument.PriceTick,
990
+ maxLimitOrderVolume: instrument.MaxLimitOrderVolume,
991
+ minLimitOrderVolume: instrument.MinLimitOrderVolume,
992
+ });
993
+ };
994
+ Trader.prototype._toCommissionRate = function (symbol, commRate) {
995
+ return Object.freeze({
996
+ symbol: symbol,
997
+ open: Object.freeze({
998
+ ratio: commRate.OpenRatioByMoney,
999
+ amount: commRate.OpenRatioByVolume,
1000
+ }),
1001
+ close: Object.freeze({
1002
+ ratio: commRate.CloseRatioByMoney,
1003
+ amount: commRate.CloseRatioByVolume,
1004
+ }),
1005
+ closeToday: Object.freeze({
1006
+ ratio: commRate.CloseTodayRatioByMoney,
1007
+ amount: commRate.CloseTodayRatioByVolume,
1008
+ }),
1009
+ });
1010
+ };
1011
+ Trader.prototype._toMarginRate = function (symbol, marginRate) {
1012
+ return Object.freeze({
1013
+ symbol: symbol,
1014
+ long: Object.freeze({
1015
+ ratio: marginRate.LongMarginRatioByMoney,
1016
+ amount: marginRate.LongMarginRatioByVolume,
1017
+ }),
1018
+ short: Object.freeze({
1019
+ ratio: marginRate.ShortMarginRatioByMoney,
1020
+ amount: marginRate.ShortMarginRatioByVolume,
1021
+ }),
1022
+ });
1023
+ };
1024
+ Trader.prototype._toTradingAccount = function (account) {
1025
+ return Object.freeze({
1026
+ id: account.AccountID,
1027
+ currency: account.CurrencyID,
1028
+ preBalance: account.PreBalance - account.Withdraw + account.Deposit,
1029
+ balance: account.Balance,
1030
+ cash: account.Available,
1031
+ margin: account.CurrMargin,
1032
+ commission: account.Commission,
1033
+ frozenMargin: account.FrozenMargin,
1034
+ frozenCash: account.FrozenCash,
1035
+ frozenCommission: account.FrozenCommission,
1036
+ });
1037
+ };
1038
+ Trader.prototype._toPositionDetail = function (positionDetail) {
1039
+ return Object.freeze({
1040
+ symbol: this.symbols.get(positionDetail.InstrumentID),
1041
+ date: parseInt(positionDetail.OpenDate),
1042
+ side: this._calcSideType(positionDetail.Direction),
1043
+ price: positionDetail.OpenPrice,
1044
+ volume: positionDetail.Volume,
1045
+ margin: positionDetail.Margin,
1046
+ });
1047
+ };
1048
+ Trader.prototype._toPositionData = function (position) {
1049
+ return Object.freeze({
1050
+ symbol: position.symbol,
1051
+ today: Object.freeze({
1052
+ long: Object.freeze(__assign({}, position.today.long)),
1053
+ short: Object.freeze(__assign({}, position.today.short)),
1054
+ }),
1055
+ history: Object.freeze({
1056
+ long: Object.freeze(__assign({}, position.history.long)),
1057
+ short: Object.freeze(__assign({}, position.history.short)),
1058
+ }),
1059
+ pending: Object.freeze(__assign({}, position.pending)),
1060
+ });
1061
+ };
1062
+ Trader.prototype._processMarginRatesQueue = function () {
1063
+ var _this = this;
1064
+ var _loop_1 = function () {
1065
+ var nextQuery = this_1.marginRatesQueue.peekFront();
1066
+ var instrumentId = this_1._parseSymbol(nextQuery.symbol)[0];
1067
+ var marginRate = this_1.marginRates.get(instrumentId);
1068
+ if (marginRate) {
1069
+ nextQuery.receiver.onMarginRate(this_1._toMarginRate(nextQuery.symbol, marginRate));
1070
+ this_1.marginRatesQueue.shift();
1071
+ }
1072
+ else {
1073
+ this_1._withRetry(function () {
1074
+ return _this.traderApi.reqQryInstrumentMarginRate(__assign(__assign({}, _this.userInfo), { HedgeFlag: napi_ctp_1.default.HedgeFlagType.Speculation, InstrumentID: instrumentId }));
1075
+ });
1076
+ }
1077
+ };
1078
+ var this_1 = this;
1079
+ while (!this.marginRatesQueue.isEmpty()) {
1080
+ _loop_1();
1081
+ }
1082
+ };
1083
+ Trader.prototype._processCommissionRatesQueue = function () {
1084
+ var _this = this;
1085
+ var _loop_2 = function () {
1086
+ var nextQuery = this_2.commRatesQueue.peekFront();
1087
+ var instrumentId = this_2._parseSymbol(nextQuery.symbol)[0];
1088
+ var commRate = this_2.commRates.get(instrumentId);
1089
+ if (commRate) {
1090
+ nextQuery.receiver.onCommissionRate(this_2._toCommissionRate(nextQuery.symbol, commRate));
1091
+ this_2.commRatesQueue.shift();
1092
+ }
1093
+ else {
1094
+ this_2._withRetry(function () {
1095
+ return _this.traderApi.reqQryInstrumentCommissionRate(__assign(__assign({}, _this.userInfo), { InstrumentID: instrumentId }));
1096
+ });
1097
+ }
1098
+ };
1099
+ var this_2 = this;
1100
+ while (!this.commRatesQueue.isEmpty()) {
1101
+ _loop_2();
1102
+ }
1103
+ };
1104
+ return Trader;
1105
+ }(provider_js_1.CTPProvider));
1106
+ exports.Trader = Trader;
1107
+ var createTrader = function (flowTdPath, frontTdAddrs, userInfo) { return new Trader(flowTdPath, frontTdAddrs, userInfo); };
1108
+ exports.createTrader = createTrader;
1109
+ //# sourceMappingURL=trader.js.map