UNPKG

hft-js

Version:

High-Frequency Trading in Node.js

200 lines 6.84 kB
/* * 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