hft-js 0.1.0 → 0.1.2
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 +15 -0
- package/lib/bar.js +114 -0
- package/lib/bar.js.map +1 -0
- package/lib/broker.d.ts +8 -1
- package/lib/broker.js +42 -2
- package/lib/broker.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/index.test.js +41 -31
- package/lib/index.test.js.map +1 -1
- package/lib/interfaces.d.ts +18 -6
- package/lib/market.d.ts +2 -0
- package/lib/market.js +19 -8
- package/lib/market.js.map +1 -1
- package/lib/provider.d.ts +0 -1
- package/lib/provider.js +0 -4
- package/lib/provider.js.map +1 -1
- package/lib/tape.js +10 -10
- package/lib/tape.js.map +1 -1
- package/lib/trader.d.ts +2 -2
- package/lib/trader.js +34 -32
- package/lib/trader.js.map +1 -1
- package/lib/typedef.d.ts +24 -7
- package/lib/utils.d.ts +6 -0
- package/lib/utils.js +106 -0
- package/lib/utils.js.map +1 -0
- package/package.json +1 -1
- package/src/bar.ts +133 -0
- package/src/broker.ts +120 -2
- package/src/index.test.ts +57 -49
- package/src/index.ts +1 -1
- package/src/interfaces.ts +54 -5
- package/src/market.ts +26 -10
- package/src/provider.ts +0 -5
- package/src/tape.ts +11 -10
- package/src/trader.ts +39 -46
- package/src/typedef.ts +25 -6
- package/src/utils.ts +110 -0
package/lib/utils.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* utils.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 __assign = (this && this.__assign) || function () {
|
|
13
|
+
__assign = Object.assign || function(t) {
|
|
14
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
15
|
+
s = arguments[i];
|
|
16
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
17
|
+
t[p] = s[p];
|
|
18
|
+
}
|
|
19
|
+
return t;
|
|
20
|
+
};
|
|
21
|
+
return __assign.apply(this, arguments);
|
|
22
|
+
};
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.mergeBarData = exports.getBarVolume = exports.getBarSellVolume = exports.getBarBuyVolume = exports.parseSymbol = void 0;
|
|
25
|
+
var parseSymbol = function (symbol) {
|
|
26
|
+
var _a = symbol.split("."), instrumentId = _a[0], exchangeId = _a[1];
|
|
27
|
+
return [instrumentId, exchangeId];
|
|
28
|
+
};
|
|
29
|
+
exports.parseSymbol = parseSymbol;
|
|
30
|
+
var getBarBuyVolume = function (bar, price) { var _a; return (_a = bar.buyVolumes[price]) !== null && _a !== void 0 ? _a : 0; };
|
|
31
|
+
exports.getBarBuyVolume = getBarBuyVolume;
|
|
32
|
+
var getBarSellVolume = function (bar, price) { var _a; return (_a = bar.sellVolumes[price]) !== null && _a !== void 0 ? _a : 0; };
|
|
33
|
+
exports.getBarSellVolume = getBarSellVolume;
|
|
34
|
+
var getBarVolume = function (bar, price) {
|
|
35
|
+
return (0, exports.getBarBuyVolume)(bar, price) + (0, exports.getBarSellVolume)(bar, price);
|
|
36
|
+
};
|
|
37
|
+
exports.getBarVolume = getBarVolume;
|
|
38
|
+
var mergeBarData = function (bars) {
|
|
39
|
+
var _a, _b;
|
|
40
|
+
if (bars.length === 0) {
|
|
41
|
+
throw new Error("Bars is empty");
|
|
42
|
+
}
|
|
43
|
+
if (bars.length === 1) {
|
|
44
|
+
return bars[1];
|
|
45
|
+
}
|
|
46
|
+
var bar = {
|
|
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: __assign({}, bars[0].buyVolumes),
|
|
60
|
+
sellVolumes: __assign({}, bars[0].sellVolumes),
|
|
61
|
+
};
|
|
62
|
+
for (var i = 1; i < bars.length; ++i) {
|
|
63
|
+
var nextBar = bars[i];
|
|
64
|
+
bar.openInterest = nextBar.openInterest;
|
|
65
|
+
bar.closePrice = nextBar.closePrice;
|
|
66
|
+
bar.highPrice = Math.max(bar.highPrice, nextBar.highPrice);
|
|
67
|
+
bar.lowPrice = Math.min(bar.lowPrice, nextBar.lowPrice);
|
|
68
|
+
bar.volume += nextBar.volume;
|
|
69
|
+
bar.amount += nextBar.amount;
|
|
70
|
+
for (var price in nextBar.buyVolumes) {
|
|
71
|
+
var volumeDelta = nextBar.buyVolumes[price];
|
|
72
|
+
if (price in bar.buyVolumes) {
|
|
73
|
+
bar.buyVolumes[price] += volumeDelta;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
bar.buyVolumes[price] = volumeDelta;
|
|
77
|
+
}
|
|
78
|
+
bar.delta += volumeDelta;
|
|
79
|
+
var priceVP = bar.buyVolumes[price] + ((_a = bar.sellVolumes[price]) !== null && _a !== void 0 ? _a : 0);
|
|
80
|
+
var pocVP = (0, exports.getBarVolume)(bar, bar.poc);
|
|
81
|
+
if (priceVP > pocVP) {
|
|
82
|
+
bar.poc = parseFloat(price);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
for (var price in nextBar.sellVolumes) {
|
|
86
|
+
var volumeDelta = nextBar.sellVolumes[price];
|
|
87
|
+
if (price in bar.sellVolumes) {
|
|
88
|
+
bar.sellVolumes[price] += volumeDelta;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
bar.sellVolumes[price] = volumeDelta;
|
|
92
|
+
}
|
|
93
|
+
bar.delta -= volumeDelta;
|
|
94
|
+
var priceVP = bar.sellVolumes[price] + ((_b = bar.buyVolumes[price]) !== null && _b !== void 0 ? _b : 0);
|
|
95
|
+
var pocVP = (0, exports.getBarVolume)(bar, bar.poc);
|
|
96
|
+
if (priceVP > pocVP) {
|
|
97
|
+
bar.poc = parseFloat(price);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
Object.freeze(bar.buyVolumes);
|
|
102
|
+
Object.freeze(bar.sellVolumes);
|
|
103
|
+
return Object.freeze(bar);
|
|
104
|
+
};
|
|
105
|
+
exports.mergeBarData = mergeBarData;
|
|
106
|
+
//# sourceMappingURL=utils.js.map
|
package/lib/utils.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/bar.ts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* bar.ts
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2025 Xiongfei Shi
|
|
5
|
+
*
|
|
6
|
+
* Author: Xiongfei Shi <xiongfei.shi(a)icloud.com>
|
|
7
|
+
* License: Apache-2.0
|
|
8
|
+
*
|
|
9
|
+
* https://github.com/shixiongfei/hft.js
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { BarData, TapeData, TickData, Writeable } from "./typedef.js";
|
|
13
|
+
import { IBarReceiver, ITickReceiver } from "./interfaces.js";
|
|
14
|
+
import { getBarVolume } from "./utils.js";
|
|
15
|
+
|
|
16
|
+
export type BarInfo = Writeable<BarData>;
|
|
17
|
+
|
|
18
|
+
export class BarGenerator implements ITickReceiver {
|
|
19
|
+
private readonly receivers: IBarReceiver[];
|
|
20
|
+
private readonly symbol: string;
|
|
21
|
+
private bar?: BarInfo;
|
|
22
|
+
|
|
23
|
+
constructor(symbol: string) {
|
|
24
|
+
this.receivers = [];
|
|
25
|
+
this.symbol = symbol;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get isWorking() {
|
|
29
|
+
return this.receivers.length > 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
addReceiver(receiver: IBarReceiver) {
|
|
33
|
+
if (!this.receivers.includes(receiver)) {
|
|
34
|
+
this.receivers.push(receiver);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
removeReceiver(receiver: IBarReceiver) {
|
|
39
|
+
const index = this.receivers.indexOf(receiver);
|
|
40
|
+
|
|
41
|
+
if (index >= 0) {
|
|
42
|
+
this.receivers.splice(index, 1);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
onTick(tick: TickData, tape: TapeData) {
|
|
47
|
+
if (tick.symbol !== this.symbol) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const date = tick.date;
|
|
52
|
+
const time = Math.floor(tick.time / 100) * 100;
|
|
53
|
+
|
|
54
|
+
if (this.bar && (this.bar.date !== date || this.bar.time !== time)) {
|
|
55
|
+
const bar = Object.freeze(this.bar);
|
|
56
|
+
|
|
57
|
+
Object.freeze(bar.buyVolumes);
|
|
58
|
+
Object.freeze(bar.sellVolumes);
|
|
59
|
+
|
|
60
|
+
this.receivers.forEach((receiver) => receiver.onBar(bar));
|
|
61
|
+
this.bar = undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (tape.volumeDelta === 0) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!this.bar) {
|
|
69
|
+
this.bar = this._createBar(date, time, tick);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
this.bar.openInterest = tick.openInterest;
|
|
73
|
+
this.bar.closePrice = tick.lastPrice;
|
|
74
|
+
|
|
75
|
+
this.bar.highPrice = Math.max(this.bar.highPrice, tick.lastPrice);
|
|
76
|
+
this.bar.lowPrice = Math.min(this.bar.lowPrice, tick.lastPrice);
|
|
77
|
+
|
|
78
|
+
this.bar.volume += tape.volumeDelta;
|
|
79
|
+
this.bar.amount += tape.amountDelta;
|
|
80
|
+
|
|
81
|
+
switch (tape.direction) {
|
|
82
|
+
case "up":
|
|
83
|
+
if (tick.lastPrice in this.bar.buyVolumes) {
|
|
84
|
+
this.bar.buyVolumes[tick.lastPrice] += tape.volumeDelta;
|
|
85
|
+
} else {
|
|
86
|
+
this.bar.buyVolumes[tick.lastPrice] = tape.volumeDelta;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
this.bar.delta += tape.volumeDelta;
|
|
90
|
+
break;
|
|
91
|
+
|
|
92
|
+
case "down":
|
|
93
|
+
if (tick.lastPrice in this.bar.sellVolumes) {
|
|
94
|
+
this.bar.sellVolumes[tick.lastPrice] += tape.volumeDelta;
|
|
95
|
+
} else {
|
|
96
|
+
this.bar.sellVolumes[tick.lastPrice] = tape.volumeDelta;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
this.bar.delta -= tape.volumeDelta;
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (tape.direction !== "none") {
|
|
104
|
+
const tickVP = getBarVolume(this.bar, tick.lastPrice);
|
|
105
|
+
const pocVP = getBarVolume(this.bar, this.bar.poc);
|
|
106
|
+
|
|
107
|
+
if (tickVP > pocVP) {
|
|
108
|
+
this.bar.poc = tick.lastPrice;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private _createBar(date: number, time: number, tick: TickData): BarInfo {
|
|
114
|
+
return {
|
|
115
|
+
symbol: this.symbol,
|
|
116
|
+
date: date,
|
|
117
|
+
time: time,
|
|
118
|
+
openInterest: tick.openInterest,
|
|
119
|
+
openPrice: tick.lastPrice,
|
|
120
|
+
highPrice: tick.lastPrice,
|
|
121
|
+
lowPrice: tick.lastPrice,
|
|
122
|
+
closePrice: tick.lastPrice,
|
|
123
|
+
volume: 0,
|
|
124
|
+
amount: 0,
|
|
125
|
+
delta: 0,
|
|
126
|
+
poc: tick.lastPrice,
|
|
127
|
+
buyVolumes: {},
|
|
128
|
+
sellVolumes: {},
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export const createBarGenerator = (symbol: string) => new BarGenerator(symbol);
|
package/src/broker.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { OffsetType, OrderData, OrderFlag, SideType } from "./typedef.js";
|
|
13
|
+
import { BarGenerator, createBarGenerator } from "./bar.js";
|
|
13
14
|
import {
|
|
14
15
|
ICancelOrderResultReceiver,
|
|
15
16
|
ErrorType,
|
|
@@ -32,6 +33,7 @@ import {
|
|
|
32
33
|
IPlaceOrderResultReceiver,
|
|
33
34
|
IPositionReceiver,
|
|
34
35
|
IPositionDetailsReceiver,
|
|
36
|
+
IBarReceiver,
|
|
35
37
|
} from "./interfaces.js";
|
|
36
38
|
|
|
37
39
|
export class Broker implements IRuntimeEngine {
|
|
@@ -42,6 +44,7 @@ export class Broker implements IRuntimeEngine {
|
|
|
42
44
|
private readonly strategies: IStrategy[] = [];
|
|
43
45
|
private readonly placeOrderRiskManagers: IPlaceOrderRiskManager[] = [];
|
|
44
46
|
private readonly cancelOrderRiskManagers: ICancelOrderRiskManager[] = [];
|
|
47
|
+
private readonly generators: Map<string, BarGenerator>;
|
|
45
48
|
|
|
46
49
|
constructor(
|
|
47
50
|
trader: ITraderProvider,
|
|
@@ -50,6 +53,7 @@ export class Broker implements IRuntimeEngine {
|
|
|
50
53
|
) {
|
|
51
54
|
this.trader = trader;
|
|
52
55
|
this.market = market;
|
|
56
|
+
this.generators = new Map();
|
|
53
57
|
|
|
54
58
|
this.marketLifecycle = {
|
|
55
59
|
onOpen: () => {
|
|
@@ -63,12 +67,14 @@ export class Broker implements IRuntimeEngine {
|
|
|
63
67
|
});
|
|
64
68
|
}
|
|
65
69
|
|
|
66
|
-
this.strategies.forEach((strategy) => strategy.onInit(
|
|
70
|
+
this.strategies.forEach((strategy) => strategy.onInit());
|
|
67
71
|
},
|
|
72
|
+
|
|
68
73
|
onClose: () => {
|
|
69
|
-
this.strategies.forEach((strategy) => strategy.onDestroy(
|
|
74
|
+
this.strategies.forEach((strategy) => strategy.onDestroy());
|
|
70
75
|
this.market.stopRecorder();
|
|
71
76
|
},
|
|
77
|
+
|
|
72
78
|
onError: (error: ErrorType, message: string) => {
|
|
73
79
|
if (errorReceiver) {
|
|
74
80
|
errorReceiver.onError(error, message);
|
|
@@ -80,9 +86,11 @@ export class Broker implements IRuntimeEngine {
|
|
|
80
86
|
onOpen: () => {
|
|
81
87
|
this.market.open(this.marketLifecycle);
|
|
82
88
|
},
|
|
89
|
+
|
|
83
90
|
onClose: () => {
|
|
84
91
|
this.market.close(this.marketLifecycle);
|
|
85
92
|
},
|
|
93
|
+
|
|
86
94
|
onError: (error: ErrorType, message: string) => {
|
|
87
95
|
if (errorReceiver) {
|
|
88
96
|
errorReceiver.onError(error, message);
|
|
@@ -137,6 +145,38 @@ export class Broker implements IRuntimeEngine {
|
|
|
137
145
|
return this.market.unsubscribe(symbols, receiver);
|
|
138
146
|
}
|
|
139
147
|
|
|
148
|
+
subscribeBar(symbols: string[], receiver: IBarReceiver) {
|
|
149
|
+
symbols.forEach((symbol) => {
|
|
150
|
+
let generator = this.generators.get(symbol);
|
|
151
|
+
|
|
152
|
+
if (!generator) {
|
|
153
|
+
generator = createBarGenerator(symbol);
|
|
154
|
+
|
|
155
|
+
this.generators.set(symbol, generator);
|
|
156
|
+
this.subscribe([symbol], generator);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
generator.addReceiver(receiver);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
unsubscribeBar(symbols: string[], receiver: IBarReceiver) {
|
|
164
|
+
symbols.forEach((symbol) => {
|
|
165
|
+
const generator = this.generators.get(symbol);
|
|
166
|
+
|
|
167
|
+
if (!generator) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
generator.removeReceiver(receiver);
|
|
172
|
+
|
|
173
|
+
if (!generator.isWorking) {
|
|
174
|
+
this.unsubscribe([symbol], generator);
|
|
175
|
+
this.generators.delete(symbol);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
140
180
|
placeOrder(
|
|
141
181
|
strategy: IStrategy,
|
|
142
182
|
symbol: string,
|
|
@@ -205,6 +245,84 @@ export class Broker implements IRuntimeEngine {
|
|
|
205
245
|
return this.trader.cancelOrder(order, receiver);
|
|
206
246
|
}
|
|
207
247
|
|
|
248
|
+
buyOpen(
|
|
249
|
+
strategy: IStrategy,
|
|
250
|
+
symbol: string,
|
|
251
|
+
volume: number,
|
|
252
|
+
price: number,
|
|
253
|
+
receiver: IPlaceOrderResultReceiver,
|
|
254
|
+
) {
|
|
255
|
+
return this.placeOrder(
|
|
256
|
+
strategy,
|
|
257
|
+
symbol,
|
|
258
|
+
"open",
|
|
259
|
+
"long",
|
|
260
|
+
volume,
|
|
261
|
+
price,
|
|
262
|
+
"limit",
|
|
263
|
+
receiver,
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
buyClose(
|
|
268
|
+
strategy: IStrategy,
|
|
269
|
+
symbol: string,
|
|
270
|
+
volume: number,
|
|
271
|
+
price: number,
|
|
272
|
+
isToday: boolean,
|
|
273
|
+
receiver: IPlaceOrderResultReceiver,
|
|
274
|
+
) {
|
|
275
|
+
return this.placeOrder(
|
|
276
|
+
strategy,
|
|
277
|
+
symbol,
|
|
278
|
+
isToday ? "close-today" : "close",
|
|
279
|
+
"long",
|
|
280
|
+
volume,
|
|
281
|
+
price,
|
|
282
|
+
"limit",
|
|
283
|
+
receiver,
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
sellOpen(
|
|
288
|
+
strategy: IStrategy,
|
|
289
|
+
symbol: string,
|
|
290
|
+
volume: number,
|
|
291
|
+
price: number,
|
|
292
|
+
receiver: IPlaceOrderResultReceiver,
|
|
293
|
+
) {
|
|
294
|
+
return this.placeOrder(
|
|
295
|
+
strategy,
|
|
296
|
+
symbol,
|
|
297
|
+
"open",
|
|
298
|
+
"short",
|
|
299
|
+
volume,
|
|
300
|
+
price,
|
|
301
|
+
"limit",
|
|
302
|
+
receiver,
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
sellClose(
|
|
307
|
+
strategy: IStrategy,
|
|
308
|
+
symbol: string,
|
|
309
|
+
volume: number,
|
|
310
|
+
price: number,
|
|
311
|
+
isToday: boolean,
|
|
312
|
+
receiver: IPlaceOrderResultReceiver,
|
|
313
|
+
) {
|
|
314
|
+
return this.placeOrder(
|
|
315
|
+
strategy,
|
|
316
|
+
symbol,
|
|
317
|
+
isToday ? "close-today" : "close",
|
|
318
|
+
"short",
|
|
319
|
+
volume,
|
|
320
|
+
price,
|
|
321
|
+
"limit",
|
|
322
|
+
receiver,
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
|
|
208
326
|
getTradingDay() {
|
|
209
327
|
return this.trader.getTradingDay();
|
|
210
328
|
}
|
package/src/index.test.ts
CHANGED
|
@@ -43,8 +43,9 @@ if (!existsFile(config.FlowMdPath)) {
|
|
|
43
43
|
fs.mkdirSync(config.FlowMdPath, { recursive: true });
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
class Strategy implements hft.IStrategy, hft.ITickReceiver {
|
|
46
|
+
class Strategy implements hft.IStrategy, hft.ITickReceiver, hft.IBarReceiver {
|
|
47
47
|
private lastTick?: hft.TickData;
|
|
48
|
+
private lastBar?: hft.BarData;
|
|
48
49
|
private engine: hft.IRuntimeEngine;
|
|
49
50
|
readonly symbol = "ni2505.SHFE";
|
|
50
51
|
|
|
@@ -52,10 +53,11 @@ class Strategy implements hft.IStrategy, hft.ITickReceiver {
|
|
|
52
53
|
this.engine = engine;
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
onInit(
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
onInit() {
|
|
57
|
+
this.engine.subscribe([this.symbol], this);
|
|
58
|
+
this.engine.subscribeBar([this.symbol], this);
|
|
58
59
|
|
|
60
|
+
console.log("Strategy init");
|
|
59
61
|
console.log("Trading Day", this.engine.getTradingDay());
|
|
60
62
|
|
|
61
63
|
this.engine.queryInstrument(this.symbol, {
|
|
@@ -111,14 +113,15 @@ class Strategy implements hft.IStrategy, hft.ITickReceiver {
|
|
|
111
113
|
return;
|
|
112
114
|
}
|
|
113
115
|
|
|
114
|
-
this.
|
|
116
|
+
if (this.lastBar) {
|
|
117
|
+
console.log(this.lastBar);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
this.engine.buyOpen(
|
|
115
121
|
this,
|
|
116
122
|
this.symbol,
|
|
117
|
-
"open",
|
|
118
|
-
"long",
|
|
119
123
|
1,
|
|
120
124
|
this.lastTick.orderBook.asks.price[0],
|
|
121
|
-
"limit",
|
|
122
125
|
{
|
|
123
126
|
onPlaceOrderSent: (receiptId) => {
|
|
124
127
|
console.log("Open Place Order Receipt Id", receiptId);
|
|
@@ -132,8 +135,9 @@ class Strategy implements hft.IStrategy, hft.ITickReceiver {
|
|
|
132
135
|
}, 30 * 1000);
|
|
133
136
|
}
|
|
134
137
|
|
|
135
|
-
onDestroy(
|
|
136
|
-
|
|
138
|
+
onDestroy() {
|
|
139
|
+
this.engine.unsubscribeBar([this.symbol], this);
|
|
140
|
+
this.engine.unsubscribe([this.symbol], this);
|
|
137
141
|
console.log("Strategy destroy");
|
|
138
142
|
}
|
|
139
143
|
|
|
@@ -147,44 +151,44 @@ class Strategy implements hft.IStrategy, hft.ITickReceiver {
|
|
|
147
151
|
|
|
148
152
|
onTrade(order: hft.OrderData, trade: hft.TradeData) {
|
|
149
153
|
console.log("Order", order, "Traded", trade);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
onFinish(order: hft.OrderData) {
|
|
153
|
-
console.log("Finish Order", order);
|
|
154
|
-
|
|
155
|
-
setTimeout(() => {
|
|
156
|
-
this.engine.queryPosition(this.symbol, {
|
|
157
|
-
onPosition: (position) => {
|
|
158
|
-
if (!position || !this.lastTick) {
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const todayLong =
|
|
163
|
-
position.today.long.position - position.today.long.frozen;
|
|
164
|
-
|
|
165
|
-
if (todayLong > 0) {
|
|
166
|
-
this.engine.placeOrder(
|
|
167
|
-
this,
|
|
168
|
-
this.symbol,
|
|
169
|
-
"close-today",
|
|
170
|
-
"short",
|
|
171
|
-
todayLong,
|
|
172
|
-
this.lastTick.orderBook.bids.price[0],
|
|
173
|
-
"limit",
|
|
174
|
-
{
|
|
175
|
-
onPlaceOrderSent: (receiptId) => {
|
|
176
|
-
console.log("Close Place Order Receipt Id", receiptId);
|
|
177
|
-
},
|
|
178
154
|
|
|
179
|
-
|
|
180
|
-
|
|
155
|
+
if (order.status === "filled") {
|
|
156
|
+
setTimeout(() => {
|
|
157
|
+
this.engine.queryPosition(this.symbol, {
|
|
158
|
+
onPosition: (position) => {
|
|
159
|
+
if (!position || !this.lastTick) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const todayLong =
|
|
164
|
+
position.today.long.position - position.today.long.frozen;
|
|
165
|
+
|
|
166
|
+
if (todayLong > 0) {
|
|
167
|
+
if (this.lastBar) {
|
|
168
|
+
console.log(this.lastBar);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
this.engine.sellClose(
|
|
172
|
+
this,
|
|
173
|
+
this.symbol,
|
|
174
|
+
todayLong,
|
|
175
|
+
this.lastTick.orderBook.bids.price[0],
|
|
176
|
+
true,
|
|
177
|
+
{
|
|
178
|
+
onPlaceOrderSent: (receiptId) => {
|
|
179
|
+
console.log("Close Place Order Receipt Id", receiptId);
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
onPlaceOrderError: (reason) => {
|
|
183
|
+
console.error("Close Place Order Error", reason);
|
|
184
|
+
},
|
|
181
185
|
},
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
}
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
}, 30 * 1000);
|
|
191
|
+
}
|
|
188
192
|
}
|
|
189
193
|
|
|
190
194
|
onCancel(order: hft.OrderData) {
|
|
@@ -195,14 +199,18 @@ class Strategy implements hft.IStrategy, hft.ITickReceiver {
|
|
|
195
199
|
console.log("Reject Order", order);
|
|
196
200
|
}
|
|
197
201
|
|
|
198
|
-
onTick(tick: hft.TickData) {
|
|
199
|
-
//const tape = hft.calcTapeData(tick, this.lastTick);
|
|
200
|
-
|
|
202
|
+
onTick(tick: hft.TickData, tape: hft.TapeData) {
|
|
201
203
|
//console.log(tick);
|
|
202
204
|
//console.log(tape);
|
|
203
205
|
|
|
204
206
|
this.lastTick = tick;
|
|
205
207
|
}
|
|
208
|
+
|
|
209
|
+
onBar(bar: hft.BarData) {
|
|
210
|
+
//console.log(bar)
|
|
211
|
+
|
|
212
|
+
this.lastBar = bar;
|
|
213
|
+
}
|
|
206
214
|
}
|
|
207
215
|
|
|
208
216
|
const trader = hft.createTrader(
|
package/src/index.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
export * from "./typedef.js";
|
|
13
13
|
export * from "./interfaces.js";
|
|
14
14
|
export * from "./broker.js";
|
|
15
|
-
export * from "./tape.js";
|
|
16
15
|
export * from "./trader.js";
|
|
17
16
|
export * from "./market.js";
|
|
17
|
+
export * from "./utils.js";
|
|
18
18
|
export { CTPUserInfo } from "./provider.js";
|