From f186803fe041de9b6ce73737963c1f93cc52feff Mon Sep 17 00:00:00 2001 From: Ronan-Yann Lorin Date: Sun, 21 Jul 2024 20:48:02 +0200 Subject: [PATCH] Unit tests updated --- src/common/errorCode.ts | 4 + .../subscription-registry.test.ts | 2 +- src/tests/unit/api/historical-data.test.ts | 84 +++++++++---------- .../api/order/placeConditionalOrder.test.ts | 22 ++++- src/tests/unit/api/order/placeOrder.test.ts | 7 +- .../unit/api/{api.test.ts => pnl.test.ts} | 40 +++++++-- src/tests/unit/api/positions.test.ts | 52 ++++++------ src/tests/unit/sample-data/contracts.ts | 4 +- 8 files changed, 125 insertions(+), 90 deletions(-) rename src/tests/unit/api/{api.test.ts => pnl.test.ts} (75%) diff --git a/src/common/errorCode.ts b/src/common/errorCode.ts index 510ef201..8e771a1f 100644 --- a/src/common/errorCode.ts +++ b/src/common/errorCode.ts @@ -18,6 +18,7 @@ export enum ErrorCode { /** No trading permissions. */ NO_TRADING_PERMISSIONS = 460, + /** * Couldn't connect to TWS. * @@ -282,6 +283,9 @@ export enum ErrorCode { /** FA Profile is not supported anymore, use FA Group instead */ FA_PROFILE_NOT_SUPPORTED = 585, + /** Invalid position trade derived value */ + INVALID_POSITION_TRADE_DERIVATED_VALUE = 2150, + /** Part of requested market data is not subscribed. */ PART_OF_REQUESTED_DATA_NOT_SUBSCRIBED = 10090, diff --git a/src/tests/unit/api-next-live/subscription-registry.test.ts b/src/tests/unit/api-next-live/subscription-registry.test.ts index f9225f54..5b0f2292 100644 --- a/src/tests/unit/api-next-live/subscription-registry.test.ts +++ b/src/tests/unit/api-next-live/subscription-registry.test.ts @@ -45,7 +45,7 @@ describe("Subscription registry Tests", () => { // Two active subscriptions for the same Event issue #193 subscription$ = api.getOpenOrders().subscribe({ next: (data) => { - console.log(data); + // console.log(data); }, complete: () => { console.log("getOpenOrders completed."); diff --git a/src/tests/unit/api/historical-data.test.ts b/src/tests/unit/api/historical-data.test.ts index a83a3d19..b62cbb84 100644 --- a/src/tests/unit/api/historical-data.test.ts +++ b/src/tests/unit/api/historical-data.test.ts @@ -113,7 +113,7 @@ describe("IBApi Historical data Tests", () => { ib.reqHistoricalData( refId, contract, - "20240508-17:00:00", + "20240719-17:00:00", "30 S", BarSizeSetting.SECONDS_FIFTEEN, WhatToShow.BID_ASK, @@ -121,52 +121,44 @@ describe("IBApi Historical data Tests", () => { 2, false, ); - }) - .on( - EventName.historicalData, - ( - reqId: number, - time: string, - open: number, - high: number, - low: number, - close: number, - volume: number, - count: number | undefined, - WAP: number, - ) => { - // console.log( - // counter, - // time, - // open, - // high, - // low, - // close, - // volume, - // count, - // WAP, - // ); - expect(reqId).toEqual(refId); - if (time.startsWith("finished")) { - expect(counter).toEqual(2); - done(); - } else if (counter++ == 1) { - expect(time).toEqual("1715187585"); - expect(open).toEqual(25.35); - expect(high).toEqual(25.35); - expect(low).toEqual(25.35); - expect(close).toEqual(25.35); - expect(volume).toEqual(-1); - expect(count).toEqual(-1); - expect(WAP).toEqual(-1); - } - }, - ) - .on(EventName.error, (err, code, reqId) => { - if (reqId == refId) done(`[${reqId}] ${err.message} (#${code})`); - }); + }).on( + EventName.historicalData, + ( + reqId: number, + time: string, + open: number, + high: number, + low: number, + close: number, + volume: number, + count: number | undefined, + WAP: number, + ) => { + // console.log(counter, time, open, high, low, close, volume, count, WAP); + expect(reqId).toEqual(refId); + if (time.startsWith("finished")) { + expect(counter).toEqual(2); + done(); + } else if (counter++ == 1) { + expect(time).toEqual("1721408385"); + expect(open).toEqual(11.95); + expect(high).toEqual(11.95); + expect(low).toEqual(11.9); + expect(close).toEqual(11.95); + expect(volume).toEqual(-1); + expect(count).toEqual(-1); + expect(WAP).toEqual(-1); + } + }, + ); - ib.connect(); + ib.on(EventName.disconnected, () => done()) + .on(EventName.info, (msg, code) => console.info("INFO", code, msg)) + .on(EventName.error, (err, code, reqId) => { + if (reqId > 0) done(`[${reqId}] ${err.message} (#${code})`); + else console.error("ERROR", err.message, code, reqId); + }) + .connect(); }); it("Weekly market data", (done) => { diff --git a/src/tests/unit/api/order/placeConditionalOrder.test.ts b/src/tests/unit/api/order/placeConditionalOrder.test.ts index e95f32be..2c56f05f 100644 --- a/src/tests/unit/api/order/placeConditionalOrder.test.ts +++ b/src/tests/unit/api/order/placeConditionalOrder.test.ts @@ -51,7 +51,7 @@ const sample_percent_condition: OrderCondition = new PercentChangeCondition( ConjunctionConnection.OR, ); const sample_time_condition: OrderCondition = new TimeCondition( - "20250101-12:00:00", + "20250102-17:00:00", true, ConjunctionConnection.OR, ); @@ -369,7 +369,25 @@ describe("Place Conditional Orders", () => { }, ); - ib.connect(); + ib.connect() + .on(EventName.error, (error, code, reqId) => { + if (reqId > 0) { + const msg = `[${reqId}] ${error.message} (Error #${code})`; + if ( + error.message.includes("Warning:") || + error.message.includes("Order Message:") + ) { + logger.warn(msg); + } else { + ib.disconnect(); + done(msg); + } + } else { + console.error("ERROR", error.message, code, reqId); + } + }) + .on(EventName.info, (msg, code) => console.info("INFO", code, msg)) + .on(EventName.disconnected, () => done()); }); test("placeOrder with VolumeCondition", (done) => { diff --git a/src/tests/unit/api/order/placeOrder.test.ts b/src/tests/unit/api/order/placeOrder.test.ts index 874421e4..33d3b4a1 100644 --- a/src/tests/unit/api/order/placeOrder.test.ts +++ b/src/tests/unit/api/order/placeOrder.test.ts @@ -16,6 +16,7 @@ import logger from "../../../../common/logger"; import { sample_crypto, sample_etf, + sample_future, sample_option, sample_stock, } from "../../sample-data/contracts"; @@ -284,11 +285,7 @@ describe("Place Orders", () => { test("Issue #203", (done) => { let refId: number; - const refContract: Contract = { - conId: 708846212, - exchange: "CME", - symbol: "MES", - }; + const refContract: Contract = sample_future; const refOrder: Order = { action: OrderAction.BUY, lmtPrice: 1, diff --git a/src/tests/unit/api/api.test.ts b/src/tests/unit/api/pnl.test.ts similarity index 75% rename from src/tests/unit/api/api.test.ts rename to src/tests/unit/api/pnl.test.ts index bda829aa..d9e1f0fe 100644 --- a/src/tests/unit/api/api.test.ts +++ b/src/tests/unit/api/pnl.test.ts @@ -43,6 +43,7 @@ describe("IBApi Tests", () => { } if (_conId === undefined && pos) { _conId = contract.conId; + // console.info(JSON.stringify(contract)); } expect(account).toBeTruthy(); expect(contract).toBeTruthy(); @@ -90,19 +91,31 @@ describe("IBApi Tests", () => { const refId = 44; let received = false; - ib.on( + ib.once(EventName.connected, () => { + console.log("reqPnLSingle", refId); + ib.reqPnLSingle(refId, _account, "", _conId); + }).on( EventName.pnlSingle, ( reqId: number, pos: number, - dailyPnL: number, + _dailyPnL: number, unrealizedPnL: number, - realizedPnL: number, + _realizedPnL: number, value: number, ) => { + console.log( + "pnlSingle", + reqId, + pos, + _dailyPnL, + unrealizedPnL, + _realizedPnL, + value, + ); expect(reqId).toEqual(refId); expect(pos).toBeTruthy(); - expect(dailyPnL).toBeTruthy(); + // expect(dailyPnL).toBeTruthy(); We may have no daily PnL (on week-ends) expect(unrealizedPnL).toBeTruthy(); // expect(realizedPnL).toBeTruthy(); We may have no realized PnL today expect(value).toBeTruthy(); @@ -113,10 +126,21 @@ describe("IBApi Tests", () => { } received = true; }, - ).on(EventName.error, (err, code, reqId) => { - if (reqId == refId) done(`[${reqId}] ${err.message} (#${code})`); - }); + ); - ib.connect().reqPnLSingle(refId, _account, null, _conId); + ib.on(EventName.disconnected, () => done()) + .on(EventName.info, (msg, code) => console.info("INFO", code, msg)) + .on(EventName.error, (err, code, reqId) => { + const msg = `[${reqId}] ${err.message} (#${code})`; + if ( + reqId > 0 && + code != ErrorCode.INVALID_POSITION_TRADE_DERIVATED_VALUE + ) { + done(msg); + } else { + console.error("ERROR", msg); + } + }) + .connect(); }); }); diff --git a/src/tests/unit/api/positions.test.ts b/src/tests/unit/api/positions.test.ts index ecaef363..65198fbf 100644 --- a/src/tests/unit/api/positions.test.ts +++ b/src/tests/unit/api/positions.test.ts @@ -35,9 +35,6 @@ describe("IBApi Tests", () => { return res; } - let _account: string; // maintain account name for further tests - let _conId: number; // maintain for conId for further tests - it("Test reqPositions / cancelPositions", (done) => { let positionsCount = 0; @@ -47,12 +44,6 @@ describe("IBApi Tests", () => { .on( EventName.position, (account: string, contract: Contract, pos: number, avgCost: number) => { - if (_account === undefined) { - _account = account; - } - if (_conId === undefined && pos) { - _conId = contract.conId; - } expect(account).toBeTruthy(); expect(contract).toBeTruthy(); // expect(pos).toBeTruthy(); pos can be 0 when it has been closed today @@ -74,6 +65,7 @@ describe("IBApi Tests", () => { it("Test reqPositionsMulti / cancelPositionsMulti", (done) => { let refId = 45; + let count = 0; ib.once(EventName.connected, () => { ib.reqPositionsMulti(refId, "", ""); @@ -81,33 +73,41 @@ describe("IBApi Tests", () => { .on( EventName.positionMulti, (reqId, account, modelCode, contract, pos, avgCost) => { - console.log( - "positionMulti", - reqId, - account, - modelCode, - JSON.stringify(contract), - pos, - avgCost, - ); + // console.log( + // "positionMulti", + // reqId, + // account, + // modelCode, + // JSON.stringify(contract), + // pos, + // avgCost, + // ); + expect(account).toBeTruthy(); + expect(contract).toBeTruthy(); }, ) .on(EventName.positionMultiEnd, (reqId) => { - console.log("positionMultiEnd", reqId); + count += 1; + // console.log("positionMultiEnd", reqId); refId = reqId + 1; ib.cancelPositionsMulti(refId); - console.log("cancelPositionsMulti sent", refId); - refId = refId + 1; - ib.reqPositionsMulti(refId, "", ""); - console.log("reqPositionsMulti sent", refId); + // console.log("cancelPositionsMulti sent", refId); + if (count < 3) { + // console.log("count", count); + refId = refId + 1; + ib.reqPositionsMulti(refId, "", ""); + // console.log("reqPositionsMulti sent", refId); + } else { + done(); + } }); - ib.connect() + ib.on(EventName.disconnected, () => done()) + .on(EventName.info, (msg, code) => console.info("INFO", code, msg)) .on(EventName.error, (err, code, reqId) => { if (reqId > 0) done(`[${reqId}] ${err.message} (#${code})`); else console.error("ERROR", err.message, code, reqId); }) - .on(EventName.info, (msg, code) => console.info("INFO", code, msg)) - .on(EventName.disconnected, () => done()); + .connect(); }); }); diff --git a/src/tests/unit/sample-data/contracts.ts b/src/tests/unit/sample-data/contracts.ts index 6ab6000b..0c6a8f11 100644 --- a/src/tests/unit/sample-data/contracts.ts +++ b/src/tests/unit/sample-data/contracts.ts @@ -23,8 +23,8 @@ export const sample_crypto: Contract = new Crypto("ETH"); // This one will need to be updated sometimes export const sample_future: Contract = new Future( "ES", - "ESM4", - "202406", + "ESH5", + "202503", "CME", 50, );