diff --git a/src/api/api.ts b/src/api/api.ts index 1c5d4257..8f4feff3 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -77,7 +77,8 @@ export interface IBApiCreationOptions { } /** Maximum supported version. */ -export const MAX_SUPPORTED_SERVER_VERSION = MIN_SERVER_VER.MANUAL_ORDER_TIME; +export const MAX_SUPPORTED_SERVER_VERSION = + MIN_SERVER_VER.WSH_EVENT_DATA_FILTERS; /** Minimum supported version. */ export const MIN_SERVER_VER_SUPPORTED = 38; diff --git a/src/api/order/enum/orderType.ts b/src/api/order/enum/orderType.ts index cd5805a5..a0f56c6a 100644 --- a/src/api/order/enum/orderType.ts +++ b/src/api/order/enum/orderType.ts @@ -24,6 +24,7 @@ export enum OrderType { PEG_MKT = "PEG MKT", PEG_PRIM = "PEG PRIM", PEG_STK = "PEG STK", + PEG_BEST = "PEG BEST", REL_PLUS_LMT = "REL + LMT", REL_PLUS_MKT = "REL + MKT", SNAP_MID = "SNAP MID", diff --git a/src/api/order/order.ts b/src/api/order/order.ts index 39afd8eb..90d68514 100644 --- a/src/api/order/order.ts +++ b/src/api/order/order.ts @@ -748,7 +748,7 @@ export interface Order { /** Defines the minimum size to compete. For IBKRATS orders. */ minCompeteSize?: number; - /** Dpecifies the offset Off The Midpoint that will be applied to the order. For IBKRATS orders. */ + /** Specifies the offset Off The Midpoint that will be applied to the order. For IBKRATS orders. */ competeAgainstBestOffset?: number; /** This offset is applied when the spread is an even number of cents wide. This offset must be in whole-penny increments or zero. For IBKRATS orders. */ @@ -758,4 +758,6 @@ export interface Order { midOffsetAtHalf?: number; } +export const COMPETE_AGAINST_BEST_OFFSET_UP_TO_MID = Infinity; + export default Order; diff --git a/src/core/io/decoder.ts b/src/core/io/decoder.ts index 86ca8e8a..6ec94531 100644 --- a/src/core/io/decoder.ts +++ b/src/core/io/decoder.ts @@ -813,6 +813,7 @@ export class Decoder { orderDecoder.readDuration(); orderDecoder.readPostToAts(); orderDecoder.readAutoCancelParent(MIN_SERVER_VER.AUTO_CANCEL_PARENT); + orderDecoder.readPegBestPegMidOrderAttributes(); this.emit(EventName.openOrder, order.orderId, contract, order, orderState); } @@ -3960,4 +3961,15 @@ class OrderDecoder { this.order.postToAts = this.decoder.readIntMax(); } } + + readPegBestPegMidOrderAttributes() { + if (this.serverVersion >= MIN_SERVER_VER.PEGBEST_PEGMID_OFFSETS) { + this.order.minTradeQty = this.decoder.readIntMax(); + this.order.minCompeteSize = this.decoder.readIntMax(); + this.order.competeAgainstBestOffset = + this.decoder.readDoubleOrUndefined(); + this.order.midOffsetAtWhole = this.decoder.readDoubleOrUndefined(); + this.order.midOffsetAtHalf = this.decoder.readDoubleOrUndefined(); + } + } } diff --git a/src/core/io/encoder.ts b/src/core/io/encoder.ts index 2afcc276..3a022814 100644 --- a/src/core/io/encoder.ts +++ b/src/core/io/encoder.ts @@ -18,7 +18,10 @@ import TimeCondition from "../../api/order/condition/time-condition"; import VolumeCondition from "../../api/order/condition/volume-condition"; import { OrderConditionType } from "../../api/order/enum/order-condition-type"; import { OrderType } from "../../api/order/enum/orderType"; -import { Order } from "../../api/order/order"; +import { + COMPETE_AGAINST_BEST_OFFSET_UP_TO_MID, + Order, +} from "../../api/order/order"; import { ExecutionFilter } from "../../api/report/executionFilter"; import { ErrorCode } from "../../common/errorCode"; @@ -1089,6 +1092,21 @@ function tagValuesToTokens(tagValues: TagValue[]): unknown[] { ); } + if ( + this.serverVersion < MIN_SERVER_VER.PEGBEST_PEGMID_OFFSETS && + (order.minTradeQty !== undefined || + order.minCompeteSize !== undefined || + order.competeAgainstBestOffset !== undefined || + order.midOffsetAtWhole !== undefined || + order.midOffsetAtHalf !== undefined) + ) { + return this.emitError( + "It does not support PEG BEST / PEG MID order parameters: minTradeQty, minCompeteSize, competeAgainstBestOffset, midOffsetAtWhole and midOffsetAtHalf", + ErrorCode.UPDATE_TWS, + id, + ); + } + const version = this.serverVersion < MIN_SERVER_VER.NOT_HELD ? 27 : 45; // send place order msg @@ -1616,6 +1634,24 @@ function tagValuesToTokens(tagValues: TagValue[]): unknown[] { if (this.serverVersion >= MIN_SERVER_VER.MANUAL_ORDER_TIME) tokens.push(order.manualOrderTime); + if (this.serverVersion >= MIN_SERVER_VER.PEGBEST_PEGMID_OFFSETS) { + let sendMidOffsets = false; + if (contract.exchange == "IBKRATS") tokens.push(order.minTradeQty); + if (order.orderType == OrderType.PEG_BEST) { + tokens.push(order.minCompeteSize); + tokens.push(order.competeAgainstBestOffset); + if ( + order.competeAgainstBestOffset == + COMPETE_AGAINST_BEST_OFFSET_UP_TO_MID + ) + sendMidOffsets = true; + } else if (order.orderType == OrderType.PEG_MID) sendMidOffsets = true; + if (sendMidOffsets) { + tokens.push(order.midOffsetAtWhole); + tokens.push(order.midOffsetAtHalf); + } + } + this.sendMsg(tokens); } diff --git a/src/tests/unit/api/order/cancelOrder.test.ts b/src/tests/unit/api/order/cancelOrder.test.ts index e9c489d5..1341f15b 100644 --- a/src/tests/unit/api/order/cancelOrder.test.ts +++ b/src/tests/unit/api/order/cancelOrder.test.ts @@ -11,13 +11,9 @@ import { OrderType, SecType, } from "../../../.."; -// import OptionType from "../../../../api/data/enum/option-type"; import configuration from "../../../../common/configuration"; import logger from "../../../../common/logger"; -const awaitTimeout = (delay: number): Promise => - new Promise((resolve): NodeJS.Timeout => setTimeout(resolve, delay * 1000)); - describe("CancelOrder", () => { jest.setTimeout(20 * 1000); diff --git a/src/tests/unit/api/order/placeOrder.test.ts b/src/tests/unit/api/order/placeOrder.test.ts index 7e451b90..23260758 100644 --- a/src/tests/unit/api/order/placeOrder.test.ts +++ b/src/tests/unit/api/order/placeOrder.test.ts @@ -15,13 +15,9 @@ import { SecType, TriggerMethod, } from "../../../.."; -// import OptionType from "../../../../api/data/enum/option-type"; import configuration from "../../../../common/configuration"; import logger from "../../../../common/logger"; -const awaitTimeout = (delay: number): Promise => - new Promise((resolve): NodeJS.Timeout => setTimeout(resolve, delay * 1000)); - describe("Place Orders", () => { jest.setTimeout(20 * 1000);