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/index.test.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* index.test.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,10 +9,10 @@
|
|
|
9
9
|
* https://github.com/shixiongfei/hft.js
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
import process from "node:process";
|
|
12
13
|
import fs from "node:fs";
|
|
13
|
-
import {
|
|
14
|
-
import
|
|
15
|
-
import * as hft from ".";
|
|
14
|
+
import type { DepthMarketDataField } from "@napi-ctp/types";
|
|
15
|
+
import * as hft from "./index.js";
|
|
16
16
|
|
|
17
17
|
export type Configure = {
|
|
18
18
|
FlowTdPath: string;
|
|
@@ -48,7 +48,7 @@ class Strategy implements hft.IStrategy, hft.ITickReceiver, hft.IBarReceiver {
|
|
|
48
48
|
onInstrument: (instrument) => {
|
|
49
49
|
if (!instrument) {
|
|
50
50
|
console.error("Symbol", this.symbol, "error");
|
|
51
|
-
exit(1);
|
|
51
|
+
process.exit(1);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
console.log("Instrument", instrument);
|
|
@@ -101,22 +101,22 @@ class Strategy implements hft.IStrategy, hft.ITickReceiver, hft.IBarReceiver {
|
|
|
101
101
|
console.log(this.lastBar);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
this.engine,
|
|
106
|
-
this,
|
|
107
|
-
this.symbol,
|
|
108
|
-
1,
|
|
109
|
-
this.lastTick.orderBook.asks.price[0],
|
|
110
|
-
{
|
|
111
|
-
onPlaceOrderSent: (receiptId) => {
|
|
112
|
-
console.log("Open Place Order Receipt Id", receiptId);
|
|
113
|
-
},
|
|
104
|
+
const price = this.lastTick.orderBook.asks.price[0];
|
|
114
105
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
106
|
+
if (!price) {
|
|
107
|
+
console.error("Invalid price");
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
hft.buyOpen(this.engine, this, this.symbol, 1, price, {
|
|
112
|
+
onPlaceOrderSent: (receiptId) => {
|
|
113
|
+
console.log("Open Place Order Receipt Id", receiptId);
|
|
118
114
|
},
|
|
119
|
-
|
|
115
|
+
|
|
116
|
+
onPlaceOrderError: (reason) => {
|
|
117
|
+
console.error("Open Place Order Error", reason);
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
120
|
}, 30 * 1000);
|
|
121
121
|
}
|
|
122
122
|
|
|
@@ -224,7 +224,15 @@ const enableRecorder = false;
|
|
|
224
224
|
if (enableRecorder && recorder) {
|
|
225
225
|
recorder.setRecorder(
|
|
226
226
|
{
|
|
227
|
-
|
|
227
|
+
onOpen: () => {
|
|
228
|
+
console.log("Market Recorder is started");
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
onClose: () => {
|
|
232
|
+
console.log("Market Recorder is stopped");
|
|
233
|
+
},
|
|
234
|
+
|
|
235
|
+
onMarketData: (marketData: DepthMarketDataField) => {
|
|
228
236
|
console.log(marketData.InstrumentID, marketData.LastPrice);
|
|
229
237
|
},
|
|
230
238
|
},
|
|
@@ -245,5 +253,5 @@ broker.addStrategy(new Strategy(broker));
|
|
|
245
253
|
|
|
246
254
|
if (!broker.start()) {
|
|
247
255
|
console.error("Broker start failed");
|
|
248
|
-
exit(1);
|
|
256
|
+
process.exit(1);
|
|
249
257
|
}
|
package/src/index.ts
CHANGED
package/src/interfaces.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* interfaces.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,7 +9,7 @@
|
|
|
9
9
|
* https://github.com/shixiongfei/hft.js
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import type {
|
|
13
13
|
BarData,
|
|
14
14
|
CommissionRate,
|
|
15
15
|
InstrumentData,
|
|
@@ -65,7 +65,7 @@ export interface IErrorReceiver {
|
|
|
65
65
|
onError: (error: ErrorType, message: string) => void;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
export interface ILifecycleListener
|
|
68
|
+
export interface ILifecycleListener {
|
|
69
69
|
onOpen: () => void;
|
|
70
70
|
onClose: () => void;
|
|
71
71
|
}
|
|
@@ -144,8 +144,12 @@ export interface IPositionDetailsReceiver {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
export interface IProvider {
|
|
147
|
-
open: (
|
|
148
|
-
|
|
147
|
+
open: (
|
|
148
|
+
lifecycle: ILifecycleListener,
|
|
149
|
+
errorReceiver: IErrorReceiver,
|
|
150
|
+
) => boolean;
|
|
151
|
+
|
|
152
|
+
close: (lifecycle: ILifecycleListener, errorReceiver: IErrorReceiver) => void;
|
|
149
153
|
}
|
|
150
154
|
|
|
151
155
|
export interface IOrderEmitter {
|
|
@@ -179,7 +183,7 @@ export interface IQueryProvider {
|
|
|
179
183
|
queryOrders: (receiver: IOrdersReceiver) => void;
|
|
180
184
|
}
|
|
181
185
|
|
|
182
|
-
export interface IMarketRecorderReceiver {
|
|
186
|
+
export interface IMarketRecorderReceiver extends ILifecycleListener {
|
|
183
187
|
onMarketData: (marketData: any) => void;
|
|
184
188
|
}
|
|
185
189
|
|
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
|
|
@@ -9,12 +9,18 @@
|
|
|
9
9
|
* https://github.com/shixiongfei/hft.js
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import ctp from "napi-ctp";
|
|
12
|
+
import ctp, { type MarketData as MarketApi } from "napi-ctp";
|
|
13
|
+
import type {
|
|
14
|
+
DepthMarketDataField,
|
|
15
|
+
RspUserLoginField,
|
|
16
|
+
SpecificInstrumentField,
|
|
17
|
+
} from "@napi-ctp/types";
|
|
13
18
|
import { CTPProvider } from "./provider.js";
|
|
14
|
-
import { InstrumentData, OrderBook, TickData } from "./typedef.js";
|
|
19
|
+
import type { InstrumentData, OrderBook, TickData } from "./typedef.js";
|
|
15
20
|
import { isValidPrice, isValidVolume, parseSymbol } from "./utils.js";
|
|
16
21
|
import { calcTapeData } from "./tape.js";
|
|
17
|
-
import {
|
|
22
|
+
import type {
|
|
23
|
+
IErrorReceiver,
|
|
18
24
|
ILifecycleListener,
|
|
19
25
|
IMarketProvider,
|
|
20
26
|
IMarketRecorderProvider,
|
|
@@ -36,7 +42,7 @@ export class Market
|
|
|
36
42
|
extends CTPProvider
|
|
37
43
|
implements IMarketProvider, IMarketRecorderProvider
|
|
38
44
|
{
|
|
39
|
-
private marketApi?:
|
|
45
|
+
private marketApi?: MarketApi;
|
|
40
46
|
private recorder?: IMarketRecorderReceiver;
|
|
41
47
|
private recorderSymbols?: IMarketRecorderSymbols;
|
|
42
48
|
private tradingDay: number;
|
|
@@ -83,7 +89,7 @@ export class Market
|
|
|
83
89
|
return this.lastTicks.get(instrumentId);
|
|
84
90
|
}
|
|
85
91
|
|
|
86
|
-
open(lifecycle: ILifecycleListener) {
|
|
92
|
+
open(lifecycle: ILifecycleListener, errorReceiver: IErrorReceiver) {
|
|
87
93
|
if (this.marketApi) {
|
|
88
94
|
return true;
|
|
89
95
|
}
|
|
@@ -96,10 +102,10 @@ export class Market
|
|
|
96
102
|
|
|
97
103
|
let fired = false;
|
|
98
104
|
|
|
99
|
-
this.marketApi.on<
|
|
105
|
+
this.marketApi.on<RspUserLoginField>(
|
|
100
106
|
ctp.MarketDataEvent.RspUserLogin,
|
|
101
107
|
(_, options) => {
|
|
102
|
-
if (this._isErrorResp(
|
|
108
|
+
if (this._isErrorResp(errorReceiver, options, "login-error")) {
|
|
103
109
|
return;
|
|
104
110
|
}
|
|
105
111
|
|
|
@@ -128,7 +134,7 @@ export class Market
|
|
|
128
134
|
},
|
|
129
135
|
);
|
|
130
136
|
|
|
131
|
-
this.marketApi.on<
|
|
137
|
+
this.marketApi.on<SpecificInstrumentField>(
|
|
132
138
|
ctp.MarketDataEvent.RspSubMarketData,
|
|
133
139
|
(instrument) => {
|
|
134
140
|
if (!this.listener) {
|
|
@@ -141,7 +147,7 @@ export class Market
|
|
|
141
147
|
},
|
|
142
148
|
);
|
|
143
149
|
|
|
144
|
-
this.marketApi.on<
|
|
150
|
+
this.marketApi.on<SpecificInstrumentField>(
|
|
145
151
|
ctp.MarketDataEvent.RspUnSubMarketData,
|
|
146
152
|
(instrument) => {
|
|
147
153
|
if (!this.listener) {
|
|
@@ -154,7 +160,7 @@ export class Market
|
|
|
154
160
|
},
|
|
155
161
|
);
|
|
156
162
|
|
|
157
|
-
this.marketApi.on<
|
|
163
|
+
this.marketApi.on<DepthMarketDataField>(
|
|
158
164
|
ctp.MarketDataEvent.RtnDepthMarketData,
|
|
159
165
|
(depthMarketData) => {
|
|
160
166
|
const instrumentId = depthMarketData.InstrumentID;
|
|
@@ -281,23 +287,22 @@ export class Market
|
|
|
281
287
|
orderBook: Object.freeze(orderBook),
|
|
282
288
|
});
|
|
283
289
|
|
|
290
|
+
const lastTick = this.lastTicks.get(instrumentId);
|
|
284
291
|
const receivers = this.subscribers.get(instrumentId);
|
|
285
292
|
|
|
293
|
+
this.lastTicks.set(instrumentId, tick);
|
|
294
|
+
|
|
286
295
|
if (receivers && receivers.length > 0) {
|
|
287
|
-
const lastTick = this.lastTicks.get(instrumentId);
|
|
288
296
|
const tape = calcTapeData(tick, lastTick);
|
|
289
|
-
|
|
290
297
|
receivers.forEach((receiver) => receiver.onTick(tick, tape));
|
|
291
298
|
}
|
|
292
|
-
|
|
293
|
-
this.lastTicks.set(instrumentId, tick);
|
|
294
299
|
},
|
|
295
300
|
);
|
|
296
301
|
|
|
297
302
|
return true;
|
|
298
303
|
}
|
|
299
304
|
|
|
300
|
-
close(lifecycle: ILifecycleListener) {
|
|
305
|
+
close(lifecycle: ILifecycleListener, _errorReceiver: IErrorReceiver) {
|
|
301
306
|
if (!this.marketApi) {
|
|
302
307
|
return;
|
|
303
308
|
}
|
|
@@ -336,6 +341,8 @@ export class Market
|
|
|
336
341
|
this.marketApi?.subscribeMarketData(Array.from(instrumentIds)),
|
|
337
342
|
);
|
|
338
343
|
}
|
|
344
|
+
|
|
345
|
+
this.recorder?.onOpen();
|
|
339
346
|
}
|
|
340
347
|
|
|
341
348
|
stopRecorder() {
|
|
@@ -359,6 +366,8 @@ export class Market
|
|
|
359
366
|
this.marketApi?.unsubscribeMarketData(Array.from(instrumentIds)),
|
|
360
367
|
);
|
|
361
368
|
}
|
|
369
|
+
|
|
370
|
+
this.recorder?.onClose();
|
|
362
371
|
}
|
|
363
372
|
|
|
364
373
|
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
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import fs from "node:fs";
|
|
13
|
-
import ctp from "napi-ctp";
|
|
14
|
-
import { ErrorType,
|
|
13
|
+
import ctp, { type CallbackOptions } from "napi-ctp";
|
|
14
|
+
import type { ErrorType, IErrorReceiver } from "./interfaces.js";
|
|
15
15
|
|
|
16
16
|
export class CTPProvider {
|
|
17
17
|
protected readonly flowPath: string;
|
|
@@ -52,15 +52,15 @@ export class CTPProvider {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
protected _isErrorResp(
|
|
55
|
-
|
|
56
|
-
options:
|
|
55
|
+
errorReceiver: IErrorReceiver,
|
|
56
|
+
options: CallbackOptions,
|
|
57
57
|
error: ErrorType,
|
|
58
58
|
) {
|
|
59
59
|
if (!options.rspInfo) {
|
|
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
|
);
|
|
@@ -69,7 +69,7 @@ export class CTPProvider {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
protected _parseTime(time: string) {
|
|
72
|
-
const [
|
|
73
|
-
return
|
|
72
|
+
const [hh = 0, mm = 0, ss = 0] = time.split(":").map((x) => parseInt(x));
|
|
73
|
+
return hh * 10000 + mm * 100 + ss;
|
|
74
74
|
}
|
|
75
75
|
}
|
package/src/tape.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* tape.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,7 +9,7 @@
|
|
|
9
9
|
* https://github.com/shixiongfei/hft.js
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import type {
|
|
13
13
|
TapeData,
|
|
14
14
|
TapeDirection,
|
|
15
15
|
TapeStatus,
|