hft-js
Version:
High-Frequency Trading in Node.js
200 lines • 6.84 kB
JavaScript
/*
* broker.ts
*
* Copyright (c) 2025 Xiongfei Shi
*
* Author: Xiongfei Shi <xiongfei.shi(a)icloud.com>
* License: Apache-2.0
*
* https://github.com/shixiongfei/hft.js
*/
import { BarGenerator, createBarGenerator } from "./bar.js";
export class Broker {
trader;
market;
traderLifecycle;
marketLifecycle;
strategies = [];
placeOrderRiskManagers = [];
cancelOrderRiskManagers = [];
generators;
constructor(trader, market, errorReceiver) {
this.trader = trader;
this.market = market;
this.generators = new Map();
this.marketLifecycle = {
onOpen: () => {
const recorder = this.market.getRecorder();
if (recorder && recorder.isRecorderReady()) {
this.trader.queryInstruments({
onInstruments: (instruments) => {
if (instruments) {
recorder.startRecorder(instruments);
}
},
});
}
this.strategies.forEach((strategy) => strategy.onInit());
},
onClose: () => {
this.strategies.forEach((strategy) => strategy.onDestroy());
const recorder = this.market.getRecorder();
if (recorder) {
recorder.stopRecorder();
}
},
onError: (error, message) => {
if (errorReceiver) {
errorReceiver.onError(error, message);
}
},
};
this.traderLifecycle = {
onOpen: () => {
this.market.open(this.marketLifecycle);
},
onClose: () => {
this.market.close(this.marketLifecycle);
},
onError: (error, message) => {
if (errorReceiver) {
errorReceiver.onError(error, message);
}
},
};
}
start() {
return this.trader.open(this.traderLifecycle);
}
stop() {
return this.trader.close(this.traderLifecycle);
}
addStrategy(strategy) {
if (!this.strategies.includes(strategy)) {
this.strategies.push(strategy);
this.trader.addOrderReceiver(strategy);
}
}
removeStrategy(strategy) {
const index = this.strategies.indexOf(strategy);
if (index < 0) {
return;
}
this.strategies.splice(index, 1);
this.trader.removeOrderReceiver(strategy);
}
addPlaceOrderRiskManager(riskMgr) {
if (!this.placeOrderRiskManagers.includes(riskMgr)) {
this.placeOrderRiskManagers.push(riskMgr);
}
}
addCancelOrderRiskManager(riskMgr) {
if (!this.cancelOrderRiskManagers.includes(riskMgr)) {
this.cancelOrderRiskManagers.push(riskMgr);
}
}
subscribe(symbols, receiver) {
return this.market.subscribe(symbols, receiver);
}
unsubscribe(symbols, receiver) {
return this.market.unsubscribe(symbols, receiver);
}
subscribeBar(symbols, receiver) {
symbols.forEach((symbol) => {
let generator = this.generators.get(symbol);
if (!generator) {
generator = createBarGenerator(symbol);
this.generators.set(symbol, generator);
this.subscribe([symbol], generator);
}
generator.addReceiver(receiver);
});
}
unsubscribeBar(symbols, receiver) {
symbols.forEach((symbol) => {
const generator = this.generators.get(symbol);
if (!generator) {
return;
}
generator.removeReceiver(receiver);
if (generator.receiverCount === 0) {
this.unsubscribe([symbol], generator);
this.generators.delete(symbol);
}
});
}
placeOrder(strategy, symbol, offset, side, volume, price, flag, receiver) {
for (const placeOrderRiskManager of this.placeOrderRiskManagers) {
const result = placeOrderRiskManager.onPlaceOrder(symbol, offset, side, volume, price, flag);
if (typeof result === "boolean") {
if (!result) {
strategy.onRisk("place-order-risk");
receiver.onPlaceOrderError("Risk Rejected");
return;
}
}
else {
strategy.onRisk("place-order-risk", result);
receiver.onPlaceOrderError("Risk Rejected");
return;
}
}
return this.trader.placeOrder(symbol, offset, side, volume, price, flag, receiver);
}
cancelOrder(strategy, order, receiver) {
for (const cancelOrderRiskManager of this.cancelOrderRiskManagers) {
const result = cancelOrderRiskManager.onCancelOrder(order);
if (typeof result === "boolean") {
if (!result) {
strategy.onRisk("cancel-order-risk");
receiver.onCancelOrderError("Risk Rejected");
return;
}
}
else {
strategy.onRisk("cancel-order-risk", result);
receiver.onCancelOrderError("Risk Rejected");
return;
}
}
return this.trader.cancelOrder(order, receiver);
}
getTradingDay() {
return this.trader.getTradingDay();
}
getOrderStatistics() {
return this.trader.getOrderStatistics();
}
getOrderStatistic(symbol) {
return this.trader.getOrderStatistic(symbol);
}
queryCommissionRate(symbol, receiver) {
return this.trader.queryCommissionRate(symbol, receiver);
}
queryMarginRate(symbol, receiver) {
return this.trader.queryMarginRate(symbol, receiver);
}
queryInstrument(symbol, receiver) {
return this.trader.queryInstrument(symbol, receiver);
}
queryPosition(symbol, receiver) {
return this.trader.queryPosition(symbol, receiver);
}
queryInstruments(receiver) {
return this.trader.queryInstruments(receiver);
}
queryTradingAccounts(receiver) {
return this.trader.queryTradingAccounts(receiver);
}
queryPositions(receiver) {
return this.trader.queryPositions(receiver);
}
queryPositionDetails(receiver) {
return this.trader.queryPositionDetails(receiver);
}
queryOrders(receiver) {
return this.trader.queryOrders(receiver);
}
}
export const createBroker = (trader, market, errorReceiver) => new Broker(trader, market, errorReceiver);
//# sourceMappingURL=broker.js.map