hft-js 0.1.8 → 0.2.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/index.test.d.ts +3 -0
- package/lib/index.test.js +10 -23
- package/lib/index.test.js.map +1 -1
- package/lib/interfaces.d.ts +1 -1
- package/lib/market.d.ts +24 -3
- package/lib/market.js +29 -26
- package/lib/market.js.map +1 -1
- package/lib/provider.js +12 -6
- package/lib/provider.js.map +1 -1
- package/lib/trader.d.ts +13 -3
- package/lib/trader.js +146 -18
- package/lib/trader.js.map +1 -1
- package/lib/utils.d.ts +2 -0
- package/lib/utils.js +9 -5
- package/lib/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/index.test.ts +16 -29
- package/src/interfaces.ts +2 -1
- package/src/market.ts +16 -8
- package/src/provider.ts +11 -3
- package/src/trader.ts +284 -25
- package/src/utils.ts +7 -4
package/src/market.ts
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
import ctp from "napi-ctp";
|
|
13
13
|
import { CTPProvider } from "./provider.js";
|
|
14
14
|
import { InstrumentData, OrderBook, TickData } from "./typedef.js";
|
|
15
|
+
import { isValidPrice, isValidVolume, parseSymbol } from "./utils.js";
|
|
15
16
|
import { calcTapeData } from "./tape.js";
|
|
16
|
-
import { parseSymbol } from "./utils.js";
|
|
17
17
|
import {
|
|
18
18
|
ILifecycleListener,
|
|
19
19
|
IMarketProvider,
|
|
@@ -23,14 +23,15 @@ import {
|
|
|
23
23
|
ITickReceiver,
|
|
24
24
|
} from "./interfaces.js";
|
|
25
25
|
|
|
26
|
-
const isValidPrice = (x: number) => x !== Number.MAX_VALUE && x !== 0;
|
|
27
|
-
const isValidVolume = (x: number) => x !== Number.MAX_VALUE && x !== 0;
|
|
28
|
-
|
|
29
26
|
export interface IMarketListener {
|
|
30
27
|
onSubscribed: (symbol: string) => void;
|
|
31
28
|
onUnsubscribed: (symbol: string) => void;
|
|
32
29
|
}
|
|
33
30
|
|
|
31
|
+
export type MarketOptions = {
|
|
32
|
+
listener?: IMarketListener;
|
|
33
|
+
};
|
|
34
|
+
|
|
34
35
|
export class Market
|
|
35
36
|
extends CTPProvider
|
|
36
37
|
implements IMarketProvider, IMarketRecorderProvider
|
|
@@ -48,15 +49,18 @@ export class Market
|
|
|
48
49
|
constructor(
|
|
49
50
|
flowMdPath: string,
|
|
50
51
|
frontMdAddrs: string | string[],
|
|
51
|
-
|
|
52
|
+
options?: MarketOptions,
|
|
52
53
|
) {
|
|
53
54
|
super(flowMdPath, frontMdAddrs);
|
|
54
|
-
this.listener = listener;
|
|
55
55
|
this.tradingDay = 0;
|
|
56
56
|
this.recordings = new Set();
|
|
57
57
|
this.symbols = new Map();
|
|
58
58
|
this.lastTicks = new Map();
|
|
59
59
|
this.subscribers = new Map();
|
|
60
|
+
|
|
61
|
+
if (options?.listener) {
|
|
62
|
+
this.listener = options.listener;
|
|
63
|
+
}
|
|
60
64
|
}
|
|
61
65
|
|
|
62
66
|
getRecorder() {
|
|
@@ -75,6 +79,10 @@ export class Market
|
|
|
75
79
|
this.recorderSymbols = symbols;
|
|
76
80
|
}
|
|
77
81
|
|
|
82
|
+
getLastTick(instrumentId: string) {
|
|
83
|
+
return this.lastTicks.get(instrumentId);
|
|
84
|
+
}
|
|
85
|
+
|
|
78
86
|
open(lifecycle: ILifecycleListener) {
|
|
79
87
|
if (this.marketApi) {
|
|
80
88
|
return true;
|
|
@@ -423,5 +431,5 @@ export class Market
|
|
|
423
431
|
export const createMarket = (
|
|
424
432
|
flowMdPath: string,
|
|
425
433
|
frontMdAddrs: string | string[],
|
|
426
|
-
|
|
427
|
-
) => new Market(flowMdPath, frontMdAddrs,
|
|
434
|
+
options?: MarketOptions,
|
|
435
|
+
) => new Market(flowMdPath, frontMdAddrs, options);
|
package/src/provider.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* https://github.com/shixiongfei/hft.js
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
import fs from "node:fs";
|
|
12
13
|
import ctp from "napi-ctp";
|
|
13
14
|
import { ErrorType, ILifecycleListener } from "./interfaces.js";
|
|
14
15
|
|
|
@@ -19,6 +20,12 @@ export class CTPProvider {
|
|
|
19
20
|
constructor(flowPath: string, frontAddrs: string | string[]) {
|
|
20
21
|
this.flowPath = flowPath;
|
|
21
22
|
this.frontAddrs = frontAddrs;
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
fs.accessSync(this.flowPath);
|
|
26
|
+
} catch {
|
|
27
|
+
fs.mkdirSync(this.flowPath, { recursive: true });
|
|
28
|
+
}
|
|
22
29
|
}
|
|
23
30
|
|
|
24
31
|
private _sleep(ms: number) {
|
|
@@ -35,11 +42,12 @@ export class CTPProvider {
|
|
|
35
42
|
return ctp.getLastRequestId();
|
|
36
43
|
}
|
|
37
44
|
|
|
38
|
-
if (-2
|
|
39
|
-
|
|
45
|
+
if (-2 === retval || -3 === retval) {
|
|
46
|
+
await this._sleep(ms);
|
|
47
|
+
continue;
|
|
40
48
|
}
|
|
41
49
|
|
|
42
|
-
|
|
50
|
+
return retval;
|
|
43
51
|
}
|
|
44
52
|
}
|
|
45
53
|
|
package/src/trader.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import Denque from "denque";
|
|
13
13
|
import ctp from "napi-ctp";
|
|
14
14
|
import { CTPProvider } from "./provider.js";
|
|
15
|
-
import { parseSymbol } from "./utils.js";
|
|
15
|
+
import { isValidPrice, parseSymbol } from "./utils.js";
|
|
16
16
|
import {
|
|
17
17
|
CommissionRate,
|
|
18
18
|
InstrumentData,
|
|
@@ -24,8 +24,10 @@ import {
|
|
|
24
24
|
OrderStatus,
|
|
25
25
|
PositionData,
|
|
26
26
|
PositionDetail,
|
|
27
|
+
PriceRange,
|
|
27
28
|
ProductType,
|
|
28
29
|
SideType,
|
|
30
|
+
TickData,
|
|
29
31
|
TradeData,
|
|
30
32
|
TradingAccount,
|
|
31
33
|
Writeable,
|
|
@@ -47,15 +49,26 @@ import {
|
|
|
47
49
|
ITradingAccountsReceiver,
|
|
48
50
|
} from "./interfaces.js";
|
|
49
51
|
|
|
50
|
-
type
|
|
52
|
+
type PositionInfo = Writeable<PositionData>;
|
|
53
|
+
type OrderStat = Writeable<OrderStatistic>;
|
|
54
|
+
|
|
55
|
+
type MarginRateQuery = {
|
|
56
|
+
symbol: string;
|
|
57
|
+
receiver: IMarginRateReceiver;
|
|
58
|
+
};
|
|
51
59
|
|
|
52
60
|
type CommissionRateQuery = {
|
|
53
61
|
symbol: string;
|
|
54
62
|
receiver: ICommissionRateReceiver;
|
|
55
63
|
};
|
|
56
64
|
|
|
57
|
-
type
|
|
58
|
-
|
|
65
|
+
type MarketOrder = {
|
|
66
|
+
symbol: string;
|
|
67
|
+
offset: OffsetType;
|
|
68
|
+
side: SideType;
|
|
69
|
+
volume: number;
|
|
70
|
+
receiver: IPlaceOrderResultReceiver;
|
|
71
|
+
};
|
|
59
72
|
|
|
60
73
|
export type CTPUserInfo = {
|
|
61
74
|
BrokerID: string;
|
|
@@ -67,6 +80,14 @@ export type CTPUserInfo = {
|
|
|
67
80
|
AppID: string;
|
|
68
81
|
};
|
|
69
82
|
|
|
83
|
+
export type FastQueryLastTickFunc = (
|
|
84
|
+
instrumentId: string,
|
|
85
|
+
) => TickData | undefined;
|
|
86
|
+
|
|
87
|
+
export type TraderOptions = {
|
|
88
|
+
fastQueryLastTick?: FastQueryLastTickFunc;
|
|
89
|
+
};
|
|
90
|
+
|
|
70
91
|
export class Trader extends CTPProvider implements ITraderProvider {
|
|
71
92
|
private traderApi?: ctp.Trader;
|
|
72
93
|
private tradingDay: number;
|
|
@@ -75,6 +96,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
75
96
|
private orderRef: number;
|
|
76
97
|
private accountsQueryTime: number;
|
|
77
98
|
private positionDetailsChanged: boolean;
|
|
99
|
+
private readonly fastQueryLastTick?: FastQueryLastTickFunc;
|
|
78
100
|
private readonly userInfo: CTPUserInfo;
|
|
79
101
|
private readonly receivers: IOrderReceiver[];
|
|
80
102
|
private readonly accounts: ctp.TradingAccountField[];
|
|
@@ -87,6 +109,8 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
87
109
|
private readonly commRates: Map<string, ctp.InstrumentCommissionRateField>;
|
|
88
110
|
private readonly placeOrders: Map<number, IPlaceOrderResultReceiver>;
|
|
89
111
|
private readonly cancelOrders: Map<number, ICancelOrderResultReceiver>;
|
|
112
|
+
private readonly marketOrdersQueue: Map<string, Denque<MarketOrder>>;
|
|
113
|
+
private readonly priceLimit: Map<string, PriceRange>;
|
|
90
114
|
private readonly orderStatistics: Map<string, OrderStat>;
|
|
91
115
|
private readonly marginRatesQueue: Denque<MarginRateQuery>;
|
|
92
116
|
private readonly commRatesQueue: Denque<CommissionRateQuery>;
|
|
@@ -97,6 +121,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
97
121
|
flowTdPath: string,
|
|
98
122
|
frontTdAddrs: string | string[],
|
|
99
123
|
userInfo: CTPUserInfo,
|
|
124
|
+
options?: TraderOptions,
|
|
100
125
|
) {
|
|
101
126
|
super(flowTdPath, frontTdAddrs);
|
|
102
127
|
this.tradingDay = 0;
|
|
@@ -117,11 +142,17 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
117
142
|
this.commRates = new Map();
|
|
118
143
|
this.placeOrders = new Map();
|
|
119
144
|
this.cancelOrders = new Map();
|
|
145
|
+
this.marketOrdersQueue = new Map();
|
|
146
|
+
this.priceLimit = new Map();
|
|
120
147
|
this.orderStatistics = new Map();
|
|
121
148
|
this.marginRatesQueue = new Denque();
|
|
122
149
|
this.commRatesQueue = new Denque();
|
|
123
150
|
this.accountsQueue = new Denque();
|
|
124
151
|
this.positionDetailsQueue = new Denque();
|
|
152
|
+
|
|
153
|
+
if (options?.fastQueryLastTick) {
|
|
154
|
+
this.fastQueryLastTick = options.fastQueryLastTick;
|
|
155
|
+
}
|
|
125
156
|
}
|
|
126
157
|
|
|
127
158
|
open(lifecycle: ILifecycleListener) {
|
|
@@ -136,6 +167,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
136
167
|
});
|
|
137
168
|
|
|
138
169
|
this.traderApi.on(ctp.TraderEvent.FrontDisconnected, () => {
|
|
170
|
+
this._clearAllMarketOrders();
|
|
139
171
|
this.placeOrders.clear();
|
|
140
172
|
this.cancelOrders.clear();
|
|
141
173
|
});
|
|
@@ -168,6 +200,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
168
200
|
this.marginRates.clear();
|
|
169
201
|
this.commRates.clear();
|
|
170
202
|
this.orderStatistics.clear();
|
|
203
|
+
this.priceLimit.clear();
|
|
171
204
|
this.tradingDay = tradingDay;
|
|
172
205
|
}
|
|
173
206
|
|
|
@@ -627,6 +660,75 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
627
660
|
},
|
|
628
661
|
);
|
|
629
662
|
|
|
663
|
+
this.traderApi.on<ctp.DepthMarketDataField>(
|
|
664
|
+
ctp.TraderEvent.RspQryDepthMarketData,
|
|
665
|
+
(depthMarketData, options) => {
|
|
666
|
+
if (
|
|
667
|
+
this._isErrorResp(lifecycle, options, "query-depth-market-data-error")
|
|
668
|
+
) {
|
|
669
|
+
this._clearAllMarketOrders();
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
const isLimitPrice =
|
|
674
|
+
!isValidPrice(depthMarketData.BandingUpperPrice) ||
|
|
675
|
+
!isValidPrice(depthMarketData.BandingLowerPrice);
|
|
676
|
+
|
|
677
|
+
if (isLimitPrice) {
|
|
678
|
+
this.priceLimit.set(depthMarketData.InstrumentID, {
|
|
679
|
+
upper: depthMarketData.UpperLimitPrice,
|
|
680
|
+
lower: depthMarketData.LowerLimitPrice,
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
const queue = this.marketOrdersQueue.get(depthMarketData.InstrumentID);
|
|
685
|
+
|
|
686
|
+
if (!queue) {
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
if (!queue.isEmpty()) {
|
|
691
|
+
const upperPrice = isLimitPrice
|
|
692
|
+
? depthMarketData.UpperLimitPrice
|
|
693
|
+
: depthMarketData.BandingUpperPrice;
|
|
694
|
+
|
|
695
|
+
const lowerPrice = isLimitPrice
|
|
696
|
+
? depthMarketData.LowerLimitPrice
|
|
697
|
+
: depthMarketData.BandingLowerPrice;
|
|
698
|
+
|
|
699
|
+
const orders = queue.toArray();
|
|
700
|
+
|
|
701
|
+
orders.forEach((order) => {
|
|
702
|
+
switch (order.side) {
|
|
703
|
+
case "long":
|
|
704
|
+
this._placeLimitOrder(
|
|
705
|
+
order.symbol,
|
|
706
|
+
order.offset,
|
|
707
|
+
order.side,
|
|
708
|
+
order.volume,
|
|
709
|
+
upperPrice,
|
|
710
|
+
order.receiver,
|
|
711
|
+
);
|
|
712
|
+
break;
|
|
713
|
+
|
|
714
|
+
case "short":
|
|
715
|
+
this._placeLimitOrder(
|
|
716
|
+
order.symbol,
|
|
717
|
+
order.offset,
|
|
718
|
+
order.side,
|
|
719
|
+
order.volume,
|
|
720
|
+
lowerPrice,
|
|
721
|
+
order.receiver,
|
|
722
|
+
);
|
|
723
|
+
break;
|
|
724
|
+
}
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
this.marketOrdersQueue.delete(depthMarketData.InstrumentID);
|
|
729
|
+
},
|
|
730
|
+
);
|
|
731
|
+
|
|
630
732
|
return true;
|
|
631
733
|
}
|
|
632
734
|
|
|
@@ -862,21 +964,15 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
862
964
|
receiver.onOrders(orders);
|
|
863
965
|
}
|
|
864
966
|
|
|
865
|
-
|
|
967
|
+
private _placeLimitOrder(
|
|
866
968
|
symbol: string,
|
|
867
969
|
offset: OffsetType,
|
|
868
970
|
side: SideType,
|
|
869
971
|
volume: number,
|
|
870
972
|
price: number,
|
|
871
|
-
flag: OrderFlag,
|
|
872
973
|
receiver: IPlaceOrderResultReceiver,
|
|
873
974
|
) {
|
|
874
|
-
|
|
875
|
-
receiver.onPlaceOrderError("Only Supports Limit Order");
|
|
876
|
-
return;
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
const [instrumentId] = parseSymbol(symbol);
|
|
975
|
+
const [instrumentId, exchangeId] = parseSymbol(symbol);
|
|
880
976
|
const instrument = this.instruments.get(instrumentId);
|
|
881
977
|
|
|
882
978
|
if (!instrument) {
|
|
@@ -884,6 +980,11 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
884
980
|
return;
|
|
885
981
|
}
|
|
886
982
|
|
|
983
|
+
if (exchangeId !== instrument.ExchangeID) {
|
|
984
|
+
receiver.onPlaceOrderError("Exchange Id Error");
|
|
985
|
+
return;
|
|
986
|
+
}
|
|
987
|
+
|
|
887
988
|
let orderRef = 0;
|
|
888
989
|
|
|
889
990
|
this._withRetry(() => {
|
|
@@ -908,12 +1009,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
908
1009
|
UserForceClose: 0,
|
|
909
1010
|
});
|
|
910
1011
|
}).then((requestId) => {
|
|
911
|
-
if (!requestId) {
|
|
912
|
-
receiver.onPlaceOrderError("Request Failed");
|
|
913
|
-
return;
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
if (requestId < 0) {
|
|
1012
|
+
if (!requestId || requestId < 0) {
|
|
917
1013
|
receiver.onPlaceOrderError("Request Error");
|
|
918
1014
|
return;
|
|
919
1015
|
}
|
|
@@ -932,6 +1028,173 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
932
1028
|
});
|
|
933
1029
|
}
|
|
934
1030
|
|
|
1031
|
+
private _clearAllMarketOrders() {
|
|
1032
|
+
this.marketOrdersQueue.forEach((queue) => {
|
|
1033
|
+
const orders = queue.toArray();
|
|
1034
|
+
|
|
1035
|
+
orders.forEach((order) => {
|
|
1036
|
+
order.receiver.onPlaceOrderError("Request Error");
|
|
1037
|
+
});
|
|
1038
|
+
});
|
|
1039
|
+
|
|
1040
|
+
this.marketOrdersQueue.clear();
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
private _placeMarketOrder(
|
|
1044
|
+
symbol: string,
|
|
1045
|
+
offset: OffsetType,
|
|
1046
|
+
side: SideType,
|
|
1047
|
+
volume: number,
|
|
1048
|
+
receiver: IPlaceOrderResultReceiver,
|
|
1049
|
+
) {
|
|
1050
|
+
const [instrumentId, exchangeId] = parseSymbol(symbol);
|
|
1051
|
+
const instrument = this.instruments.get(instrumentId);
|
|
1052
|
+
|
|
1053
|
+
if (!instrument) {
|
|
1054
|
+
receiver.onPlaceOrderError("Instrument Not Found");
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
if (exchangeId !== instrument.ExchangeID) {
|
|
1059
|
+
receiver.onPlaceOrderError("Exchange Id Error");
|
|
1060
|
+
return;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
const priceRange = this.priceLimit.get(instrumentId);
|
|
1064
|
+
|
|
1065
|
+
if (priceRange) {
|
|
1066
|
+
switch (side) {
|
|
1067
|
+
case "long":
|
|
1068
|
+
this._placeLimitOrder(
|
|
1069
|
+
symbol,
|
|
1070
|
+
offset,
|
|
1071
|
+
side,
|
|
1072
|
+
volume,
|
|
1073
|
+
priceRange.upper,
|
|
1074
|
+
receiver,
|
|
1075
|
+
);
|
|
1076
|
+
break;
|
|
1077
|
+
|
|
1078
|
+
case "short":
|
|
1079
|
+
this._placeLimitOrder(
|
|
1080
|
+
symbol,
|
|
1081
|
+
offset,
|
|
1082
|
+
side,
|
|
1083
|
+
volume,
|
|
1084
|
+
priceRange.lower,
|
|
1085
|
+
receiver,
|
|
1086
|
+
);
|
|
1087
|
+
break;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
return;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
if (this.fastQueryLastTick) {
|
|
1094
|
+
const lastTick = this.fastQueryLastTick(instrumentId);
|
|
1095
|
+
|
|
1096
|
+
if (lastTick && lastTick.tradingDay === this.tradingDay) {
|
|
1097
|
+
const isLimitPrice =
|
|
1098
|
+
!isValidPrice(lastTick.bandings.upper) ||
|
|
1099
|
+
!isValidPrice(lastTick.bandings.lower);
|
|
1100
|
+
|
|
1101
|
+
if (isLimitPrice) {
|
|
1102
|
+
this.priceLimit.set(instrumentId, { ...lastTick.limits });
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
const upperPrice = isLimitPrice
|
|
1106
|
+
? lastTick.limits.upper
|
|
1107
|
+
: lastTick.bandings.upper;
|
|
1108
|
+
|
|
1109
|
+
const lowerPrice = isLimitPrice
|
|
1110
|
+
? lastTick.limits.lower
|
|
1111
|
+
: lastTick.bandings.lower;
|
|
1112
|
+
|
|
1113
|
+
switch (side) {
|
|
1114
|
+
case "long":
|
|
1115
|
+
this._placeLimitOrder(
|
|
1116
|
+
symbol,
|
|
1117
|
+
offset,
|
|
1118
|
+
side,
|
|
1119
|
+
volume,
|
|
1120
|
+
upperPrice,
|
|
1121
|
+
receiver,
|
|
1122
|
+
);
|
|
1123
|
+
break;
|
|
1124
|
+
|
|
1125
|
+
case "short":
|
|
1126
|
+
this._placeLimitOrder(
|
|
1127
|
+
symbol,
|
|
1128
|
+
offset,
|
|
1129
|
+
side,
|
|
1130
|
+
volume,
|
|
1131
|
+
lowerPrice,
|
|
1132
|
+
receiver,
|
|
1133
|
+
);
|
|
1134
|
+
break;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
let queue = this.marketOrdersQueue.get(instrumentId);
|
|
1142
|
+
|
|
1143
|
+
if (queue) {
|
|
1144
|
+
queue.push({ symbol, offset, side, volume, receiver });
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
queue = new Denque();
|
|
1149
|
+
this.marketOrdersQueue.set(instrumentId, queue);
|
|
1150
|
+
|
|
1151
|
+
this._withRetry(() =>
|
|
1152
|
+
this.traderApi?.reqQryDepthMarketData({
|
|
1153
|
+
ExchangeID: exchangeId,
|
|
1154
|
+
InstrumentID: instrumentId,
|
|
1155
|
+
}),
|
|
1156
|
+
).then((requestId) => {
|
|
1157
|
+
if (!requestId || requestId < 0) {
|
|
1158
|
+
const orders = queue.toArray();
|
|
1159
|
+
|
|
1160
|
+
orders.forEach((order) => {
|
|
1161
|
+
order.receiver.onPlaceOrderError("Request Error");
|
|
1162
|
+
});
|
|
1163
|
+
|
|
1164
|
+
this.marketOrdersQueue.delete(instrumentId);
|
|
1165
|
+
|
|
1166
|
+
return;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
queue.push({ symbol, offset, side, volume, receiver });
|
|
1170
|
+
});
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
placeOrder(
|
|
1174
|
+
symbol: string,
|
|
1175
|
+
offset: OffsetType,
|
|
1176
|
+
side: SideType,
|
|
1177
|
+
volume: number,
|
|
1178
|
+
price: number,
|
|
1179
|
+
flag: OrderFlag,
|
|
1180
|
+
receiver: IPlaceOrderResultReceiver,
|
|
1181
|
+
) {
|
|
1182
|
+
switch (flag) {
|
|
1183
|
+
case "limit":
|
|
1184
|
+
return this._placeLimitOrder(
|
|
1185
|
+
symbol,
|
|
1186
|
+
offset,
|
|
1187
|
+
side,
|
|
1188
|
+
volume,
|
|
1189
|
+
price,
|
|
1190
|
+
receiver,
|
|
1191
|
+
);
|
|
1192
|
+
|
|
1193
|
+
case "market":
|
|
1194
|
+
return this._placeMarketOrder(symbol, offset, side, volume, receiver);
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
|
|
935
1198
|
cancelOrder(order: OrderData, receiver: ICancelOrderResultReceiver) {
|
|
936
1199
|
const current = this.orders.get(order.id);
|
|
937
1200
|
|
|
@@ -957,12 +1220,7 @@ export class Trader extends CTPProvider implements ITraderProvider {
|
|
|
957
1220
|
ActionFlag: ctp.ActionFlagType.Delete,
|
|
958
1221
|
}),
|
|
959
1222
|
).then((requestId) => {
|
|
960
|
-
if (!requestId) {
|
|
961
|
-
receiver.onCancelOrderError("Request Failed");
|
|
962
|
-
return;
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
if (requestId < 0) {
|
|
1223
|
+
if (!requestId || requestId < 0) {
|
|
966
1224
|
receiver.onCancelOrderError("Request Error");
|
|
967
1225
|
return;
|
|
968
1226
|
}
|
|
@@ -1605,4 +1863,5 @@ export const createTrader = (
|
|
|
1605
1863
|
flowTdPath: string,
|
|
1606
1864
|
frontTdAddrs: string | string[],
|
|
1607
1865
|
userInfo: CTPUserInfo,
|
|
1608
|
-
|
|
1866
|
+
options?: TraderOptions,
|
|
1867
|
+
) => new Trader(flowTdPath, frontTdAddrs, userInfo, options);
|
package/src/utils.ts
CHANGED
|
@@ -17,6 +17,9 @@ import {
|
|
|
17
17
|
IStrategy,
|
|
18
18
|
} from "./interfaces.js";
|
|
19
19
|
|
|
20
|
+
export const isValidPrice = (x: number) => x !== Number.MAX_VALUE && x !== 0;
|
|
21
|
+
export const isValidVolume = (x: number) => x !== Number.MAX_VALUE && x !== 0;
|
|
22
|
+
|
|
20
23
|
export const parseSymbol = (symbol: string): [string, string] => {
|
|
21
24
|
const [instrumentId, exchangeId] = symbol.split(".");
|
|
22
25
|
return [instrumentId, exchangeId];
|
|
@@ -130,7 +133,7 @@ export const buyOpen = (
|
|
|
130
133
|
"long",
|
|
131
134
|
volume,
|
|
132
135
|
price,
|
|
133
|
-
flag,
|
|
136
|
+
price > 0 && flag === "limit" ? "limit" : "market",
|
|
134
137
|
receiver,
|
|
135
138
|
);
|
|
136
139
|
|
|
@@ -151,7 +154,7 @@ export const buyClose = (
|
|
|
151
154
|
"long",
|
|
152
155
|
volume,
|
|
153
156
|
price,
|
|
154
|
-
flag,
|
|
157
|
+
price > 0 && flag === "limit" ? "limit" : "market",
|
|
155
158
|
receiver,
|
|
156
159
|
);
|
|
157
160
|
|
|
@@ -171,7 +174,7 @@ export const sellOpen = (
|
|
|
171
174
|
"short",
|
|
172
175
|
volume,
|
|
173
176
|
price,
|
|
174
|
-
flag,
|
|
177
|
+
price > 0 && flag === "limit" ? "limit" : "market",
|
|
175
178
|
receiver,
|
|
176
179
|
);
|
|
177
180
|
|
|
@@ -192,6 +195,6 @@ export const sellClose = (
|
|
|
192
195
|
"short",
|
|
193
196
|
volume,
|
|
194
197
|
price,
|
|
195
|
-
flag,
|
|
198
|
+
price > 0 && flag === "limit" ? "limit" : "market",
|
|
196
199
|
receiver,
|
|
197
200
|
);
|